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