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