timezone.cpp (56507B)
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 53 static void debug_tz_loc(const char *f, int32_t l) 54 { 55 fprintf(stderr, "%s:%d: ", f, l); 56 } 57 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 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 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 = static_cast<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 */ 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 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, static_cast<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 */ 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 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 325 TimeZone::getUnknown() 326 { 327 umtx_initOnce(gStaticZonesInitOnce, &initStaticTimeZones); 328 return *reinterpret_cast<SimpleTimeZone*>(gRawUNKNOWN); 329 } 330 331 const TimeZone* U_EXPORT2 332 TimeZone::getGMT() 333 { 334 umtx_initOnce(gStaticZonesInitOnce, &initStaticTimeZones); 335 return reinterpret_cast<SimpleTimeZone*>(gRawGMT); 336 } 337 338 // ***************************************************************************** 339 // class TimeZone 340 // ***************************************************************************** 341 342 UOBJECT_DEFINE_ABSTRACT_RTTI_IMPLEMENTATION(TimeZone) 343 344 TimeZone::TimeZone() 345 : UObject(), fID() 346 { 347 } 348 349 // ------------------------------------- 350 351 TimeZone::TimeZone(const UnicodeString &id) 352 : UObject(), fID(id) 353 { 354 } 355 356 // ------------------------------------- 357 358 TimeZone::~TimeZone() 359 { 360 } 361 362 // ------------------------------------- 363 364 TimeZone::TimeZone(const TimeZone &source) 365 : UObject(source), fID(source.fID) 366 { 367 } 368 369 // ------------------------------------- 370 371 TimeZone & 372 TimeZone::operator=(const TimeZone &right) 373 { 374 if (this != &right) fID = right.fID; 375 return *this; 376 } 377 378 // ------------------------------------- 379 380 bool 381 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* 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* 422 createSystemTimeZone(const UnicodeString& id) { 423 UErrorCode ec = U_ZERO_ERROR; 424 return createSystemTimeZone(id, ec); 425 } 426 427 } 428 429 TimeZone* U_EXPORT2 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 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 */ 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 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 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 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 618 TimeZone::setDefault(const TimeZone& zone) 619 { 620 adoptDefault(zone.clone()); 621 } 622 623 //---------------------------------------------------------------------- 624 625 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 = static_cast<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 = static_cast<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 */ 709 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, millis; 734 int8_t month, dom, dow; 735 Grego::timeToFields(date, year, month, dom, dow, millis, ec); 736 if (U_FAILURE(ec)) return; 737 738 dstOffset = getOffset(GregorianCalendar::AD, year, month, dom, 739 static_cast<uint8_t>(dow), millis, 740 Grego::monthLength(year, month), 741 ec) - rawOffset; 742 743 // Recompute if local==true, dstOffset!=0. 744 if (pass!=0 || !local || dstOffset == 0) { 745 break; 746 } 747 // adjust to local standard millis 748 date -= dstOffset; 749 } 750 } 751 752 // ------------------------------------- 753 754 // New available IDs API as of ICU 2.4. Uses StringEnumeration API. 755 756 class TZEnumeration : public StringEnumeration { 757 private: 758 759 // Map into to zones. Our results are zone[map[i]] for 760 // i=0..len-1, where zone[i] is the i-th Olson zone. If map==nullptr 761 // then our results are zone[i] for i=0..len-1. Len will be zero 762 // if the zone data could not be loaded. 763 int32_t* map; 764 int32_t* localMap; 765 int32_t len; 766 int32_t pos; 767 768 TZEnumeration(int32_t* mapData, int32_t mapLen, UBool adoptMapData) : pos(0) { 769 map = mapData; 770 localMap = adoptMapData ? mapData : nullptr; 771 len = mapLen; 772 } 773 774 UBool getID(int32_t i, UErrorCode& ec) { 775 int32_t idLen = 0; 776 const char16_t* id = nullptr; 777 UResourceBundle *top = ures_openDirect(nullptr, kZONEINFO, &ec); 778 top = ures_getByKey(top, kNAMES, top, &ec); // dereference Zones section 779 id = ures_getStringByIndex(top, i, &idLen, &ec); 780 if(U_FAILURE(ec)) { 781 unistr.truncate(0); 782 } 783 else { 784 unistr.fastCopyFrom(UnicodeString(true, id, idLen)); 785 } 786 ures_close(top); 787 return U_SUCCESS(ec); 788 } 789 790 static int32_t* getMap(USystemTimeZoneType type, int32_t& len, UErrorCode& ec) { 791 len = 0; 792 if (U_FAILURE(ec)) { 793 return nullptr; 794 } 795 int32_t* m = nullptr; 796 switch (type) { 797 case UCAL_ZONE_TYPE_ANY: 798 umtx_initOnce(gSystemZonesInitOnce, &initMap, type, ec); 799 m = MAP_SYSTEM_ZONES; 800 len = LEN_SYSTEM_ZONES; 801 break; 802 case UCAL_ZONE_TYPE_CANONICAL: 803 umtx_initOnce(gCanonicalZonesInitOnce, &initMap, type, ec); 804 m = MAP_CANONICAL_SYSTEM_ZONES; 805 len = LEN_CANONICAL_SYSTEM_ZONES; 806 break; 807 case UCAL_ZONE_TYPE_CANONICAL_LOCATION: 808 umtx_initOnce(gCanonicalLocationZonesInitOnce, &initMap, type, ec); 809 m = MAP_CANONICAL_SYSTEM_LOCATION_ZONES; 810 len = LEN_CANONICAL_SYSTEM_LOCATION_ZONES; 811 break; 812 default: 813 ec = U_ILLEGAL_ARGUMENT_ERROR; 814 m = nullptr; 815 len = 0; 816 break; 817 } 818 return m; 819 } 820 821 public: 822 823 #define DEFAULT_FILTERED_MAP_SIZE 8 824 #define MAP_INCREMENT_SIZE 8 825 826 static TZEnumeration* create(USystemTimeZoneType type, const char* region, const int32_t* rawOffset, UErrorCode& ec) { 827 if (U_FAILURE(ec)) { 828 return nullptr; 829 } 830 831 int32_t baseLen; 832 int32_t *baseMap = getMap(type, baseLen, ec); 833 834 if (U_FAILURE(ec)) { 835 return nullptr; 836 } 837 838 // If any additional conditions are available, 839 // create instance local map filtered by the conditions. 840 841 int32_t *filteredMap = nullptr; 842 int32_t numEntries = 0; 843 844 if (region != nullptr || rawOffset != nullptr) { 845 int32_t filteredMapSize = DEFAULT_FILTERED_MAP_SIZE; 846 filteredMap = static_cast<int32_t*>(uprv_malloc(filteredMapSize * sizeof(int32_t))); 847 if (filteredMap == nullptr) { 848 ec = U_MEMORY_ALLOCATION_ERROR; 849 return nullptr; 850 } 851 852 // Walk through the base map 853 UResourceBundle *res = ures_openDirect(nullptr, kZONEINFO, &ec); 854 res = ures_getByKey(res, kNAMES, res, &ec); // dereference Zones section 855 for (int32_t i = 0; i < baseLen; i++) { 856 int32_t zidx = baseMap[i]; 857 UnicodeString id = ures_getUnicodeStringByIndex(res, zidx, &ec); 858 if (U_FAILURE(ec)) { 859 break; 860 } 861 if (region != nullptr) { 862 // Filter by region 863 char tzregion[4]; // max 3 letters + null term 864 TimeZone::getRegion(id, tzregion, sizeof(tzregion), ec); 865 if (U_FAILURE(ec)) { 866 break; 867 } 868 if (uprv_stricmp(tzregion, region) != 0) { 869 // region does not match 870 continue; 871 } 872 } 873 if (rawOffset != nullptr) { 874 // Filter by raw offset 875 // Note: This is VERY inefficient 876 TimeZone *z = createSystemTimeZone(id, ec); 877 if (U_FAILURE(ec)) { 878 break; 879 } 880 int32_t tzoffset = z->getRawOffset(); 881 delete z; 882 883 if (tzoffset != *rawOffset) { 884 continue; 885 } 886 } 887 888 if (filteredMapSize <= numEntries) { 889 filteredMapSize += MAP_INCREMENT_SIZE; 890 int32_t* tmp = static_cast<int32_t*>(uprv_realloc(filteredMap, filteredMapSize * sizeof(int32_t))); 891 if (tmp == nullptr) { 892 ec = U_MEMORY_ALLOCATION_ERROR; 893 break; 894 } else { 895 filteredMap = tmp; 896 } 897 } 898 899 filteredMap[numEntries++] = zidx; 900 } 901 902 if (U_FAILURE(ec)) { 903 uprv_free(filteredMap); 904 filteredMap = nullptr; 905 } 906 907 ures_close(res); 908 } 909 910 TZEnumeration *result = nullptr; 911 if (U_SUCCESS(ec)) { 912 // Finally, create a new enumeration instance 913 if (filteredMap == nullptr) { 914 result = new TZEnumeration(baseMap, baseLen, false); 915 } else { 916 result = new TZEnumeration(filteredMap, numEntries, true); 917 filteredMap = nullptr; 918 } 919 if (result == nullptr) { 920 ec = U_MEMORY_ALLOCATION_ERROR; 921 } 922 } 923 924 if (filteredMap != nullptr) { 925 uprv_free(filteredMap); 926 } 927 928 return result; 929 } 930 931 TZEnumeration(const TZEnumeration &other) : StringEnumeration(), map(nullptr), localMap(nullptr), len(0), pos(0) { 932 if (other.localMap != nullptr) { 933 localMap = static_cast<int32_t*>(uprv_malloc(other.len * sizeof(int32_t))); 934 if (localMap != nullptr) { 935 len = other.len; 936 uprv_memcpy(localMap, other.localMap, len * sizeof(int32_t)); 937 pos = other.pos; 938 map = localMap; 939 } else { 940 len = 0; 941 pos = 0; 942 map = nullptr; 943 } 944 } else { 945 map = other.map; 946 localMap = nullptr; 947 len = other.len; 948 pos = other.pos; 949 } 950 } 951 952 virtual ~TZEnumeration(); 953 954 virtual StringEnumeration *clone() const override { 955 return new TZEnumeration(*this); 956 } 957 958 virtual int32_t count(UErrorCode& status) const override { 959 return U_FAILURE(status) ? 0 : len; 960 } 961 962 virtual const UnicodeString* snext(UErrorCode& status) override { 963 if (U_SUCCESS(status) && map != nullptr && pos < len) { 964 getID(map[pos], status); 965 ++pos; 966 return &unistr; 967 } 968 return nullptr; 969 } 970 971 virtual void reset(UErrorCode& /*status*/) override { 972 pos = 0; 973 } 974 975 public: 976 static UClassID U_EXPORT2 getStaticClassID(); 977 virtual UClassID getDynamicClassID() const override; 978 }; 979 980 TZEnumeration::~TZEnumeration() { 981 if (localMap != nullptr) { 982 uprv_free(localMap); 983 } 984 } 985 986 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(TZEnumeration) 987 988 StringEnumeration* U_EXPORT2 989 TimeZone::createTimeZoneIDEnumeration( 990 USystemTimeZoneType zoneType, 991 const char* region, 992 const int32_t* rawOffset, 993 UErrorCode& ec) { 994 return TZEnumeration::create(zoneType, region, rawOffset, ec); 995 } 996 997 StringEnumeration* U_EXPORT2 998 TimeZone::createEnumeration(UErrorCode& status) { 999 return TZEnumeration::create(UCAL_ZONE_TYPE_ANY, nullptr, nullptr, status); 1000 } 1001 1002 StringEnumeration* U_EXPORT2 1003 TimeZone::createEnumerationForRawOffset(int32_t rawOffset, UErrorCode& status) { 1004 return TZEnumeration::create(UCAL_ZONE_TYPE_ANY, nullptr, &rawOffset, status); 1005 } 1006 1007 StringEnumeration* U_EXPORT2 1008 TimeZone::createEnumerationForRegion(const char* region, UErrorCode& status) { 1009 return TZEnumeration::create(UCAL_ZONE_TYPE_ANY, region, nullptr, status); 1010 } 1011 1012 // 1013 // Next 3 methods are equivalent to above, but ignores UErrorCode. 1014 // These methods were deprecated in ICU 70. 1015 1016 StringEnumeration* U_EXPORT2 1017 TimeZone::createEnumeration() { 1018 UErrorCode ec = U_ZERO_ERROR; 1019 return createEnumeration(ec); 1020 } 1021 1022 StringEnumeration* U_EXPORT2 1023 TimeZone::createEnumeration(int32_t rawOffset) { 1024 UErrorCode ec = U_ZERO_ERROR; 1025 return createEnumerationForRawOffset(rawOffset, ec); 1026 } 1027 1028 StringEnumeration* U_EXPORT2 1029 TimeZone::createEnumeration(const char* region) { 1030 UErrorCode ec = U_ZERO_ERROR; 1031 return createEnumerationForRegion(region, ec); 1032 } 1033 1034 // --------------------------------------- 1035 1036 int32_t U_EXPORT2 1037 TimeZone::countEquivalentIDs(const UnicodeString& id) { 1038 int32_t result = 0; 1039 UErrorCode ec = U_ZERO_ERROR; 1040 StackUResourceBundle res; 1041 U_DEBUG_TZ_MSG(("countEquivalentIDs..\n")); 1042 UResourceBundle *top = openOlsonResource(id, res.ref(), ec); 1043 if (U_SUCCESS(ec)) { 1044 StackUResourceBundle r; 1045 ures_getByKey(res.getAlias(), kLINKS, r.getAlias(), &ec); 1046 ures_getIntVector(r.getAlias(), &result, &ec); 1047 } 1048 ures_close(top); 1049 return result; 1050 } 1051 1052 // --------------------------------------- 1053 1054 UnicodeString U_EXPORT2 1055 TimeZone::getEquivalentID(const UnicodeString& id, int32_t index) { 1056 U_DEBUG_TZ_MSG(("gEI(%d)\n", index)); 1057 UnicodeString result; 1058 UErrorCode ec = U_ZERO_ERROR; 1059 StackUResourceBundle res; 1060 UResourceBundle *top = openOlsonResource(id, res.ref(), ec); 1061 int32_t zone = -1; 1062 if (U_SUCCESS(ec)) { 1063 StackUResourceBundle r; 1064 int32_t size; 1065 ures_getByKey(res.getAlias(), kLINKS, r.getAlias(), &ec); 1066 const int32_t *v = ures_getIntVector(r.getAlias(), &size, &ec); 1067 if (U_SUCCESS(ec)) { 1068 if (index >= 0 && index < size) { 1069 zone = v[index]; 1070 } 1071 } 1072 } 1073 if (zone >= 0) { 1074 UResourceBundle *ares = ures_getByKey(top, kNAMES, nullptr, &ec); // dereference Zones section 1075 if (U_SUCCESS(ec)) { 1076 int32_t idLen = 0; 1077 const char16_t* id2 = ures_getStringByIndex(ares, zone, &idLen, &ec); 1078 result.fastCopyFrom(UnicodeString(true, id2, idLen)); 1079 U_DEBUG_TZ_MSG(("gei(%d) -> %d, len%d, %s\n", index, zone, result.length(), u_errorName(ec))); 1080 } 1081 ures_close(ares); 1082 } 1083 ures_close(top); 1084 #if defined(U_DEBUG_TZ) 1085 if(result.length() ==0) { 1086 U_DEBUG_TZ_MSG(("equiv [__, #%d] -> 0 (%s)\n", index, u_errorName(ec))); 1087 } 1088 #endif 1089 return result; 1090 } 1091 1092 // --------------------------------------- 1093 1094 // These methods are used by ZoneMeta class only. 1095 1096 const char16_t* 1097 TimeZone::findID(const UnicodeString& id) { 1098 const char16_t *result = nullptr; 1099 UErrorCode ec = U_ZERO_ERROR; 1100 UResourceBundle *rb = ures_openDirect(nullptr, kZONEINFO, &ec); 1101 1102 // resolve zone index by name 1103 UResourceBundle *names = ures_getByKey(rb, kNAMES, nullptr, &ec); 1104 int32_t idx = findInStringArray(names, id, ec); 1105 result = ures_getStringByIndex(names, idx, nullptr, &ec); 1106 if (U_FAILURE(ec)) { 1107 result = nullptr; 1108 } 1109 ures_close(names); 1110 ures_close(rb); 1111 return result; 1112 } 1113 1114 1115 const char16_t* 1116 TimeZone::dereferOlsonLink(const UnicodeString& id) { 1117 const char16_t *result = nullptr; 1118 UErrorCode ec = U_ZERO_ERROR; 1119 UResourceBundle *rb = ures_openDirect(nullptr, kZONEINFO, &ec); 1120 1121 // resolve zone index by name 1122 UResourceBundle *names = ures_getByKey(rb, kNAMES, nullptr, &ec); 1123 int32_t idx = findInStringArray(names, id, ec); 1124 result = ures_getStringByIndex(names, idx, nullptr, &ec); 1125 1126 // open the zone bundle by index 1127 ures_getByKey(rb, kZONES, rb, &ec); 1128 ures_getByIndex(rb, idx, rb, &ec); 1129 1130 if (U_SUCCESS(ec)) { 1131 if (ures_getType(rb) == URES_INT) { 1132 // this is a link - dereference the link 1133 int32_t deref = ures_getInt(rb, &ec); 1134 const char16_t* tmp = ures_getStringByIndex(names, deref, nullptr, &ec); 1135 if (U_SUCCESS(ec)) { 1136 result = tmp; 1137 } 1138 } 1139 } 1140 1141 ures_close(names); 1142 ures_close(rb); 1143 1144 return result; 1145 } 1146 1147 const char16_t* 1148 TimeZone::getRegion(const UnicodeString& id) { 1149 UErrorCode status = U_ZERO_ERROR; 1150 return getRegion(id, status); 1151 } 1152 1153 const char16_t* 1154 TimeZone::getRegion(const UnicodeString& id, UErrorCode& status) { 1155 if (U_FAILURE(status)) { 1156 return nullptr; 1157 } 1158 const char16_t *result = nullptr; 1159 UResourceBundle *rb = ures_openDirect(nullptr, kZONEINFO, &status); 1160 1161 // resolve zone index by name 1162 UResourceBundle *res = ures_getByKey(rb, kNAMES, nullptr, &status); 1163 int32_t idx = findInStringArray(res, id, status); 1164 1165 // get region mapping 1166 ures_getByKey(rb, kREGIONS, res, &status); 1167 const char16_t *tmp = ures_getStringByIndex(res, idx, nullptr, &status); 1168 if (U_SUCCESS(status)) { 1169 result = tmp; 1170 } 1171 1172 ures_close(res); 1173 ures_close(rb); 1174 1175 return result; 1176 } 1177 1178 1179 // --------------------------------------- 1180 int32_t 1181 TimeZone::getRegion(const UnicodeString& id, char *region, int32_t capacity, UErrorCode& status) 1182 { 1183 int32_t resultLen = 0; 1184 *region = 0; 1185 if (U_FAILURE(status)) { 1186 return 0; 1187 } 1188 1189 const char16_t *uregion = nullptr; 1190 // "Etc/Unknown" is not a system zone ID, 1191 // but in the zone data 1192 if (id.compare(UNKNOWN_ZONE_ID, UNKNOWN_ZONE_ID_LENGTH) != 0) { 1193 uregion = getRegion(id); 1194 } 1195 if (uregion == nullptr) { 1196 status = U_ILLEGAL_ARGUMENT_ERROR; 1197 return 0; 1198 } 1199 resultLen = u_strlen(uregion); 1200 // A region code is represented by invariant characters 1201 u_UCharsToChars(uregion, region, uprv_min(resultLen, capacity)); 1202 1203 if (capacity < resultLen) { 1204 status = U_BUFFER_OVERFLOW_ERROR; 1205 return resultLen; 1206 } 1207 1208 return u_terminateChars(region, capacity, resultLen, &status); 1209 } 1210 1211 // --------------------------------------- 1212 1213 1214 UnicodeString& 1215 TimeZone::getDisplayName(UnicodeString& result) const 1216 { 1217 return getDisplayName(false,LONG,Locale::getDefault(), result); 1218 } 1219 1220 UnicodeString& 1221 TimeZone::getDisplayName(const Locale& locale, UnicodeString& result) const 1222 { 1223 return getDisplayName(false, LONG, locale, result); 1224 } 1225 1226 UnicodeString& 1227 TimeZone::getDisplayName(UBool inDaylight, EDisplayType style, UnicodeString& result) const 1228 { 1229 return getDisplayName(inDaylight,style, Locale::getDefault(), result); 1230 } 1231 //-------------------------------------- 1232 int32_t 1233 TimeZone::getDSTSavings()const { 1234 if (useDaylightTime()) { 1235 return 3600000; 1236 } 1237 return 0; 1238 } 1239 //--------------------------------------- 1240 UnicodeString& 1241 TimeZone::getDisplayName(UBool inDaylight, EDisplayType style, const Locale& locale, UnicodeString& result) const 1242 { 1243 UErrorCode status = U_ZERO_ERROR; 1244 UDate date = Calendar::getNow(); 1245 UTimeZoneFormatTimeType timeType = UTZFMT_TIME_TYPE_UNKNOWN; 1246 int32_t offset; 1247 1248 if (style == GENERIC_LOCATION || style == LONG_GENERIC || style == SHORT_GENERIC) { 1249 LocalPointer<TimeZoneFormat> tzfmt(TimeZoneFormat::createInstance(locale, status)); 1250 if (U_FAILURE(status)) { 1251 result.remove(); 1252 return result; 1253 } 1254 // Generic format 1255 switch (style) { 1256 case GENERIC_LOCATION: 1257 tzfmt->format(UTZFMT_STYLE_GENERIC_LOCATION, *this, date, result, &timeType); 1258 break; 1259 case LONG_GENERIC: 1260 tzfmt->format(UTZFMT_STYLE_GENERIC_LONG, *this, date, result, &timeType); 1261 break; 1262 case SHORT_GENERIC: 1263 tzfmt->format(UTZFMT_STYLE_GENERIC_SHORT, *this, date, result, &timeType); 1264 break; 1265 default: 1266 UPRV_UNREACHABLE_EXIT; 1267 } 1268 // Generic format many use Localized GMT as the final fallback. 1269 // When Localized GMT format is used, the result might not be 1270 // appropriate for the requested daylight value. 1271 if ((inDaylight && timeType == UTZFMT_TIME_TYPE_STANDARD) || (!inDaylight && timeType == UTZFMT_TIME_TYPE_DAYLIGHT)) { 1272 offset = inDaylight ? getRawOffset() + getDSTSavings() : getRawOffset(); 1273 if (style == SHORT_GENERIC) { 1274 tzfmt->formatOffsetShortLocalizedGMT(offset, result, status); 1275 } else { 1276 tzfmt->formatOffsetLocalizedGMT(offset, result, status); 1277 } 1278 } 1279 } else if (style == LONG_GMT || style == SHORT_GMT) { 1280 LocalPointer<TimeZoneFormat> tzfmt(TimeZoneFormat::createInstance(locale, status)); 1281 if (U_FAILURE(status)) { 1282 result.remove(); 1283 return result; 1284 } 1285 offset = inDaylight && useDaylightTime() ? getRawOffset() + getDSTSavings() : getRawOffset(); 1286 switch (style) { 1287 case LONG_GMT: 1288 tzfmt->formatOffsetLocalizedGMT(offset, result, status); 1289 break; 1290 case SHORT_GMT: 1291 tzfmt->formatOffsetISO8601Basic(offset, false, false, false, result, status); 1292 break; 1293 default: 1294 UPRV_UNREACHABLE_EXIT; 1295 } 1296 1297 } else { 1298 U_ASSERT(style == LONG || style == SHORT || style == SHORT_COMMONLY_USED); 1299 UTimeZoneNameType nameType = UTZNM_UNKNOWN; 1300 switch (style) { 1301 case LONG: 1302 nameType = inDaylight ? UTZNM_LONG_DAYLIGHT : UTZNM_LONG_STANDARD; 1303 break; 1304 case SHORT: 1305 case SHORT_COMMONLY_USED: 1306 nameType = inDaylight ? UTZNM_SHORT_DAYLIGHT : UTZNM_SHORT_STANDARD; 1307 break; 1308 default: 1309 UPRV_UNREACHABLE_EXIT; 1310 } 1311 LocalPointer<TimeZoneNames> tznames(TimeZoneNames::createInstance(locale, status)); 1312 if (U_FAILURE(status)) { 1313 result.remove(); 1314 return result; 1315 } 1316 UnicodeString canonicalID(ZoneMeta::getCanonicalCLDRID(*this)); 1317 tznames->getDisplayName(canonicalID, nameType, date, result); 1318 if (result.isEmpty()) { 1319 // Fallback to localized GMT 1320 LocalPointer<TimeZoneFormat> tzfmt(TimeZoneFormat::createInstance(locale, status)); 1321 offset = inDaylight && useDaylightTime() ? getRawOffset() + getDSTSavings() : getRawOffset(); 1322 if (style == LONG) { 1323 tzfmt->formatOffsetLocalizedGMT(offset, result, status); 1324 } else { 1325 tzfmt->formatOffsetShortLocalizedGMT(offset, result, status); 1326 } 1327 } 1328 } 1329 if (U_FAILURE(status)) { 1330 result.remove(); 1331 } 1332 return result; 1333 } 1334 1335 /** 1336 * Parse a custom time zone identifier and return a corresponding zone. 1337 * @param id a string of the form GMT[+-]hh:mm, GMT[+-]hhmm, or 1338 * GMT[+-]hh. 1339 * @return a newly created SimpleTimeZone with the given offset and 1340 * no Daylight Savings Time, or null if the id cannot be parsed. 1341 */ 1342 TimeZone* 1343 TimeZone::createCustomTimeZone(const UnicodeString& id) 1344 { 1345 int32_t sign, hour, min, sec; 1346 if (parseCustomID(id, sign, hour, min, sec)) { 1347 UnicodeString customID; 1348 formatCustomID(hour, min, sec, (sign < 0), customID); 1349 int32_t offset = sign * ((hour * 60 + min) * 60 + sec) * 1000; 1350 return new SimpleTimeZone(offset, customID); 1351 } 1352 return nullptr; 1353 } 1354 1355 UnicodeString& 1356 TimeZone::getCustomID(const UnicodeString& id, UnicodeString& normalized, UErrorCode& status) { 1357 normalized.remove(); 1358 if (U_FAILURE(status)) { 1359 return normalized; 1360 } 1361 int32_t sign, hour, min, sec; 1362 if (parseCustomID(id, sign, hour, min, sec)) { 1363 formatCustomID(hour, min, sec, (sign < 0), normalized); 1364 } else { 1365 status = U_ILLEGAL_ARGUMENT_ERROR; 1366 } 1367 return normalized; 1368 } 1369 1370 UBool 1371 TimeZone::parseCustomID(const UnicodeString& id, int32_t& sign, 1372 int32_t& hour, int32_t& min, int32_t& sec) { 1373 if (id.length() < GMT_ID_LENGTH) { 1374 return false; 1375 } 1376 if (0 != u_strncasecmp(id.getBuffer(), GMT_ID, GMT_ID_LENGTH, 0)) { 1377 return false; 1378 } 1379 sign = 1; 1380 hour = 0; 1381 min = 0; 1382 sec = 0; 1383 1384 if (id[GMT_ID_LENGTH] == MINUS /*'-'*/) { 1385 sign = -1; 1386 } else if (id[GMT_ID_LENGTH] != PLUS /*'+'*/) { 1387 return false; 1388 } 1389 1390 int32_t start = GMT_ID_LENGTH + 1; 1391 int32_t pos = start; 1392 hour = ICU_Utility::parseNumber(id, pos, 10); 1393 if (pos == id.length()) { 1394 // Handle the following cases 1395 // HHmmss 1396 // Hmmss 1397 // HHmm 1398 // Hmm 1399 // HH 1400 // H 1401 1402 // Get all digits 1403 // Should be 1 to 6 digits. 1404 int32_t length = pos - start; 1405 switch (length) { 1406 case 1: // H 1407 case 2: // HH 1408 // already set to hour 1409 break; 1410 case 3: // Hmm 1411 case 4: // HHmm 1412 min = hour % 100; 1413 hour /= 100; 1414 break; 1415 case 5: // Hmmss 1416 case 6: // HHmmss 1417 sec = hour % 100; 1418 min = (hour/100) % 100; 1419 hour /= 10000; 1420 break; 1421 default: 1422 // invalid range 1423 return false; 1424 } 1425 } else { 1426 // Handle the following cases 1427 // HH:mm:ss 1428 // H:mm:ss 1429 // HH:mm 1430 // H:mm 1431 if (pos - start < 1 || pos - start > 2 || id[pos] != COLON) { 1432 return false; 1433 } 1434 pos++; // skip : after H or HH 1435 if (id.length() == pos) { 1436 return false; 1437 } 1438 start = pos; 1439 min = ICU_Utility::parseNumber(id, pos, 10); 1440 if (pos - start != 2) { 1441 return false; 1442 } 1443 if (id.length() > pos) { 1444 if (id[pos] != COLON) { 1445 return false; 1446 } 1447 pos++; // skip : after mm 1448 start = pos; 1449 sec = ICU_Utility::parseNumber(id, pos, 10); 1450 if (pos - start != 2 || id.length() > pos) { 1451 return false; 1452 } 1453 } 1454 } 1455 if (hour > kMAX_CUSTOM_HOUR || 1456 min > kMAX_CUSTOM_MIN || 1457 sec > kMAX_CUSTOM_SEC) { 1458 return false; 1459 } 1460 return true; 1461 } 1462 1463 UnicodeString& 1464 TimeZone::formatCustomID(int32_t hour, int32_t min, int32_t sec, 1465 UBool negative, UnicodeString& id) { 1466 // Create time zone ID - GMT[+|-]hhmm[ss] 1467 id.setTo(GMT_ID, GMT_ID_LENGTH); 1468 if (hour | min | sec) { 1469 if (negative) { 1470 id += static_cast<char16_t>(MINUS); 1471 } else { 1472 id += static_cast<char16_t>(PLUS); 1473 } 1474 1475 if (hour < 10) { 1476 id += static_cast<char16_t>(ZERO_DIGIT); 1477 } else { 1478 id += static_cast<char16_t>(ZERO_DIGIT + hour / 10); 1479 } 1480 id += static_cast<char16_t>(ZERO_DIGIT + hour % 10); 1481 id += static_cast<char16_t>(COLON); 1482 if (min < 10) { 1483 id += static_cast<char16_t>(ZERO_DIGIT); 1484 } else { 1485 id += static_cast<char16_t>(ZERO_DIGIT + min / 10); 1486 } 1487 id += static_cast<char16_t>(ZERO_DIGIT + min % 10); 1488 1489 if (sec) { 1490 id += static_cast<char16_t>(COLON); 1491 if (sec < 10) { 1492 id += static_cast<char16_t>(ZERO_DIGIT); 1493 } else { 1494 id += static_cast<char16_t>(ZERO_DIGIT + sec / 10); 1495 } 1496 id += static_cast<char16_t>(ZERO_DIGIT + sec % 10); 1497 } 1498 } 1499 return id; 1500 } 1501 1502 1503 UBool 1504 TimeZone::hasSameRules(const TimeZone& other) const 1505 { 1506 return (getRawOffset() == other.getRawOffset() && 1507 useDaylightTime() == other.useDaylightTime()); 1508 } 1509 1510 static void U_CALLCONV initTZDataVersion(UErrorCode &status) { 1511 ucln_i18n_registerCleanup(UCLN_I18N_TIMEZONE, timeZone_cleanup); 1512 int32_t len = 0; 1513 StackUResourceBundle bundle; 1514 ures_openDirectFillIn(bundle.getAlias(), nullptr, kZONEINFO, &status); 1515 const char16_t *tzver = ures_getStringByKey(bundle.getAlias(), kTZVERSION, &len, &status); 1516 1517 if (U_SUCCESS(status)) { 1518 if (len >= static_cast<int32_t>(sizeof(TZDATA_VERSION))) { 1519 // Ensure that there is always space for a trailing nul in TZDATA_VERSION 1520 len = sizeof(TZDATA_VERSION) - 1; 1521 } 1522 u_UCharsToChars(tzver, TZDATA_VERSION, len); 1523 } 1524 } 1525 1526 const char* 1527 TimeZone::getTZDataVersion(UErrorCode& status) 1528 { 1529 umtx_initOnce(gTZDataVersionInitOnce, &initTZDataVersion, status); 1530 return (const char*)TZDATA_VERSION; 1531 } 1532 1533 UnicodeString& 1534 TimeZone::getCanonicalID(const UnicodeString& id, UnicodeString& canonicalID, UErrorCode& status) 1535 { 1536 UBool isSystemID = false; 1537 return getCanonicalID(id, canonicalID, isSystemID, status); 1538 } 1539 1540 UnicodeString& 1541 TimeZone::getCanonicalID(const UnicodeString& id, UnicodeString& canonicalID, UBool& isSystemID, 1542 UErrorCode& status) 1543 { 1544 canonicalID.remove(); 1545 isSystemID = false; 1546 if (U_FAILURE(status)) { 1547 return canonicalID; 1548 } 1549 if (id.compare(UNKNOWN_ZONE_ID, UNKNOWN_ZONE_ID_LENGTH) == 0) { 1550 // special case - Etc/Unknown is a canonical ID, but not system ID 1551 canonicalID.fastCopyFrom(id); 1552 isSystemID = false; 1553 } else { 1554 ZoneMeta::getCanonicalCLDRID(id, canonicalID, status); 1555 if (U_SUCCESS(status)) { 1556 isSystemID = true; 1557 } else { 1558 // Not a system ID 1559 status = U_ZERO_ERROR; 1560 getCustomID(id, canonicalID, status); 1561 } 1562 } 1563 return canonicalID; 1564 } 1565 1566 UnicodeString& 1567 TimeZone::getIanaID(const UnicodeString& id, UnicodeString& ianaID, UErrorCode& status) 1568 { 1569 ianaID.remove(); 1570 if (U_FAILURE(status)) { 1571 return ianaID; 1572 } 1573 if (id.compare(ConstChar16Ptr(UNKNOWN_ZONE_ID), UNKNOWN_ZONE_ID_LENGTH) == 0) { 1574 status = U_ILLEGAL_ARGUMENT_ERROR; 1575 ianaID.setToBogus(); 1576 } else { 1577 ZoneMeta::getIanaID(id, ianaID, status); 1578 } 1579 return ianaID; 1580 } 1581 1582 UnicodeString& 1583 TimeZone::getWindowsID(const UnicodeString& id, UnicodeString& winid, UErrorCode& status) { 1584 winid.remove(); 1585 if (U_FAILURE(status)) { 1586 return winid; 1587 } 1588 1589 // canonicalize the input ID 1590 UnicodeString canonicalID; 1591 UBool isSystemID = false; 1592 1593 getCanonicalID(id, canonicalID, isSystemID, status); 1594 if (U_FAILURE(status) || !isSystemID) { 1595 // mapping data is only applicable to tz database IDs 1596 if (status == U_ILLEGAL_ARGUMENT_ERROR) { 1597 // getWindowsID() sets an empty string where 1598 // getCanonicalID() sets a U_ILLEGAL_ARGUMENT_ERROR. 1599 status = U_ZERO_ERROR; 1600 } 1601 return winid; 1602 } 1603 1604 LocalUResourceBundlePointer mapTimezones(ures_openDirect(nullptr, "windowsZones", &status)); 1605 if (U_FAILURE(status)) { 1606 return winid; 1607 } 1608 ures_getByKey(mapTimezones.getAlias(), "mapTimezones", mapTimezones.getAlias(), &status); 1609 1610 if (U_FAILURE(status)) { 1611 return winid; 1612 } 1613 1614 UResourceBundle *winzone = nullptr; 1615 UBool found = false; 1616 while (ures_hasNext(mapTimezones.getAlias()) && !found) { 1617 winzone = ures_getNextResource(mapTimezones.getAlias(), winzone, &status); 1618 if (U_FAILURE(status)) { 1619 break; 1620 } 1621 if (ures_getType(winzone) != URES_TABLE) { 1622 continue; 1623 } 1624 UResourceBundle *regionalData = nullptr; 1625 while (ures_hasNext(winzone) && !found) { 1626 regionalData = ures_getNextResource(winzone, regionalData, &status); 1627 if (U_FAILURE(status)) { 1628 break; 1629 } 1630 if (ures_getType(regionalData) != URES_STRING) { 1631 continue; 1632 } 1633 int32_t len; 1634 const char16_t *tzids = ures_getString(regionalData, &len, &status); 1635 if (U_FAILURE(status)) { 1636 break; 1637 } 1638 1639 const char16_t *start = tzids; 1640 UBool hasNext = true; 1641 while (hasNext) { 1642 const char16_t* end = u_strchr(start, static_cast<char16_t>(0x20)); 1643 if (end == nullptr) { 1644 end = tzids + len; 1645 hasNext = false; 1646 } 1647 if (canonicalID.compare(start, static_cast<int32_t>(end - start)) == 0) { 1648 winid = UnicodeString(ures_getKey(winzone), -1 , US_INV); 1649 found = true; 1650 break; 1651 } 1652 start = end + 1; 1653 } 1654 } 1655 ures_close(regionalData); 1656 } 1657 ures_close(winzone); 1658 1659 return winid; 1660 } 1661 1662 #define MAX_WINDOWS_ID_SIZE 128 1663 1664 UnicodeString& 1665 TimeZone::getIDForWindowsID(const UnicodeString& winid, const char* region, UnicodeString& id, UErrorCode& status) { 1666 id.remove(); 1667 if (U_FAILURE(status)) { 1668 return id; 1669 } 1670 1671 UResourceBundle *zones = ures_openDirect(nullptr, "windowsZones", &status); 1672 ures_getByKey(zones, "mapTimezones", zones, &status); 1673 if (U_FAILURE(status)) { 1674 ures_close(zones); 1675 return id; 1676 } 1677 1678 UErrorCode tmperr = U_ZERO_ERROR; 1679 char winidKey[MAX_WINDOWS_ID_SIZE]; 1680 int32_t winKeyLen = winid.extract(0, winid.length(), winidKey, sizeof(winidKey) - 1, US_INV); 1681 1682 if (winKeyLen == 0 || winKeyLen >= static_cast<int32_t>(sizeof(winidKey))) { 1683 ures_close(zones); 1684 return id; 1685 } 1686 winidKey[winKeyLen] = 0; 1687 1688 ures_getByKey(zones, winidKey, zones, &tmperr); // use tmperr, because windows mapping might not 1689 // be available by design 1690 if (U_FAILURE(tmperr)) { 1691 ures_close(zones); 1692 return id; 1693 } 1694 1695 const char16_t *tzid = nullptr; 1696 int32_t len = 0; 1697 UBool gotID = false; 1698 if (region) { 1699 const char16_t *tzids = ures_getStringByKey(zones, region, &len, &tmperr); // use tmperr, because 1700 // regional mapping is optional 1701 if (U_SUCCESS(tmperr)) { 1702 // first ID delimited by space is the default one 1703 const char16_t* end = u_strchr(tzids, static_cast<char16_t>(0x20)); 1704 if (end == nullptr) { 1705 id.setTo(tzids, -1); 1706 } else { 1707 id.setTo(tzids, static_cast<int32_t>(end - tzids)); 1708 } 1709 gotID = true; 1710 } 1711 } 1712 1713 if (!gotID) { 1714 tzid = ures_getStringByKey(zones, "001", &len, &status); // using status, because "001" must be 1715 // available at this point 1716 if (U_SUCCESS(status)) { 1717 id.setTo(tzid, len); 1718 } 1719 } 1720 1721 ures_close(zones); 1722 return id; 1723 } 1724 1725 1726 U_NAMESPACE_END 1727 1728 #endif /* #if !UCONFIG_NO_FORMATTING */ 1729 1730 //eof