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