1 /* 2 ******************************************************************************* 3 * Copyright (C) 2011-2013, 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 UnicodeString usCountryCode; 550 UBool isPrimary = FALSE; 551 552 ZoneMeta::getCanonicalCountry(tzCanonicalID, usCountryCode, &isPrimary); 553 554 if (!usCountryCode.isEmpty()) { 555 FieldPosition fpos; 556 557 if (isPrimary) { 558 // If this is the primary zone in the country, use the country name. 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 Formattable param[] = { 568 Formattable(country) 569 }; 570 571 fRegionFormat->format(param, 1, name, fpos, status); 572 } else { 573 // If this is not the primary zone in the country, 574 // use the exemplar city name. 575 576 // getExemplarLocationName should retur non-empty string 577 // if the time zone is associated with a region 578 579 UnicodeString city; 580 fTimeZoneNames->getExemplarLocationName(tzCanonicalID, city); 581 582 Formattable param[] = { 583 Formattable(city), 584 }; 585 586 fRegionFormat->format(param, 1, name, fpos, status); 587 } 588 if (U_FAILURE(status)) { 589 return NULL; 590 } 591 } 592 593 locname = name.isEmpty() ? NULL : fStringPool.get(name, status); 594 if (U_SUCCESS(status)) { 595 // Cache the result 596 const UChar* cacheID = ZoneMeta::findTimeZoneID(tzCanonicalID); 597 U_ASSERT(cacheID != NULL); 598 if (locname == NULL) { 599 // gEmpty to indicate - no location name available 600 uhash_put(fLocationNamesMap, (void *)cacheID, (void *)gEmpty, &status); 601 } else { 602 uhash_put(fLocationNamesMap, (void *)cacheID, (void *)locname, &status); 603 if (U_FAILURE(status)) { 604 locname = NULL; 605 } else { 606 // put the name info into the trie 607 GNameInfo *nameinfo = (ZNameInfo *)uprv_malloc(sizeof(GNameInfo)); 608 if (nameinfo != NULL) { 609 nameinfo->type = UTZGNM_LOCATION; 610 nameinfo->tzID = cacheID; 611 fGNamesTrie.put(locname, nameinfo, status); 612 } 613 } 614 } 615 } 616 617 return locname; 618 } 619 620 UnicodeString& 621 TZGNCore::formatGenericNonLocationName(const TimeZone& tz, UTimeZoneGenericNameType type, UDate date, UnicodeString& name) const { 622 U_ASSERT(type == UTZGNM_LONG || type == UTZGNM_SHORT); 623 name.setToBogus(); 624 625 const UChar* uID = ZoneMeta::getCanonicalCLDRID(tz); 626 if (uID == NULL) { 627 return name; 628 } 629 630 UnicodeString tzID(uID); 631 632 // Try to get a name from time zone first 633 UTimeZoneNameType nameType = (type == UTZGNM_LONG) ? UTZNM_LONG_GENERIC : UTZNM_SHORT_GENERIC; 634 fTimeZoneNames->getTimeZoneDisplayName(tzID, nameType, name); 635 636 if (!name.isEmpty()) { 637 return name; 638 } 639 640 // Try meta zone 641 UnicodeString mzID; 642 fTimeZoneNames->getMetaZoneID(tzID, date, mzID); 643 if (!mzID.isEmpty()) { 644 UErrorCode status = U_ZERO_ERROR; 645 UBool useStandard = FALSE; 646 int32_t raw, sav; 647 648 tz.getOffset(date, FALSE, raw, sav, status); 649 if (U_FAILURE(status)) { 650 return name; 651 } 652 653 if (sav == 0) { 654 useStandard = TRUE; 655 656 TimeZone *tmptz = tz.clone(); 657 // Check if the zone actually uses daylight saving time around the time 658 BasicTimeZone *btz = NULL; 659 if (dynamic_cast<OlsonTimeZone *>(tmptz) != NULL 660 || dynamic_cast<SimpleTimeZone *>(tmptz) != NULL 661 || dynamic_cast<RuleBasedTimeZone *>(tmptz) != NULL 662 || dynamic_cast<VTimeZone *>(tmptz) != NULL) { 663 btz = (BasicTimeZone*)tmptz; 664 } 665 666 if (btz != NULL) { 667 TimeZoneTransition before; 668 UBool beforTrs = btz->getPreviousTransition(date, TRUE, before); 669 if (beforTrs 670 && (date - before.getTime() < kDstCheckRange) 671 && before.getFrom()->getDSTSavings() != 0) { 672 useStandard = FALSE; 673 } else { 674 TimeZoneTransition after; 675 UBool afterTrs = btz->getNextTransition(date, FALSE, after); 676 if (afterTrs 677 && (after.getTime() - date < kDstCheckRange) 678 && after.getTo()->getDSTSavings() != 0) { 679 useStandard = FALSE; 680 } 681 } 682 } else { 683 // If not BasicTimeZone... only if the instance is not an ICU's implementation. 684 // We may get a wrong answer in edge case, but it should practically work OK. 685 tmptz->getOffset(date - kDstCheckRange, FALSE, raw, sav, status); 686 if (sav != 0) { 687 useStandard = FALSE; 688 } else { 689 tmptz->getOffset(date + kDstCheckRange, FALSE, raw, sav, status); 690 if (sav != 0){ 691 useStandard = FALSE; 692 } 693 } 694 if (U_FAILURE(status)) { 695 delete tmptz; 696 return name; 697 } 698 } 699 delete tmptz; 700 } 701 if (useStandard) { 702 UTimeZoneNameType stdNameType = (nameType == UTZNM_LONG_GENERIC) 703 ? UTZNM_LONG_STANDARD : UTZNM_SHORT_STANDARD; 704 UnicodeString stdName; 705 fTimeZoneNames->getDisplayName(tzID, stdNameType, date, stdName); 706 if (!stdName.isEmpty()) { 707 name.setTo(stdName); 708 709 // TODO: revisit this issue later 710 // In CLDR, a same display name is used for both generic and standard 711 // for some meta zones in some locales. This looks like a data bugs. 712 // For now, we check if the standard name is different from its generic 713 // name below. 714 UnicodeString mzGenericName; 715 fTimeZoneNames->getMetaZoneDisplayName(mzID, nameType, mzGenericName); 716 if (stdName.caseCompare(mzGenericName, 0) == 0) { 717 name.setToBogus(); 718 } 719 } 720 } 721 if (name.isEmpty()) { 722 // Get a name from meta zone 723 UnicodeString mzName; 724 fTimeZoneNames->getMetaZoneDisplayName(mzID, nameType, mzName); 725 if (!mzName.isEmpty()) { 726 // Check if we need to use a partial location format. 727 // This check is done by comparing offset with the meta zone's 728 // golden zone at the given date. 729 UnicodeString goldenID; 730 fTimeZoneNames->getReferenceZoneID(mzID, fTargetRegion, goldenID); 731 if (!goldenID.isEmpty() && goldenID != tzID) { 732 TimeZone *goldenZone = TimeZone::createTimeZone(goldenID); 733 int32_t raw1, sav1; 734 735 // Check offset in the golden zone with wall time. 736 // With getOffset(date, false, offsets1), 737 // you may get incorrect results because of time overlap at DST->STD 738 // transition. 739 goldenZone->getOffset(date + raw + sav, TRUE, raw1, sav1, status); 740 delete goldenZone; 741 if (U_SUCCESS(status)) { 742 if (raw != raw1 || sav != sav1) { 743 // Now we need to use a partial location format 744 getPartialLocationName(tzID, mzID, (nameType == UTZNM_LONG_GENERIC), mzName, name); 745 } else { 746 name.setTo(mzName); 747 } 748 } 749 } else { 750 name.setTo(mzName); 751 } 752 } 753 } 754 } 755 return name; 756 } 757 758 UnicodeString& 759 TZGNCore::getPartialLocationName(const UnicodeString& tzCanonicalID, 760 const UnicodeString& mzID, UBool isLong, const UnicodeString& mzDisplayName, 761 UnicodeString& name) const { 762 name.setToBogus(); 763 if (tzCanonicalID.isEmpty() || mzID.isEmpty() || mzDisplayName.isEmpty()) { 764 return name; 765 } 766 767 const UChar *uplname = NULL; 768 TZGNCore *nonConstThis = const_cast<TZGNCore *>(this); 769 umtx_lock(&gLock); 770 { 771 uplname = nonConstThis->getPartialLocationName(tzCanonicalID, mzID, isLong, mzDisplayName); 772 } 773 umtx_unlock(&gLock); 774 775 if (uplname == NULL) { 776 name.setToBogus(); 777 } else { 778 name.setTo(TRUE, uplname, -1); 779 } 780 return name; 781 } 782 783 /* 784 * This method updates the cache and must be called with a lock 785 */ 786 const UChar* 787 TZGNCore::getPartialLocationName(const UnicodeString& tzCanonicalID, 788 const UnicodeString& mzID, UBool isLong, const UnicodeString& mzDisplayName) { 789 U_ASSERT(!tzCanonicalID.isEmpty()); 790 U_ASSERT(!mzID.isEmpty()); 791 U_ASSERT(!mzDisplayName.isEmpty()); 792 793 PartialLocationKey key; 794 key.tzID = ZoneMeta::findTimeZoneID(tzCanonicalID); 795 key.mzID = ZoneMeta::findMetaZoneID(mzID); 796 key.isLong = isLong; 797 U_ASSERT(key.tzID != NULL && key.mzID != NULL); 798 799 const UChar* uplname = (const UChar*)uhash_get(fPartialLocationNamesMap, (void *)&key); 800 if (uplname != NULL) { 801 return uplname; 802 } 803 804 UnicodeString location; 805 UnicodeString usCountryCode; 806 ZoneMeta::getCanonicalCountry(tzCanonicalID, usCountryCode); 807 if (!usCountryCode.isEmpty()) { 808 char countryCode[ULOC_COUNTRY_CAPACITY]; 809 U_ASSERT(usCountryCode.length() < ULOC_COUNTRY_CAPACITY); 810 int32_t ccLen = usCountryCode.extract(0, usCountryCode.length(), countryCode, sizeof(countryCode), US_INV); 811 countryCode[ccLen] = 0; 812 813 UnicodeString regionalGolden; 814 fTimeZoneNames->getReferenceZoneID(mzID, countryCode, regionalGolden); 815 if (tzCanonicalID == regionalGolden) { 816 // Use country name 817 fLocaleDisplayNames->regionDisplayName(countryCode, location); 818 } else { 819 // Otherwise, use exemplar city name 820 fTimeZoneNames->getExemplarLocationName(tzCanonicalID, location); 821 } 822 } else { 823 fTimeZoneNames->getExemplarLocationName(tzCanonicalID, location); 824 if (location.isEmpty()) { 825 // This could happen when the time zone is not associated with a country, 826 // and its ID is not hierarchical, for example, CST6CDT. 827 // We use the canonical ID itself as the location for this case. 828 location.setTo(tzCanonicalID); 829 } 830 } 831 832 UErrorCode status = U_ZERO_ERROR; 833 UnicodeString name; 834 835 FieldPosition fpos; 836 Formattable param[] = { 837 Formattable(location), 838 Formattable(mzDisplayName) 839 }; 840 fFallbackFormat->format(param, 2, name, fpos, status); 841 if (U_FAILURE(status)) { 842 return NULL; 843 } 844 845 uplname = fStringPool.get(name, status); 846 if (U_SUCCESS(status)) { 847 // Add the name to cache 848 PartialLocationKey* cacheKey = (PartialLocationKey *)uprv_malloc(sizeof(PartialLocationKey)); 849 if (cacheKey != NULL) { 850 cacheKey->tzID = key.tzID; 851 cacheKey->mzID = key.mzID; 852 cacheKey->isLong = key.isLong; 853 uhash_put(fPartialLocationNamesMap, (void *)cacheKey, (void *)uplname, &status); 854 if (U_FAILURE(status)) { 855 uprv_free(cacheKey); 856 } else { 857 // put the name to the local trie as well 858 GNameInfo *nameinfo = (ZNameInfo *)uprv_malloc(sizeof(GNameInfo)); 859 if (nameinfo != NULL) { 860 nameinfo->type = isLong ? UTZGNM_LONG : UTZGNM_SHORT; 861 nameinfo->tzID = key.tzID; 862 fGNamesTrie.put(uplname, nameinfo, status); 863 } 864 } 865 } 866 } 867 return uplname; 868 } 869 870 /* 871 * This method updates the cache and must be called with a lock, 872 * except initializer. 873 */ 874 void 875 TZGNCore::loadStrings(const UnicodeString& tzCanonicalID) { 876 // load the generic location name 877 getGenericLocationName(tzCanonicalID); 878 879 // partial location names 880 UErrorCode status = U_ZERO_ERROR; 881 882 const UnicodeString *mzID; 883 UnicodeString goldenID; 884 UnicodeString mzGenName; 885 UTimeZoneNameType genNonLocTypes[] = { 886 UTZNM_LONG_GENERIC, UTZNM_SHORT_GENERIC, 887 UTZNM_UNKNOWN /*terminator*/ 888 }; 889 890 StringEnumeration *mzIDs = fTimeZoneNames->getAvailableMetaZoneIDs(tzCanonicalID, status); 891 while ((mzID = mzIDs->snext(status))) { 892 if (U_FAILURE(status)) { 893 break; 894 } 895 // if this time zone is not the golden zone of the meta zone, 896 // partial location name (such as "PT (Los Angeles)") might be 897 // available. 898 fTimeZoneNames->getReferenceZoneID(*mzID, fTargetRegion, goldenID); 899 if (tzCanonicalID != goldenID) { 900 for (int32_t i = 0; genNonLocTypes[i] != UTZNM_UNKNOWN; i++) { 901 fTimeZoneNames->getMetaZoneDisplayName(*mzID, genNonLocTypes[i], mzGenName); 902 if (!mzGenName.isEmpty()) { 903 // getPartialLocationName formats a name and put it into the trie 904 getPartialLocationName(tzCanonicalID, *mzID, 905 (genNonLocTypes[i] == UTZNM_LONG_GENERIC), mzGenName); 906 } 907 } 908 } 909 } 910 if (mzIDs != NULL) { 911 delete mzIDs; 912 } 913 } 914 915 int32_t 916 TZGNCore::findBestMatch(const UnicodeString& text, int32_t start, uint32_t types, 917 UnicodeString& tzID, UTimeZoneFormatTimeType& timeType, UErrorCode& status) const { 918 timeType = UTZFMT_TIME_TYPE_UNKNOWN; 919 tzID.setToBogus(); 920 921 if (U_FAILURE(status)) { 922 return 0; 923 } 924 925 // Find matches in the TimeZoneNames first 926 TimeZoneNames::MatchInfoCollection *tznamesMatches = findTimeZoneNames(text, start, types, status); 927 if (U_FAILURE(status)) { 928 return 0; 929 } 930 931 int32_t bestMatchLen = 0; 932 UTimeZoneFormatTimeType bestMatchTimeType = UTZFMT_TIME_TYPE_UNKNOWN; 933 UnicodeString bestMatchTzID; 934 // UBool isLongStandard = FALSE; // workaround - see the comments below 935 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 936 937 if (tznamesMatches != NULL) { 938 UnicodeString mzID; 939 for (int32_t i = 0; i < tznamesMatches->size(); i++) { 940 int32_t len = tznamesMatches->getMatchLengthAt(i); 941 if (len > bestMatchLen) { 942 bestMatchLen = len; 943 if (!tznamesMatches->getTimeZoneIDAt(i, bestMatchTzID)) { 944 // name for a meta zone 945 if (tznamesMatches->getMetaZoneIDAt(i, mzID)) { 946 fTimeZoneNames->getReferenceZoneID(mzID, fTargetRegion, bestMatchTzID); 947 } 948 } 949 UTimeZoneNameType nameType = tznamesMatches->getNameTypeAt(i); 950 if (U_FAILURE(status)) { 951 break; 952 } 953 switch (nameType) { 954 case UTZNM_LONG_STANDARD: 955 // isLongStandard = TRUE; 956 case UTZNM_SHORT_STANDARD: // this one is never used for generic, but just in case 957 isStandard = TRUE; // TODO: Remove this later, see the comments above. 958 bestMatchTimeType = UTZFMT_TIME_TYPE_STANDARD; 959 break; 960 case UTZNM_LONG_DAYLIGHT: 961 case UTZNM_SHORT_DAYLIGHT: // this one is never used for generic, but just in case 962 bestMatchTimeType = UTZFMT_TIME_TYPE_DAYLIGHT; 963 break; 964 default: 965 bestMatchTimeType = UTZFMT_TIME_TYPE_UNKNOWN; 966 } 967 } 968 } 969 delete tznamesMatches; 970 if (U_FAILURE(status)) { 971 return 0; 972 } 973 974 if (bestMatchLen == (text.length() - start)) { 975 // Full match 976 977 //tzID.setTo(bestMatchTzID); 978 //timeType = bestMatchTimeType; 979 //return bestMatchLen; 980 981 // TODO Some time zone uses a same name for the long standard name 982 // and the location name. When the match is a long standard name, 983 // then we need to check if the name is same with the location name. 984 // This is probably a data error or a design bug. 985 /* 986 if (!isLongStandard) { 987 tzID.setTo(bestMatchTzID); 988 timeType = bestMatchTimeType; 989 return bestMatchLen; 990 } 991 */ 992 // TODO The deprecation of commonlyUsed flag introduced the name 993 // conflict not only for long standard names, but short standard names too. 994 // These short names (found in zh_Hant) should be gone once we clean 995 // up CLDR time zone display name data. Once the short name conflict 996 // problem (with location name) is resolved, we should change the condition 997 // below back to the original one above. -Yoshito (2011-09-14) 998 if (!isStandard) { 999 tzID.setTo(bestMatchTzID); 1000 timeType = bestMatchTimeType; 1001 return bestMatchLen; 1002 } 1003 } 1004 } 1005 1006 // Find matches in the local trie 1007 TimeZoneGenericNameMatchInfo *localMatches = findLocal(text, start, types, status); 1008 if (U_FAILURE(status)) { 1009 return 0; 1010 } 1011 if (localMatches != NULL) { 1012 for (int32_t i = 0; i < localMatches->size(); i++) { 1013 int32_t len = localMatches->getMatchLength(i); 1014 1015 // TODO See the above TODO. We use len >= bestMatchLen 1016 // because of the long standard/location name collision 1017 // problem. If it is also a location name, carrying 1018 // timeType = UTZFMT_TIME_TYPE_STANDARD will cause a 1019 // problem in SimpleDateFormat 1020 if (len >= bestMatchLen) { 1021 bestMatchLen = localMatches->getMatchLength(i); 1022 bestMatchTimeType = UTZFMT_TIME_TYPE_UNKNOWN; // because generic 1023 localMatches->getTimeZoneID(i, bestMatchTzID); 1024 } 1025 } 1026 delete localMatches; 1027 } 1028 1029 if (bestMatchLen > 0) { 1030 timeType = bestMatchTimeType; 1031 tzID.setTo(bestMatchTzID); 1032 } 1033 return bestMatchLen; 1034 } 1035 1036 TimeZoneGenericNameMatchInfo* 1037 TZGNCore::findLocal(const UnicodeString& text, int32_t start, uint32_t types, UErrorCode& status) const { 1038 GNameSearchHandler handler(types); 1039 1040 TZGNCore *nonConstThis = const_cast<TZGNCore *>(this); 1041 1042 umtx_lock(&gLock); 1043 { 1044 fGNamesTrie.search(text, start, (TextTrieMapSearchResultHandler *)&handler, status); 1045 } 1046 umtx_unlock(&gLock); 1047 1048 if (U_FAILURE(status)) { 1049 return NULL; 1050 } 1051 1052 TimeZoneGenericNameMatchInfo *gmatchInfo = NULL; 1053 1054 int32_t maxLen = 0; 1055 UVector *results = handler.getMatches(maxLen); 1056 if (results != NULL && ((maxLen == (text.length() - start)) || fGNamesTrieFullyLoaded)) { 1057 // perfect match 1058 gmatchInfo = new TimeZoneGenericNameMatchInfo(results); 1059 if (gmatchInfo == NULL) { 1060 status = U_MEMORY_ALLOCATION_ERROR; 1061 delete results; 1062 return NULL; 1063 } 1064 return gmatchInfo; 1065 } 1066 1067 if (results != NULL) { 1068 delete results; 1069 } 1070 1071 // All names are not yet loaded into the local trie. 1072 // Load all available names into the trie. This could be very heavy. 1073 umtx_lock(&gLock); 1074 { 1075 if (!fGNamesTrieFullyLoaded) { 1076 StringEnumeration *tzIDs = TimeZone::createTimeZoneIDEnumeration(UCAL_ZONE_TYPE_CANONICAL, NULL, NULL, status); 1077 if (U_SUCCESS(status)) { 1078 const UnicodeString *tzID; 1079 while ((tzID = tzIDs->snext(status))) { 1080 if (U_FAILURE(status)) { 1081 break; 1082 } 1083 nonConstThis->loadStrings(*tzID); 1084 } 1085 } 1086 if (tzIDs != NULL) { 1087 delete tzIDs; 1088 } 1089 1090 if (U_SUCCESS(status)) { 1091 nonConstThis->fGNamesTrieFullyLoaded = TRUE; 1092 } 1093 } 1094 } 1095 umtx_unlock(&gLock); 1096 1097 if (U_FAILURE(status)) { 1098 return NULL; 1099 } 1100 1101 umtx_lock(&gLock); 1102 { 1103 // now try it again 1104 fGNamesTrie.search(text, start, (TextTrieMapSearchResultHandler *)&handler, status); 1105 } 1106 umtx_unlock(&gLock); 1107 1108 results = handler.getMatches(maxLen); 1109 if (results != NULL && maxLen > 0) { 1110 gmatchInfo = new TimeZoneGenericNameMatchInfo(results); 1111 if (gmatchInfo == NULL) { 1112 status = U_MEMORY_ALLOCATION_ERROR; 1113 delete results; 1114 return NULL; 1115 } 1116 } 1117 1118 return gmatchInfo; 1119 } 1120 1121 TimeZoneNames::MatchInfoCollection* 1122 TZGNCore::findTimeZoneNames(const UnicodeString& text, int32_t start, uint32_t types, UErrorCode& status) const { 1123 // Check if the target name typs is really in the TimeZoneNames 1124 uint32_t nameTypes = 0; 1125 if (types & UTZGNM_LONG) { 1126 nameTypes |= (UTZNM_LONG_GENERIC | UTZNM_LONG_STANDARD); 1127 } 1128 if (types & UTZGNM_SHORT) { 1129 nameTypes |= (UTZNM_SHORT_GENERIC | UTZNM_SHORT_STANDARD); 1130 } 1131 1132 if (types) { 1133 // Find matches in the TimeZoneNames 1134 return fTimeZoneNames->find(text, start, nameTypes, status); 1135 } 1136 1137 return NULL; 1138 } 1139 1140 typedef struct TZGNCoreRef { 1141 TZGNCore* obj; 1142 int32_t refCount; 1143 double lastAccess; 1144 } TZGNCoreRef; 1145 1146 // TZGNCore object cache handling 1147 static UMutex gTZGNLock = U_MUTEX_INITIALIZER; 1148 static UHashtable *gTZGNCoreCache = NULL; 1149 static UBool gTZGNCoreCacheInitialized = FALSE; 1150 1151 // Access count - incremented every time up to SWEEP_INTERVAL, 1152 // then reset to 0 1153 static int32_t gAccessCount = 0; 1154 1155 // Interval for calling the cache sweep function - every 100 times 1156 #define SWEEP_INTERVAL 100 1157 1158 // Cache expiration in millisecond. When a cached entry is no 1159 // longer referenced and exceeding this threshold since last 1160 // access time, then the cache entry will be deleted by the sweep 1161 // function. For now, 3 minutes. 1162 #define CACHE_EXPIRATION 180000.0 1163 1164 U_CDECL_BEGIN 1165 /** 1166 * Cleanup callback func 1167 */ 1168 static UBool U_CALLCONV tzgnCore_cleanup(void) 1169 { 1170 if (gTZGNCoreCache != NULL) { 1171 uhash_close(gTZGNCoreCache); 1172 gTZGNCoreCache = NULL; 1173 } 1174 gTZGNCoreCacheInitialized = FALSE; 1175 return TRUE; 1176 } 1177 1178 /** 1179 * Deleter for TZGNCoreRef 1180 */ 1181 static void U_CALLCONV 1182 deleteTZGNCoreRef(void *obj) { 1183 icu::TZGNCoreRef *entry = (icu::TZGNCoreRef*)obj; 1184 delete (icu::TZGNCore*) entry->obj; 1185 uprv_free(entry); 1186 } 1187 U_CDECL_END 1188 1189 /** 1190 * Function used for removing unreferrenced cache entries exceeding 1191 * the expiration time. This function must be called with in the mutex 1192 * block. 1193 */ 1194 static void sweepCache() { 1195 int32_t pos = -1; 1196 const UHashElement* elem; 1197 double now = (double)uprv_getUTCtime(); 1198 1199 while ((elem = uhash_nextElement(gTZGNCoreCache, &pos))) { 1200 TZGNCoreRef *entry = (TZGNCoreRef *)elem->value.pointer; 1201 if (entry->refCount <= 0 && (now - entry->lastAccess) > CACHE_EXPIRATION) { 1202 // delete this entry 1203 uhash_removeElement(gTZGNCoreCache, elem); 1204 } 1205 } 1206 } 1207 1208 TimeZoneGenericNames::TimeZoneGenericNames() 1209 : fRef(0) { 1210 } 1211 1212 TimeZoneGenericNames::~TimeZoneGenericNames() { 1213 umtx_lock(&gTZGNLock); 1214 { 1215 U_ASSERT(fRef->refCount > 0); 1216 // Just decrement the reference count 1217 fRef->refCount--; 1218 } 1219 umtx_unlock(&gTZGNLock); 1220 } 1221 1222 TimeZoneGenericNames* 1223 TimeZoneGenericNames::createInstance(const Locale& locale, UErrorCode& status) { 1224 if (U_FAILURE(status)) { 1225 return NULL; 1226 } 1227 TimeZoneGenericNames* instance = new TimeZoneGenericNames(); 1228 if (instance == NULL) { 1229 status = U_MEMORY_ALLOCATION_ERROR; 1230 return NULL; 1231 } 1232 1233 UBool initialized; 1234 UMTX_CHECK(&gTZGNLock, gTZGNCoreCacheInitialized, initialized); 1235 if (!initialized) { 1236 // Create empty hashtable 1237 umtx_lock(&gTZGNLock); 1238 { 1239 if (!gTZGNCoreCacheInitialized) { 1240 gTZGNCoreCache = uhash_open(uhash_hashChars, uhash_compareChars, NULL, &status); 1241 if (U_SUCCESS(status)) { 1242 uhash_setKeyDeleter(gTZGNCoreCache, uprv_free); 1243 uhash_setValueDeleter(gTZGNCoreCache, deleteTZGNCoreRef); 1244 gTZGNCoreCacheInitialized = TRUE; 1245 ucln_i18n_registerCleanup(UCLN_I18N_TIMEZONEGENERICNAMES, tzgnCore_cleanup); 1246 } 1247 } 1248 } 1249 umtx_unlock(&gTZGNLock); 1250 1251 if (U_FAILURE(status)) { 1252 return NULL; 1253 } 1254 } 1255 1256 // Check the cache, if not available, create new one and cache 1257 TZGNCoreRef *cacheEntry = NULL; 1258 umtx_lock(&gTZGNLock); 1259 { 1260 const char *key = locale.getName(); 1261 cacheEntry = (TZGNCoreRef *)uhash_get(gTZGNCoreCache, key); 1262 if (cacheEntry == NULL) { 1263 TZGNCore *tzgnCore = NULL; 1264 char *newKey = NULL; 1265 1266 tzgnCore = new TZGNCore(locale, status); 1267 if (tzgnCore == NULL) { 1268 status = U_MEMORY_ALLOCATION_ERROR; 1269 } 1270 if (U_SUCCESS(status)) { 1271 newKey = (char *)uprv_malloc(uprv_strlen(key) + 1); 1272 if (newKey == NULL) { 1273 status = U_MEMORY_ALLOCATION_ERROR; 1274 } else { 1275 uprv_strcpy(newKey, key); 1276 } 1277 } 1278 if (U_SUCCESS(status)) { 1279 cacheEntry = (TZGNCoreRef *)uprv_malloc(sizeof(TZGNCoreRef)); 1280 if (cacheEntry == NULL) { 1281 status = U_MEMORY_ALLOCATION_ERROR; 1282 } else { 1283 cacheEntry->obj = tzgnCore; 1284 cacheEntry->refCount = 1; 1285 cacheEntry->lastAccess = (double)uprv_getUTCtime(); 1286 1287 uhash_put(gTZGNCoreCache, newKey, cacheEntry, &status); 1288 } 1289 } 1290 if (U_FAILURE(status)) { 1291 if (tzgnCore != NULL) { 1292 delete tzgnCore; 1293 } 1294 if (newKey != NULL) { 1295 uprv_free(newKey); 1296 } 1297 if (cacheEntry != NULL) { 1298 uprv_free(cacheEntry); 1299 } 1300 cacheEntry = NULL; 1301 } 1302 } else { 1303 // Update the reference count 1304 cacheEntry->refCount++; 1305 cacheEntry->lastAccess = (double)uprv_getUTCtime(); 1306 } 1307 gAccessCount++; 1308 if (gAccessCount >= SWEEP_INTERVAL) { 1309 // sweep 1310 sweepCache(); 1311 gAccessCount = 0; 1312 } 1313 } 1314 umtx_unlock(&gTZGNLock); 1315 1316 if (cacheEntry == NULL) { 1317 delete instance; 1318 return NULL; 1319 } 1320 1321 instance->fRef = cacheEntry; 1322 return instance; 1323 } 1324 1325 UBool 1326 TimeZoneGenericNames::operator==(const TimeZoneGenericNames& other) const { 1327 // Just compare if the other object also use the same 1328 // ref entry 1329 return fRef == other.fRef; 1330 } 1331 1332 TimeZoneGenericNames* 1333 TimeZoneGenericNames::clone() const { 1334 TimeZoneGenericNames* other = new TimeZoneGenericNames(); 1335 if (other) { 1336 umtx_lock(&gTZGNLock); 1337 { 1338 // Just increments the reference count 1339 fRef->refCount++; 1340 other->fRef = fRef; 1341 } 1342 umtx_unlock(&gTZGNLock); 1343 } 1344 return other; 1345 } 1346 1347 UnicodeString& 1348 TimeZoneGenericNames::getDisplayName(const TimeZone& tz, UTimeZoneGenericNameType type, 1349 UDate date, UnicodeString& name) const { 1350 return fRef->obj->getDisplayName(tz, type, date, name); 1351 } 1352 1353 UnicodeString& 1354 TimeZoneGenericNames::getGenericLocationName(const UnicodeString& tzCanonicalID, UnicodeString& name) const { 1355 return fRef->obj->getGenericLocationName(tzCanonicalID, name); 1356 } 1357 1358 int32_t 1359 TimeZoneGenericNames::findBestMatch(const UnicodeString& text, int32_t start, uint32_t types, 1360 UnicodeString& tzID, UTimeZoneFormatTimeType& timeType, UErrorCode& status) const { 1361 return fRef->obj->findBestMatch(text, start, types, tzID, timeType, status); 1362 } 1363 1364 U_NAMESPACE_END 1365 #endif 1366