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