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