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