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 TimeZone* U_EXPORT2
forLocaleOrDefault(const Locale & locale)583 TimeZone::forLocaleOrDefault(const Locale& locale)
584 {
585     char buffer[ULOC_KEYWORDS_CAPACITY] = "";
586     UErrorCode localStatus = U_ZERO_ERROR;
587     int32_t count = locale.getKeywordValue("timezone", buffer, sizeof(buffer), localStatus);
588     if (U_FAILURE(localStatus) || localStatus == U_STRING_NOT_TERMINATED_WARNING) {
589         // the "timezone" keyword exceeds ULOC_KEYWORDS_CAPACITY; ignore and use default.
590         count = 0;
591     }
592     if (count > 0) {
593         return TimeZone::createTimeZone(UnicodeString(buffer, count, US_INV));
594     }
595     return TimeZone::createDefault();
596 }
597 
598 // -------------------------------------
599 
600 void U_EXPORT2
adoptDefault(TimeZone * zone)601 TimeZone::adoptDefault(TimeZone* zone)
602 {
603     if (zone != NULL)
604     {
605         {
606             Mutex lock(&gDefaultZoneMutex);
607             TimeZone *old = DEFAULT_ZONE;
608             DEFAULT_ZONE = zone;
609             delete old;
610         }
611         ucln_i18n_registerCleanup(UCLN_I18N_TIMEZONE, timeZone_cleanup);
612     }
613 }
614 // -------------------------------------
615 
616 void U_EXPORT2
setDefault(const TimeZone & zone)617 TimeZone::setDefault(const TimeZone& zone)
618 {
619     adoptDefault(zone.clone());
620 }
621 
622 //----------------------------------------------------------------------
623 
624 
initMap(USystemTimeZoneType type,UErrorCode & ec)625 static void U_CALLCONV initMap(USystemTimeZoneType type, UErrorCode& ec) {
626     ucln_i18n_registerCleanup(UCLN_I18N_TIMEZONE, timeZone_cleanup);
627 
628     UResourceBundle *res = ures_openDirect(0, kZONEINFO, &ec);
629     res = ures_getByKey(res, kNAMES, res, &ec); // dereference Zones section
630     if (U_SUCCESS(ec)) {
631         int32_t size = ures_getSize(res);
632         int32_t *m = (int32_t *)uprv_malloc(size * sizeof(int32_t));
633         if (m == NULL) {
634             ec = U_MEMORY_ALLOCATION_ERROR;
635         } else {
636             int32_t numEntries = 0;
637             for (int32_t i = 0; i < size; i++) {
638                 UnicodeString id = ures_getUnicodeStringByIndex(res, i, &ec);
639                 if (U_FAILURE(ec)) {
640                     break;
641                 }
642                 if (0 == id.compare(UNKNOWN_ZONE_ID, UNKNOWN_ZONE_ID_LENGTH)) {
643                     // exclude Etc/Unknown
644                     continue;
645                 }
646                 if (type == UCAL_ZONE_TYPE_CANONICAL || type == UCAL_ZONE_TYPE_CANONICAL_LOCATION) {
647                     UnicodeString canonicalID;
648                     ZoneMeta::getCanonicalCLDRID(id, canonicalID, ec);
649                     if (U_FAILURE(ec)) {
650                         break;
651                     }
652                     if (canonicalID != id) {
653                         // exclude aliases
654                         continue;
655                     }
656                 }
657                 if (type == UCAL_ZONE_TYPE_CANONICAL_LOCATION) {
658                     const UChar *region = TimeZone::getRegion(id, ec);
659                     if (U_FAILURE(ec)) {
660                         break;
661                     }
662                     if (u_strcmp(region, WORLD) == 0) {
663                        // exclude non-location ("001")
664                         continue;
665                     }
666                 }
667                 m[numEntries++] = i;
668             }
669             if (U_SUCCESS(ec)) {
670                 int32_t *tmp = m;
671                 m = (int32_t *)uprv_realloc(tmp, numEntries * sizeof(int32_t));
672                 if (m == NULL) {
673                     // realloc failed.. use the original one even it has unused
674                     // area at the end
675                     m = tmp;
676                 }
677 
678                 switch(type) {
679                 case UCAL_ZONE_TYPE_ANY:
680                     U_ASSERT(MAP_SYSTEM_ZONES == NULL);
681                     MAP_SYSTEM_ZONES = m;
682                     LEN_SYSTEM_ZONES = numEntries;
683                     break;
684                 case UCAL_ZONE_TYPE_CANONICAL:
685                     U_ASSERT(MAP_CANONICAL_SYSTEM_ZONES == NULL);
686                     MAP_CANONICAL_SYSTEM_ZONES = m;
687                     LEN_CANONICAL_SYSTEM_ZONES = numEntries;
688                     break;
689                 case UCAL_ZONE_TYPE_CANONICAL_LOCATION:
690                     U_ASSERT(MAP_CANONICAL_SYSTEM_LOCATION_ZONES == NULL);
691                     MAP_CANONICAL_SYSTEM_LOCATION_ZONES = m;
692                     LEN_CANONICAL_SYSTEM_LOCATION_ZONES = numEntries;
693                     break;
694                 }
695             }
696         }
697     }
698     ures_close(res);
699 }
700 
701 
702 /**
703  * This is the default implementation for subclasses that do not
704  * override this method.  This implementation calls through to the
705  * 8-argument getOffset() method after suitable computations, and
706  * correctly adjusts GMT millis to local millis when necessary.
707  */
getOffset(UDate date,UBool local,int32_t & rawOffset,int32_t & dstOffset,UErrorCode & ec) const708 void TimeZone::getOffset(UDate date, UBool local, int32_t& rawOffset,
709                          int32_t& dstOffset, UErrorCode& ec) const {
710     if (U_FAILURE(ec)) {
711         return;
712     }
713 
714     rawOffset = getRawOffset();
715     if (!local) {
716         date += rawOffset; // now in local standard millis
717     }
718 
719     // When local == TRUE, date might not be in local standard
720     // millis.  getOffset taking 7 parameters used here assume
721     // the given time in day is local standard time.
722     // At STD->DST transition, there is a range of time which
723     // does not exist.  When 'date' is in this time range
724     // (and local == TRUE), this method interprets the specified
725     // local time as DST.  At DST->STD transition, there is a
726     // range of time which occurs twice.  In this case, this
727     // method interprets the specified local time as STD.
728     // To support the behavior above, we need to call getOffset
729     // (with 7 args) twice when local == true and DST is
730     // detected in the initial call.
731     for (int32_t pass=0; ; ++pass) {
732         int32_t year, month, dom, dow;
733         double day = uprv_floor(date / U_MILLIS_PER_DAY);
734         int32_t millis = (int32_t) (date - day * U_MILLIS_PER_DAY);
735 
736         Grego::dayToFields(day, year, month, dom, dow);
737 
738         dstOffset = getOffset(GregorianCalendar::AD, year, month, dom,
739                               (uint8_t) dow, millis,
740                               Grego::monthLength(year, month),
741                               ec) - rawOffset;
742 
743         // Recompute if local==TRUE, dstOffset!=0.
744         if (pass!=0 || !local || dstOffset == 0) {
745             break;
746         }
747         // adjust to local standard millis
748         date -= dstOffset;
749     }
750 }
751 
752 // -------------------------------------
753 
754 // New available IDs API as of ICU 2.4.  Uses StringEnumeration API.
755 
756 class TZEnumeration : public StringEnumeration {
757 private:
758 
759     // Map into to zones.  Our results are zone[map[i]] for
760     // i=0..len-1, where zone[i] is the i-th Olson zone.  If map==NULL
761     // then our results are zone[i] for i=0..len-1.  Len will be zero
762     // if the zone data could not be loaded.
763     int32_t* map;
764     int32_t* localMap;
765     int32_t  len;
766     int32_t  pos;
767 
TZEnumeration(int32_t * mapData,int32_t mapLen,UBool adoptMapData)768     TZEnumeration(int32_t* mapData, int32_t mapLen, UBool adoptMapData) : pos(0) {
769         map = mapData;
770         localMap = adoptMapData ? mapData : NULL;
771         len = mapLen;
772     }
773 
getID(int32_t i,UErrorCode & ec)774     UBool getID(int32_t i, UErrorCode& ec) {
775         int32_t idLen = 0;
776         const UChar* id = NULL;
777         UResourceBundle *top = ures_openDirect(0, kZONEINFO, &ec);
778         top = ures_getByKey(top, kNAMES, top, &ec); // dereference Zones section
779         id = ures_getStringByIndex(top, i, &idLen, &ec);
780         if(U_FAILURE(ec)) {
781             unistr.truncate(0);
782         }
783         else {
784             unistr.fastCopyFrom(UnicodeString(TRUE, id, idLen));
785         }
786         ures_close(top);
787         return U_SUCCESS(ec);
788     }
789 
getMap(USystemTimeZoneType type,int32_t & len,UErrorCode & ec)790     static int32_t* getMap(USystemTimeZoneType type, int32_t& len, UErrorCode& ec) {
791         len = 0;
792         if (U_FAILURE(ec)) {
793             return NULL;
794         }
795         int32_t* m = NULL;
796         switch (type) {
797         case UCAL_ZONE_TYPE_ANY:
798             umtx_initOnce(gSystemZonesInitOnce, &initMap, type, ec);
799             m = MAP_SYSTEM_ZONES;
800             len = LEN_SYSTEM_ZONES;
801             break;
802         case UCAL_ZONE_TYPE_CANONICAL:
803             umtx_initOnce(gCanonicalZonesInitOnce, &initMap, type, ec);
804             m = MAP_CANONICAL_SYSTEM_ZONES;
805             len = LEN_CANONICAL_SYSTEM_ZONES;
806             break;
807         case UCAL_ZONE_TYPE_CANONICAL_LOCATION:
808             umtx_initOnce(gCanonicalLocationZonesInitOnce, &initMap, type, ec);
809             m = MAP_CANONICAL_SYSTEM_LOCATION_ZONES;
810             len = LEN_CANONICAL_SYSTEM_LOCATION_ZONES;
811             break;
812         default:
813             ec = U_ILLEGAL_ARGUMENT_ERROR;
814             m = NULL;
815             len = 0;
816             break;
817         }
818         return m;
819     }
820 
821 public:
822 
823 #define DEFAULT_FILTERED_MAP_SIZE 8
824 #define MAP_INCREMENT_SIZE 8
825 
create(USystemTimeZoneType type,const char * region,const int32_t * rawOffset,UErrorCode & ec)826     static TZEnumeration* create(USystemTimeZoneType type, const char* region, const int32_t* rawOffset, UErrorCode& ec) {
827         if (U_FAILURE(ec)) {
828             return NULL;
829         }
830 
831         int32_t baseLen;
832         int32_t *baseMap = getMap(type, baseLen, ec);
833 
834         if (U_FAILURE(ec)) {
835             return NULL;
836         }
837 
838         // If any additional conditions are available,
839         // create instance local map filtered by the conditions.
840 
841         int32_t *filteredMap = NULL;
842         int32_t numEntries = 0;
843 
844         if (region != NULL || rawOffset != NULL) {
845             int32_t filteredMapSize = DEFAULT_FILTERED_MAP_SIZE;
846             filteredMap = (int32_t *)uprv_malloc(filteredMapSize * sizeof(int32_t));
847             if (filteredMap == NULL) {
848                 ec = U_MEMORY_ALLOCATION_ERROR;
849                 return NULL;
850             }
851 
852             // Walk through the base map
853             UResourceBundle *res = ures_openDirect(0, kZONEINFO, &ec);
854             res = ures_getByKey(res, kNAMES, res, &ec); // dereference Zones section
855             for (int32_t i = 0; i < baseLen; i++) {
856                 int32_t zidx = baseMap[i];
857                 UnicodeString id = ures_getUnicodeStringByIndex(res, zidx, &ec);
858                 if (U_FAILURE(ec)) {
859                     break;
860                 }
861                 if (region != NULL) {
862                     // Filter by region
863                     char tzregion[4]; // max 3 letters + null term
864                     TimeZone::getRegion(id, tzregion, sizeof(tzregion), ec);
865                     if (U_FAILURE(ec)) {
866                         break;
867                     }
868                     if (uprv_stricmp(tzregion, region) != 0) {
869                         // region does not match
870                         continue;
871                     }
872                 }
873                 if (rawOffset != NULL) {
874                     // Filter by raw offset
875                     // Note: This is VERY inefficient
876                     TimeZone *z = createSystemTimeZone(id, ec);
877                     if (U_FAILURE(ec)) {
878                         break;
879                     }
880                     int32_t tzoffset = z->getRawOffset();
881                     delete z;
882 
883                     if (tzoffset != *rawOffset) {
884                         continue;
885                     }
886                 }
887 
888                 if (filteredMapSize <= numEntries) {
889                     filteredMapSize += MAP_INCREMENT_SIZE;
890                     int32_t *tmp = (int32_t *)uprv_realloc(filteredMap, filteredMapSize * sizeof(int32_t));
891                     if (tmp == NULL) {
892                         ec = U_MEMORY_ALLOCATION_ERROR;
893                         break;
894                     } else {
895                         filteredMap = tmp;
896                     }
897                 }
898 
899                 filteredMap[numEntries++] = zidx;
900             }
901 
902             if (U_FAILURE(ec)) {
903                 uprv_free(filteredMap);
904                 filteredMap = NULL;
905             }
906 
907             ures_close(res);
908         }
909 
910         TZEnumeration *result = NULL;
911         if (U_SUCCESS(ec)) {
912             // Finally, create a new enumeration instance
913             if (filteredMap == NULL) {
914                 result = new TZEnumeration(baseMap, baseLen, FALSE);
915             } else {
916                 result = new TZEnumeration(filteredMap, numEntries, TRUE);
917                 filteredMap = NULL;
918             }
919             if (result == NULL) {
920                 ec = U_MEMORY_ALLOCATION_ERROR;
921             }
922         }
923 
924         if (filteredMap != NULL) {
925             uprv_free(filteredMap);
926         }
927 
928         return result;
929     }
930 
TZEnumeration(const TZEnumeration & other)931     TZEnumeration(const TZEnumeration &other) : StringEnumeration(), map(NULL), localMap(NULL), len(0), pos(0) {
932         if (other.localMap != NULL) {
933             localMap = (int32_t *)uprv_malloc(other.len * sizeof(int32_t));
934             if (localMap != NULL) {
935                 len = other.len;
936                 uprv_memcpy(localMap, other.localMap, len * sizeof(int32_t));
937                 pos = other.pos;
938                 map = localMap;
939             } else {
940                 len = 0;
941                 pos = 0;
942                 map = NULL;
943             }
944         } else {
945             map = other.map;
946             localMap = NULL;
947             len = other.len;
948             pos = other.pos;
949         }
950     }
951 
952     virtual ~TZEnumeration();
953 
clone() const954     virtual StringEnumeration *clone() const {
955         return new TZEnumeration(*this);
956     }
957 
count(UErrorCode & status) const958     virtual int32_t count(UErrorCode& status) const {
959         return U_FAILURE(status) ? 0 : len;
960     }
961 
snext(UErrorCode & status)962     virtual const UnicodeString* snext(UErrorCode& status) {
963         if (U_SUCCESS(status) && map != NULL && pos < len) {
964             getID(map[pos], status);
965             ++pos;
966             return &unistr;
967         }
968         return 0;
969     }
970 
reset(UErrorCode &)971     virtual void reset(UErrorCode& /*status*/) {
972         pos = 0;
973     }
974 
975 public:
976     static UClassID U_EXPORT2 getStaticClassID(void);
977     virtual UClassID getDynamicClassID(void) const;
978 };
979 
~TZEnumeration()980 TZEnumeration::~TZEnumeration() {
981     if (localMap != NULL) {
982         uprv_free(localMap);
983     }
984 }
985 
UOBJECT_DEFINE_RTTI_IMPLEMENTATION(TZEnumeration)986 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(TZEnumeration)
987 
988 StringEnumeration* U_EXPORT2
989 TimeZone::createTimeZoneIDEnumeration(
990             USystemTimeZoneType zoneType,
991             const char* region,
992             const int32_t* rawOffset,
993             UErrorCode& ec) {
994     return TZEnumeration::create(zoneType, region, rawOffset, ec);
995 }
996 
997 StringEnumeration* U_EXPORT2
createEnumeration()998 TimeZone::createEnumeration() {
999     UErrorCode ec = U_ZERO_ERROR;
1000     return TZEnumeration::create(UCAL_ZONE_TYPE_ANY, NULL, NULL, ec);
1001 }
1002 
1003 StringEnumeration* U_EXPORT2
createEnumeration(int32_t rawOffset)1004 TimeZone::createEnumeration(int32_t rawOffset) {
1005     UErrorCode ec = U_ZERO_ERROR;
1006     return TZEnumeration::create(UCAL_ZONE_TYPE_ANY, NULL, &rawOffset, ec);
1007 }
1008 
1009 StringEnumeration* U_EXPORT2
createEnumeration(const char * country)1010 TimeZone::createEnumeration(const char* country) {
1011     UErrorCode ec = U_ZERO_ERROR;
1012     return TZEnumeration::create(UCAL_ZONE_TYPE_ANY, country, NULL, ec);
1013 }
1014 
1015 // ---------------------------------------
1016 
1017 int32_t U_EXPORT2
countEquivalentIDs(const UnicodeString & id)1018 TimeZone::countEquivalentIDs(const UnicodeString& id) {
1019     int32_t result = 0;
1020     UErrorCode ec = U_ZERO_ERROR;
1021     StackUResourceBundle res;
1022     U_DEBUG_TZ_MSG(("countEquivalentIDs..\n"));
1023     UResourceBundle *top = openOlsonResource(id, res.ref(), ec);
1024     if (U_SUCCESS(ec)) {
1025         StackUResourceBundle r;
1026         ures_getByKey(res.getAlias(), kLINKS, r.getAlias(), &ec);
1027         ures_getIntVector(r.getAlias(), &result, &ec);
1028     }
1029     ures_close(top);
1030     return result;
1031 }
1032 
1033 // ---------------------------------------
1034 
1035 const UnicodeString U_EXPORT2
getEquivalentID(const UnicodeString & id,int32_t index)1036 TimeZone::getEquivalentID(const UnicodeString& id, int32_t index) {
1037     U_DEBUG_TZ_MSG(("gEI(%d)\n", index));
1038     UnicodeString result;
1039     UErrorCode ec = U_ZERO_ERROR;
1040     StackUResourceBundle res;
1041     UResourceBundle *top = openOlsonResource(id, res.ref(), ec);
1042     int32_t zone = -1;
1043     if (U_SUCCESS(ec)) {
1044         StackUResourceBundle r;
1045         int32_t size;
1046         ures_getByKey(res.getAlias(), kLINKS, r.getAlias(), &ec);
1047         const int32_t *v = ures_getIntVector(r.getAlias(), &size, &ec);
1048         if (U_SUCCESS(ec)) {
1049             if (index >= 0 && index < size) {
1050                 zone = v[index];
1051             }
1052         }
1053     }
1054     if (zone >= 0) {
1055         UResourceBundle *ares = ures_getByKey(top, kNAMES, NULL, &ec); // dereference Zones section
1056         if (U_SUCCESS(ec)) {
1057             int32_t idLen = 0;
1058             const UChar* id2 = ures_getStringByIndex(ares, zone, &idLen, &ec);
1059             result.fastCopyFrom(UnicodeString(TRUE, id2, idLen));
1060             U_DEBUG_TZ_MSG(("gei(%d) -> %d, len%d, %s\n", index, zone, result.length(), u_errorName(ec)));
1061         }
1062         ures_close(ares);
1063     }
1064     ures_close(top);
1065 #if defined(U_DEBUG_TZ)
1066     if(result.length() ==0) {
1067       U_DEBUG_TZ_MSG(("equiv [__, #%d] -> 0 (%s)\n", index, u_errorName(ec)));
1068     }
1069 #endif
1070     return result;
1071 }
1072 
1073 // ---------------------------------------
1074 
1075 // These methods are used by ZoneMeta class only.
1076 
1077 const UChar*
findID(const UnicodeString & id)1078 TimeZone::findID(const UnicodeString& id) {
1079     const UChar *result = NULL;
1080     UErrorCode ec = U_ZERO_ERROR;
1081     UResourceBundle *rb = ures_openDirect(NULL, kZONEINFO, &ec);
1082 
1083     // resolve zone index by name
1084     UResourceBundle *names = ures_getByKey(rb, kNAMES, NULL, &ec);
1085     int32_t idx = findInStringArray(names, id, ec);
1086     result = ures_getStringByIndex(names, idx, NULL, &ec);
1087     if (U_FAILURE(ec)) {
1088         result = NULL;
1089     }
1090     ures_close(names);
1091     ures_close(rb);
1092     return result;
1093 }
1094 
1095 
1096 const UChar*
dereferOlsonLink(const UnicodeString & id)1097 TimeZone::dereferOlsonLink(const UnicodeString& id) {
1098     const UChar *result = NULL;
1099     UErrorCode ec = U_ZERO_ERROR;
1100     UResourceBundle *rb = ures_openDirect(NULL, kZONEINFO, &ec);
1101 
1102     // resolve zone index by name
1103     UResourceBundle *names = ures_getByKey(rb, kNAMES, NULL, &ec);
1104     int32_t idx = findInStringArray(names, id, ec);
1105     result = ures_getStringByIndex(names, idx, NULL, &ec);
1106 
1107     // open the zone bundle by index
1108     ures_getByKey(rb, kZONES, rb, &ec);
1109     ures_getByIndex(rb, idx, rb, &ec);
1110 
1111     if (U_SUCCESS(ec)) {
1112         if (ures_getType(rb) == URES_INT) {
1113             // this is a link - dereference the link
1114             int32_t deref = ures_getInt(rb, &ec);
1115             const UChar* tmp = ures_getStringByIndex(names, deref, NULL, &ec);
1116             if (U_SUCCESS(ec)) {
1117                 result = tmp;
1118             }
1119         }
1120     }
1121 
1122     ures_close(names);
1123     ures_close(rb);
1124 
1125     return result;
1126 }
1127 
1128 const UChar*
getRegion(const UnicodeString & id)1129 TimeZone::getRegion(const UnicodeString& id) {
1130     UErrorCode status = U_ZERO_ERROR;
1131     return getRegion(id, status);
1132 }
1133 
1134 const UChar*
getRegion(const UnicodeString & id,UErrorCode & status)1135 TimeZone::getRegion(const UnicodeString& id, UErrorCode& status) {
1136     if (U_FAILURE(status)) {
1137         return NULL;
1138     }
1139     const UChar *result = NULL;
1140     UResourceBundle *rb = ures_openDirect(NULL, kZONEINFO, &status);
1141 
1142     // resolve zone index by name
1143     UResourceBundle *res = ures_getByKey(rb, kNAMES, NULL, &status);
1144     int32_t idx = findInStringArray(res, id, status);
1145 
1146     // get region mapping
1147     ures_getByKey(rb, kREGIONS, res, &status);
1148     const UChar *tmp = ures_getStringByIndex(res, idx, NULL, &status);
1149     if (U_SUCCESS(status)) {
1150         result = tmp;
1151     }
1152 
1153     ures_close(res);
1154     ures_close(rb);
1155 
1156     return result;
1157 }
1158 
1159 
1160 // ---------------------------------------
1161 int32_t
getRegion(const UnicodeString & id,char * region,int32_t capacity,UErrorCode & status)1162 TimeZone::getRegion(const UnicodeString& id, char *region, int32_t capacity, UErrorCode& status)
1163 {
1164     int32_t resultLen = 0;
1165     *region = 0;
1166     if (U_FAILURE(status)) {
1167         return 0;
1168     }
1169 
1170     const UChar *uregion = NULL;
1171     // "Etc/Unknown" is not a system zone ID,
1172     // but in the zone data
1173     if (id.compare(UNKNOWN_ZONE_ID, UNKNOWN_ZONE_ID_LENGTH) != 0) {
1174         uregion = getRegion(id);
1175     }
1176     if (uregion == NULL) {
1177         status = U_ILLEGAL_ARGUMENT_ERROR;
1178         return 0;
1179     }
1180     resultLen = u_strlen(uregion);
1181     // A region code is represented by invariant characters
1182     u_UCharsToChars(uregion, region, uprv_min(resultLen, capacity));
1183 
1184     if (capacity < resultLen) {
1185         status = U_BUFFER_OVERFLOW_ERROR;
1186         return resultLen;
1187     }
1188 
1189     return u_terminateChars(region, capacity, resultLen, &status);
1190 }
1191 
1192 // ---------------------------------------
1193 
1194 
1195 UnicodeString&
getDisplayName(UnicodeString & result) const1196 TimeZone::getDisplayName(UnicodeString& result) const
1197 {
1198     return getDisplayName(FALSE,LONG,Locale::getDefault(), result);
1199 }
1200 
1201 UnicodeString&
getDisplayName(const Locale & locale,UnicodeString & result) const1202 TimeZone::getDisplayName(const Locale& locale, UnicodeString& result) const
1203 {
1204     return getDisplayName(FALSE, LONG, locale, result);
1205 }
1206 
1207 UnicodeString&
getDisplayName(UBool inDaylight,EDisplayType style,UnicodeString & result) const1208 TimeZone::getDisplayName(UBool inDaylight, EDisplayType style, UnicodeString& result)  const
1209 {
1210     return getDisplayName(inDaylight,style, Locale::getDefault(), result);
1211 }
1212 //--------------------------------------
1213 int32_t
getDSTSavings() const1214 TimeZone::getDSTSavings()const {
1215     if (useDaylightTime()) {
1216         return 3600000;
1217     }
1218     return 0;
1219 }
1220 //---------------------------------------
1221 UnicodeString&
getDisplayName(UBool inDaylight,EDisplayType style,const Locale & locale,UnicodeString & result) const1222 TimeZone::getDisplayName(UBool inDaylight, EDisplayType style, const Locale& locale, UnicodeString& result) const
1223 {
1224     UErrorCode status = U_ZERO_ERROR;
1225     UDate date = Calendar::getNow();
1226     UTimeZoneFormatTimeType timeType = UTZFMT_TIME_TYPE_UNKNOWN;
1227     int32_t offset;
1228 
1229     if (style == GENERIC_LOCATION || style == LONG_GENERIC || style == SHORT_GENERIC) {
1230         LocalPointer<TimeZoneFormat> tzfmt(TimeZoneFormat::createInstance(locale, status));
1231         if (U_FAILURE(status)) {
1232             result.remove();
1233             return result;
1234         }
1235         // Generic format
1236         switch (style) {
1237         case GENERIC_LOCATION:
1238             tzfmt->format(UTZFMT_STYLE_GENERIC_LOCATION, *this, date, result, &timeType);
1239             break;
1240         case LONG_GENERIC:
1241             tzfmt->format(UTZFMT_STYLE_GENERIC_LONG, *this, date, result, &timeType);
1242             break;
1243         case SHORT_GENERIC:
1244             tzfmt->format(UTZFMT_STYLE_GENERIC_SHORT, *this, date, result, &timeType);
1245             break;
1246         default:
1247             UPRV_UNREACHABLE;
1248         }
1249         // Generic format many use Localized GMT as the final fallback.
1250         // When Localized GMT format is used, the result might not be
1251         // appropriate for the requested daylight value.
1252         if ((inDaylight && timeType == UTZFMT_TIME_TYPE_STANDARD) || (!inDaylight && timeType == UTZFMT_TIME_TYPE_DAYLIGHT)) {
1253             offset = inDaylight ? getRawOffset() + getDSTSavings() : getRawOffset();
1254             if (style == SHORT_GENERIC) {
1255                 tzfmt->formatOffsetShortLocalizedGMT(offset, result, status);
1256             } else {
1257                 tzfmt->formatOffsetLocalizedGMT(offset, result, status);
1258             }
1259         }
1260     } else if (style == LONG_GMT || style == SHORT_GMT) {
1261         LocalPointer<TimeZoneFormat> tzfmt(TimeZoneFormat::createInstance(locale, status));
1262         if (U_FAILURE(status)) {
1263             result.remove();
1264             return result;
1265         }
1266         offset = inDaylight && useDaylightTime() ? getRawOffset() + getDSTSavings() : getRawOffset();
1267         switch (style) {
1268         case LONG_GMT:
1269             tzfmt->formatOffsetLocalizedGMT(offset, result, status);
1270             break;
1271         case SHORT_GMT:
1272             tzfmt->formatOffsetISO8601Basic(offset, FALSE, FALSE, FALSE, result, status);
1273             break;
1274         default:
1275             UPRV_UNREACHABLE;
1276         }
1277 
1278     } else {
1279         U_ASSERT(style == LONG || style == SHORT || style == SHORT_COMMONLY_USED);
1280         UTimeZoneNameType nameType = UTZNM_UNKNOWN;
1281         switch (style) {
1282         case LONG:
1283             nameType = inDaylight ? UTZNM_LONG_DAYLIGHT : UTZNM_LONG_STANDARD;
1284             break;
1285         case SHORT:
1286         case SHORT_COMMONLY_USED:
1287             nameType = inDaylight ? UTZNM_SHORT_DAYLIGHT : UTZNM_SHORT_STANDARD;
1288             break;
1289         default:
1290             UPRV_UNREACHABLE;
1291         }
1292         LocalPointer<TimeZoneNames> tznames(TimeZoneNames::createInstance(locale, status));
1293         if (U_FAILURE(status)) {
1294             result.remove();
1295             return result;
1296         }
1297         UnicodeString canonicalID(ZoneMeta::getCanonicalCLDRID(*this));
1298         tznames->getDisplayName(canonicalID, nameType, date, result);
1299         if (result.isEmpty()) {
1300             // Fallback to localized GMT
1301             LocalPointer<TimeZoneFormat> tzfmt(TimeZoneFormat::createInstance(locale, status));
1302             offset = inDaylight && useDaylightTime() ? getRawOffset() + getDSTSavings() : getRawOffset();
1303             if (style == LONG) {
1304                 tzfmt->formatOffsetLocalizedGMT(offset, result, status);
1305             } else {
1306                 tzfmt->formatOffsetShortLocalizedGMT(offset, result, status);
1307             }
1308         }
1309     }
1310     if (U_FAILURE(status)) {
1311         result.remove();
1312     }
1313     return  result;
1314 }
1315 
1316 /**
1317  * Parse a custom time zone identifier and return a corresponding zone.
1318  * @param id a string of the form GMT[+-]hh:mm, GMT[+-]hhmm, or
1319  * GMT[+-]hh.
1320  * @return a newly created SimpleTimeZone with the given offset and
1321  * no Daylight Savings Time, or null if the id cannot be parsed.
1322 */
1323 TimeZone*
createCustomTimeZone(const UnicodeString & id)1324 TimeZone::createCustomTimeZone(const UnicodeString& id)
1325 {
1326     int32_t sign, hour, min, sec;
1327     if (parseCustomID(id, sign, hour, min, sec)) {
1328         UnicodeString customID;
1329         formatCustomID(hour, min, sec, (sign < 0), customID);
1330         int32_t offset = sign * ((hour * 60 + min) * 60 + sec) * 1000;
1331         return new SimpleTimeZone(offset, customID);
1332     }
1333     return NULL;
1334 }
1335 
1336 UnicodeString&
getCustomID(const UnicodeString & id,UnicodeString & normalized,UErrorCode & status)1337 TimeZone::getCustomID(const UnicodeString& id, UnicodeString& normalized, UErrorCode& status) {
1338     normalized.remove();
1339     if (U_FAILURE(status)) {
1340         return normalized;
1341     }
1342     int32_t sign, hour, min, sec;
1343     if (parseCustomID(id, sign, hour, min, sec)) {
1344         formatCustomID(hour, min, sec, (sign < 0), normalized);
1345     } else {
1346         status = U_ILLEGAL_ARGUMENT_ERROR;
1347     }
1348     return normalized;
1349 }
1350 
1351 UBool
parseCustomID(const UnicodeString & id,int32_t & sign,int32_t & hour,int32_t & min,int32_t & sec)1352 TimeZone::parseCustomID(const UnicodeString& id, int32_t& sign,
1353                         int32_t& hour, int32_t& min, int32_t& sec) {
1354     static const int32_t         kParseFailed = -99999;
1355 
1356     NumberFormat* numberFormat = 0;
1357     UnicodeString idUppercase = id;
1358     idUppercase.toUpper("");
1359 
1360     if (id.length() > GMT_ID_LENGTH &&
1361         idUppercase.startsWith(GMT_ID, GMT_ID_LENGTH))
1362     {
1363         ParsePosition pos(GMT_ID_LENGTH);
1364         sign = 1;
1365         hour = 0;
1366         min = 0;
1367         sec = 0;
1368 
1369         if (id[pos.getIndex()] == MINUS /*'-'*/) {
1370             sign = -1;
1371         } else if (id[pos.getIndex()] != PLUS /*'+'*/) {
1372             return FALSE;
1373         }
1374         pos.setIndex(pos.getIndex() + 1);
1375 
1376         UErrorCode success = U_ZERO_ERROR;
1377         numberFormat = NumberFormat::createInstance(success);
1378         if(U_FAILURE(success)){
1379             return FALSE;
1380         }
1381         numberFormat->setParseIntegerOnly(TRUE);
1382         //numberFormat->setLenient(TRUE); // TODO: May need to set this, depends on latest timezone parsing
1383 
1384         // Look for either hh:mm, hhmm, or hh
1385         int32_t start = pos.getIndex();
1386         Formattable n(kParseFailed);
1387         numberFormat->parse(id, n, pos);
1388         if (pos.getIndex() == start) {
1389             delete numberFormat;
1390             return FALSE;
1391         }
1392         hour = n.getLong();
1393 
1394         if (pos.getIndex() < id.length()) {
1395             if (pos.getIndex() - start > 2
1396                 || id[pos.getIndex()] != COLON) {
1397                 delete numberFormat;
1398                 return FALSE;
1399             }
1400             // hh:mm
1401             pos.setIndex(pos.getIndex() + 1);
1402             int32_t oldPos = pos.getIndex();
1403             n.setLong(kParseFailed);
1404             numberFormat->parse(id, n, pos);
1405             if ((pos.getIndex() - oldPos) != 2) {
1406                 // must be 2 digits
1407                 delete numberFormat;
1408                 return FALSE;
1409             }
1410             min = n.getLong();
1411             if (pos.getIndex() < id.length()) {
1412                 if (id[pos.getIndex()] != COLON) {
1413                     delete numberFormat;
1414                     return FALSE;
1415                 }
1416                 // [:ss]
1417                 pos.setIndex(pos.getIndex() + 1);
1418                 oldPos = pos.getIndex();
1419                 n.setLong(kParseFailed);
1420                 numberFormat->parse(id, n, pos);
1421                 if (pos.getIndex() != id.length()
1422                         || (pos.getIndex() - oldPos) != 2) {
1423                     delete numberFormat;
1424                     return FALSE;
1425                 }
1426                 sec = n.getLong();
1427             }
1428         } else {
1429             // Supported formats are below -
1430             //
1431             // HHmmss
1432             // Hmmss
1433             // HHmm
1434             // Hmm
1435             // HH
1436             // H
1437 
1438             int32_t length = pos.getIndex() - start;
1439             if (length <= 0 || 6 < length) {
1440                 // invalid length
1441                 delete numberFormat;
1442                 return FALSE;
1443             }
1444             switch (length) {
1445                 case 1:
1446                 case 2:
1447                     // already set to hour
1448                     break;
1449                 case 3:
1450                 case 4:
1451                     min = hour % 100;
1452                     hour /= 100;
1453                     break;
1454                 case 5:
1455                 case 6:
1456                     sec = hour % 100;
1457                     min = (hour/100) % 100;
1458                     hour /= 10000;
1459                     break;
1460             }
1461         }
1462 
1463         delete numberFormat;
1464 
1465         if (hour > kMAX_CUSTOM_HOUR || min > kMAX_CUSTOM_MIN || sec > kMAX_CUSTOM_SEC) {
1466             return FALSE;
1467         }
1468         return TRUE;
1469     }
1470     return FALSE;
1471 }
1472 
1473 UnicodeString&
formatCustomID(int32_t hour,int32_t min,int32_t sec,UBool negative,UnicodeString & id)1474 TimeZone::formatCustomID(int32_t hour, int32_t min, int32_t sec,
1475                          UBool negative, UnicodeString& id) {
1476     // Create time zone ID - GMT[+|-]hhmm[ss]
1477     id.setTo(GMT_ID, GMT_ID_LENGTH);
1478     if (hour | min | sec) {
1479         if (negative) {
1480             id += (UChar)MINUS;
1481         } else {
1482             id += (UChar)PLUS;
1483         }
1484 
1485         if (hour < 10) {
1486             id += (UChar)ZERO_DIGIT;
1487         } else {
1488             id += (UChar)(ZERO_DIGIT + hour/10);
1489         }
1490         id += (UChar)(ZERO_DIGIT + hour%10);
1491         id += (UChar)COLON;
1492         if (min < 10) {
1493             id += (UChar)ZERO_DIGIT;
1494         } else {
1495             id += (UChar)(ZERO_DIGIT + min/10);
1496         }
1497         id += (UChar)(ZERO_DIGIT + min%10);
1498 
1499         if (sec) {
1500             id += (UChar)COLON;
1501             if (sec < 10) {
1502                 id += (UChar)ZERO_DIGIT;
1503             } else {
1504                 id += (UChar)(ZERO_DIGIT + sec/10);
1505             }
1506             id += (UChar)(ZERO_DIGIT + sec%10);
1507         }
1508     }
1509     return id;
1510 }
1511 
1512 
1513 UBool
hasSameRules(const TimeZone & other) const1514 TimeZone::hasSameRules(const TimeZone& other) const
1515 {
1516     return (getRawOffset() == other.getRawOffset() &&
1517             useDaylightTime() == other.useDaylightTime());
1518 }
1519 
initTZDataVersion(UErrorCode & status)1520 static void U_CALLCONV initTZDataVersion(UErrorCode &status) {
1521     ucln_i18n_registerCleanup(UCLN_I18N_TIMEZONE, timeZone_cleanup);
1522     int32_t len = 0;
1523     StackUResourceBundle bundle;
1524     ures_openDirectFillIn(bundle.getAlias(), NULL, kZONEINFO, &status);
1525     const UChar *tzver = ures_getStringByKey(bundle.getAlias(), kTZVERSION, &len, &status);
1526 
1527     if (U_SUCCESS(status)) {
1528         if (len >= (int32_t)sizeof(TZDATA_VERSION)) {
1529             // Ensure that there is always space for a trailing nul in TZDATA_VERSION
1530             len = sizeof(TZDATA_VERSION) - 1;
1531         }
1532         u_UCharsToChars(tzver, TZDATA_VERSION, len);
1533     }
1534 }
1535 
1536 const char*
getTZDataVersion(UErrorCode & status)1537 TimeZone::getTZDataVersion(UErrorCode& status)
1538 {
1539     umtx_initOnce(gTZDataVersionInitOnce, &initTZDataVersion, status);
1540     return (const char*)TZDATA_VERSION;
1541 }
1542 
1543 UnicodeString&
getCanonicalID(const UnicodeString & id,UnicodeString & canonicalID,UErrorCode & status)1544 TimeZone::getCanonicalID(const UnicodeString& id, UnicodeString& canonicalID, UErrorCode& status)
1545 {
1546     UBool isSystemID = FALSE;
1547     return getCanonicalID(id, canonicalID, isSystemID, status);
1548 }
1549 
1550 UnicodeString&
getCanonicalID(const UnicodeString & id,UnicodeString & canonicalID,UBool & isSystemID,UErrorCode & status)1551 TimeZone::getCanonicalID(const UnicodeString& id, UnicodeString& canonicalID, UBool& isSystemID,
1552                          UErrorCode& status)
1553 {
1554     canonicalID.remove();
1555     isSystemID = FALSE;
1556     if (U_FAILURE(status)) {
1557         return canonicalID;
1558     }
1559     if (id.compare(UNKNOWN_ZONE_ID, UNKNOWN_ZONE_ID_LENGTH) == 0) {
1560         // special case - Etc/Unknown is a canonical ID, but not system ID
1561         canonicalID.fastCopyFrom(id);
1562         isSystemID = FALSE;
1563     } else {
1564         ZoneMeta::getCanonicalCLDRID(id, canonicalID, status);
1565         if (U_SUCCESS(status)) {
1566             isSystemID = TRUE;
1567         } else {
1568             // Not a system ID
1569             status = U_ZERO_ERROR;
1570             getCustomID(id, canonicalID, status);
1571         }
1572     }
1573     return canonicalID;
1574 }
1575 
1576 UnicodeString&
getWindowsID(const UnicodeString & id,UnicodeString & winid,UErrorCode & status)1577 TimeZone::getWindowsID(const UnicodeString& id, UnicodeString& winid, UErrorCode& status) {
1578     winid.remove();
1579     if (U_FAILURE(status)) {
1580         return winid;
1581     }
1582 
1583     // canonicalize the input ID
1584     UnicodeString canonicalID;
1585     UBool isSystemID = FALSE;
1586 
1587     getCanonicalID(id, canonicalID, isSystemID, status);
1588     if (U_FAILURE(status) || !isSystemID) {
1589         // mapping data is only applicable to tz database IDs
1590         if (status == U_ILLEGAL_ARGUMENT_ERROR) {
1591             // getWindowsID() sets an empty string where
1592             // getCanonicalID() sets a U_ILLEGAL_ARGUMENT_ERROR.
1593             status = U_ZERO_ERROR;
1594         }
1595         return winid;
1596     }
1597 
1598     UResourceBundle *mapTimezones = ures_openDirect(NULL, "windowsZones", &status);
1599     ures_getByKey(mapTimezones, "mapTimezones", mapTimezones, &status);
1600 
1601     if (U_FAILURE(status)) {
1602         return winid;
1603     }
1604 
1605     UResourceBundle *winzone = NULL;
1606     UBool found = FALSE;
1607     while (ures_hasNext(mapTimezones) && !found) {
1608         winzone = ures_getNextResource(mapTimezones, winzone, &status);
1609         if (U_FAILURE(status)) {
1610             break;
1611         }
1612         if (ures_getType(winzone) != URES_TABLE) {
1613             continue;
1614         }
1615         UResourceBundle *regionalData = NULL;
1616         while (ures_hasNext(winzone) && !found) {
1617             regionalData = ures_getNextResource(winzone, regionalData, &status);
1618             if (U_FAILURE(status)) {
1619                 break;
1620             }
1621             if (ures_getType(regionalData) != URES_STRING) {
1622                 continue;
1623             }
1624             int32_t len;
1625             const UChar *tzids = ures_getString(regionalData, &len, &status);
1626             if (U_FAILURE(status)) {
1627                 break;
1628             }
1629 
1630             const UChar *start = tzids;
1631             UBool hasNext = TRUE;
1632             while (hasNext) {
1633                 const UChar *end = u_strchr(start, (UChar)0x20);
1634                 if (end == NULL) {
1635                     end = tzids + len;
1636                     hasNext = FALSE;
1637                 }
1638                 if (canonicalID.compare(start, static_cast<int32_t>(end - start)) == 0) {
1639                     winid = UnicodeString(ures_getKey(winzone), -1 , US_INV);
1640                     found = TRUE;
1641                     break;
1642                 }
1643                 start = end + 1;
1644             }
1645         }
1646         ures_close(regionalData);
1647     }
1648     ures_close(winzone);
1649     ures_close(mapTimezones);
1650 
1651     return winid;
1652 }
1653 
1654 #define MAX_WINDOWS_ID_SIZE 128
1655 
1656 UnicodeString&
getIDForWindowsID(const UnicodeString & winid,const char * region,UnicodeString & id,UErrorCode & status)1657 TimeZone::getIDForWindowsID(const UnicodeString& winid, const char* region, UnicodeString& id, UErrorCode& status) {
1658     id.remove();
1659     if (U_FAILURE(status)) {
1660         return id;
1661     }
1662 
1663     UResourceBundle *zones = ures_openDirect(NULL, "windowsZones", &status);
1664     ures_getByKey(zones, "mapTimezones", zones, &status);
1665     if (U_FAILURE(status)) {
1666         ures_close(zones);
1667         return id;
1668     }
1669 
1670     UErrorCode tmperr = U_ZERO_ERROR;
1671     char winidKey[MAX_WINDOWS_ID_SIZE];
1672     int32_t winKeyLen = winid.extract(0, winid.length(), winidKey, sizeof(winidKey) - 1, US_INV);
1673 
1674     if (winKeyLen == 0 || winKeyLen >= (int32_t)sizeof(winidKey)) {
1675         ures_close(zones);
1676         return id;
1677     }
1678     winidKey[winKeyLen] = 0;
1679 
1680     ures_getByKey(zones, winidKey, zones, &tmperr); // use tmperr, because windows mapping might not
1681                                                     // be avaiable by design
1682     if (U_FAILURE(tmperr)) {
1683         ures_close(zones);
1684         return id;
1685     }
1686 
1687     const UChar *tzid = NULL;
1688     int32_t len = 0;
1689     UBool gotID = FALSE;
1690     if (region) {
1691         const UChar *tzids = ures_getStringByKey(zones, region, &len, &tmperr); // use tmperr, because
1692                                                                                 // regional mapping is optional
1693         if (U_SUCCESS(tmperr)) {
1694             // first ID delimited by space is the defasult one
1695             const UChar *end = u_strchr(tzids, (UChar)0x20);
1696             if (end == NULL) {
1697                 id.setTo(tzids, -1);
1698             } else {
1699                 id.setTo(tzids, static_cast<int32_t>(end - tzids));
1700             }
1701             gotID = TRUE;
1702         }
1703     }
1704 
1705     if (!gotID) {
1706         tzid = ures_getStringByKey(zones, "001", &len, &status);    // using status, because "001" must be
1707                                                                 // available at this point
1708         if (U_SUCCESS(status)) {
1709             id.setTo(tzid, len);
1710         }
1711     }
1712 
1713     ures_close(zones);
1714     return id;
1715 }
1716 
1717 
1718 U_NAMESPACE_END
1719 
1720 #endif /* #if !UCONFIG_NO_FORMATTING */
1721 
1722 //eof
1723