1 /*
2 *******************************************************************************
3 * Copyright (C) 1997-2015, International Business Machines Corporation and
4 * others. All Rights Reserved.
5 *******************************************************************************
6 *
7 * File TIMEZONE.CPP
8 *
9 * Modification History:
10 *
11 *   Date        Name        Description
12 *   12/05/96    clhuang     Creation.
13 *   04/21/97    aliu        General clean-up and bug fixing.
14 *   05/08/97    aliu        Fixed Hashtable code per code review.
15 *   07/09/97    helena      Changed createInstance to createDefault.
16 *   07/29/97    aliu        Updated with all-new list of 96 UNIX-derived
17 *                           TimeZones.  Changed mechanism to load from static
18 *                           array rather than resource bundle.
19 *   07/07/1998  srl         Bugfixes from the Java side: UTC GMT CAT NST
20 *                           Added getDisplayName API
21 *                           going to add custom parsing.
22 *
23 *                           ISSUES:
24 *                               - should getDisplayName cache something?
25 *                               - should custom time zones be cached? [probably]
26 *  08/10/98     stephen     Brought getDisplayName() API in-line w/ conventions
27 *  08/19/98     stephen     Changed createTimeZone() to never return 0
28 *  09/02/98     stephen     Added getOffset(monthLen) and hasSameRules()
29 *  09/15/98     stephen     Added getStaticClassID()
30 *  02/22/99     stephen     Removed character literals for EBCDIC safety
31 *  05/04/99     stephen     Changed initDefault() for Mutex issues
32 *  07/12/99     helena      HPUX 11 CC Port.
33 *  12/03/99     aliu        Moved data out of static table into icudata.dll.
34 *                           Substantial rewrite of zone lookup, default zone, and
35 *                           available IDs code.  Misc. cleanup.
36 *********************************************************************************/
37 
38 #include "utypeinfo.h"  // for 'typeid' to work
39 
40 #include "unicode/utypes.h"
41 #include "unicode/ustring.h"
42 #include "uassert.h"
43 #include "ustr_imp.h"
44 
45 #ifdef U_DEBUG_TZ
46 # include <stdio.h>
47 # include "uresimp.h" // for debugging
48 
debug_tz_loc(const char * f,int32_t l)49 static void debug_tz_loc(const char *f, int32_t l)
50 {
51   fprintf(stderr, "%s:%d: ", f, l);
52 }
53 
debug_tz_msg(const char * pat,...)54 static void debug_tz_msg(const char *pat, ...)
55 {
56   va_list ap;
57   va_start(ap, pat);
58   vfprintf(stderr, pat, ap);
59   fflush(stderr);
60 }
61 static char gStrBuf[256];
62 #define U_DEBUG_TZ_STR(x) u_austrncpy(gStrBuf,x,sizeof(gStrBuf)-1)
63 // must use double parens, i.e.:  U_DEBUG_TZ_MSG(("four is: %d",4));
64 #define U_DEBUG_TZ_MSG(x) {debug_tz_loc(__FILE__,__LINE__);debug_tz_msg x;}
65 #else
66 #define U_DEBUG_TZ_MSG(x)
67 #endif
68 
69 #if !UCONFIG_NO_FORMATTING
70 
71 #include "unicode/simpletz.h"
72 #include "unicode/calendar.h"
73 #include "unicode/gregocal.h"
74 #include "unicode/ures.h"
75 #include "unicode/tzfmt.h"
76 #include "unicode/numfmt.h"
77 #include "gregoimp.h"
78 #include "uresimp.h" // struct UResourceBundle
79 #include "olsontz.h"
80 #include "mutex.h"
81 #include "unicode/udata.h"
82 #include "ucln_in.h"
83 #include "cstring.h"
84 #include "cmemory.h"
85 #include "unicode/strenum.h"
86 #include "uassert.h"
87 #include "zonemeta.h"
88 
89 #define kZONEINFO "zoneinfo64"
90 #define kREGIONS  "Regions"
91 #define kZONES    "Zones"
92 #define kRULES    "Rules"
93 #define kNAMES    "Names"
94 #define kTZVERSION  "TZVersion"
95 #define kLINKS    "links"
96 #define kMAX_CUSTOM_HOUR    23
97 #define kMAX_CUSTOM_MIN     59
98 #define kMAX_CUSTOM_SEC     59
99 #define MINUS 0x002D
100 #define PLUS 0x002B
101 #define ZERO_DIGIT 0x0030
102 #define COLON 0x003A
103 
104 // Static data and constants
105 
106 static const UChar         WORLD[] = {0x30, 0x30, 0x31, 0x00}; /* "001" */
107 
108 static const UChar         GMT_ID[] = {0x47, 0x4D, 0x54, 0x00}; /* "GMT" */
109 static const UChar         UNKNOWN_ZONE_ID[] = {0x45, 0x74, 0x63, 0x2F, 0x55, 0x6E, 0x6B, 0x6E, 0x6F, 0x77, 0x6E, 0x00}; /* "Etc/Unknown" */
110 static const int32_t       GMT_ID_LENGTH = 3;
111 static const int32_t       UNKNOWN_ZONE_ID_LENGTH = 11;
112 
113 static icu::TimeZone* DEFAULT_ZONE = NULL;
114 static icu::UInitOnce gDefaultZoneInitOnce = U_INITONCE_INITIALIZER;
115 
116 static icu::TimeZone* _GMT = NULL;
117 static icu::TimeZone* _UNKNOWN_ZONE = NULL;
118 static icu::UInitOnce gStaticZonesInitOnce = U_INITONCE_INITIALIZER;
119 
120 static char TZDATA_VERSION[16];
121 static icu::UInitOnce gTZDataVersionInitOnce = U_INITONCE_INITIALIZER;
122 
123 static int32_t* MAP_SYSTEM_ZONES = NULL;
124 static int32_t* MAP_CANONICAL_SYSTEM_ZONES = NULL;
125 static int32_t* MAP_CANONICAL_SYSTEM_LOCATION_ZONES = NULL;
126 
127 static int32_t LEN_SYSTEM_ZONES = 0;
128 static int32_t LEN_CANONICAL_SYSTEM_ZONES = 0;
129 static int32_t LEN_CANONICAL_SYSTEM_LOCATION_ZONES = 0;
130 
131 static icu::UInitOnce gSystemZonesInitOnce = U_INITONCE_INITIALIZER;
132 static icu::UInitOnce gCanonicalZonesInitOnce = U_INITONCE_INITIALIZER;
133 static icu::UInitOnce gCanonicalLocationZonesInitOnce = U_INITONCE_INITIALIZER;
134 
135 U_CDECL_BEGIN
timeZone_cleanup(void)136 static UBool U_CALLCONV timeZone_cleanup(void)
137 {
138     U_NAMESPACE_USE
139     delete DEFAULT_ZONE;
140     DEFAULT_ZONE = NULL;
141     gDefaultZoneInitOnce.reset();
142 
143     delete _GMT;
144     _GMT = NULL;
145     delete _UNKNOWN_ZONE;
146     _UNKNOWN_ZONE = NULL;
147     gStaticZonesInitOnce.reset();
148 
149     uprv_memset(TZDATA_VERSION, 0, sizeof(TZDATA_VERSION));
150     gTZDataVersionInitOnce.reset();
151 
152     LEN_SYSTEM_ZONES = 0;
153     uprv_free(MAP_SYSTEM_ZONES);
154     MAP_SYSTEM_ZONES = 0;
155     gSystemZonesInitOnce.reset();
156 
157     LEN_CANONICAL_SYSTEM_ZONES = 0;
158     uprv_free(MAP_CANONICAL_SYSTEM_ZONES);
159     MAP_CANONICAL_SYSTEM_ZONES = 0;
160     gCanonicalZonesInitOnce.reset();
161 
162     LEN_CANONICAL_SYSTEM_LOCATION_ZONES = 0;
163     uprv_free(MAP_CANONICAL_SYSTEM_LOCATION_ZONES);
164     MAP_CANONICAL_SYSTEM_LOCATION_ZONES = 0;
165     gCanonicalLocationZonesInitOnce.reset();
166 
167     return TRUE;
168 }
169 U_CDECL_END
170 
171 U_NAMESPACE_BEGIN
172 
findInStringArray(UResourceBundle * array,const UnicodeString & id,UErrorCode & status)173 static int32_t findInStringArray(UResourceBundle* array, const UnicodeString& id, UErrorCode &status)
174 {
175     UnicodeString copy;
176     const UChar *u;
177     int32_t len;
178 
179     int32_t start = 0;
180     int32_t limit = ures_getSize(array);
181     int32_t mid;
182     int32_t lastMid = INT32_MAX;
183     if(U_FAILURE(status) || (limit < 1)) {
184         return -1;
185     }
186     U_DEBUG_TZ_MSG(("fisa: Looking for %s, between %d and %d\n", U_DEBUG_TZ_STR(UnicodeString(id).getTerminatedBuffer()), start, limit));
187 
188     for (;;) {
189         mid = (int32_t)((start + limit) / 2);
190         if (lastMid == mid) {   /* Have we moved? */
191             break;  /* We haven't moved, and it wasn't found. */
192         }
193         lastMid = mid;
194         u = ures_getStringByIndex(array, mid, &len, &status);
195         if (U_FAILURE(status)) {
196             break;
197         }
198         U_DEBUG_TZ_MSG(("tz: compare to %s, %d .. [%d] .. %d\n", U_DEBUG_TZ_STR(u), start, mid, limit));
199         copy.setTo(TRUE, u, len);
200         int r = id.compare(copy);
201         if(r==0) {
202             U_DEBUG_TZ_MSG(("fisa: found at %d\n", mid));
203             return mid;
204         } else if(r<0) {
205             limit = mid;
206         } else {
207             start = mid;
208         }
209     }
210     U_DEBUG_TZ_MSG(("fisa: not found\n"));
211     return -1;
212 }
213 
214 /**
215  * Fetch a specific zone by name.  Replaces the getByKey call.
216  * @param top Top timezone resource
217  * @param id Time zone ID
218  * @param oldbundle Bundle for reuse (or NULL).   see 'ures_open()'
219  * @return the zone's bundle if found, or undefined if error.  Reuses oldbundle.
220  */
getZoneByName(const UResourceBundle * top,const UnicodeString & id,UResourceBundle * oldbundle,UErrorCode & status)221 static UResourceBundle* getZoneByName(const UResourceBundle* top, const UnicodeString& id, UResourceBundle *oldbundle, UErrorCode& status) {
222     // load the Rules object
223     UResourceBundle *tmp = ures_getByKey(top, kNAMES, NULL, &status);
224 
225     // search for the string
226     int32_t idx = findInStringArray(tmp, id, status);
227 
228     if((idx == -1) && U_SUCCESS(status)) {
229         // not found
230         status = U_MISSING_RESOURCE_ERROR;
231         //ures_close(oldbundle);
232         //oldbundle = NULL;
233     } else {
234         U_DEBUG_TZ_MSG(("gzbn: oldbundle= size %d, type %d, %s\n", ures_getSize(tmp), ures_getType(tmp), u_errorName(status)));
235         tmp = ures_getByKey(top, kZONES, tmp, &status); // get Zones object from top
236         U_DEBUG_TZ_MSG(("gzbn: loaded ZONES, size %d, type %d, path %s %s\n", ures_getSize(tmp), ures_getType(tmp), ures_getPath(tmp), u_errorName(status)));
237         oldbundle = ures_getByIndex(tmp, idx, oldbundle, &status); // get nth Zone object
238         U_DEBUG_TZ_MSG(("gzbn: loaded z#%d, size %d, type %d, path %s, %s\n", idx, ures_getSize(oldbundle), ures_getType(oldbundle), ures_getPath(oldbundle),  u_errorName(status)));
239     }
240     ures_close(tmp);
241     if(U_FAILURE(status)) {
242         //ures_close(oldbundle);
243         return NULL;
244     } else {
245         return oldbundle;
246     }
247 }
248 
249 
loadRule(const UResourceBundle * top,const UnicodeString & ruleid,UResourceBundle * oldbundle,UErrorCode & status)250 UResourceBundle* TimeZone::loadRule(const UResourceBundle* top, const UnicodeString& ruleid, UResourceBundle* oldbundle, UErrorCode& status) {
251     char key[64];
252     ruleid.extract(0, sizeof(key)-1, key, (int32_t)sizeof(key)-1, US_INV);
253     U_DEBUG_TZ_MSG(("loadRule(%s)\n", key));
254     UResourceBundle *r = ures_getByKey(top, kRULES, oldbundle, &status);
255     U_DEBUG_TZ_MSG(("loadRule(%s) -> kRULES [%s]\n", key, u_errorName(status)));
256     r = ures_getByKey(r, key, r, &status);
257     U_DEBUG_TZ_MSG(("loadRule(%s) -> item [%s]\n", key, u_errorName(status)));
258     return r;
259 }
260 
261 /**
262  * Given an ID, open the appropriate resource for the given time zone.
263  * Dereference aliases if necessary.
264  * @param id zone id
265  * @param res resource, which must be ready for use (initialized but not open)
266  * @param ec input-output error code
267  * @return top-level resource bundle
268  */
openOlsonResource(const UnicodeString & id,UResourceBundle & res,UErrorCode & ec)269 static UResourceBundle* openOlsonResource(const UnicodeString& id,
270                                           UResourceBundle& res,
271                                           UErrorCode& ec)
272 {
273 #if U_DEBUG_TZ
274     char buf[128];
275     id.extract(0, sizeof(buf)-1, buf, sizeof(buf), "");
276 #endif
277     UResourceBundle *top = ures_openDirect(0, kZONEINFO, &ec);
278     U_DEBUG_TZ_MSG(("pre: res sz=%d\n", ures_getSize(&res)));
279     /* &res = */ getZoneByName(top, id, &res, ec);
280     // Dereference if this is an alias.  Docs say result should be 1
281     // but it is 0 in 2.8 (?).
282     U_DEBUG_TZ_MSG(("Loading zone '%s' (%s, size %d) - %s\n", buf, ures_getKey((UResourceBundle*)&res), ures_getSize(&res), u_errorName(ec)));
283     if (ures_getType(&res) == URES_INT) {
284         int32_t deref = ures_getInt(&res, &ec) + 0;
285         U_DEBUG_TZ_MSG(("getInt: %s - type is %d\n", u_errorName(ec), ures_getType(&res)));
286         UResourceBundle *ares = ures_getByKey(top, kZONES, NULL, &ec); // dereference Zones section
287         ures_getByIndex(ares, deref, &res, &ec);
288         ures_close(ares);
289         U_DEBUG_TZ_MSG(("alias to #%d (%s) - %s\n", deref, "??", u_errorName(ec)));
290     } else {
291         U_DEBUG_TZ_MSG(("not an alias - size %d\n", ures_getSize(&res)));
292     }
293     U_DEBUG_TZ_MSG(("%s - final status is %s\n", buf, u_errorName(ec)));
294     return top;
295 }
296 
297 // -------------------------------------
298 
299 namespace {
300 
initStaticTimeZones()301 void U_CALLCONV initStaticTimeZones() {
302     // Initialize _GMT independently of other static data; it should
303     // be valid even if we can't load the time zone UDataMemory.
304     ucln_i18n_registerCleanup(UCLN_I18N_TIMEZONE, timeZone_cleanup);
305     _UNKNOWN_ZONE = new SimpleTimeZone(0, UnicodeString(TRUE, UNKNOWN_ZONE_ID, UNKNOWN_ZONE_ID_LENGTH));
306     _GMT = new SimpleTimeZone(0, UnicodeString(TRUE, GMT_ID, GMT_ID_LENGTH));
307 }
308 
309 }  // anonymous namespace
310 
311 const TimeZone& U_EXPORT2
getUnknown()312 TimeZone::getUnknown()
313 {
314     umtx_initOnce(gStaticZonesInitOnce, &initStaticTimeZones);
315     return *_UNKNOWN_ZONE;
316 }
317 
318 const TimeZone* U_EXPORT2
getGMT(void)319 TimeZone::getGMT(void)
320 {
321     umtx_initOnce(gStaticZonesInitOnce, &initStaticTimeZones);
322     return _GMT;
323 }
324 
325 // *****************************************************************************
326 // class TimeZone
327 // *****************************************************************************
328 
UOBJECT_DEFINE_ABSTRACT_RTTI_IMPLEMENTATION(TimeZone)329 UOBJECT_DEFINE_ABSTRACT_RTTI_IMPLEMENTATION(TimeZone)
330 
331 TimeZone::TimeZone()
332     :   UObject(), fID()
333 {
334 }
335 
336 // -------------------------------------
337 
TimeZone(const UnicodeString & id)338 TimeZone::TimeZone(const UnicodeString &id)
339     :   UObject(), fID(id)
340 {
341 }
342 
343 // -------------------------------------
344 
~TimeZone()345 TimeZone::~TimeZone()
346 {
347 }
348 
349 // -------------------------------------
350 
TimeZone(const TimeZone & source)351 TimeZone::TimeZone(const TimeZone &source)
352     :   UObject(source), fID(source.fID)
353 {
354 }
355 
356 // -------------------------------------
357 
358 TimeZone &
operator =(const TimeZone & right)359 TimeZone::operator=(const TimeZone &right)
360 {
361     if (this != &right) fID = right.fID;
362     return *this;
363 }
364 
365 // -------------------------------------
366 
367 UBool
operator ==(const TimeZone & that) const368 TimeZone::operator==(const TimeZone& that) const
369 {
370     return typeid(*this) == typeid(that) &&
371         fID == that.fID;
372 }
373 
374 // -------------------------------------
375 
376 namespace {
377 TimeZone*
createSystemTimeZone(const UnicodeString & id,UErrorCode & ec)378 createSystemTimeZone(const UnicodeString& id, UErrorCode& ec) {
379     if (U_FAILURE(ec)) {
380         return NULL;
381     }
382     TimeZone* z = 0;
383     UResourceBundle res;
384     ures_initStackObject(&res);
385     U_DEBUG_TZ_MSG(("pre-err=%s\n", u_errorName(ec)));
386     UResourceBundle *top = openOlsonResource(id, res, ec);
387     U_DEBUG_TZ_MSG(("post-err=%s\n", u_errorName(ec)));
388     if (U_SUCCESS(ec)) {
389         z = new OlsonTimeZone(top, &res, id, ec);
390         if (z == NULL) {
391           U_DEBUG_TZ_MSG(("cstz: olson time zone failed to initialize - err %s\n", u_errorName(ec)));
392         }
393     }
394     ures_close(&res);
395     ures_close(top);
396     if (U_FAILURE(ec)) {
397         U_DEBUG_TZ_MSG(("cstz: failed to create, err %s\n", u_errorName(ec)));
398         delete z;
399         z = 0;
400     }
401     return z;
402 }
403 
404 /**
405  * Lookup the given name in our system zone table.  If found,
406  * instantiate a new zone of that name and return it.  If not
407  * found, return 0.
408  */
409 TimeZone*
createSystemTimeZone(const UnicodeString & id)410 createSystemTimeZone(const UnicodeString& id) {
411     UErrorCode ec = U_ZERO_ERROR;
412     return createSystemTimeZone(id, ec);
413 }
414 
415 }
416 
417 TimeZone* U_EXPORT2
createTimeZone(const UnicodeString & ID)418 TimeZone::createTimeZone(const UnicodeString& ID)
419 {
420     /* We first try to lookup the zone ID in our system list.  If this
421      * fails, we try to parse it as a custom string GMT[+-]hh:mm.  If
422      * all else fails, we return GMT, which is probably not what the
423      * user wants, but at least is a functioning TimeZone object.
424      *
425      * We cannot return NULL, because that would break compatibility
426      * with the JDK.
427      */
428     TimeZone* result = createSystemTimeZone(ID);
429 
430     if (result == NULL) {
431         U_DEBUG_TZ_MSG(("failed to load system time zone with id - falling to custom"));
432         result = createCustomTimeZone(ID);
433     }
434     if (result == NULL) {
435         U_DEBUG_TZ_MSG(("failed to load time zone with id - falling to Etc/Unknown(GMT)"));
436         const TimeZone& unknown = getUnknown();
437         if (_UNKNOWN_ZONE == NULL) {                   // Cannot test (&unknown == NULL) because the
438           U_DEBUG_TZ_MSG(("failed to getUnknown()"));  // behavior of NULL references is undefined.
439         } else {
440           result = unknown.clone();
441         }
442     }
443     return result;
444 }
445 
446 // -------------------------------------
447 
448 TimeZone* U_EXPORT2
detectHostTimeZone()449 TimeZone::detectHostTimeZone()
450 {
451     // We access system timezone data through TPlatformUtilities,
452     // including tzset(), timezone, and tzname[].
453     int32_t rawOffset = 0;
454     const char *hostID;
455 
456     // First, try to create a system timezone, based
457     // on the string ID in tzname[0].
458 
459     uprv_tzset(); // Initialize tz... system data
460 
461     // Get the timezone ID from the host.  This function should do
462     // any required host-specific remapping; e.g., on Windows this
463     // function maps the Date and Time control panel setting to an
464     // ICU timezone ID.
465     hostID = uprv_tzname(0);
466 
467     // Invert sign because UNIX semantics are backwards
468     rawOffset = uprv_timezone() * -U_MILLIS_PER_SECOND;
469 
470     TimeZone* hostZone = NULL;
471 
472     /* Make sure that the string is NULL terminated to prevent BoundsChecker/Purify warnings. */
473     UnicodeString hostStrID(hostID, -1, US_INV);
474     hostStrID.append((UChar)0);
475     hostStrID.truncate(hostStrID.length()-1);
476     hostZone = createSystemTimeZone(hostStrID);
477 
478 #if U_PLATFORM_USES_ONLY_WIN32_API
479     // hostID points to a heap-allocated location on Windows.
480     uprv_free(const_cast<char *>(hostID));
481 #endif
482 
483     int32_t hostIDLen = hostStrID.length();
484     if (hostZone != NULL && rawOffset != hostZone->getRawOffset()
485         && (3 <= hostIDLen && hostIDLen <= 4))
486     {
487         // Uh oh. This probably wasn't a good id.
488         // It was probably an ambiguous abbreviation
489         delete hostZone;
490         hostZone = NULL;
491     }
492 
493     // Construct a fixed standard zone with the host's ID
494     // and raw offset.
495     if (hostZone == NULL) {
496         hostZone = new SimpleTimeZone(rawOffset, hostStrID);
497     }
498 
499     // If we _still_ don't have a time zone, use GMT.
500     //
501     // Note: This is extremely unlikely situation. If
502     // new SimpleTimeZone(...) above fails, the following
503     // code may also fail.
504     if (hostZone == NULL) {
505         const TimeZone* temptz = TimeZone::getGMT();
506         // If we can't use GMT, get out.
507         if (temptz == NULL) {
508             return NULL;
509         }
510         hostZone = temptz->clone();
511     }
512 
513     return hostZone;
514 }
515 
516 // -------------------------------------
517 
518 /**
519  * Initialize DEFAULT_ZONE from the system default time zone.
520  * Upon return, DEFAULT_ZONE will not be NULL, unless operator new()
521  * returns NULL.
522  */
initDefault()523 static void U_CALLCONV initDefault()
524 {
525     ucln_i18n_registerCleanup(UCLN_I18N_TIMEZONE, timeZone_cleanup);
526 
527     // If setDefault() has already been called we can skip getting the
528     // default zone information from the system.
529     if (DEFAULT_ZONE != NULL) {
530         return;
531     }
532 
533     // NOTE:  this code is safely single threaded, being only
534     // run via umtx_initOnce().
535     //
536     // Some of the locale/timezone OS functions may not be thread safe,
537     //
538     // The operating system might actually use ICU to implement timezones.
539     // So we may have ICU calling ICU here, like on AIX.
540     // There shouldn't be a problem with this; initOnce does not hold a mutex
541     // while the init function is being run.
542 
543     // The code detecting the host time zone was separated from this
544     // and implemented as TimeZone::detectHostTimeZone()
545 
546     TimeZone *default_zone = TimeZone::detectHostTimeZone();
547 
548     // The only way for DEFAULT_ZONE to be non-null at this point is if the user
549     // made a thread-unsafe call to setDefault() or adoptDefault() in another
550     // thread while this thread was doing something that required getting the default.
551     U_ASSERT(DEFAULT_ZONE == NULL);
552 
553     DEFAULT_ZONE = default_zone;
554 }
555 
556 // -------------------------------------
557 
558 TimeZone* U_EXPORT2
createDefault()559 TimeZone::createDefault()
560 {
561     umtx_initOnce(gDefaultZoneInitOnce, initDefault);
562     return (DEFAULT_ZONE != NULL) ? DEFAULT_ZONE->clone() : NULL;
563 }
564 
565 // -------------------------------------
566 
567 void U_EXPORT2
adoptDefault(TimeZone * zone)568 TimeZone::adoptDefault(TimeZone* zone)
569 {
570     if (zone != NULL)
571     {
572         TimeZone *old = DEFAULT_ZONE;
573         DEFAULT_ZONE = zone;
574         delete old;
575         ucln_i18n_registerCleanup(UCLN_I18N_TIMEZONE, timeZone_cleanup);
576     }
577 }
578 // -------------------------------------
579 
580 void U_EXPORT2
setDefault(const TimeZone & zone)581 TimeZone::setDefault(const TimeZone& zone)
582 {
583     adoptDefault(zone.clone());
584 }
585 
586 //----------------------------------------------------------------------
587 
588 
initMap(USystemTimeZoneType type,UErrorCode & ec)589 static void U_CALLCONV initMap(USystemTimeZoneType type, UErrorCode& ec) {
590     ucln_i18n_registerCleanup(UCLN_I18N_TIMEZONE, timeZone_cleanup);
591 
592     UResourceBundle *res = ures_openDirect(0, kZONEINFO, &ec);
593     res = ures_getByKey(res, kNAMES, res, &ec); // dereference Zones section
594     if (U_SUCCESS(ec)) {
595         int32_t size = ures_getSize(res);
596         int32_t *m = (int32_t *)uprv_malloc(size * sizeof(int32_t));
597         if (m == NULL) {
598             ec = U_MEMORY_ALLOCATION_ERROR;
599         } else {
600             int32_t numEntries = 0;
601             for (int32_t i = 0; i < size; i++) {
602                 UnicodeString id = ures_getUnicodeStringByIndex(res, i, &ec);
603                 if (U_FAILURE(ec)) {
604                     break;
605                 }
606                 if (0 == id.compare(UNKNOWN_ZONE_ID, UNKNOWN_ZONE_ID_LENGTH)) {
607                     // exclude Etc/Unknown
608                     continue;
609                 }
610                 if (type == UCAL_ZONE_TYPE_CANONICAL || type == UCAL_ZONE_TYPE_CANONICAL_LOCATION) {
611                     UnicodeString canonicalID;
612                     ZoneMeta::getCanonicalCLDRID(id, canonicalID, ec);
613                     if (U_FAILURE(ec)) {
614                         break;
615                     }
616                     if (canonicalID != id) {
617                         // exclude aliases
618                         continue;
619                     }
620                 }
621                 if (type == UCAL_ZONE_TYPE_CANONICAL_LOCATION) {
622                     const UChar *region = TimeZone::getRegion(id, ec);
623                     if (U_FAILURE(ec)) {
624                         break;
625                     }
626                     if (u_strcmp(region, WORLD) == 0) {
627                        // exclude non-location ("001")
628                         continue;
629                     }
630                 }
631                 m[numEntries++] = i;
632             }
633             if (U_SUCCESS(ec)) {
634                 int32_t *tmp = m;
635                 m = (int32_t *)uprv_realloc(tmp, numEntries * sizeof(int32_t));
636                 if (m == NULL) {
637                     // realloc failed.. use the original one even it has unused
638                     // area at the end
639                     m = tmp;
640                 }
641 
642                 switch(type) {
643                 case UCAL_ZONE_TYPE_ANY:
644                     U_ASSERT(MAP_SYSTEM_ZONES == NULL);
645                     MAP_SYSTEM_ZONES = m;
646                     LEN_SYSTEM_ZONES = numEntries;
647                     break;
648                 case UCAL_ZONE_TYPE_CANONICAL:
649                     U_ASSERT(MAP_CANONICAL_SYSTEM_ZONES == NULL);
650                     MAP_CANONICAL_SYSTEM_ZONES = m;
651                     LEN_CANONICAL_SYSTEM_ZONES = numEntries;
652                     break;
653                 case UCAL_ZONE_TYPE_CANONICAL_LOCATION:
654                     U_ASSERT(MAP_CANONICAL_SYSTEM_LOCATION_ZONES == NULL);
655                     MAP_CANONICAL_SYSTEM_LOCATION_ZONES = m;
656                     LEN_CANONICAL_SYSTEM_LOCATION_ZONES = numEntries;
657                     break;
658                 }
659             }
660         }
661     }
662     ures_close(res);
663 }
664 
665 
666 /**
667  * This is the default implementation for subclasses that do not
668  * override this method.  This implementation calls through to the
669  * 8-argument getOffset() method after suitable computations, and
670  * correctly adjusts GMT millis to local millis when necessary.
671  */
getOffset(UDate date,UBool local,int32_t & rawOffset,int32_t & dstOffset,UErrorCode & ec) const672 void TimeZone::getOffset(UDate date, UBool local, int32_t& rawOffset,
673                          int32_t& dstOffset, UErrorCode& ec) const {
674     if (U_FAILURE(ec)) {
675         return;
676     }
677 
678     rawOffset = getRawOffset();
679     if (!local) {
680         date += rawOffset; // now in local standard millis
681     }
682 
683     // When local == TRUE, date might not be in local standard
684     // millis.  getOffset taking 7 parameters used here assume
685     // the given time in day is local standard time.
686     // At STD->DST transition, there is a range of time which
687     // does not exist.  When 'date' is in this time range
688     // (and local == TRUE), this method interprets the specified
689     // local time as DST.  At DST->STD transition, there is a
690     // range of time which occurs twice.  In this case, this
691     // method interprets the specified local time as STD.
692     // To support the behavior above, we need to call getOffset
693     // (with 7 args) twice when local == true and DST is
694     // detected in the initial call.
695     for (int32_t pass=0; ; ++pass) {
696         int32_t year, month, dom, dow;
697         double day = uprv_floor(date / U_MILLIS_PER_DAY);
698         int32_t millis = (int32_t) (date - day * U_MILLIS_PER_DAY);
699 
700         Grego::dayToFields(day, year, month, dom, dow);
701 
702         dstOffset = getOffset(GregorianCalendar::AD, year, month, dom,
703                               (uint8_t) dow, millis,
704                               Grego::monthLength(year, month),
705                               ec) - rawOffset;
706 
707         // Recompute if local==TRUE, dstOffset!=0.
708         if (pass!=0 || !local || dstOffset == 0) {
709             break;
710         }
711         // adjust to local standard millis
712         date -= dstOffset;
713     }
714 }
715 
716 // -------------------------------------
717 
718 // New available IDs API as of ICU 2.4.  Uses StringEnumeration API.
719 
720 class TZEnumeration : public StringEnumeration {
721 private:
722 
723     // Map into to zones.  Our results are zone[map[i]] for
724     // i=0..len-1, where zone[i] is the i-th Olson zone.  If map==NULL
725     // then our results are zone[i] for i=0..len-1.  Len will be zero
726     // if the zone data could not be loaded.
727     int32_t* map;
728     int32_t* localMap;
729     int32_t  len;
730     int32_t  pos;
731 
TZEnumeration(int32_t * mapData,int32_t mapLen,UBool adoptMapData)732     TZEnumeration(int32_t* mapData, int32_t mapLen, UBool adoptMapData) : pos(0) {
733         map = mapData;
734         localMap = adoptMapData ? mapData : NULL;
735         len = mapLen;
736     }
737 
getID(int32_t i)738     UBool getID(int32_t i) {
739         UErrorCode ec = U_ZERO_ERROR;
740         int32_t idLen = 0;
741         const UChar* id = NULL;
742         UResourceBundle *top = ures_openDirect(0, kZONEINFO, &ec);
743         top = ures_getByKey(top, kNAMES, top, &ec); // dereference Zones section
744         id = ures_getStringByIndex(top, i, &idLen, &ec);
745         if(U_FAILURE(ec)) {
746             unistr.truncate(0);
747         }
748         else {
749             unistr.fastCopyFrom(UnicodeString(TRUE, id, idLen));
750         }
751         ures_close(top);
752         return U_SUCCESS(ec);
753     }
754 
getMap(USystemTimeZoneType type,int32_t & len,UErrorCode & ec)755     static int32_t* getMap(USystemTimeZoneType type, int32_t& len, UErrorCode& ec) {
756         len = 0;
757         if (U_FAILURE(ec)) {
758             return NULL;
759         }
760         int32_t* m = NULL;
761         switch (type) {
762         case UCAL_ZONE_TYPE_ANY:
763             umtx_initOnce(gSystemZonesInitOnce, &initMap, type, ec);
764             m = MAP_SYSTEM_ZONES;
765             len = LEN_SYSTEM_ZONES;
766             break;
767         case UCAL_ZONE_TYPE_CANONICAL:
768             umtx_initOnce(gCanonicalZonesInitOnce, &initMap, type, ec);
769             m = MAP_CANONICAL_SYSTEM_ZONES;
770             len = LEN_CANONICAL_SYSTEM_ZONES;
771             break;
772         case UCAL_ZONE_TYPE_CANONICAL_LOCATION:
773             umtx_initOnce(gCanonicalLocationZonesInitOnce, &initMap, type, ec);
774             m = MAP_CANONICAL_SYSTEM_LOCATION_ZONES;
775             len = LEN_CANONICAL_SYSTEM_LOCATION_ZONES;
776             break;
777         default:
778             ec = U_ILLEGAL_ARGUMENT_ERROR;
779             m = NULL;
780             len = 0;
781             break;
782         }
783         return m;
784     }
785 
786 public:
787 
788 #define DEFAULT_FILTERED_MAP_SIZE 8
789 #define MAP_INCREMENT_SIZE 8
790 
create(USystemTimeZoneType type,const char * region,const int32_t * rawOffset,UErrorCode & ec)791     static TZEnumeration* create(USystemTimeZoneType type, const char* region, const int32_t* rawOffset, UErrorCode& ec) {
792         if (U_FAILURE(ec)) {
793             return NULL;
794         }
795 
796         int32_t baseLen;
797         int32_t *baseMap = getMap(type, baseLen, ec);
798 
799         if (U_FAILURE(ec)) {
800             return NULL;
801         }
802 
803         // If any additional conditions are available,
804         // create instance local map filtered by the conditions.
805 
806         int32_t *filteredMap = NULL;
807         int32_t numEntries = 0;
808 
809         if (region != NULL || rawOffset != NULL) {
810             int32_t filteredMapSize = DEFAULT_FILTERED_MAP_SIZE;
811             filteredMap = (int32_t *)uprv_malloc(filteredMapSize * sizeof(int32_t));
812             if (filteredMap == NULL) {
813                 ec = U_MEMORY_ALLOCATION_ERROR;
814                 return NULL;
815             }
816 
817             // Walk through the base map
818             UResourceBundle *res = ures_openDirect(0, kZONEINFO, &ec);
819             res = ures_getByKey(res, kNAMES, res, &ec); // dereference Zones section
820             for (int32_t i = 0; i < baseLen; i++) {
821                 int32_t zidx = baseMap[i];
822                 UnicodeString id = ures_getUnicodeStringByIndex(res, zidx, &ec);
823                 if (U_FAILURE(ec)) {
824                     break;
825                 }
826                 if (region != NULL) {
827                     // Filter by region
828                     char tzregion[4]; // max 3 letters + null term
829                     TimeZone::getRegion(id, tzregion, sizeof(tzregion), ec);
830                     if (U_FAILURE(ec)) {
831                         break;
832                     }
833                     if (uprv_stricmp(tzregion, region) != 0) {
834                         // region does not match
835                         continue;
836                     }
837                 }
838                 if (rawOffset != NULL) {
839                     // Filter by raw offset
840                     // Note: This is VERY inefficient
841                     TimeZone *z = createSystemTimeZone(id, ec);
842                     if (U_FAILURE(ec)) {
843                         break;
844                     }
845                     int32_t tzoffset = z->getRawOffset();
846                     delete z;
847 
848                     if (tzoffset != *rawOffset) {
849                         continue;
850                     }
851                 }
852 
853                 if (filteredMapSize <= numEntries) {
854                     filteredMapSize += MAP_INCREMENT_SIZE;
855                     int32_t *tmp = (int32_t *)uprv_realloc(filteredMap, filteredMapSize * sizeof(int32_t));
856                     if (tmp == NULL) {
857                         ec = U_MEMORY_ALLOCATION_ERROR;
858                         break;
859                     } else {
860                         filteredMap = tmp;
861                     }
862                 }
863 
864                 filteredMap[numEntries++] = zidx;
865             }
866 
867             if (U_FAILURE(ec)) {
868                 uprv_free(filteredMap);
869                 filteredMap = NULL;
870             }
871 
872             ures_close(res);
873         }
874 
875         TZEnumeration *result = NULL;
876         if (U_SUCCESS(ec)) {
877             // Finally, create a new enumeration instance
878             if (filteredMap == NULL) {
879                 result = new TZEnumeration(baseMap, baseLen, FALSE);
880             } else {
881                 result = new TZEnumeration(filteredMap, numEntries, TRUE);
882                 filteredMap = NULL;
883             }
884             if (result == NULL) {
885                 ec = U_MEMORY_ALLOCATION_ERROR;
886             }
887         }
888 
889         if (filteredMap != NULL) {
890             uprv_free(filteredMap);
891         }
892 
893         return result;
894     }
895 
TZEnumeration(const TZEnumeration & other)896     TZEnumeration(const TZEnumeration &other) : StringEnumeration(), map(NULL), localMap(NULL), len(0), pos(0) {
897         if (other.localMap != NULL) {
898             localMap = (int32_t *)uprv_malloc(other.len * sizeof(int32_t));
899             if (localMap != NULL) {
900                 len = other.len;
901                 uprv_memcpy(localMap, other.localMap, len * sizeof(int32_t));
902                 pos = other.pos;
903                 map = localMap;
904             } else {
905                 len = 0;
906                 pos = 0;
907                 map = NULL;
908             }
909         } else {
910             map = other.map;
911             localMap = NULL;
912             len = other.len;
913             pos = other.pos;
914         }
915     }
916 
917     virtual ~TZEnumeration();
918 
clone() const919     virtual StringEnumeration *clone() const {
920         return new TZEnumeration(*this);
921     }
922 
count(UErrorCode & status) const923     virtual int32_t count(UErrorCode& status) const {
924         return U_FAILURE(status) ? 0 : len;
925     }
926 
snext(UErrorCode & status)927     virtual const UnicodeString* snext(UErrorCode& status) {
928         if (U_SUCCESS(status) && map != NULL && pos < len) {
929             getID(map[pos]);
930             ++pos;
931             return &unistr;
932         }
933         return 0;
934     }
935 
reset(UErrorCode &)936     virtual void reset(UErrorCode& /*status*/) {
937         pos = 0;
938     }
939 
940 public:
941     static UClassID U_EXPORT2 getStaticClassID(void);
942     virtual UClassID getDynamicClassID(void) const;
943 };
944 
~TZEnumeration()945 TZEnumeration::~TZEnumeration() {
946     if (localMap != NULL) {
947         uprv_free(localMap);
948     }
949 }
950 
UOBJECT_DEFINE_RTTI_IMPLEMENTATION(TZEnumeration)951 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(TZEnumeration)
952 
953 StringEnumeration* U_EXPORT2
954 TimeZone::createTimeZoneIDEnumeration(
955             USystemTimeZoneType zoneType,
956             const char* region,
957             const int32_t* rawOffset,
958             UErrorCode& ec) {
959     return TZEnumeration::create(zoneType, region, rawOffset, ec);
960 }
961 
962 StringEnumeration* U_EXPORT2
createEnumeration()963 TimeZone::createEnumeration() {
964     UErrorCode ec = U_ZERO_ERROR;
965     return TZEnumeration::create(UCAL_ZONE_TYPE_ANY, NULL, NULL, ec);
966 }
967 
968 StringEnumeration* U_EXPORT2
createEnumeration(int32_t rawOffset)969 TimeZone::createEnumeration(int32_t rawOffset) {
970     UErrorCode ec = U_ZERO_ERROR;
971     return TZEnumeration::create(UCAL_ZONE_TYPE_ANY, NULL, &rawOffset, ec);
972 }
973 
974 StringEnumeration* U_EXPORT2
createEnumeration(const char * country)975 TimeZone::createEnumeration(const char* country) {
976     UErrorCode ec = U_ZERO_ERROR;
977     return TZEnumeration::create(UCAL_ZONE_TYPE_ANY, country, NULL, ec);
978 }
979 
980 // ---------------------------------------
981 
982 int32_t U_EXPORT2
countEquivalentIDs(const UnicodeString & id)983 TimeZone::countEquivalentIDs(const UnicodeString& id) {
984     int32_t result = 0;
985     UErrorCode ec = U_ZERO_ERROR;
986     UResourceBundle res;
987     ures_initStackObject(&res);
988     U_DEBUG_TZ_MSG(("countEquivalentIDs..\n"));
989     UResourceBundle *top = openOlsonResource(id, res, ec);
990     if (U_SUCCESS(ec)) {
991         UResourceBundle r;
992         ures_initStackObject(&r);
993         ures_getByKey(&res, kLINKS, &r, &ec);
994         ures_getIntVector(&r, &result, &ec);
995         ures_close(&r);
996     }
997     ures_close(&res);
998     ures_close(top);
999     return result;
1000 }
1001 
1002 // ---------------------------------------
1003 
1004 const UnicodeString U_EXPORT2
getEquivalentID(const UnicodeString & id,int32_t index)1005 TimeZone::getEquivalentID(const UnicodeString& id, int32_t index) {
1006     U_DEBUG_TZ_MSG(("gEI(%d)\n", index));
1007     UnicodeString result;
1008     UErrorCode ec = U_ZERO_ERROR;
1009     UResourceBundle res;
1010     ures_initStackObject(&res);
1011     UResourceBundle *top = openOlsonResource(id, res, ec);
1012     int32_t zone = -1;
1013     if (U_SUCCESS(ec)) {
1014         UResourceBundle r;
1015         ures_initStackObject(&r);
1016         int32_t size;
1017         ures_getByKey(&res, kLINKS, &r, &ec);
1018         const int32_t* v = ures_getIntVector(&r, &size, &ec);
1019         if (U_SUCCESS(ec)) {
1020             if (index >= 0 && index < size) {
1021                 zone = v[index];
1022             }
1023         }
1024         ures_close(&r);
1025     }
1026     ures_close(&res);
1027     if (zone >= 0) {
1028         UResourceBundle *ares = ures_getByKey(top, kNAMES, NULL, &ec); // dereference Zones section
1029         if (U_SUCCESS(ec)) {
1030             int32_t idLen = 0;
1031             const UChar* id = ures_getStringByIndex(ares, zone, &idLen, &ec);
1032             result.fastCopyFrom(UnicodeString(TRUE, id, idLen));
1033             U_DEBUG_TZ_MSG(("gei(%d) -> %d, len%d, %s\n", index, zone, result.length(), u_errorName(ec)));
1034         }
1035         ures_close(ares);
1036     }
1037     ures_close(top);
1038 #if defined(U_DEBUG_TZ)
1039     if(result.length() ==0) {
1040       U_DEBUG_TZ_MSG(("equiv [__, #%d] -> 0 (%s)\n", index, u_errorName(ec)));
1041     }
1042 #endif
1043     return result;
1044 }
1045 
1046 // ---------------------------------------
1047 
1048 // These methods are used by ZoneMeta class only.
1049 
1050 const UChar*
findID(const UnicodeString & id)1051 TimeZone::findID(const UnicodeString& id) {
1052     const UChar *result = NULL;
1053     UErrorCode ec = U_ZERO_ERROR;
1054     UResourceBundle *rb = ures_openDirect(NULL, kZONEINFO, &ec);
1055 
1056     // resolve zone index by name
1057     UResourceBundle *names = ures_getByKey(rb, kNAMES, NULL, &ec);
1058     int32_t idx = findInStringArray(names, id, ec);
1059     result = ures_getStringByIndex(names, idx, NULL, &ec);
1060     if (U_FAILURE(ec)) {
1061         result = NULL;
1062     }
1063     ures_close(names);
1064     ures_close(rb);
1065     return result;
1066 }
1067 
1068 
1069 const UChar*
dereferOlsonLink(const UnicodeString & id)1070 TimeZone::dereferOlsonLink(const UnicodeString& id) {
1071     const UChar *result = NULL;
1072     UErrorCode ec = U_ZERO_ERROR;
1073     UResourceBundle *rb = ures_openDirect(NULL, kZONEINFO, &ec);
1074 
1075     // resolve zone index by name
1076     UResourceBundle *names = ures_getByKey(rb, kNAMES, NULL, &ec);
1077     int32_t idx = findInStringArray(names, id, ec);
1078     result = ures_getStringByIndex(names, idx, NULL, &ec);
1079 
1080     // open the zone bundle by index
1081     ures_getByKey(rb, kZONES, rb, &ec);
1082     ures_getByIndex(rb, idx, rb, &ec);
1083 
1084     if (U_SUCCESS(ec)) {
1085         if (ures_getType(rb) == URES_INT) {
1086             // this is a link - dereference the link
1087             int32_t deref = ures_getInt(rb, &ec);
1088             const UChar* tmp = ures_getStringByIndex(names, deref, NULL, &ec);
1089             if (U_SUCCESS(ec)) {
1090                 result = tmp;
1091             }
1092         }
1093     }
1094 
1095     ures_close(names);
1096     ures_close(rb);
1097 
1098     return result;
1099 }
1100 
1101 const UChar*
getRegion(const UnicodeString & id)1102 TimeZone::getRegion(const UnicodeString& id) {
1103     UErrorCode status = U_ZERO_ERROR;
1104     return getRegion(id, status);
1105 }
1106 
1107 const UChar*
getRegion(const UnicodeString & id,UErrorCode & status)1108 TimeZone::getRegion(const UnicodeString& id, UErrorCode& status) {
1109     if (U_FAILURE(status)) {
1110         return NULL;
1111     }
1112     const UChar *result = NULL;
1113     UResourceBundle *rb = ures_openDirect(NULL, kZONEINFO, &status);
1114 
1115     // resolve zone index by name
1116     UResourceBundle *res = ures_getByKey(rb, kNAMES, NULL, &status);
1117     int32_t idx = findInStringArray(res, id, status);
1118 
1119     // get region mapping
1120     ures_getByKey(rb, kREGIONS, res, &status);
1121     const UChar *tmp = ures_getStringByIndex(res, idx, NULL, &status);
1122     if (U_SUCCESS(status)) {
1123         result = tmp;
1124     }
1125 
1126     ures_close(res);
1127     ures_close(rb);
1128 
1129     return result;
1130 }
1131 
1132 
1133 // ---------------------------------------
1134 int32_t
getRegion(const UnicodeString & id,char * region,int32_t capacity,UErrorCode & status)1135 TimeZone::getRegion(const UnicodeString& id, char *region, int32_t capacity, UErrorCode& status)
1136 {
1137     int32_t resultLen = 0;
1138     *region = 0;
1139     if (U_FAILURE(status)) {
1140         return 0;
1141     }
1142 
1143     const UChar *uregion = NULL;
1144     // "Etc/Unknown" is not a system zone ID,
1145     // but in the zone data
1146     if (id.compare(UNKNOWN_ZONE_ID, UNKNOWN_ZONE_ID_LENGTH) != 0) {
1147         uregion = getRegion(id);
1148     }
1149     if (uregion == NULL) {
1150         status = U_ILLEGAL_ARGUMENT_ERROR;
1151         return 0;
1152     }
1153     resultLen = u_strlen(uregion);
1154     // A region code is represented by invariant characters
1155     u_UCharsToChars(uregion, region, uprv_min(resultLen, capacity));
1156 
1157     if (capacity < resultLen) {
1158         status = U_BUFFER_OVERFLOW_ERROR;
1159         return resultLen;
1160     }
1161 
1162     return u_terminateChars(region, capacity, resultLen, &status);
1163 }
1164 
1165 // ---------------------------------------
1166 
1167 
1168 UnicodeString&
getDisplayName(UnicodeString & result) const1169 TimeZone::getDisplayName(UnicodeString& result) const
1170 {
1171     return getDisplayName(FALSE,LONG,Locale::getDefault(), result);
1172 }
1173 
1174 UnicodeString&
getDisplayName(const Locale & locale,UnicodeString & result) const1175 TimeZone::getDisplayName(const Locale& locale, UnicodeString& result) const
1176 {
1177     return getDisplayName(FALSE, LONG, locale, result);
1178 }
1179 
1180 UnicodeString&
getDisplayName(UBool daylight,EDisplayType style,UnicodeString & result) const1181 TimeZone::getDisplayName(UBool daylight, EDisplayType style, UnicodeString& result)  const
1182 {
1183     return getDisplayName(daylight,style, Locale::getDefault(), result);
1184 }
1185 //--------------------------------------
1186 int32_t
getDSTSavings() const1187 TimeZone::getDSTSavings()const {
1188     if (useDaylightTime()) {
1189         return 3600000;
1190     }
1191     return 0;
1192 }
1193 //---------------------------------------
1194 UnicodeString&
getDisplayName(UBool daylight,EDisplayType style,const Locale & locale,UnicodeString & result) const1195 TimeZone::getDisplayName(UBool daylight, EDisplayType style, const Locale& locale, UnicodeString& result) const
1196 {
1197     UErrorCode status = U_ZERO_ERROR;
1198     UDate date = Calendar::getNow();
1199     UTimeZoneFormatTimeType timeType;
1200     int32_t offset;
1201 
1202     if (style == GENERIC_LOCATION || style == LONG_GENERIC || style == SHORT_GENERIC) {
1203         LocalPointer<TimeZoneFormat> tzfmt(TimeZoneFormat::createInstance(locale, status));
1204         if (U_FAILURE(status)) {
1205             result.remove();
1206             return result;
1207         }
1208         // Generic format
1209         switch (style) {
1210         case GENERIC_LOCATION:
1211             tzfmt->format(UTZFMT_STYLE_GENERIC_LOCATION, *this, date, result, &timeType);
1212             break;
1213         case LONG_GENERIC:
1214             tzfmt->format(UTZFMT_STYLE_GENERIC_LONG, *this, date, result, &timeType);
1215             break;
1216         case SHORT_GENERIC:
1217             tzfmt->format(UTZFMT_STYLE_GENERIC_SHORT, *this, date, result, &timeType);
1218             break;
1219         default:
1220             U_ASSERT(FALSE);
1221         }
1222         // Generic format many use Localized GMT as the final fallback.
1223         // When Localized GMT format is used, the result might not be
1224         // appropriate for the requested daylight value.
1225         if ((daylight && timeType == UTZFMT_TIME_TYPE_STANDARD) || (!daylight && timeType == UTZFMT_TIME_TYPE_DAYLIGHT)) {
1226             offset = daylight ? getRawOffset() + getDSTSavings() : getRawOffset();
1227             if (style == SHORT_GENERIC) {
1228                 tzfmt->formatOffsetShortLocalizedGMT(offset, result, status);
1229             } else {
1230                 tzfmt->formatOffsetLocalizedGMT(offset, result, status);
1231             }
1232         }
1233     } else if (style == LONG_GMT || style == SHORT_GMT) {
1234         LocalPointer<TimeZoneFormat> tzfmt(TimeZoneFormat::createInstance(locale, status));
1235         if (U_FAILURE(status)) {
1236             result.remove();
1237             return result;
1238         }
1239         offset = daylight && useDaylightTime() ? getRawOffset() + getDSTSavings() : getRawOffset();
1240         switch (style) {
1241         case LONG_GMT:
1242             tzfmt->formatOffsetLocalizedGMT(offset, result, status);
1243             break;
1244         case SHORT_GMT:
1245             tzfmt->formatOffsetISO8601Basic(offset, FALSE, FALSE, FALSE, result, status);
1246             break;
1247         default:
1248             U_ASSERT(FALSE);
1249         }
1250 
1251     } else {
1252         U_ASSERT(style == LONG || style == SHORT || style == SHORT_COMMONLY_USED);
1253         UTimeZoneNameType nameType = UTZNM_UNKNOWN;
1254         switch (style) {
1255         case LONG:
1256             nameType = daylight ? UTZNM_LONG_DAYLIGHT : UTZNM_LONG_STANDARD;
1257             break;
1258         case SHORT:
1259         case SHORT_COMMONLY_USED:
1260             nameType = daylight ? UTZNM_SHORT_DAYLIGHT : UTZNM_SHORT_STANDARD;
1261             break;
1262         default:
1263             U_ASSERT(FALSE);
1264         }
1265         LocalPointer<TimeZoneNames> tznames(TimeZoneNames::createInstance(locale, status));
1266         if (U_FAILURE(status)) {
1267             result.remove();
1268             return result;
1269         }
1270         UnicodeString canonicalID(ZoneMeta::getCanonicalCLDRID(*this));
1271         tznames->getDisplayName(canonicalID, nameType, date, result);
1272         if (result.isEmpty()) {
1273             // Fallback to localized GMT
1274             LocalPointer<TimeZoneFormat> tzfmt(TimeZoneFormat::createInstance(locale, status));
1275             offset = daylight && useDaylightTime() ? getRawOffset() + getDSTSavings() : getRawOffset();
1276             if (style == LONG) {
1277                 tzfmt->formatOffsetLocalizedGMT(offset, result, status);
1278             } else {
1279                 tzfmt->formatOffsetShortLocalizedGMT(offset, result, status);
1280             }
1281         }
1282     }
1283     if (U_FAILURE(status)) {
1284         result.remove();
1285     }
1286     return  result;
1287 }
1288 
1289 /**
1290  * Parse a custom time zone identifier and return a corresponding zone.
1291  * @param id a string of the form GMT[+-]hh:mm, GMT[+-]hhmm, or
1292  * GMT[+-]hh.
1293  * @return a newly created SimpleTimeZone with the given offset and
1294  * no Daylight Savings Time, or null if the id cannot be parsed.
1295 */
1296 TimeZone*
createCustomTimeZone(const UnicodeString & id)1297 TimeZone::createCustomTimeZone(const UnicodeString& id)
1298 {
1299     int32_t sign, hour, min, sec;
1300     if (parseCustomID(id, sign, hour, min, sec)) {
1301         UnicodeString customID;
1302         formatCustomID(hour, min, sec, (sign < 0), customID);
1303         int32_t offset = sign * ((hour * 60 + min) * 60 + sec) * 1000;
1304         return new SimpleTimeZone(offset, customID);
1305     }
1306     return NULL;
1307 }
1308 
1309 UnicodeString&
getCustomID(const UnicodeString & id,UnicodeString & normalized,UErrorCode & status)1310 TimeZone::getCustomID(const UnicodeString& id, UnicodeString& normalized, UErrorCode& status) {
1311     normalized.remove();
1312     if (U_FAILURE(status)) {
1313         return normalized;
1314     }
1315     int32_t sign, hour, min, sec;
1316     if (parseCustomID(id, sign, hour, min, sec)) {
1317         formatCustomID(hour, min, sec, (sign < 0), normalized);
1318     } else {
1319         status = U_ILLEGAL_ARGUMENT_ERROR;
1320     }
1321     return normalized;
1322 }
1323 
1324 UBool
parseCustomID(const UnicodeString & id,int32_t & sign,int32_t & hour,int32_t & min,int32_t & sec)1325 TimeZone::parseCustomID(const UnicodeString& id, int32_t& sign,
1326                         int32_t& hour, int32_t& min, int32_t& sec) {
1327     static const int32_t         kParseFailed = -99999;
1328 
1329     NumberFormat* numberFormat = 0;
1330     UnicodeString idUppercase = id;
1331     idUppercase.toUpper("");
1332 
1333     if (id.length() > GMT_ID_LENGTH &&
1334         idUppercase.startsWith(GMT_ID, GMT_ID_LENGTH))
1335     {
1336         ParsePosition pos(GMT_ID_LENGTH);
1337         sign = 1;
1338         hour = 0;
1339         min = 0;
1340         sec = 0;
1341 
1342         if (id[pos.getIndex()] == MINUS /*'-'*/) {
1343             sign = -1;
1344         } else if (id[pos.getIndex()] != PLUS /*'+'*/) {
1345             return FALSE;
1346         }
1347         pos.setIndex(pos.getIndex() + 1);
1348 
1349         UErrorCode success = U_ZERO_ERROR;
1350         numberFormat = NumberFormat::createInstance(success);
1351         if(U_FAILURE(success)){
1352             return FALSE;
1353         }
1354         numberFormat->setParseIntegerOnly(TRUE);
1355         //numberFormat->setLenient(TRUE); // TODO: May need to set this, depends on latest timezone parsing
1356 
1357         // Look for either hh:mm, hhmm, or hh
1358         int32_t start = pos.getIndex();
1359         Formattable n(kParseFailed);
1360         numberFormat->parse(id, n, pos);
1361         if (pos.getIndex() == start) {
1362             delete numberFormat;
1363             return FALSE;
1364         }
1365         hour = n.getLong();
1366 
1367         if (pos.getIndex() < id.length()) {
1368             if (pos.getIndex() - start > 2
1369                 || id[pos.getIndex()] != COLON) {
1370                 delete numberFormat;
1371                 return FALSE;
1372             }
1373             // hh:mm
1374             pos.setIndex(pos.getIndex() + 1);
1375             int32_t oldPos = pos.getIndex();
1376             n.setLong(kParseFailed);
1377             numberFormat->parse(id, n, pos);
1378             if ((pos.getIndex() - oldPos) != 2) {
1379                 // must be 2 digits
1380                 delete numberFormat;
1381                 return FALSE;
1382             }
1383             min = n.getLong();
1384             if (pos.getIndex() < id.length()) {
1385                 if (id[pos.getIndex()] != COLON) {
1386                     delete numberFormat;
1387                     return FALSE;
1388                 }
1389                 // [:ss]
1390                 pos.setIndex(pos.getIndex() + 1);
1391                 oldPos = pos.getIndex();
1392                 n.setLong(kParseFailed);
1393                 numberFormat->parse(id, n, pos);
1394                 if (pos.getIndex() != id.length()
1395                         || (pos.getIndex() - oldPos) != 2) {
1396                     delete numberFormat;
1397                     return FALSE;
1398                 }
1399                 sec = n.getLong();
1400             }
1401         } else {
1402             // Supported formats are below -
1403             //
1404             // HHmmss
1405             // Hmmss
1406             // HHmm
1407             // Hmm
1408             // HH
1409             // H
1410 
1411             int32_t length = pos.getIndex() - start;
1412             if (length <= 0 || 6 < length) {
1413                 // invalid length
1414                 delete numberFormat;
1415                 return FALSE;
1416             }
1417             switch (length) {
1418                 case 1:
1419                 case 2:
1420                     // already set to hour
1421                     break;
1422                 case 3:
1423                 case 4:
1424                     min = hour % 100;
1425                     hour /= 100;
1426                     break;
1427                 case 5:
1428                 case 6:
1429                     sec = hour % 100;
1430                     min = (hour/100) % 100;
1431                     hour /= 10000;
1432                     break;
1433             }
1434         }
1435 
1436         delete numberFormat;
1437 
1438         if (hour > kMAX_CUSTOM_HOUR || min > kMAX_CUSTOM_MIN || sec > kMAX_CUSTOM_SEC) {
1439             return FALSE;
1440         }
1441         return TRUE;
1442     }
1443     return FALSE;
1444 }
1445 
1446 UnicodeString&
formatCustomID(int32_t hour,int32_t min,int32_t sec,UBool negative,UnicodeString & id)1447 TimeZone::formatCustomID(int32_t hour, int32_t min, int32_t sec,
1448                          UBool negative, UnicodeString& id) {
1449     // Create time zone ID - GMT[+|-]hhmm[ss]
1450     id.setTo(GMT_ID, GMT_ID_LENGTH);
1451     if (hour | min | sec) {
1452         if (negative) {
1453             id += (UChar)MINUS;
1454         } else {
1455             id += (UChar)PLUS;
1456         }
1457 
1458         if (hour < 10) {
1459             id += (UChar)ZERO_DIGIT;
1460         } else {
1461             id += (UChar)(ZERO_DIGIT + hour/10);
1462         }
1463         id += (UChar)(ZERO_DIGIT + hour%10);
1464         id += (UChar)COLON;
1465         if (min < 10) {
1466             id += (UChar)ZERO_DIGIT;
1467         } else {
1468             id += (UChar)(ZERO_DIGIT + min/10);
1469         }
1470         id += (UChar)(ZERO_DIGIT + min%10);
1471 
1472         if (sec) {
1473             id += (UChar)COLON;
1474             if (sec < 10) {
1475                 id += (UChar)ZERO_DIGIT;
1476             } else {
1477                 id += (UChar)(ZERO_DIGIT + sec/10);
1478             }
1479             id += (UChar)(ZERO_DIGIT + sec%10);
1480         }
1481     }
1482     return id;
1483 }
1484 
1485 
1486 UBool
hasSameRules(const TimeZone & other) const1487 TimeZone::hasSameRules(const TimeZone& other) const
1488 {
1489     return (getRawOffset() == other.getRawOffset() &&
1490             useDaylightTime() == other.useDaylightTime());
1491 }
1492 
initTZDataVersion(UErrorCode & status)1493 static void U_CALLCONV initTZDataVersion(UErrorCode &status) {
1494     ucln_i18n_registerCleanup(UCLN_I18N_TIMEZONE, timeZone_cleanup);
1495     int32_t len = 0;
1496     UResourceBundle *bundle = ures_openDirect(NULL, kZONEINFO, &status);
1497     const UChar *tzver = ures_getStringByKey(bundle, kTZVERSION, &len, &status);
1498 
1499     if (U_SUCCESS(status)) {
1500         if (len >= (int32_t)sizeof(TZDATA_VERSION)) {
1501             // Ensure that there is always space for a trailing nul in TZDATA_VERSION
1502             len = sizeof(TZDATA_VERSION) - 1;
1503         }
1504         u_UCharsToChars(tzver, TZDATA_VERSION, len);
1505     }
1506     ures_close(bundle);
1507 
1508 }
1509 
1510 const char*
getTZDataVersion(UErrorCode & status)1511 TimeZone::getTZDataVersion(UErrorCode& status)
1512 {
1513     umtx_initOnce(gTZDataVersionInitOnce, &initTZDataVersion, status);
1514     return (const char*)TZDATA_VERSION;
1515 }
1516 
1517 UnicodeString&
getCanonicalID(const UnicodeString & id,UnicodeString & canonicalID,UErrorCode & status)1518 TimeZone::getCanonicalID(const UnicodeString& id, UnicodeString& canonicalID, UErrorCode& status)
1519 {
1520     UBool isSystemID = FALSE;
1521     return getCanonicalID(id, canonicalID, isSystemID, status);
1522 }
1523 
1524 UnicodeString&
getCanonicalID(const UnicodeString & id,UnicodeString & canonicalID,UBool & isSystemID,UErrorCode & status)1525 TimeZone::getCanonicalID(const UnicodeString& id, UnicodeString& canonicalID, UBool& isSystemID,
1526                          UErrorCode& status)
1527 {
1528     canonicalID.remove();
1529     isSystemID = FALSE;
1530     if (U_FAILURE(status)) {
1531         return canonicalID;
1532     }
1533     if (id.compare(UNKNOWN_ZONE_ID, UNKNOWN_ZONE_ID_LENGTH) == 0) {
1534         // special case - Etc/Unknown is a canonical ID, but not system ID
1535         canonicalID.fastCopyFrom(id);
1536         isSystemID = FALSE;
1537     } else {
1538         ZoneMeta::getCanonicalCLDRID(id, canonicalID, status);
1539         if (U_SUCCESS(status)) {
1540             isSystemID = TRUE;
1541         } else {
1542             // Not a system ID
1543             status = U_ZERO_ERROR;
1544             getCustomID(id, canonicalID, status);
1545         }
1546     }
1547     return canonicalID;
1548 }
1549 
1550 UnicodeString&
getWindowsID(const UnicodeString & id,UnicodeString & winid,UErrorCode & status)1551 TimeZone::getWindowsID(const UnicodeString& id, UnicodeString& winid, UErrorCode& status) {
1552     winid.remove();
1553     if (U_FAILURE(status)) {
1554         return winid;
1555     }
1556 
1557     // canonicalize the input ID
1558     UnicodeString canonicalID;
1559     UBool isSystemID = FALSE;
1560 
1561     getCanonicalID(id, canonicalID, isSystemID, status);
1562     if (U_FAILURE(status) || !isSystemID) {
1563         // mapping data is only applicable to tz database IDs
1564         if (status == U_ILLEGAL_ARGUMENT_ERROR) {
1565             // getWindowsID() sets an empty string where
1566             // getCanonicalID() sets a U_ILLEGAL_ARGUMENT_ERROR.
1567             status = U_ZERO_ERROR;
1568         }
1569         return winid;
1570     }
1571 
1572     UResourceBundle *mapTimezones = ures_openDirect(NULL, "windowsZones", &status);
1573     ures_getByKey(mapTimezones, "mapTimezones", mapTimezones, &status);
1574 
1575     if (U_FAILURE(status)) {
1576         return winid;
1577     }
1578 
1579     UResourceBundle *winzone = NULL;
1580     UBool found = FALSE;
1581     while (ures_hasNext(mapTimezones) && !found) {
1582         winzone = ures_getNextResource(mapTimezones, winzone, &status);
1583         if (U_FAILURE(status)) {
1584             break;
1585         }
1586         if (ures_getType(winzone) != URES_TABLE) {
1587             continue;
1588         }
1589         UResourceBundle *regionalData = NULL;
1590         while (ures_hasNext(winzone) && !found) {
1591             regionalData = ures_getNextResource(winzone, regionalData, &status);
1592             if (U_FAILURE(status)) {
1593                 break;
1594             }
1595             if (ures_getType(regionalData) != URES_STRING) {
1596                 continue;
1597             }
1598             int32_t len;
1599             const UChar *tzids = ures_getString(regionalData, &len, &status);
1600             if (U_FAILURE(status)) {
1601                 break;
1602             }
1603 
1604             const UChar *start = tzids;
1605             UBool hasNext = TRUE;
1606             while (hasNext) {
1607                 const UChar *end = u_strchr(start, (UChar)0x20);
1608                 if (end == NULL) {
1609                     end = tzids + len;
1610                     hasNext = FALSE;
1611                 }
1612                 if (canonicalID.compare(start, end - start) == 0) {
1613                     winid = UnicodeString(ures_getKey(winzone), -1 , US_INV);
1614                     found = TRUE;
1615                     break;
1616                 }
1617                 start = end + 1;
1618             }
1619         }
1620         ures_close(regionalData);
1621     }
1622     ures_close(winzone);
1623     ures_close(mapTimezones);
1624 
1625     return winid;
1626 }
1627 
1628 #define MAX_WINDOWS_ID_SIZE 128
1629 
1630 UnicodeString&
getIDForWindowsID(const UnicodeString & winid,const char * region,UnicodeString & id,UErrorCode & status)1631 TimeZone::getIDForWindowsID(const UnicodeString& winid, const char* region, UnicodeString& id, UErrorCode& status) {
1632     id.remove();
1633     if (U_FAILURE(status)) {
1634         return id;
1635     }
1636 
1637     UResourceBundle *zones = ures_openDirect(NULL, "windowsZones", &status);
1638     ures_getByKey(zones, "mapTimezones", zones, &status);
1639     if (U_FAILURE(status)) {
1640         ures_close(zones);
1641         return id;
1642     }
1643 
1644     UErrorCode tmperr = U_ZERO_ERROR;
1645     char winidKey[MAX_WINDOWS_ID_SIZE];
1646     int32_t winKeyLen = winid.extract(0, winid.length(), winidKey, sizeof(winidKey) - 1, US_INV);
1647 
1648     if (winKeyLen == 0 || winKeyLen >= (int32_t)sizeof(winidKey)) {
1649         ures_close(zones);
1650         return id;
1651     }
1652     winidKey[winKeyLen] = 0;
1653 
1654     ures_getByKey(zones, winidKey, zones, &tmperr); // use tmperr, because windows mapping might not
1655                                                     // be avaiable by design
1656     if (U_FAILURE(tmperr)) {
1657         ures_close(zones);
1658         return id;
1659     }
1660 
1661     const UChar *tzid = NULL;
1662     int32_t len = 0;
1663     UBool gotID = FALSE;
1664     if (region) {
1665         const UChar *tzids = ures_getStringByKey(zones, region, &len, &tmperr); // use tmperr, because
1666                                                                                 // regional mapping is optional
1667         if (U_SUCCESS(tmperr)) {
1668             // first ID delimited by space is the defasult one
1669             const UChar *end = u_strchr(tzids, (UChar)0x20);
1670             if (end == NULL) {
1671                 id.setTo(tzids, -1);
1672             } else {
1673                 id.setTo(tzids, end - tzids);
1674             }
1675             gotID = TRUE;
1676         }
1677     }
1678 
1679     if (!gotID) {
1680         tzid = ures_getStringByKey(zones, "001", &len, &status);    // using status, because "001" must be
1681                                                                 // available at this point
1682         if (U_SUCCESS(status)) {
1683             id.setTo(tzid, len);
1684         }
1685     }
1686 
1687     ures_close(zones);
1688     return id;
1689 }
1690 
1691 
1692 U_NAMESPACE_END
1693 
1694 #endif /* #if !UCONFIG_NO_FORMATTING */
1695 
1696 //eof
1697