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