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