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