1 /* 2 ******************************************************************************* 3 * Copyright (C) 2011-2012, International Business Machines Corporation and 4 * others. All Rights Reserved. 5 ******************************************************************************* 6 */ 7 8 #include "unicode/utypes.h" 9 10 #if !UCONFIG_NO_FORMATTING 11 12 #include "tzgnames.h" 13 14 #include "unicode/basictz.h" 15 #include "unicode/locdspnm.h" 16 #include "unicode/msgfmt.h" 17 #include "unicode/rbtz.h" 18 #include "unicode/simpletz.h" 19 #include "unicode/vtzone.h" 20 21 #include "cmemory.h" 22 #include "cstring.h" 23 #include "uhash.h" 24 #include "uassert.h" 25 #include "umutex.h" 26 #include "uresimp.h" 27 #include "ureslocs.h" 28 #include "zonemeta.h" 29 #include "tznames_impl.h" 30 #include "olsontz.h" 31 #include "ucln_in.h" 32 33 U_NAMESPACE_BEGIN 34 35 #define ZID_KEY_MAX 128 36 37 static const char gZoneStrings[] = "zoneStrings"; 38 39 static const char gRegionFormatTag[] = "regionFormat"; 40 static const char gFallbackRegionFormatTag[] = "fallbackRegionFormat"; 41 static const char gFallbackFormatTag[] = "fallbackFormat"; 42 43 static const UChar gEmpty[] = {0x00}; 44 45 static const UChar gDefRegionPattern[] = {0x7B, 0x30, 0x7D, 0x00}; // "{0}" 46 static const UChar gDefFallbackRegionPattern[] = {0x7B, 0x31, 0x7D, 0x20, 0x28, 0x7B, 0x30, 0x7D, 0x29, 0x00}; // "{1} ({0})" 47 static const UChar gDefFallbackPattern[] = {0x7B, 0x31, 0x7D, 0x20, 0x28, 0x7B, 0x30, 0x7D, 0x29, 0x00}; // "{1} ({0})" 48 49 static const double kDstCheckRange = (double)184*U_MILLIS_PER_DAY; 50 51 52 53 U_CDECL_BEGIN 54 55 typedef struct PartialLocationKey { 56 const UChar* tzID; 57 const UChar* mzID; 58 UBool isLong; 59 } PartialLocationKey; 60 61 /** 62 * Hash function for partial location name hash key 63 */ 64 static int32_t U_CALLCONV 65 hashPartialLocationKey(const UHashTok key) { 66 // <tzID>&<mzID>#[L|S] 67 PartialLocationKey *p = (PartialLocationKey *)key.pointer; 68 UnicodeString str(p->tzID); 69 str.append((UChar)0x26) 70 .append(p->mzID, -1) 71 .append((UChar)0x23) 72 .append((UChar)(p->isLong ? 0x4C : 0x53)); 73 return str.hashCode(); 74 } 75 76 /** 77 * Comparer for partial location name hash key 78 */ 79 static UBool U_CALLCONV 80 comparePartialLocationKey(const UHashTok key1, const UHashTok key2) { 81 PartialLocationKey *p1 = (PartialLocationKey *)key1.pointer; 82 PartialLocationKey *p2 = (PartialLocationKey *)key2.pointer; 83 84 if (p1 == p2) { 85 return TRUE; 86 } 87 if (p1 == NULL || p2 == NULL) { 88 return FALSE; 89 } 90 // We just check identity of tzID/mzID 91 return (p1->tzID == p2->tzID && p1->mzID == p2->mzID && p1->isLong == p2->isLong); 92 } 93 94 /** 95 * Deleter for GNameInfo 96 */ 97 static void U_CALLCONV 98 deleteGNameInfo(void *obj) { 99 uprv_free(obj); 100 } 101 102 /** 103 * GNameInfo stores zone name information in the local trie 104 */ 105 typedef struct GNameInfo { 106 UTimeZoneGenericNameType type; 107 const UChar* tzID; 108 } ZNameInfo; 109 110 /** 111 * GMatchInfo stores zone name match information used by find method 112 */ 113 typedef struct GMatchInfo { 114 const GNameInfo* gnameInfo; 115 int32_t matchLength; 116 UTimeZoneFormatTimeType timeType; 117 } ZMatchInfo; 118 119 U_CDECL_END 120 121 // --------------------------------------------------- 122 // The class stores time zone generic name match information 123 // --------------------------------------------------- 124 class TimeZoneGenericNameMatchInfo : public UMemory { 125 public: 126 TimeZoneGenericNameMatchInfo(UVector* matches); 127 ~TimeZoneGenericNameMatchInfo(); 128 129 int32_t size() const; 130 UTimeZoneGenericNameType getGenericNameType(int32_t index) const; 131 int32_t getMatchLength(int32_t index) const; 132 UnicodeString& getTimeZoneID(int32_t index, UnicodeString& tzID) const; 133 134 private: 135 UVector* fMatches; // vector of MatchEntry 136 }; 137 138 TimeZoneGenericNameMatchInfo::TimeZoneGenericNameMatchInfo(UVector* matches) 139 : fMatches(matches) { 140 } 141 142 TimeZoneGenericNameMatchInfo::~TimeZoneGenericNameMatchInfo() { 143 if (fMatches != NULL) { 144 delete fMatches; 145 } 146 } 147 148 int32_t 149 TimeZoneGenericNameMatchInfo::size() const { 150 if (fMatches == NULL) { 151 return 0; 152 } 153 return fMatches->size(); 154 } 155 156 UTimeZoneGenericNameType 157 TimeZoneGenericNameMatchInfo::getGenericNameType(int32_t index) const { 158 GMatchInfo *minfo = (GMatchInfo *)fMatches->elementAt(index); 159 if (minfo != NULL) { 160 return static_cast<UTimeZoneGenericNameType>(minfo->gnameInfo->type); 161 } 162 return UTZGNM_UNKNOWN; 163 } 164 165 int32_t 166 TimeZoneGenericNameMatchInfo::getMatchLength(int32_t index) const { 167 ZMatchInfo *minfo = (ZMatchInfo *)fMatches->elementAt(index); 168 if (minfo != NULL) { 169 return minfo->matchLength; 170 } 171 return -1; 172 } 173 174 UnicodeString& 175 TimeZoneGenericNameMatchInfo::getTimeZoneID(int32_t index, UnicodeString& tzID) const { 176 GMatchInfo *minfo = (GMatchInfo *)fMatches->elementAt(index); 177 if (minfo != NULL && minfo->gnameInfo->tzID != NULL) { 178 tzID.setTo(TRUE, minfo->gnameInfo->tzID, -1); 179 } else { 180 tzID.setToBogus(); 181 } 182 return tzID; 183 } 184 185 // --------------------------------------------------- 186 // GNameSearchHandler 187 // --------------------------------------------------- 188 class GNameSearchHandler : public TextTrieMapSearchResultHandler { 189 public: 190 GNameSearchHandler(uint32_t types); 191 virtual ~GNameSearchHandler(); 192 193 UBool handleMatch(int32_t matchLength, const CharacterNode *node, UErrorCode &status); 194 UVector* getMatches(int32_t& maxMatchLen); 195 196 private: 197 uint32_t fTypes; 198 UVector* fResults; 199 int32_t fMaxMatchLen; 200 }; 201 202 GNameSearchHandler::GNameSearchHandler(uint32_t types) 203 : fTypes(types), fResults(NULL), fMaxMatchLen(0) { 204 } 205 206 GNameSearchHandler::~GNameSearchHandler() { 207 if (fResults != NULL) { 208 delete fResults; 209 } 210 } 211 212 UBool 213 GNameSearchHandler::handleMatch(int32_t matchLength, const CharacterNode *node, UErrorCode &status) { 214 if (U_FAILURE(status)) { 215 return FALSE; 216 } 217 if (node->hasValues()) { 218 int32_t valuesCount = node->countValues(); 219 for (int32_t i = 0; i < valuesCount; i++) { 220 GNameInfo *nameinfo = (ZNameInfo *)node->getValue(i); 221 if (nameinfo == NULL) { 222 break; 223 } 224 if ((nameinfo->type & fTypes) != 0) { 225 // matches a requested type 226 if (fResults == NULL) { 227 fResults = new UVector(uprv_free, NULL, status); 228 if (fResults == NULL) { 229 status = U_MEMORY_ALLOCATION_ERROR; 230 } 231 } 232 if (U_SUCCESS(status)) { 233 U_ASSERT(fResults != NULL); 234 GMatchInfo *gmatch = (GMatchInfo *)uprv_malloc(sizeof(GMatchInfo)); 235 if (gmatch == NULL) { 236 status = U_MEMORY_ALLOCATION_ERROR; 237 } else { 238 // add the match to the vector 239 gmatch->gnameInfo = nameinfo; 240 gmatch->matchLength = matchLength; 241 gmatch->timeType = UTZFMT_TIME_TYPE_UNKNOWN; 242 fResults->addElement(gmatch, status); 243 if (U_FAILURE(status)) { 244 uprv_free(gmatch); 245 } else { 246 if (matchLength > fMaxMatchLen) { 247 fMaxMatchLen = matchLength; 248 } 249 } 250 } 251 } 252 } 253 } 254 } 255 return TRUE; 256 } 257 258 UVector* 259 GNameSearchHandler::getMatches(int32_t& maxMatchLen) { 260 // give the ownership to the caller 261 UVector *results = fResults; 262 maxMatchLen = fMaxMatchLen; 263 264 // reset 265 fResults = NULL; 266 fMaxMatchLen = 0; 267 return results; 268 } 269 270 static UMutex gLock = U_MUTEX_INITIALIZER; 271 272 class TZGNCore : public UMemory { 273 public: 274 TZGNCore(const Locale& locale, UErrorCode& status); 275 virtual ~TZGNCore(); 276 277 UnicodeString& getDisplayName(const TimeZone& tz, UTimeZoneGenericNameType type, 278 UDate date, UnicodeString& name) const; 279 280 UnicodeString& getGenericLocationName(const UnicodeString& tzCanonicalID, UnicodeString& name) const; 281 282 int32_t findBestMatch(const UnicodeString& text, int32_t start, uint32_t types, 283 UnicodeString& tzID, UTimeZoneFormatTimeType& timeType, UErrorCode& status) const; 284 285 private: 286 Locale fLocale; 287 const TimeZoneNames* fTimeZoneNames; 288 UHashtable* fLocationNamesMap; 289 UHashtable* fPartialLocationNamesMap; 290 291 MessageFormat* fRegionFormat; 292 MessageFormat* fFallbackFormat; 293 294 LocaleDisplayNames* fLocaleDisplayNames; 295 ZNStringPool fStringPool; 296 297 TextTrieMap fGNamesTrie; 298 UBool fGNamesTrieFullyLoaded; 299 300 char fTargetRegion[ULOC_COUNTRY_CAPACITY]; 301 302 void initialize(const Locale& locale, UErrorCode& status); 303 void cleanup(); 304 305 void loadStrings(const UnicodeString& tzCanonicalID); 306 307 const UChar* getGenericLocationName(const UnicodeString& tzCanonicalID); 308 309 UnicodeString& formatGenericNonLocationName(const TimeZone& tz, UTimeZoneGenericNameType type, 310 UDate date, UnicodeString& name) const; 311 312 UnicodeString& getPartialLocationName(const UnicodeString& tzCanonicalID, 313 const UnicodeString& mzID, UBool isLong, const UnicodeString& mzDisplayName, 314 UnicodeString& name) const; 315 316 const UChar* getPartialLocationName(const UnicodeString& tzCanonicalID, 317 const UnicodeString& mzID, UBool isLong, const UnicodeString& mzDisplayName); 318 319 TimeZoneGenericNameMatchInfo* findLocal(const UnicodeString& text, int32_t start, uint32_t types, UErrorCode& status) const; 320 321 TimeZoneNames::MatchInfoCollection* findTimeZoneNames(const UnicodeString& text, int32_t start, uint32_t types, UErrorCode& status) const; 322 }; 323 324 325 // --------------------------------------------------- 326 // TZGNCore - core implmentation of TimeZoneGenericNames 327 // 328 // TimeZoneGenericNames is parallel to TimeZoneNames, 329 // but handles run-time generated time zone names. 330 // This is the main part of this module. 331 // --------------------------------------------------- 332 TZGNCore::TZGNCore(const Locale& locale, UErrorCode& status) 333 : fLocale(locale), 334 fTimeZoneNames(NULL), 335 fLocationNamesMap(NULL), 336 fPartialLocationNamesMap(NULL), 337 fRegionFormat(NULL), 338 fFallbackFormat(NULL), 339 fLocaleDisplayNames(NULL), 340 fStringPool(status), 341 fGNamesTrie(TRUE, deleteGNameInfo), 342 fGNamesTrieFullyLoaded(FALSE) { 343 initialize(locale, status); 344 } 345 346 TZGNCore::~TZGNCore() { 347 cleanup(); 348 } 349 350 void 351 TZGNCore::initialize(const Locale& locale, UErrorCode& status) { 352 if (U_FAILURE(status)) { 353 return; 354 } 355 356 // TimeZoneNames 357 fTimeZoneNames = TimeZoneNames::createInstance(locale, status); 358 if (U_FAILURE(status)) { 359 return; 360 } 361 362 // Initialize format patterns 363 UnicodeString rpat(TRUE, gDefRegionPattern, -1); 364 UnicodeString frpat(TRUE, gDefFallbackRegionPattern, -1); 365 UnicodeString fpat(TRUE, gDefFallbackPattern, -1); 366 367 UErrorCode tmpsts = U_ZERO_ERROR; // OK with fallback warning.. 368 UResourceBundle *zoneStrings = ures_open(U_ICUDATA_ZONE, locale.getName(), &tmpsts); 369 zoneStrings = ures_getByKeyWithFallback(zoneStrings, gZoneStrings, zoneStrings, &tmpsts); 370 371 if (U_SUCCESS(tmpsts)) { 372 const UChar *regionPattern = ures_getStringByKeyWithFallback(zoneStrings, gRegionFormatTag, NULL, &tmpsts); 373 if (U_SUCCESS(tmpsts) && u_strlen(regionPattern) > 0) { 374 rpat.setTo(regionPattern, -1); 375 } 376 tmpsts = U_ZERO_ERROR; 377 const UChar *fallbackRegionPattern = ures_getStringByKeyWithFallback(zoneStrings, gFallbackRegionFormatTag, NULL, &tmpsts); 378 if (U_SUCCESS(tmpsts) && u_strlen(fallbackRegionPattern) > 0) { 379 frpat.setTo(fallbackRegionPattern, -1); 380 } 381 tmpsts = U_ZERO_ERROR; 382 const UChar *fallbackPattern = ures_getStringByKeyWithFallback(zoneStrings, gFallbackFormatTag, NULL, &tmpsts); 383 if (U_SUCCESS(tmpsts) && u_strlen(fallbackPattern) > 0) { 384 fpat.setTo(fallbackPattern, -1); 385 } 386 } 387 ures_close(zoneStrings); 388 389 fRegionFormat = new MessageFormat(rpat, status); 390 if (fRegionFormat == NULL) { 391 status = U_MEMORY_ALLOCATION_ERROR; 392 } 393 fFallbackFormat = new MessageFormat(fpat, status); 394 if (fFallbackFormat == NULL) { 395 status = U_MEMORY_ALLOCATION_ERROR; 396 } 397 if (U_FAILURE(status)) { 398 cleanup(); 399 return; 400 } 401 402 // locale display names 403 fLocaleDisplayNames = LocaleDisplayNames::createInstance(locale); 404 405 // hash table for names - no key/value deleters 406 fLocationNamesMap = uhash_open(uhash_hashUChars, uhash_compareUChars, NULL, &status); 407 if (U_FAILURE(status)) { 408 cleanup(); 409 return; 410 } 411 412 fPartialLocationNamesMap = uhash_open(hashPartialLocationKey, comparePartialLocationKey, NULL, &status); 413 if (U_FAILURE(status)) { 414 cleanup(); 415 return; 416 } 417 uhash_setKeyDeleter(fPartialLocationNamesMap, uprv_free); 418 // no value deleter 419 420 // target region 421 const char* region = fLocale.getCountry(); 422 int32_t regionLen = uprv_strlen(region); 423 if (regionLen == 0) { 424 char loc[ULOC_FULLNAME_CAPACITY]; 425 uloc_addLikelySubtags(fLocale.getName(), loc, sizeof(loc), &status); 426 427 regionLen = uloc_getCountry(loc, fTargetRegion, sizeof(fTargetRegion), &status); 428 if (U_SUCCESS(status)) { 429 fTargetRegion[regionLen] = 0; 430 } else { 431 cleanup(); 432 return; 433 } 434 } else if (regionLen < (int32_t)sizeof(fTargetRegion)) { 435 uprv_strcpy(fTargetRegion, region); 436 } else { 437 fTargetRegion[0] = 0; 438 } 439 440 // preload generic names for the default zone 441 TimeZone *tz = TimeZone::createDefault(); 442 const UChar *tzID = ZoneMeta::getCanonicalCLDRID(*tz); 443 if (tzID != NULL) { 444 loadStrings(UnicodeString(tzID)); 445 } 446 delete tz; 447 } 448 449 void 450 TZGNCore::cleanup() { 451 if (fRegionFormat != NULL) { 452 delete fRegionFormat; 453 } 454 if (fFallbackFormat != NULL) { 455 delete fFallbackFormat; 456 } 457 if (fLocaleDisplayNames != NULL) { 458 delete fLocaleDisplayNames; 459 } 460 if (fTimeZoneNames != NULL) { 461 delete fTimeZoneNames; 462 } 463 464 uhash_close(fLocationNamesMap); 465 uhash_close(fPartialLocationNamesMap); 466 } 467 468 469 UnicodeString& 470 TZGNCore::getDisplayName(const TimeZone& tz, UTimeZoneGenericNameType type, UDate date, UnicodeString& name) const { 471 name.setToBogus(); 472 switch (type) { 473 case UTZGNM_LOCATION: 474 { 475 const UChar* tzCanonicalID = ZoneMeta::getCanonicalCLDRID(tz); 476 if (tzCanonicalID != NULL) { 477 getGenericLocationName(UnicodeString(tzCanonicalID), name); 478 } 479 } 480 break; 481 case UTZGNM_LONG: 482 case UTZGNM_SHORT: 483 formatGenericNonLocationName(tz, type, date, name); 484 if (name.isEmpty()) { 485 const UChar* tzCanonicalID = ZoneMeta::getCanonicalCLDRID(tz); 486 if (tzCanonicalID != NULL) { 487 getGenericLocationName(UnicodeString(tzCanonicalID), name); 488 } 489 } 490 break; 491 default: 492 break; 493 } 494 return name; 495 } 496 497 UnicodeString& 498 TZGNCore::getGenericLocationName(const UnicodeString& tzCanonicalID, UnicodeString& name) const { 499 if (tzCanonicalID.isEmpty()) { 500 name.setToBogus(); 501 return name; 502 } 503 504 const UChar *locname = NULL; 505 TZGNCore *nonConstThis = const_cast<TZGNCore *>(this); 506 umtx_lock(&gLock); 507 { 508 locname = nonConstThis->getGenericLocationName(tzCanonicalID); 509 } 510 umtx_unlock(&gLock); 511 512 if (locname == NULL) { 513 name.setToBogus(); 514 } else { 515 name.setTo(locname, u_strlen(locname)); 516 } 517 518 return name; 519 } 520 521 /* 522 * This method updates the cache and must be called with a lock 523 */ 524 const UChar* 525 TZGNCore::getGenericLocationName(const UnicodeString& tzCanonicalID) { 526 U_ASSERT(!tzCanonicalID.isEmpty()); 527 if (tzCanonicalID.length() > ZID_KEY_MAX) { 528 return NULL; 529 } 530 531 UErrorCode status = U_ZERO_ERROR; 532 UChar tzIDKey[ZID_KEY_MAX + 1]; 533 int32_t tzIDKeyLen = tzCanonicalID.extract(tzIDKey, ZID_KEY_MAX + 1, status); 534 U_ASSERT(status == U_ZERO_ERROR); // already checked length above 535 tzIDKey[tzIDKeyLen] = 0; 536 537 const UChar *locname = (const UChar *)uhash_get(fLocationNamesMap, tzIDKey); 538 539 if (locname != NULL) { 540 // gEmpty indicate the name is not available 541 if (locname == gEmpty) { 542 return NULL; 543 } 544 return locname; 545 } 546 547 // Construct location name 548 UnicodeString name; 549 UBool isSingleCountry = FALSE; 550 UnicodeString usCountryCode; 551 ZoneMeta::getSingleCountry(tzCanonicalID, usCountryCode); 552 if (!usCountryCode.isEmpty()) { 553 isSingleCountry = TRUE; 554 } else { 555 ZoneMeta::getCanonicalCountry(tzCanonicalID, usCountryCode); 556 } 557 558 if (!usCountryCode.isEmpty()) { 559 char countryCode[ULOC_COUNTRY_CAPACITY]; 560 U_ASSERT(usCountryCode.length() < ULOC_COUNTRY_CAPACITY); 561 int32_t ccLen = usCountryCode.extract(0, usCountryCode.length(), countryCode, sizeof(countryCode), US_INV); 562 countryCode[ccLen] = 0; 563 564 UnicodeString country; 565 fLocaleDisplayNames->regionDisplayName(countryCode, country); 566 567 // Format 568 FieldPosition fpos; 569 if (isSingleCountry) { 570 // If this is only the single zone in the country, use the country name 571 Formattable param[] = { 572 Formattable(country) 573 }; 574 fRegionFormat->format(param, 1, name, fpos, status); 575 } else { 576 // If there are multiple zones including this in the country, 577 // use the exemplar city name 578 579 // getExemplarLocationName should retur non-empty string 580 // if the time zone is associated with a region 581 582 UnicodeString city; 583 fTimeZoneNames->getExemplarLocationName(tzCanonicalID, city); 584 585 Formattable param[] = { 586 Formattable(city), 587 }; 588 fRegionFormat->format(param, 1, name, fpos, status); 589 } 590 if (U_FAILURE(status)) { 591 return NULL; 592 } 593 } 594 595 locname = name.isEmpty() ? NULL : fStringPool.get(name, status); 596 if (U_SUCCESS(status)) { 597 // Cache the result 598 const UChar* cacheID = ZoneMeta::findTimeZoneID(tzCanonicalID); 599 U_ASSERT(cacheID != NULL); 600 if (locname == NULL) { 601 // gEmpty to indicate - no location name available 602 uhash_put(fLocationNamesMap, (void *)cacheID, (void *)gEmpty, &status); 603 } else { 604 uhash_put(fLocationNamesMap, (void *)cacheID, (void *)locname, &status); 605 if (U_FAILURE(status)) { 606 locname = NULL; 607 } else { 608 // put the name info into the trie 609 GNameInfo *nameinfo = (ZNameInfo *)uprv_malloc(sizeof(GNameInfo)); 610 if (nameinfo != NULL) { 611 nameinfo->type = UTZGNM_LOCATION; 612 nameinfo->tzID = cacheID; 613 fGNamesTrie.put(locname, nameinfo, status); 614 } 615 } 616 } 617 } 618 619 return locname; 620 } 621 622 UnicodeString& 623 TZGNCore::formatGenericNonLocationName(const TimeZone& tz, UTimeZoneGenericNameType type, UDate date, UnicodeString& name) const { 624 U_ASSERT(type == UTZGNM_LONG || type == UTZGNM_SHORT); 625 name.setToBogus(); 626 627 const UChar* uID = ZoneMeta::getCanonicalCLDRID(tz); 628 if (uID == NULL) { 629 return name; 630 } 631 632 UnicodeString tzID(uID); 633 634 // Try to get a name from time zone first 635 UTimeZoneNameType nameType = (type == UTZGNM_LONG) ? UTZNM_LONG_GENERIC : UTZNM_SHORT_GENERIC; 636 fTimeZoneNames->getTimeZoneDisplayName(tzID, nameType, name); 637 638 if (!name.isEmpty()) { 639 return name; 640 } 641 642 // Try meta zone 643 UnicodeString mzID; 644 fTimeZoneNames->getMetaZoneID(tzID, date, mzID); 645 if (!mzID.isEmpty()) { 646 UErrorCode status = U_ZERO_ERROR; 647 UBool useStandard = FALSE; 648 int32_t raw, sav; 649 650 tz.getOffset(date, FALSE, raw, sav, status); 651 if (U_FAILURE(status)) { 652 return name; 653 } 654 655 if (sav == 0) { 656 useStandard = TRUE; 657 658 TimeZone *tmptz = tz.clone(); 659 // Check if the zone actually uses daylight saving time around the time 660 BasicTimeZone *btz = NULL; 661 if (dynamic_cast<OlsonTimeZone *>(tmptz) != NULL 662 || dynamic_cast<SimpleTimeZone *>(tmptz) != NULL 663 || dynamic_cast<RuleBasedTimeZone *>(tmptz) != NULL 664 || dynamic_cast<VTimeZone *>(tmptz) != NULL) { 665 btz = (BasicTimeZone*)tmptz; 666 } 667 668 if (btz != NULL) { 669 TimeZoneTransition before; 670 UBool beforTrs = btz->getPreviousTransition(date, TRUE, before); 671 if (beforTrs 672 && (date - before.getTime() < kDstCheckRange) 673 && before.getFrom()->getDSTSavings() != 0) { 674 useStandard = FALSE; 675 } else { 676 TimeZoneTransition after; 677 UBool afterTrs = btz->getNextTransition(date, FALSE, after); 678 if (afterTrs 679 && (after.getTime() - date < kDstCheckRange) 680 && after.getTo()->getDSTSavings() != 0) { 681 useStandard = FALSE; 682 } 683 } 684 } else { 685 // If not BasicTimeZone... only if the instance is not an ICU's implementation. 686 // We may get a wrong answer in edge case, but it should practically work OK. 687 tmptz->getOffset(date - kDstCheckRange, FALSE, raw, sav, status); 688 if (sav != 0) { 689 useStandard = FALSE; 690 } else { 691 tmptz->getOffset(date + kDstCheckRange, FALSE, raw, sav, status); 692 if (sav != 0){ 693 useStandard = FALSE; 694 } 695 } 696 if (U_FAILURE(status)) { 697 delete tmptz; 698 return name; 699 } 700 } 701 delete tmptz; 702 } 703 if (useStandard) { 704 UTimeZoneNameType stdNameType = (nameType == UTZNM_LONG_GENERIC) 705 ? UTZNM_LONG_STANDARD : UTZNM_SHORT_STANDARD; 706 UnicodeString stdName; 707 fTimeZoneNames->getDisplayName(tzID, stdNameType, date, stdName); 708 if (!stdName.isEmpty()) { 709 name.setTo(stdName); 710 711 // TODO: revisit this issue later 712 // In CLDR, a same display name is used for both generic and standard 713 // for some meta zones in some locales. This looks like a data bugs. 714 // For now, we check if the standard name is different from its generic 715 // name below. 716 UnicodeString mzGenericName; 717 fTimeZoneNames->getMetaZoneDisplayName(mzID, nameType, mzGenericName); 718 if (stdName.caseCompare(mzGenericName, 0) == 0) { 719 name.setToBogus(); 720 } 721 } 722 } 723 if (name.isEmpty()) { 724 // Get a name from meta zone 725 UnicodeString mzName; 726 fTimeZoneNames->getMetaZoneDisplayName(mzID, nameType, mzName); 727 if (!mzName.isEmpty()) { 728 // Check if we need to use a partial location format. 729 // This check is done by comparing offset with the meta zone's 730 // golden zone at the given date. 731 UnicodeString goldenID; 732 fTimeZoneNames->getReferenceZoneID(mzID, fTargetRegion, goldenID); 733 if (!goldenID.isEmpty() && goldenID != tzID) { 734 TimeZone *goldenZone = TimeZone::createTimeZone(goldenID); 735 int32_t raw1, sav1; 736 737 // Check offset in the golden zone with wall time. 738 // With getOffset(date, false, offsets1), 739 // you may get incorrect results because of time overlap at DST->STD 740 // transition. 741 goldenZone->getOffset(date + raw + sav, TRUE, raw1, sav1, status); 742 delete goldenZone; 743 if (U_SUCCESS(status)) { 744 if (raw != raw1 || sav != sav1) { 745 // Now we need to use a partial location format 746 getPartialLocationName(tzID, mzID, (nameType == UTZNM_LONG_GENERIC), mzName, name); 747 } else { 748 name.setTo(mzName); 749 } 750 } 751 } else { 752 name.setTo(mzName); 753 } 754 } 755 } 756 } 757 return name; 758 } 759 760 UnicodeString& 761 TZGNCore::getPartialLocationName(const UnicodeString& tzCanonicalID, 762 const UnicodeString& mzID, UBool isLong, const UnicodeString& mzDisplayName, 763 UnicodeString& name) const { 764 name.setToBogus(); 765 if (tzCanonicalID.isEmpty() || mzID.isEmpty() || mzDisplayName.isEmpty()) { 766 return name; 767 } 768 769 const UChar *uplname = NULL; 770 TZGNCore *nonConstThis = const_cast<TZGNCore *>(this); 771 umtx_lock(&gLock); 772 { 773 uplname = nonConstThis->getPartialLocationName(tzCanonicalID, mzID, isLong, mzDisplayName); 774 } 775 umtx_unlock(&gLock); 776 777 if (uplname == NULL) { 778 name.setToBogus(); 779 } else { 780 name.setTo(TRUE, uplname, -1); 781 } 782 return name; 783 } 784 785 /* 786 * This method updates the cache and must be called with a lock 787 */ 788 const UChar* 789 TZGNCore::getPartialLocationName(const UnicodeString& tzCanonicalID, 790 const UnicodeString& mzID, UBool isLong, const UnicodeString& mzDisplayName) { 791 U_ASSERT(!tzCanonicalID.isEmpty()); 792 U_ASSERT(!mzID.isEmpty()); 793 U_ASSERT(!mzDisplayName.isEmpty()); 794 795 PartialLocationKey key; 796 key.tzID = ZoneMeta::findTimeZoneID(tzCanonicalID); 797 key.mzID = ZoneMeta::findMetaZoneID(mzID); 798 key.isLong = isLong; 799 U_ASSERT(key.tzID != NULL && key.mzID != NULL); 800 801 const UChar* uplname = (const UChar*)uhash_get(fPartialLocationNamesMap, (void *)&key); 802 if (uplname != NULL) { 803 return uplname; 804 } 805 806 UnicodeString location; 807 UnicodeString usCountryCode; 808 ZoneMeta::getCanonicalCountry(tzCanonicalID, usCountryCode); 809 if (!usCountryCode.isEmpty()) { 810 char countryCode[ULOC_COUNTRY_CAPACITY]; 811 U_ASSERT(usCountryCode.length() < ULOC_COUNTRY_CAPACITY); 812 int32_t ccLen = usCountryCode.extract(0, usCountryCode.length(), countryCode, sizeof(countryCode), US_INV); 813 countryCode[ccLen] = 0; 814 815 UnicodeString regionalGolden; 816 fTimeZoneNames->getReferenceZoneID(mzID, countryCode, regionalGolden); 817 if (tzCanonicalID == regionalGolden) { 818 // Use country name 819 fLocaleDisplayNames->regionDisplayName(countryCode, location); 820 } else { 821 // Otherwise, use exemplar city name 822 fTimeZoneNames->getExemplarLocationName(tzCanonicalID, location); 823 } 824 } else { 825 fTimeZoneNames->getExemplarLocationName(tzCanonicalID, location); 826 if (location.isEmpty()) { 827 // This could happen when the time zone is not associated with a country, 828 // and its ID is not hierarchical, for example, CST6CDT. 829 // We use the canonical ID itself as the location for this case. 830 location.setTo(tzCanonicalID); 831 } 832 } 833 834 UErrorCode status = U_ZERO_ERROR; 835 UnicodeString name; 836 837 FieldPosition fpos; 838 Formattable param[] = { 839 Formattable(location), 840 Formattable(mzDisplayName) 841 }; 842 fFallbackFormat->format(param, 2, name, fpos, status); 843 if (U_FAILURE(status)) { 844 return NULL; 845 } 846 847 uplname = fStringPool.get(name, status); 848 if (U_SUCCESS(status)) { 849 // Add the name to cache 850 PartialLocationKey* cacheKey = (PartialLocationKey *)uprv_malloc(sizeof(PartialLocationKey)); 851 if (cacheKey != NULL) { 852 cacheKey->tzID = key.tzID; 853 cacheKey->mzID = key.mzID; 854 cacheKey->isLong = key.isLong; 855 uhash_put(fPartialLocationNamesMap, (void *)cacheKey, (void *)uplname, &status); 856 if (U_FAILURE(status)) { 857 uprv_free(cacheKey); 858 } else { 859 // put the name to the local trie as well 860 GNameInfo *nameinfo = (ZNameInfo *)uprv_malloc(sizeof(GNameInfo)); 861 if (nameinfo != NULL) { 862 nameinfo->type = isLong ? UTZGNM_LONG : UTZGNM_SHORT; 863 nameinfo->tzID = key.tzID; 864 fGNamesTrie.put(uplname, nameinfo, status); 865 } 866 } 867 } 868 } 869 return uplname; 870 } 871 872 /* 873 * This method updates the cache and must be called with a lock, 874 * except initializer. 875 */ 876 void 877 TZGNCore::loadStrings(const UnicodeString& tzCanonicalID) { 878 // load the generic location name 879 getGenericLocationName(tzCanonicalID); 880 881 // partial location names 882 UErrorCode status = U_ZERO_ERROR; 883 884 const UnicodeString *mzID; 885 UnicodeString goldenID; 886 UnicodeString mzGenName; 887 UTimeZoneNameType genNonLocTypes[] = { 888 UTZNM_LONG_GENERIC, UTZNM_SHORT_GENERIC, 889 UTZNM_UNKNOWN /*terminator*/ 890 }; 891 892 StringEnumeration *mzIDs = fTimeZoneNames->getAvailableMetaZoneIDs(tzCanonicalID, status); 893 while ((mzID = mzIDs->snext(status))) { 894 if (U_FAILURE(status)) { 895 break; 896 } 897 // if this time zone is not the golden zone of the meta zone, 898 // partial location name (such as "PT (Los Angeles)") might be 899 // available. 900 fTimeZoneNames->getReferenceZoneID(*mzID, fTargetRegion, goldenID); 901 if (tzCanonicalID != goldenID) { 902 for (int32_t i = 0; genNonLocTypes[i] != UTZNM_UNKNOWN; i++) { 903 fTimeZoneNames->getMetaZoneDisplayName(*mzID, genNonLocTypes[i], mzGenName); 904 if (!mzGenName.isEmpty()) { 905 // getPartialLocationName formats a name and put it into the trie 906 getPartialLocationName(tzCanonicalID, *mzID, 907 (genNonLocTypes[i] == UTZNM_LONG_GENERIC), mzGenName); 908 } 909 } 910 } 911 } 912 if (mzIDs != NULL) { 913 delete mzIDs; 914 } 915 } 916 917 int32_t 918 TZGNCore::findBestMatch(const UnicodeString& text, int32_t start, uint32_t types, 919 UnicodeString& tzID, UTimeZoneFormatTimeType& timeType, UErrorCode& status) const { 920 timeType = UTZFMT_TIME_TYPE_UNKNOWN; 921 tzID.setToBogus(); 922 923 if (U_FAILURE(status)) { 924 return 0; 925 } 926 927 // Find matches in the TimeZoneNames first 928 TimeZoneNames::MatchInfoCollection *tznamesMatches = findTimeZoneNames(text, start, types, status); 929 if (U_FAILURE(status)) { 930 return 0; 931 } 932 933 int32_t bestMatchLen = 0; 934 UTimeZoneFormatTimeType bestMatchTimeType = UTZFMT_TIME_TYPE_UNKNOWN; 935 UnicodeString bestMatchTzID; 936 // UBool isLongStandard = FALSE; // workaround - see the comments below 937 UBool isStandard = FALSE; // TODO: Temporary hack (on hack) for short standard name/location name conflict (found in zh_Hant), should be removed after CLDR 21m1 integration 938 939 if (tznamesMatches != NULL) { 940 UnicodeString mzID; 941 for (int32_t i = 0; i < tznamesMatches->size(); i++) { 942 int32_t len = tznamesMatches->getMatchLengthAt(i); 943 if (len > bestMatchLen) { 944 bestMatchLen = len; 945 if (!tznamesMatches->getTimeZoneIDAt(i, bestMatchTzID)) { 946 // name for a meta zone 947 if (tznamesMatches->getMetaZoneIDAt(i, mzID)) { 948 fTimeZoneNames->getReferenceZoneID(mzID, fTargetRegion, bestMatchTzID); 949 } 950 } 951 UTimeZoneNameType nameType = tznamesMatches->getNameTypeAt(i); 952 if (U_FAILURE(status)) { 953 break; 954 } 955 switch (nameType) { 956 case UTZNM_LONG_STANDARD: 957 // isLongStandard = TRUE; 958 case UTZNM_SHORT_STANDARD: // this one is never used for generic, but just in case 959 isStandard = TRUE; // TODO: Remove this later, see the comments above. 960 bestMatchTimeType = UTZFMT_TIME_TYPE_STANDARD; 961 break; 962 case UTZNM_LONG_DAYLIGHT: 963 case UTZNM_SHORT_DAYLIGHT: // this one is never used for generic, but just in case 964 bestMatchTimeType = UTZFMT_TIME_TYPE_DAYLIGHT; 965 break; 966 default: 967 bestMatchTimeType = UTZFMT_TIME_TYPE_UNKNOWN; 968 } 969 } 970 } 971 delete tznamesMatches; 972 if (U_FAILURE(status)) { 973 return 0; 974 } 975 976 if (bestMatchLen == (text.length() - start)) { 977 // Full match 978 979 //tzID.setTo(bestMatchTzID); 980 //timeType = bestMatchTimeType; 981 //return bestMatchLen; 982 983 // TODO Some time zone uses a same name for the long standard name 984 // and the location name. When the match is a long standard name, 985 // then we need to check if the name is same with the location name. 986 // This is probably a data error or a design bug. 987 /* 988 if (!isLongStandard) { 989 tzID.setTo(bestMatchTzID); 990 timeType = bestMatchTimeType; 991 return bestMatchLen; 992 } 993 */ 994 // TODO The deprecation of commonlyUsed flag introduced the name 995 // conflict not only for long standard names, but short standard names too. 996 // These short names (found in zh_Hant) should be gone once we clean 997 // up CLDR time zone display name data. Once the short name conflict 998 // problem (with location name) is resolved, we should change the condition 999 // below back to the original one above. -Yoshito (2011-09-14) 1000 if (!isStandard) { 1001 tzID.setTo(bestMatchTzID); 1002 timeType = bestMatchTimeType; 1003 return bestMatchLen; 1004 } 1005 } 1006 } 1007 1008 // Find matches in the local trie 1009 TimeZoneGenericNameMatchInfo *localMatches = findLocal(text, start, types, status); 1010 if (U_FAILURE(status)) { 1011 return 0; 1012 } 1013 if (localMatches != NULL) { 1014 for (int32_t i = 0; i < localMatches->size(); i++) { 1015 int32_t len = localMatches->getMatchLength(i); 1016 1017 // TODO See the above TODO. We use len >= bestMatchLen 1018 // because of the long standard/location name collision 1019 // problem. If it is also a location name, carrying 1020 // timeType = UTZFMT_TIME_TYPE_STANDARD will cause a 1021 // problem in SimpleDateFormat 1022 if (len >= bestMatchLen) { 1023 bestMatchLen = localMatches->getMatchLength(i); 1024 bestMatchTimeType = UTZFMT_TIME_TYPE_UNKNOWN; // because generic 1025 localMatches->getTimeZoneID(i, bestMatchTzID); 1026 } 1027 } 1028 delete localMatches; 1029 } 1030 1031 if (bestMatchLen > 0) { 1032 timeType = bestMatchTimeType; 1033 tzID.setTo(bestMatchTzID); 1034 } 1035 return bestMatchLen; 1036 } 1037 1038 TimeZoneGenericNameMatchInfo* 1039 TZGNCore::findLocal(const UnicodeString& text, int32_t start, uint32_t types, UErrorCode& status) const { 1040 GNameSearchHandler handler(types); 1041 1042 TZGNCore *nonConstThis = const_cast<TZGNCore *>(this); 1043 1044 umtx_lock(&gLock); 1045 { 1046 fGNamesTrie.search(text, start, (TextTrieMapSearchResultHandler *)&handler, status); 1047 } 1048 umtx_unlock(&gLock); 1049 1050 if (U_FAILURE(status)) { 1051 return NULL; 1052 } 1053 1054 TimeZoneGenericNameMatchInfo *gmatchInfo = NULL; 1055 1056 int32_t maxLen = 0; 1057 UVector *results = handler.getMatches(maxLen); 1058 if (results != NULL && ((maxLen == (text.length() - start)) || fGNamesTrieFullyLoaded)) { 1059 // perfect match 1060 gmatchInfo = new TimeZoneGenericNameMatchInfo(results); 1061 if (gmatchInfo == NULL) { 1062 status = U_MEMORY_ALLOCATION_ERROR; 1063 delete results; 1064 return NULL; 1065 } 1066 return gmatchInfo; 1067 } 1068 1069 if (results != NULL) { 1070 delete results; 1071 } 1072 1073 // All names are not yet loaded into the local trie. 1074 // Load all available names into the trie. This could be very heavy. 1075 umtx_lock(&gLock); 1076 { 1077 if (!fGNamesTrieFullyLoaded) { 1078 StringEnumeration *tzIDs = TimeZone::createTimeZoneIDEnumeration(UCAL_ZONE_TYPE_CANONICAL, NULL, NULL, status); 1079 if (U_SUCCESS(status)) { 1080 const UnicodeString *tzID; 1081 while ((tzID = tzIDs->snext(status))) { 1082 if (U_FAILURE(status)) { 1083 break; 1084 } 1085 nonConstThis->loadStrings(*tzID); 1086 } 1087 } 1088 if (tzIDs != NULL) { 1089 delete tzIDs; 1090 } 1091 1092 if (U_SUCCESS(status)) { 1093 nonConstThis->fGNamesTrieFullyLoaded = TRUE; 1094 } 1095 } 1096 } 1097 umtx_unlock(&gLock); 1098 1099 if (U_FAILURE(status)) { 1100 return NULL; 1101 } 1102 1103 umtx_lock(&gLock); 1104 { 1105 // now try it again 1106 fGNamesTrie.search(text, start, (TextTrieMapSearchResultHandler *)&handler, status); 1107 } 1108 umtx_unlock(&gLock); 1109 1110 results = handler.getMatches(maxLen); 1111 if (results != NULL && maxLen > 0) { 1112 gmatchInfo = new TimeZoneGenericNameMatchInfo(results); 1113 if (gmatchInfo == NULL) { 1114 status = U_MEMORY_ALLOCATION_ERROR; 1115 delete results; 1116 return NULL; 1117 } 1118 } 1119 1120 return gmatchInfo; 1121 } 1122 1123 TimeZoneNames::MatchInfoCollection* 1124 TZGNCore::findTimeZoneNames(const UnicodeString& text, int32_t start, uint32_t types, UErrorCode& status) const { 1125 // Check if the target name typs is really in the TimeZoneNames 1126 uint32_t nameTypes = 0; 1127 if (types & UTZGNM_LONG) { 1128 nameTypes |= (UTZNM_LONG_GENERIC | UTZNM_LONG_STANDARD); 1129 } 1130 if (types & UTZGNM_SHORT) { 1131 nameTypes |= (UTZNM_SHORT_GENERIC | UTZNM_SHORT_STANDARD); 1132 } 1133 1134 if (types) { 1135 // Find matches in the TimeZoneNames 1136 return fTimeZoneNames->find(text, start, nameTypes, status); 1137 } 1138 1139 return NULL; 1140 } 1141 1142 typedef struct TZGNCoreRef { 1143 TZGNCore* obj; 1144 int32_t refCount; 1145 double lastAccess; 1146 } TZGNCoreRef; 1147 1148 // TZGNCore object cache handling 1149 static UMutex gTZGNLock = U_MUTEX_INITIALIZER; 1150 static UHashtable *gTZGNCoreCache = NULL; 1151 static UBool gTZGNCoreCacheInitialized = FALSE; 1152 1153 // Access count - incremented every time up to SWEEP_INTERVAL, 1154 // then reset to 0 1155 static int32_t gAccessCount = 0; 1156 1157 // Interval for calling the cache sweep function - every 100 times 1158 #define SWEEP_INTERVAL 100 1159 1160 // Cache expiration in millisecond. When a cached entry is no 1161 // longer referenced and exceeding this threshold since last 1162 // access time, then the cache entry will be deleted by the sweep 1163 // function. For now, 3 minutes. 1164 #define CACHE_EXPIRATION 180000.0 1165 1166 U_CDECL_BEGIN 1167 /** 1168 * Cleanup callback func 1169 */ 1170 static UBool U_CALLCONV tzgnCore_cleanup(void) 1171 { 1172 if (gTZGNCoreCache != NULL) { 1173 uhash_close(gTZGNCoreCache); 1174 gTZGNCoreCache = NULL; 1175 } 1176 gTZGNCoreCacheInitialized = FALSE; 1177 return TRUE; 1178 } 1179 1180 /** 1181 * Deleter for TZGNCoreRef 1182 */ 1183 static void U_CALLCONV 1184 deleteTZGNCoreRef(void *obj) { 1185 icu::TZGNCoreRef *entry = (icu::TZGNCoreRef*)obj; 1186 delete (icu::TZGNCore*) entry->obj; 1187 uprv_free(entry); 1188 } 1189 U_CDECL_END 1190 1191 /** 1192 * Function used for removing unreferrenced cache entries exceeding 1193 * the expiration time. This function must be called with in the mutex 1194 * block. 1195 */ 1196 static void sweepCache() { 1197 int32_t pos = -1; 1198 const UHashElement* elem; 1199 double now = (double)uprv_getUTCtime(); 1200 1201 while ((elem = uhash_nextElement(gTZGNCoreCache, &pos))) { 1202 TZGNCoreRef *entry = (TZGNCoreRef *)elem->value.pointer; 1203 if (entry->refCount <= 0 && (now - entry->lastAccess) > CACHE_EXPIRATION) { 1204 // delete this entry 1205 uhash_removeElement(gTZGNCoreCache, elem); 1206 } 1207 } 1208 } 1209 1210 TimeZoneGenericNames::TimeZoneGenericNames() 1211 : fRef(0) { 1212 } 1213 1214 TimeZoneGenericNames::~TimeZoneGenericNames() { 1215 umtx_lock(&gTZGNLock); 1216 { 1217 U_ASSERT(fRef->refCount > 0); 1218 // Just decrement the reference count 1219 fRef->refCount--; 1220 } 1221 umtx_unlock(&gTZGNLock); 1222 } 1223 1224 TimeZoneGenericNames* 1225 TimeZoneGenericNames::createInstance(const Locale& locale, UErrorCode& status) { 1226 if (U_FAILURE(status)) { 1227 return NULL; 1228 } 1229 TimeZoneGenericNames* instance = new TimeZoneGenericNames(); 1230 if (instance == NULL) { 1231 status = U_MEMORY_ALLOCATION_ERROR; 1232 return NULL; 1233 } 1234 1235 UBool initialized; 1236 UMTX_CHECK(&gTZGNLock, gTZGNCoreCacheInitialized, initialized); 1237 if (!initialized) { 1238 // Create empty hashtable 1239 umtx_lock(&gTZGNLock); 1240 { 1241 if (!gTZGNCoreCacheInitialized) { 1242 gTZGNCoreCache = uhash_open(uhash_hashChars, uhash_compareChars, NULL, &status); 1243 if (U_SUCCESS(status)) { 1244 uhash_setKeyDeleter(gTZGNCoreCache, uprv_free); 1245 uhash_setValueDeleter(gTZGNCoreCache, deleteTZGNCoreRef); 1246 gTZGNCoreCacheInitialized = TRUE; 1247 ucln_i18n_registerCleanup(UCLN_I18N_TIMEZONEGENERICNAMES, tzgnCore_cleanup); 1248 } 1249 } 1250 } 1251 umtx_unlock(&gTZGNLock); 1252 1253 if (U_FAILURE(status)) { 1254 return NULL; 1255 } 1256 } 1257 1258 // Check the cache, if not available, create new one and cache 1259 TZGNCoreRef *cacheEntry = NULL; 1260 umtx_lock(&gTZGNLock); 1261 { 1262 const char *key = locale.getName(); 1263 cacheEntry = (TZGNCoreRef *)uhash_get(gTZGNCoreCache, key); 1264 if (cacheEntry == NULL) { 1265 TZGNCore *tzgnCore = NULL; 1266 char *newKey = NULL; 1267 1268 tzgnCore = new TZGNCore(locale, status); 1269 if (tzgnCore == NULL) { 1270 status = U_MEMORY_ALLOCATION_ERROR; 1271 } 1272 if (U_SUCCESS(status)) { 1273 newKey = (char *)uprv_malloc(uprv_strlen(key) + 1); 1274 if (newKey == NULL) { 1275 status = U_MEMORY_ALLOCATION_ERROR; 1276 } else { 1277 uprv_strcpy(newKey, key); 1278 } 1279 } 1280 if (U_SUCCESS(status)) { 1281 cacheEntry = (TZGNCoreRef *)uprv_malloc(sizeof(TZGNCoreRef)); 1282 if (cacheEntry == NULL) { 1283 status = U_MEMORY_ALLOCATION_ERROR; 1284 } else { 1285 cacheEntry->obj = tzgnCore; 1286 cacheEntry->refCount = 1; 1287 cacheEntry->lastAccess = (double)uprv_getUTCtime(); 1288 1289 uhash_put(gTZGNCoreCache, newKey, cacheEntry, &status); 1290 } 1291 } 1292 if (U_FAILURE(status)) { 1293 if (tzgnCore != NULL) { 1294 delete tzgnCore; 1295 } 1296 if (newKey != NULL) { 1297 uprv_free(newKey); 1298 } 1299 if (cacheEntry != NULL) { 1300 uprv_free(cacheEntry); 1301 } 1302 cacheEntry = NULL; 1303 } 1304 } else { 1305 // Update the reference count 1306 cacheEntry->refCount++; 1307 cacheEntry->lastAccess = (double)uprv_getUTCtime(); 1308 } 1309 gAccessCount++; 1310 if (gAccessCount >= SWEEP_INTERVAL) { 1311 // sweep 1312 sweepCache(); 1313 gAccessCount = 0; 1314 } 1315 } 1316 umtx_unlock(&gTZGNLock); 1317 1318 if (cacheEntry == NULL) { 1319 delete instance; 1320 return NULL; 1321 } 1322 1323 instance->fRef = cacheEntry; 1324 return instance; 1325 } 1326 1327 UBool 1328 TimeZoneGenericNames::operator==(const TimeZoneGenericNames& other) const { 1329 // Just compare if the other object also use the same 1330 // ref entry 1331 return fRef == other.fRef; 1332 } 1333 1334 TimeZoneGenericNames* 1335 TimeZoneGenericNames::clone() const { 1336 TimeZoneGenericNames* other = new TimeZoneGenericNames(); 1337 if (other) { 1338 umtx_lock(&gTZGNLock); 1339 { 1340 // Just increments the reference count 1341 fRef->refCount++; 1342 other->fRef = fRef; 1343 } 1344 umtx_unlock(&gTZGNLock); 1345 } 1346 return other; 1347 } 1348 1349 UnicodeString& 1350 TimeZoneGenericNames::getDisplayName(const TimeZone& tz, UTimeZoneGenericNameType type, 1351 UDate date, UnicodeString& name) const { 1352 return fRef->obj->getDisplayName(tz, type, date, name); 1353 } 1354 1355 UnicodeString& 1356 TimeZoneGenericNames::getGenericLocationName(const UnicodeString& tzCanonicalID, UnicodeString& name) const { 1357 return fRef->obj->getGenericLocationName(tzCanonicalID, name); 1358 } 1359 1360 int32_t 1361 TimeZoneGenericNames::findBestMatch(const UnicodeString& text, int32_t start, uint32_t types, 1362 UnicodeString& tzID, UTimeZoneFormatTimeType& timeType, UErrorCode& status) const { 1363 return fRef->obj->findBestMatch(text, start, types, tzID, timeType, status); 1364 } 1365 1366 U_NAMESPACE_END 1367 #endif 1368