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