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