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