1 /* 2 ******************************************************************************* 3 * Copyright (C) 1997-2014, 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 == 0) { 431 U_DEBUG_TZ_MSG(("failed to load system time zone with id - falling to custom")); 432 result = createCustomTimeZone(ID); 433 } 434 if (result == 0) { 435 U_DEBUG_TZ_MSG(("failed to load time zone with id - falling to Etc/Unknown(GMT)")); 436 result = getUnknown().clone(); 437 } 438 return result; 439 } 440 441 // ------------------------------------- 442 443 /** 444 * Initialize DEFAULT_ZONE from the system default time zone. 445 * Upon return, DEFAULT_ZONE will not be NULL, unless operator new() 446 * returns NULL. 447 */ 448 static void U_CALLCONV initDefault() 449 { 450 ucln_i18n_registerCleanup(UCLN_I18N_TIMEZONE, timeZone_cleanup); 451 452 // If setDefault() has already been called we can skip getting the 453 // default zone information from the system. 454 if (DEFAULT_ZONE != NULL) { 455 return; 456 } 457 458 // We access system timezone data through TPlatformUtilities, 459 // including tzset(), timezone, and tzname[]. 460 int32_t rawOffset = 0; 461 const char *hostID; 462 463 // First, try to create a system timezone, based 464 // on the string ID in tzname[0]. 465 466 // NOTE: this code is safely single threaded, being only 467 // run via umtx_initOnce(). 468 // 469 // Some of the locale/timezone OS functions may not be thread safe, 470 // 471 // The operating system might actually use ICU to implement timezones. 472 // So we may have ICU calling ICU here, like on AIX. 473 // There shouldn't be a problem with this; initOnce does not hold a mutex 474 // while the init function is being run. 475 476 uprv_tzset(); // Initialize tz... system data 477 478 // Get the timezone ID from the host. This function should do 479 // any required host-specific remapping; e.g., on Windows this 480 // function maps the Date and Time control panel setting to an 481 // ICU timezone ID. 482 hostID = uprv_tzname(0); 483 484 // Invert sign because UNIX semantics are backwards 485 rawOffset = uprv_timezone() * -U_MILLIS_PER_SECOND; 486 487 TimeZone* default_zone = NULL; 488 489 /* Make sure that the string is NULL terminated to prevent BoundsChecker/Purify warnings. */ 490 UnicodeString hostStrID(hostID, -1, US_INV); 491 hostStrID.append((UChar)0); 492 hostStrID.truncate(hostStrID.length()-1); 493 default_zone = createSystemTimeZone(hostStrID); 494 495 #if U_PLATFORM_USES_ONLY_WIN32_API 496 // hostID points to a heap-allocated location on Windows. 497 uprv_free(const_cast<char *>(hostID)); 498 #endif 499 500 int32_t hostIDLen = hostStrID.length(); 501 if (default_zone != NULL && rawOffset != default_zone->getRawOffset() 502 && (3 <= hostIDLen && hostIDLen <= 4)) 503 { 504 // Uh oh. This probably wasn't a good id. 505 // It was probably an ambiguous abbreviation 506 delete default_zone; 507 default_zone = NULL; 508 } 509 510 // Construct a fixed standard zone with the host's ID 511 // and raw offset. 512 if (default_zone == NULL) { 513 default_zone = new SimpleTimeZone(rawOffset, hostStrID); 514 } 515 516 // If we _still_ don't have a time zone, use GMT. 517 if (default_zone == NULL) { 518 const TimeZone* temptz = TimeZone::getGMT(); 519 // If we can't use GMT, get out. 520 if (temptz == NULL) { 521 return; 522 } 523 default_zone = temptz->clone(); 524 } 525 526 // The only way for DEFAULT_ZONE to be non-null at this point is if the user 527 // made a thread-unsafe call to setDefault() or adoptDefault() in another 528 // thread while this thread was doing something that required getting the default. 529 U_ASSERT(DEFAULT_ZONE == NULL); 530 531 DEFAULT_ZONE = default_zone; 532 } 533 534 // ------------------------------------- 535 536 TimeZone* U_EXPORT2 537 TimeZone::createDefault() 538 { 539 umtx_initOnce(gDefaultZoneInitOnce, initDefault); 540 return (DEFAULT_ZONE != NULL) ? DEFAULT_ZONE->clone() : NULL; 541 } 542 543 // ------------------------------------- 544 545 void U_EXPORT2 546 TimeZone::adoptDefault(TimeZone* zone) 547 { 548 if (zone != NULL) 549 { 550 TimeZone *old = DEFAULT_ZONE; 551 DEFAULT_ZONE = zone; 552 delete old; 553 ucln_i18n_registerCleanup(UCLN_I18N_TIMEZONE, timeZone_cleanup); 554 } 555 } 556 // ------------------------------------- 557 558 void U_EXPORT2 559 TimeZone::setDefault(const TimeZone& zone) 560 { 561 adoptDefault(zone.clone()); 562 } 563 564 //---------------------------------------------------------------------- 565 566 567 static void U_CALLCONV initMap(USystemTimeZoneType type, UErrorCode& ec) { 568 ucln_i18n_registerCleanup(UCLN_I18N_TIMEZONE, timeZone_cleanup); 569 570 UResourceBundle *res = ures_openDirect(0, kZONEINFO, &ec); 571 res = ures_getByKey(res, kNAMES, res, &ec); // dereference Zones section 572 if (U_SUCCESS(ec)) { 573 int32_t size = ures_getSize(res); 574 int32_t *m = (int32_t *)uprv_malloc(size * sizeof(int32_t)); 575 if (m == NULL) { 576 ec = U_MEMORY_ALLOCATION_ERROR; 577 } else { 578 int32_t numEntries = 0; 579 for (int32_t i = 0; i < size; i++) { 580 UnicodeString id = ures_getUnicodeStringByIndex(res, i, &ec); 581 if (U_FAILURE(ec)) { 582 break; 583 } 584 if (0 == id.compare(UNKNOWN_ZONE_ID, UNKNOWN_ZONE_ID_LENGTH)) { 585 // exclude Etc/Unknown 586 continue; 587 } 588 if (type == UCAL_ZONE_TYPE_CANONICAL || type == UCAL_ZONE_TYPE_CANONICAL_LOCATION) { 589 UnicodeString canonicalID; 590 ZoneMeta::getCanonicalCLDRID(id, canonicalID, ec); 591 if (U_FAILURE(ec)) { 592 break; 593 } 594 if (canonicalID != id) { 595 // exclude aliases 596 continue; 597 } 598 } 599 if (type == UCAL_ZONE_TYPE_CANONICAL_LOCATION) { 600 const UChar *region = TimeZone::getRegion(id, ec); 601 if (U_FAILURE(ec)) { 602 break; 603 } 604 if (u_strcmp(region, WORLD) == 0) { 605 // exclude non-location ("001") 606 continue; 607 } 608 } 609 m[numEntries++] = i; 610 } 611 if (U_SUCCESS(ec)) { 612 int32_t *tmp = m; 613 m = (int32_t *)uprv_realloc(tmp, numEntries * sizeof(int32_t)); 614 if (m == NULL) { 615 // realloc failed.. use the original one even it has unused 616 // area at the end 617 m = tmp; 618 } 619 620 switch(type) { 621 case UCAL_ZONE_TYPE_ANY: 622 U_ASSERT(MAP_SYSTEM_ZONES == NULL); 623 MAP_SYSTEM_ZONES = m; 624 LEN_SYSTEM_ZONES = numEntries; 625 break; 626 case UCAL_ZONE_TYPE_CANONICAL: 627 U_ASSERT(MAP_CANONICAL_SYSTEM_ZONES == NULL); 628 MAP_CANONICAL_SYSTEM_ZONES = m; 629 LEN_CANONICAL_SYSTEM_ZONES = numEntries; 630 break; 631 case UCAL_ZONE_TYPE_CANONICAL_LOCATION: 632 U_ASSERT(MAP_CANONICAL_SYSTEM_LOCATION_ZONES == NULL); 633 MAP_CANONICAL_SYSTEM_LOCATION_ZONES = m; 634 LEN_CANONICAL_SYSTEM_LOCATION_ZONES = numEntries; 635 break; 636 } 637 } 638 } 639 } 640 ures_close(res); 641 } 642 643 644 /** 645 * This is the default implementation for subclasses that do not 646 * override this method. This implementation calls through to the 647 * 8-argument getOffset() method after suitable computations, and 648 * correctly adjusts GMT millis to local millis when necessary. 649 */ 650 void TimeZone::getOffset(UDate date, UBool local, int32_t& rawOffset, 651 int32_t& dstOffset, UErrorCode& ec) const { 652 if (U_FAILURE(ec)) { 653 return; 654 } 655 656 rawOffset = getRawOffset(); 657 if (!local) { 658 date += rawOffset; // now in local standard millis 659 } 660 661 // When local == TRUE, date might not be in local standard 662 // millis. getOffset taking 7 parameters used here assume 663 // the given time in day is local standard time. 664 // At STD->DST transition, there is a range of time which 665 // does not exist. When 'date' is in this time range 666 // (and local == TRUE), this method interprets the specified 667 // local time as DST. At DST->STD transition, there is a 668 // range of time which occurs twice. In this case, this 669 // method interprets the specified local time as STD. 670 // To support the behavior above, we need to call getOffset 671 // (with 7 args) twice when local == true and DST is 672 // detected in the initial call. 673 for (int32_t pass=0; ; ++pass) { 674 int32_t year, month, dom, dow; 675 double day = uprv_floor(date / U_MILLIS_PER_DAY); 676 int32_t millis = (int32_t) (date - day * U_MILLIS_PER_DAY); 677 678 Grego::dayToFields(day, year, month, dom, dow); 679 680 dstOffset = getOffset(GregorianCalendar::AD, year, month, dom, 681 (uint8_t) dow, millis, 682 Grego::monthLength(year, month), 683 ec) - rawOffset; 684 685 // Recompute if local==TRUE, dstOffset!=0. 686 if (pass!=0 || !local || dstOffset == 0) { 687 break; 688 } 689 // adjust to local standard millis 690 date -= dstOffset; 691 } 692 } 693 694 // ------------------------------------- 695 696 // New available IDs API as of ICU 2.4. Uses StringEnumeration API. 697 698 class TZEnumeration : public StringEnumeration { 699 private: 700 701 // Map into to zones. Our results are zone[map[i]] for 702 // i=0..len-1, where zone[i] is the i-th Olson zone. If map==NULL 703 // then our results are zone[i] for i=0..len-1. Len will be zero 704 // if the zone data could not be loaded. 705 int32_t* map; 706 int32_t* localMap; 707 int32_t len; 708 int32_t pos; 709 710 TZEnumeration(int32_t* mapData, int32_t mapLen, UBool adoptMapData) : pos(0) { 711 map = mapData; 712 localMap = adoptMapData ? mapData : NULL; 713 len = mapLen; 714 } 715 716 UBool getID(int32_t i) { 717 UErrorCode ec = U_ZERO_ERROR; 718 int32_t idLen = 0; 719 const UChar* id = NULL; 720 UResourceBundle *top = ures_openDirect(0, kZONEINFO, &ec); 721 top = ures_getByKey(top, kNAMES, top, &ec); // dereference Zones section 722 id = ures_getStringByIndex(top, i, &idLen, &ec); 723 if(U_FAILURE(ec)) { 724 unistr.truncate(0); 725 } 726 else { 727 unistr.fastCopyFrom(UnicodeString(TRUE, id, idLen)); 728 } 729 ures_close(top); 730 return U_SUCCESS(ec); 731 } 732 733 static int32_t* getMap(USystemTimeZoneType type, int32_t& len, UErrorCode& ec) { 734 len = 0; 735 if (U_FAILURE(ec)) { 736 return NULL; 737 } 738 int32_t* m = NULL; 739 switch (type) { 740 case UCAL_ZONE_TYPE_ANY: 741 umtx_initOnce(gSystemZonesInitOnce, &initMap, type, ec); 742 m = MAP_SYSTEM_ZONES; 743 len = LEN_SYSTEM_ZONES; 744 break; 745 case UCAL_ZONE_TYPE_CANONICAL: 746 umtx_initOnce(gCanonicalZonesInitOnce, &initMap, type, ec); 747 m = MAP_CANONICAL_SYSTEM_ZONES; 748 len = LEN_CANONICAL_SYSTEM_ZONES; 749 break; 750 case UCAL_ZONE_TYPE_CANONICAL_LOCATION: 751 umtx_initOnce(gCanonicalLocationZonesInitOnce, &initMap, type, ec); 752 m = MAP_CANONICAL_SYSTEM_LOCATION_ZONES; 753 len = LEN_CANONICAL_SYSTEM_LOCATION_ZONES; 754 break; 755 default: 756 ec = U_ILLEGAL_ARGUMENT_ERROR; 757 m = NULL; 758 len = 0; 759 break; 760 } 761 return m; 762 } 763 764 public: 765 766 #define DEFAULT_FILTERED_MAP_SIZE 8 767 #define MAP_INCREMENT_SIZE 8 768 769 static TZEnumeration* create(USystemTimeZoneType type, const char* region, const int32_t* rawOffset, UErrorCode& ec) { 770 if (U_FAILURE(ec)) { 771 return NULL; 772 } 773 774 int32_t baseLen; 775 int32_t *baseMap = getMap(type, baseLen, ec); 776 777 if (U_FAILURE(ec)) { 778 return NULL; 779 } 780 781 // If any additional conditions are available, 782 // create instance local map filtered by the conditions. 783 784 int32_t *filteredMap = NULL; 785 int32_t numEntries = 0; 786 787 if (region != NULL || rawOffset != NULL) { 788 int32_t filteredMapSize = DEFAULT_FILTERED_MAP_SIZE; 789 filteredMap = (int32_t *)uprv_malloc(filteredMapSize * sizeof(int32_t)); 790 if (filteredMap == NULL) { 791 ec = U_MEMORY_ALLOCATION_ERROR; 792 return NULL; 793 } 794 795 // Walk through the base map 796 UResourceBundle *res = ures_openDirect(0, kZONEINFO, &ec); 797 res = ures_getByKey(res, kNAMES, res, &ec); // dereference Zones section 798 for (int32_t i = 0; i < baseLen; i++) { 799 int32_t zidx = baseMap[i]; 800 UnicodeString id = ures_getUnicodeStringByIndex(res, zidx, &ec); 801 if (U_FAILURE(ec)) { 802 break; 803 } 804 if (region != NULL) { 805 // Filter by region 806 char tzregion[4]; // max 3 letters + null term 807 TimeZone::getRegion(id, tzregion, sizeof(tzregion), ec); 808 if (U_FAILURE(ec)) { 809 break; 810 } 811 if (uprv_stricmp(tzregion, region) != 0) { 812 // region does not match 813 continue; 814 } 815 } 816 if (rawOffset != NULL) { 817 // Filter by raw offset 818 // Note: This is VERY inefficient 819 TimeZone *z = createSystemTimeZone(id, ec); 820 if (U_FAILURE(ec)) { 821 break; 822 } 823 int32_t tzoffset = z->getRawOffset(); 824 delete z; 825 826 if (tzoffset != *rawOffset) { 827 continue; 828 } 829 } 830 831 if (filteredMapSize <= numEntries) { 832 filteredMapSize += MAP_INCREMENT_SIZE; 833 int32_t *tmp = (int32_t *)uprv_realloc(filteredMap, filteredMapSize * sizeof(int32_t)); 834 if (tmp == NULL) { 835 ec = U_MEMORY_ALLOCATION_ERROR; 836 break; 837 } else { 838 filteredMap = tmp; 839 } 840 } 841 842 filteredMap[numEntries++] = zidx; 843 } 844 845 if (U_FAILURE(ec)) { 846 uprv_free(filteredMap); 847 filteredMap = NULL; 848 } 849 850 ures_close(res); 851 } 852 853 TZEnumeration *result = NULL; 854 if (U_SUCCESS(ec)) { 855 // Finally, create a new enumeration instance 856 if (filteredMap == NULL) { 857 result = new TZEnumeration(baseMap, baseLen, FALSE); 858 } else { 859 result = new TZEnumeration(filteredMap, numEntries, TRUE); 860 filteredMap = NULL; 861 } 862 if (result == NULL) { 863 ec = U_MEMORY_ALLOCATION_ERROR; 864 } 865 } 866 867 if (filteredMap != NULL) { 868 uprv_free(filteredMap); 869 } 870 871 return result; 872 } 873 874 TZEnumeration(const TZEnumeration &other) : StringEnumeration(), map(NULL), localMap(NULL), len(0), pos(0) { 875 if (other.localMap != NULL) { 876 localMap = (int32_t *)uprv_malloc(other.len * sizeof(int32_t)); 877 if (localMap != NULL) { 878 len = other.len; 879 uprv_memcpy(localMap, other.localMap, len * sizeof(int32_t)); 880 pos = other.pos; 881 map = localMap; 882 } else { 883 len = 0; 884 pos = 0; 885 map = NULL; 886 } 887 } else { 888 map = other.map; 889 localMap = NULL; 890 len = other.len; 891 pos = other.pos; 892 } 893 } 894 895 virtual ~TZEnumeration(); 896 897 virtual StringEnumeration *clone() const { 898 return new TZEnumeration(*this); 899 } 900 901 virtual int32_t count(UErrorCode& status) const { 902 return U_FAILURE(status) ? 0 : len; 903 } 904 905 virtual const UnicodeString* snext(UErrorCode& status) { 906 if (U_SUCCESS(status) && map != NULL && pos < len) { 907 getID(map[pos]); 908 ++pos; 909 return &unistr; 910 } 911 return 0; 912 } 913 914 virtual void reset(UErrorCode& /*status*/) { 915 pos = 0; 916 } 917 918 public: 919 static UClassID U_EXPORT2 getStaticClassID(void); 920 virtual UClassID getDynamicClassID(void) const; 921 }; 922 923 TZEnumeration::~TZEnumeration() { 924 if (localMap != NULL) { 925 uprv_free(localMap); 926 } 927 } 928 929 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(TZEnumeration) 930 931 StringEnumeration* U_EXPORT2 932 TimeZone::createTimeZoneIDEnumeration( 933 USystemTimeZoneType zoneType, 934 const char* region, 935 const int32_t* rawOffset, 936 UErrorCode& ec) { 937 return TZEnumeration::create(zoneType, region, rawOffset, ec); 938 } 939 940 StringEnumeration* U_EXPORT2 941 TimeZone::createEnumeration() { 942 UErrorCode ec = U_ZERO_ERROR; 943 return TZEnumeration::create(UCAL_ZONE_TYPE_ANY, NULL, NULL, ec); 944 } 945 946 StringEnumeration* U_EXPORT2 947 TimeZone::createEnumeration(int32_t rawOffset) { 948 UErrorCode ec = U_ZERO_ERROR; 949 return TZEnumeration::create(UCAL_ZONE_TYPE_ANY, NULL, &rawOffset, ec); 950 } 951 952 StringEnumeration* U_EXPORT2 953 TimeZone::createEnumeration(const char* country) { 954 UErrorCode ec = U_ZERO_ERROR; 955 return TZEnumeration::create(UCAL_ZONE_TYPE_ANY, country, NULL, ec); 956 } 957 958 // --------------------------------------- 959 960 int32_t U_EXPORT2 961 TimeZone::countEquivalentIDs(const UnicodeString& id) { 962 int32_t result = 0; 963 UErrorCode ec = U_ZERO_ERROR; 964 UResourceBundle res; 965 ures_initStackObject(&res); 966 U_DEBUG_TZ_MSG(("countEquivalentIDs..\n")); 967 UResourceBundle *top = openOlsonResource(id, res, ec); 968 if (U_SUCCESS(ec)) { 969 UResourceBundle r; 970 ures_initStackObject(&r); 971 ures_getByKey(&res, kLINKS, &r, &ec); 972 ures_getIntVector(&r, &result, &ec); 973 ures_close(&r); 974 } 975 ures_close(&res); 976 ures_close(top); 977 return result; 978 } 979 980 // --------------------------------------- 981 982 const UnicodeString U_EXPORT2 983 TimeZone::getEquivalentID(const UnicodeString& id, int32_t index) { 984 U_DEBUG_TZ_MSG(("gEI(%d)\n", index)); 985 UnicodeString result; 986 UErrorCode ec = U_ZERO_ERROR; 987 UResourceBundle res; 988 ures_initStackObject(&res); 989 UResourceBundle *top = openOlsonResource(id, res, ec); 990 int32_t zone = -1; 991 if (U_SUCCESS(ec)) { 992 UResourceBundle r; 993 ures_initStackObject(&r); 994 int32_t size; 995 ures_getByKey(&res, kLINKS, &r, &ec); 996 const int32_t* v = ures_getIntVector(&r, &size, &ec); 997 if (U_SUCCESS(ec)) { 998 if (index >= 0 && index < size) { 999 zone = v[index]; 1000 } 1001 } 1002 ures_close(&r); 1003 } 1004 ures_close(&res); 1005 if (zone >= 0) { 1006 UResourceBundle *ares = ures_getByKey(top, kNAMES, NULL, &ec); // dereference Zones section 1007 if (U_SUCCESS(ec)) { 1008 int32_t idLen = 0; 1009 const UChar* id = ures_getStringByIndex(ares, zone, &idLen, &ec); 1010 result.fastCopyFrom(UnicodeString(TRUE, id, idLen)); 1011 U_DEBUG_TZ_MSG(("gei(%d) -> %d, len%d, %s\n", index, zone, result.length(), u_errorName(ec))); 1012 } 1013 ures_close(ares); 1014 } 1015 ures_close(top); 1016 #if defined(U_DEBUG_TZ) 1017 if(result.length() ==0) { 1018 U_DEBUG_TZ_MSG(("equiv [__, #%d] -> 0 (%s)\n", index, u_errorName(ec))); 1019 } 1020 #endif 1021 return result; 1022 } 1023 1024 // --------------------------------------- 1025 1026 // These methods are used by ZoneMeta class only. 1027 1028 const UChar* 1029 TimeZone::findID(const UnicodeString& id) { 1030 const UChar *result = NULL; 1031 UErrorCode ec = U_ZERO_ERROR; 1032 UResourceBundle *rb = ures_openDirect(NULL, kZONEINFO, &ec); 1033 1034 // resolve zone index by name 1035 UResourceBundle *names = ures_getByKey(rb, kNAMES, NULL, &ec); 1036 int32_t idx = findInStringArray(names, id, ec); 1037 result = ures_getStringByIndex(names, idx, NULL, &ec); 1038 if (U_FAILURE(ec)) { 1039 result = NULL; 1040 } 1041 ures_close(names); 1042 ures_close(rb); 1043 return result; 1044 } 1045 1046 1047 const UChar* 1048 TimeZone::dereferOlsonLink(const UnicodeString& id) { 1049 const UChar *result = NULL; 1050 UErrorCode ec = U_ZERO_ERROR; 1051 UResourceBundle *rb = ures_openDirect(NULL, kZONEINFO, &ec); 1052 1053 // resolve zone index by name 1054 UResourceBundle *names = ures_getByKey(rb, kNAMES, NULL, &ec); 1055 int32_t idx = findInStringArray(names, id, ec); 1056 result = ures_getStringByIndex(names, idx, NULL, &ec); 1057 1058 // open the zone bundle by index 1059 ures_getByKey(rb, kZONES, rb, &ec); 1060 ures_getByIndex(rb, idx, rb, &ec); 1061 1062 if (U_SUCCESS(ec)) { 1063 if (ures_getType(rb) == URES_INT) { 1064 // this is a link - dereference the link 1065 int32_t deref = ures_getInt(rb, &ec); 1066 const UChar* tmp = ures_getStringByIndex(names, deref, NULL, &ec); 1067 if (U_SUCCESS(ec)) { 1068 result = tmp; 1069 } 1070 } 1071 } 1072 1073 ures_close(names); 1074 ures_close(rb); 1075 1076 return result; 1077 } 1078 1079 const UChar* 1080 TimeZone::getRegion(const UnicodeString& id) { 1081 UErrorCode status = U_ZERO_ERROR; 1082 return getRegion(id, status); 1083 } 1084 1085 const UChar* 1086 TimeZone::getRegion(const UnicodeString& id, UErrorCode& status) { 1087 if (U_FAILURE(status)) { 1088 return NULL; 1089 } 1090 const UChar *result = NULL; 1091 UResourceBundle *rb = ures_openDirect(NULL, kZONEINFO, &status); 1092 1093 // resolve zone index by name 1094 UResourceBundle *res = ures_getByKey(rb, kNAMES, NULL, &status); 1095 int32_t idx = findInStringArray(res, id, status); 1096 1097 // get region mapping 1098 ures_getByKey(rb, kREGIONS, res, &status); 1099 const UChar *tmp = ures_getStringByIndex(res, idx, NULL, &status); 1100 if (U_SUCCESS(status)) { 1101 result = tmp; 1102 } 1103 1104 ures_close(res); 1105 ures_close(rb); 1106 1107 return result; 1108 } 1109 1110 1111 // --------------------------------------- 1112 int32_t 1113 TimeZone::getRegion(const UnicodeString& id, char *region, int32_t capacity, UErrorCode& status) 1114 { 1115 int32_t resultLen = 0; 1116 *region = 0; 1117 if (U_FAILURE(status)) { 1118 return 0; 1119 } 1120 1121 const UChar *uregion = NULL; 1122 // "Etc/Unknown" is not a system zone ID, 1123 // but in the zone data 1124 if (id.compare(UNKNOWN_ZONE_ID, UNKNOWN_ZONE_ID_LENGTH) != 0) { 1125 uregion = getRegion(id); 1126 } 1127 if (uregion == NULL) { 1128 status = U_ILLEGAL_ARGUMENT_ERROR; 1129 return 0; 1130 } 1131 resultLen = u_strlen(uregion); 1132 // A region code is represented by invariant characters 1133 u_UCharsToChars(uregion, region, uprv_min(resultLen, capacity)); 1134 1135 if (capacity < resultLen) { 1136 status = U_BUFFER_OVERFLOW_ERROR; 1137 return resultLen; 1138 } 1139 1140 return u_terminateChars(region, capacity, resultLen, &status); 1141 } 1142 1143 // --------------------------------------- 1144 1145 1146 UnicodeString& 1147 TimeZone::getDisplayName(UnicodeString& result) const 1148 { 1149 return getDisplayName(FALSE,LONG,Locale::getDefault(), result); 1150 } 1151 1152 UnicodeString& 1153 TimeZone::getDisplayName(const Locale& locale, UnicodeString& result) const 1154 { 1155 return getDisplayName(FALSE, LONG, locale, result); 1156 } 1157 1158 UnicodeString& 1159 TimeZone::getDisplayName(UBool daylight, EDisplayType style, UnicodeString& result) const 1160 { 1161 return getDisplayName(daylight,style, Locale::getDefault(), result); 1162 } 1163 //-------------------------------------- 1164 int32_t 1165 TimeZone::getDSTSavings()const { 1166 if (useDaylightTime()) { 1167 return 3600000; 1168 } 1169 return 0; 1170 } 1171 //--------------------------------------- 1172 UnicodeString& 1173 TimeZone::getDisplayName(UBool daylight, EDisplayType style, const Locale& locale, UnicodeString& result) const 1174 { 1175 UErrorCode status = U_ZERO_ERROR; 1176 UDate date = Calendar::getNow(); 1177 UTimeZoneFormatTimeType timeType; 1178 int32_t offset; 1179 1180 if (style == GENERIC_LOCATION || style == LONG_GENERIC || style == SHORT_GENERIC) { 1181 LocalPointer<TimeZoneFormat> tzfmt(TimeZoneFormat::createInstance(locale, status)); 1182 if (U_FAILURE(status)) { 1183 result.remove(); 1184 return result; 1185 } 1186 // Generic format 1187 switch (style) { 1188 case GENERIC_LOCATION: 1189 tzfmt->format(UTZFMT_STYLE_GENERIC_LOCATION, *this, date, result, &timeType); 1190 break; 1191 case LONG_GENERIC: 1192 tzfmt->format(UTZFMT_STYLE_GENERIC_LONG, *this, date, result, &timeType); 1193 break; 1194 case SHORT_GENERIC: 1195 tzfmt->format(UTZFMT_STYLE_GENERIC_SHORT, *this, date, result, &timeType); 1196 break; 1197 default: 1198 U_ASSERT(FALSE); 1199 } 1200 // Generic format many use Localized GMT as the final fallback. 1201 // When Localized GMT format is used, the result might not be 1202 // appropriate for the requested daylight value. 1203 if ((daylight && timeType == UTZFMT_TIME_TYPE_STANDARD) || (!daylight && timeType == UTZFMT_TIME_TYPE_DAYLIGHT)) { 1204 offset = daylight ? getRawOffset() + getDSTSavings() : getRawOffset(); 1205 if (style == SHORT_GENERIC) { 1206 tzfmt->formatOffsetShortLocalizedGMT(offset, result, status); 1207 } else { 1208 tzfmt->formatOffsetLocalizedGMT(offset, result, status); 1209 } 1210 } 1211 } else if (style == LONG_GMT || style == SHORT_GMT) { 1212 LocalPointer<TimeZoneFormat> tzfmt(TimeZoneFormat::createInstance(locale, status)); 1213 if (U_FAILURE(status)) { 1214 result.remove(); 1215 return result; 1216 } 1217 offset = daylight && useDaylightTime() ? getRawOffset() + getDSTSavings() : getRawOffset(); 1218 switch (style) { 1219 case LONG_GMT: 1220 tzfmt->formatOffsetLocalizedGMT(offset, result, status); 1221 break; 1222 case SHORT_GMT: 1223 tzfmt->formatOffsetISO8601Basic(offset, FALSE, FALSE, FALSE, result, status); 1224 break; 1225 default: 1226 U_ASSERT(FALSE); 1227 } 1228 1229 } else { 1230 U_ASSERT(style == LONG || style == SHORT || style == SHORT_COMMONLY_USED); 1231 UTimeZoneNameType nameType = UTZNM_UNKNOWN; 1232 switch (style) { 1233 case LONG: 1234 nameType = daylight ? UTZNM_LONG_DAYLIGHT : UTZNM_LONG_STANDARD; 1235 break; 1236 case SHORT: 1237 case SHORT_COMMONLY_USED: 1238 nameType = daylight ? UTZNM_SHORT_DAYLIGHT : UTZNM_SHORT_STANDARD; 1239 break; 1240 default: 1241 U_ASSERT(FALSE); 1242 } 1243 LocalPointer<TimeZoneNames> tznames(TimeZoneNames::createInstance(locale, status)); 1244 if (U_FAILURE(status)) { 1245 result.remove(); 1246 return result; 1247 } 1248 UnicodeString canonicalID(ZoneMeta::getCanonicalCLDRID(*this)); 1249 tznames->getDisplayName(canonicalID, nameType, date, result); 1250 if (result.isEmpty()) { 1251 // Fallback to localized GMT 1252 LocalPointer<TimeZoneFormat> tzfmt(TimeZoneFormat::createInstance(locale, status)); 1253 offset = daylight && useDaylightTime() ? getRawOffset() + getDSTSavings() : getRawOffset(); 1254 if (style == LONG) { 1255 tzfmt->formatOffsetLocalizedGMT(offset, result, status); 1256 } else { 1257 tzfmt->formatOffsetShortLocalizedGMT(offset, result, status); 1258 } 1259 } 1260 } 1261 if (U_FAILURE(status)) { 1262 result.remove(); 1263 } 1264 return result; 1265 } 1266 1267 /** 1268 * Parse a custom time zone identifier and return a corresponding zone. 1269 * @param id a string of the form GMT[+-]hh:mm, GMT[+-]hhmm, or 1270 * GMT[+-]hh. 1271 * @return a newly created SimpleTimeZone with the given offset and 1272 * no Daylight Savings Time, or null if the id cannot be parsed. 1273 */ 1274 TimeZone* 1275 TimeZone::createCustomTimeZone(const UnicodeString& id) 1276 { 1277 int32_t sign, hour, min, sec; 1278 if (parseCustomID(id, sign, hour, min, sec)) { 1279 UnicodeString customID; 1280 formatCustomID(hour, min, sec, (sign < 0), customID); 1281 int32_t offset = sign * ((hour * 60 + min) * 60 + sec) * 1000; 1282 return new SimpleTimeZone(offset, customID); 1283 } 1284 return NULL; 1285 } 1286 1287 UnicodeString& 1288 TimeZone::getCustomID(const UnicodeString& id, UnicodeString& normalized, UErrorCode& status) { 1289 normalized.remove(); 1290 if (U_FAILURE(status)) { 1291 return normalized; 1292 } 1293 int32_t sign, hour, min, sec; 1294 if (parseCustomID(id, sign, hour, min, sec)) { 1295 formatCustomID(hour, min, sec, (sign < 0), normalized); 1296 } else { 1297 status = U_ILLEGAL_ARGUMENT_ERROR; 1298 } 1299 return normalized; 1300 } 1301 1302 UBool 1303 TimeZone::parseCustomID(const UnicodeString& id, int32_t& sign, 1304 int32_t& hour, int32_t& min, int32_t& sec) { 1305 static const int32_t kParseFailed = -99999; 1306 1307 NumberFormat* numberFormat = 0; 1308 UnicodeString idUppercase = id; 1309 idUppercase.toUpper(""); 1310 1311 if (id.length() > GMT_ID_LENGTH && 1312 idUppercase.startsWith(GMT_ID, GMT_ID_LENGTH)) 1313 { 1314 ParsePosition pos(GMT_ID_LENGTH); 1315 sign = 1; 1316 hour = 0; 1317 min = 0; 1318 sec = 0; 1319 1320 if (id[pos.getIndex()] == MINUS /*'-'*/) { 1321 sign = -1; 1322 } else if (id[pos.getIndex()] != PLUS /*'+'*/) { 1323 return FALSE; 1324 } 1325 pos.setIndex(pos.getIndex() + 1); 1326 1327 UErrorCode success = U_ZERO_ERROR; 1328 numberFormat = NumberFormat::createInstance(success); 1329 if(U_FAILURE(success)){ 1330 return FALSE; 1331 } 1332 numberFormat->setParseIntegerOnly(TRUE); 1333 //numberFormat->setLenient(TRUE); // TODO: May need to set this, depends on latest timezone parsing 1334 1335 // Look for either hh:mm, hhmm, or hh 1336 int32_t start = pos.getIndex(); 1337 Formattable n(kParseFailed); 1338 numberFormat->parse(id, n, pos); 1339 if (pos.getIndex() == start) { 1340 delete numberFormat; 1341 return FALSE; 1342 } 1343 hour = n.getLong(); 1344 1345 if (pos.getIndex() < id.length()) { 1346 if (pos.getIndex() - start > 2 1347 || id[pos.getIndex()] != COLON) { 1348 delete numberFormat; 1349 return FALSE; 1350 } 1351 // hh:mm 1352 pos.setIndex(pos.getIndex() + 1); 1353 int32_t oldPos = pos.getIndex(); 1354 n.setLong(kParseFailed); 1355 numberFormat->parse(id, n, pos); 1356 if ((pos.getIndex() - oldPos) != 2) { 1357 // must be 2 digits 1358 delete numberFormat; 1359 return FALSE; 1360 } 1361 min = n.getLong(); 1362 if (pos.getIndex() < id.length()) { 1363 if (id[pos.getIndex()] != COLON) { 1364 delete numberFormat; 1365 return FALSE; 1366 } 1367 // [:ss] 1368 pos.setIndex(pos.getIndex() + 1); 1369 oldPos = pos.getIndex(); 1370 n.setLong(kParseFailed); 1371 numberFormat->parse(id, n, pos); 1372 if (pos.getIndex() != id.length() 1373 || (pos.getIndex() - oldPos) != 2) { 1374 delete numberFormat; 1375 return FALSE; 1376 } 1377 sec = n.getLong(); 1378 } 1379 } else { 1380 // Supported formats are below - 1381 // 1382 // HHmmss 1383 // Hmmss 1384 // HHmm 1385 // Hmm 1386 // HH 1387 // H 1388 1389 int32_t length = pos.getIndex() - start; 1390 if (length <= 0 || 6 < length) { 1391 // invalid length 1392 delete numberFormat; 1393 return FALSE; 1394 } 1395 switch (length) { 1396 case 1: 1397 case 2: 1398 // already set to hour 1399 break; 1400 case 3: 1401 case 4: 1402 min = hour % 100; 1403 hour /= 100; 1404 break; 1405 case 5: 1406 case 6: 1407 sec = hour % 100; 1408 min = (hour/100) % 100; 1409 hour /= 10000; 1410 break; 1411 } 1412 } 1413 1414 delete numberFormat; 1415 1416 if (hour > kMAX_CUSTOM_HOUR || min > kMAX_CUSTOM_MIN || sec > kMAX_CUSTOM_SEC) { 1417 return FALSE; 1418 } 1419 return TRUE; 1420 } 1421 return FALSE; 1422 } 1423 1424 UnicodeString& 1425 TimeZone::formatCustomID(int32_t hour, int32_t min, int32_t sec, 1426 UBool negative, UnicodeString& id) { 1427 // Create time zone ID - GMT[+|-]hhmm[ss] 1428 id.setTo(GMT_ID, GMT_ID_LENGTH); 1429 if (hour | min | sec) { 1430 if (negative) { 1431 id += (UChar)MINUS; 1432 } else { 1433 id += (UChar)PLUS; 1434 } 1435 1436 if (hour < 10) { 1437 id += (UChar)ZERO_DIGIT; 1438 } else { 1439 id += (UChar)(ZERO_DIGIT + hour/10); 1440 } 1441 id += (UChar)(ZERO_DIGIT + hour%10); 1442 id += (UChar)COLON; 1443 if (min < 10) { 1444 id += (UChar)ZERO_DIGIT; 1445 } else { 1446 id += (UChar)(ZERO_DIGIT + min/10); 1447 } 1448 id += (UChar)(ZERO_DIGIT + min%10); 1449 1450 if (sec) { 1451 id += (UChar)COLON; 1452 if (sec < 10) { 1453 id += (UChar)ZERO_DIGIT; 1454 } else { 1455 id += (UChar)(ZERO_DIGIT + sec/10); 1456 } 1457 id += (UChar)(ZERO_DIGIT + sec%10); 1458 } 1459 } 1460 return id; 1461 } 1462 1463 1464 UBool 1465 TimeZone::hasSameRules(const TimeZone& other) const 1466 { 1467 return (getRawOffset() == other.getRawOffset() && 1468 useDaylightTime() == other.useDaylightTime()); 1469 } 1470 1471 static void U_CALLCONV initTZDataVersion(UErrorCode &status) { 1472 ucln_i18n_registerCleanup(UCLN_I18N_TIMEZONE, timeZone_cleanup); 1473 int32_t len = 0; 1474 UResourceBundle *bundle = ures_openDirect(NULL, kZONEINFO, &status); 1475 const UChar *tzver = ures_getStringByKey(bundle, kTZVERSION, &len, &status); 1476 1477 if (U_SUCCESS(status)) { 1478 if (len >= (int32_t)sizeof(TZDATA_VERSION)) { 1479 // Ensure that there is always space for a trailing nul in TZDATA_VERSION 1480 len = sizeof(TZDATA_VERSION) - 1; 1481 } 1482 u_UCharsToChars(tzver, TZDATA_VERSION, len); 1483 } 1484 ures_close(bundle); 1485 1486 } 1487 1488 const char* 1489 TimeZone::getTZDataVersion(UErrorCode& status) 1490 { 1491 umtx_initOnce(gTZDataVersionInitOnce, &initTZDataVersion, status); 1492 return (const char*)TZDATA_VERSION; 1493 } 1494 1495 UnicodeString& 1496 TimeZone::getCanonicalID(const UnicodeString& id, UnicodeString& canonicalID, UErrorCode& status) 1497 { 1498 UBool isSystemID = FALSE; 1499 return getCanonicalID(id, canonicalID, isSystemID, status); 1500 } 1501 1502 UnicodeString& 1503 TimeZone::getCanonicalID(const UnicodeString& id, UnicodeString& canonicalID, UBool& isSystemID, 1504 UErrorCode& status) 1505 { 1506 canonicalID.remove(); 1507 isSystemID = FALSE; 1508 if (U_FAILURE(status)) { 1509 return canonicalID; 1510 } 1511 if (id.compare(UNKNOWN_ZONE_ID, UNKNOWN_ZONE_ID_LENGTH) == 0) { 1512 // special case - Etc/Unknown is a canonical ID, but not system ID 1513 canonicalID.fastCopyFrom(id); 1514 isSystemID = FALSE; 1515 } else { 1516 ZoneMeta::getCanonicalCLDRID(id, canonicalID, status); 1517 if (U_SUCCESS(status)) { 1518 isSystemID = TRUE; 1519 } else { 1520 // Not a system ID 1521 status = U_ZERO_ERROR; 1522 getCustomID(id, canonicalID, status); 1523 } 1524 } 1525 return canonicalID; 1526 } 1527 1528 #ifndef U_HIDE_DRAFT_API 1529 UnicodeString& 1530 TimeZone::getWindowsID(const UnicodeString& id, UnicodeString& winid, UErrorCode& status) { 1531 winid.remove(); 1532 if (U_FAILURE(status)) { 1533 return winid; 1534 } 1535 1536 // canonicalize the input ID 1537 UnicodeString canonicalID; 1538 UBool isSystemID = FALSE; 1539 1540 getCanonicalID(id, canonicalID, isSystemID, status); 1541 if (U_FAILURE(status) || !isSystemID) { 1542 // mapping data is only applicable to tz database IDs 1543 if (status == U_ILLEGAL_ARGUMENT_ERROR) { 1544 // getWindowsID() sets an empty string where 1545 // getCanonicalID() sets a U_ILLEGAL_ARGUMENT_ERROR. 1546 status = U_ZERO_ERROR; 1547 } 1548 return winid; 1549 } 1550 1551 UResourceBundle *mapTimezones = ures_openDirect(NULL, "windowsZones", &status); 1552 ures_getByKey(mapTimezones, "mapTimezones", mapTimezones, &status); 1553 1554 if (U_FAILURE(status)) { 1555 return winid; 1556 } 1557 1558 UResourceBundle *winzone = NULL; 1559 UBool found = FALSE; 1560 while (ures_hasNext(mapTimezones) && !found) { 1561 winzone = ures_getNextResource(mapTimezones, winzone, &status); 1562 if (U_FAILURE(status)) { 1563 break; 1564 } 1565 if (ures_getType(winzone) != URES_TABLE) { 1566 continue; 1567 } 1568 UResourceBundle *regionalData = NULL; 1569 while (ures_hasNext(winzone) && !found) { 1570 regionalData = ures_getNextResource(winzone, regionalData, &status); 1571 if (U_FAILURE(status)) { 1572 break; 1573 } 1574 if (ures_getType(regionalData) != URES_STRING) { 1575 continue; 1576 } 1577 int32_t len; 1578 const UChar *tzids = ures_getString(regionalData, &len, &status); 1579 if (U_FAILURE(status)) { 1580 break; 1581 } 1582 1583 const UChar *start = tzids; 1584 UBool hasNext = TRUE; 1585 while (hasNext) { 1586 const UChar *end = u_strchr(start, (UChar)0x20); 1587 if (end == NULL) { 1588 end = tzids + len; 1589 hasNext = FALSE; 1590 } 1591 if (canonicalID.compare(start, end - start) == 0) { 1592 winid = UnicodeString(ures_getKey(winzone), -1 , US_INV); 1593 found = TRUE; 1594 break; 1595 } 1596 start = end + 1; 1597 } 1598 } 1599 ures_close(regionalData); 1600 } 1601 ures_close(winzone); 1602 ures_close(mapTimezones); 1603 1604 return winid; 1605 } 1606 1607 #define MAX_WINDOWS_ID_SIZE 128 1608 1609 UnicodeString& 1610 TimeZone::getIDForWindowsID(const UnicodeString& winid, const char* region, UnicodeString& id, UErrorCode& status) { 1611 id.remove(); 1612 if (U_FAILURE(status)) { 1613 return id; 1614 } 1615 1616 UResourceBundle *zones = ures_openDirect(NULL, "windowsZones", &status); 1617 ures_getByKey(zones, "mapTimezones", zones, &status); 1618 if (U_FAILURE(status)) { 1619 ures_close(zones); 1620 return id; 1621 } 1622 1623 UErrorCode tmperr = U_ZERO_ERROR; 1624 char winidKey[MAX_WINDOWS_ID_SIZE]; 1625 int32_t winKeyLen = winid.extract(0, winid.length(), winidKey, sizeof(winidKey) - 1, US_INV); 1626 1627 if (winKeyLen == 0 || winKeyLen >= (int32_t)sizeof(winidKey)) { 1628 ures_close(zones); 1629 return id; 1630 } 1631 winidKey[winKeyLen] = 0; 1632 1633 ures_getByKey(zones, winidKey, zones, &tmperr); // use tmperr, because windows mapping might not 1634 // be avaiable by design 1635 if (U_FAILURE(tmperr)) { 1636 ures_close(zones); 1637 return id; 1638 } 1639 1640 const UChar *tzid = NULL; 1641 int32_t len = 0; 1642 UBool gotID = FALSE; 1643 if (region) { 1644 const UChar *tzids = ures_getStringByKey(zones, region, &len, &tmperr); // use tmperr, because 1645 // regional mapping is optional 1646 if (U_SUCCESS(tmperr)) { 1647 // first ID delimited by space is the defasult one 1648 const UChar *end = u_strchr(tzids, (UChar)0x20); 1649 if (end == NULL) { 1650 id.setTo(tzids, -1); 1651 } else { 1652 id.setTo(tzids, end - tzids); 1653 } 1654 gotID = TRUE; 1655 } 1656 } 1657 1658 if (!gotID) { 1659 tzid = ures_getStringByKey(zones, "001", &len, &status); // using status, because "001" must be 1660 // available at this point 1661 if (U_SUCCESS(status)) { 1662 id.setTo(tzid, len); 1663 } 1664 } 1665 1666 ures_close(zones); 1667 return id; 1668 } 1669 #endif /* U_HIDE_DRAFT_API */ 1670 1671 1672 U_NAMESPACE_END 1673 1674 #endif /* #if !UCONFIG_NO_FORMATTING */ 1675 1676 //eof 1677