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