1 // Copyright (C) 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/vtzone.h" 22 23 #include "cmemory.h" 24 #include "cstring.h" 25 #include "mutex.h" 26 #include "uhash.h" 27 #include "uassert.h" 28 #include "umutex.h" 29 #include "uresimp.h" 30 #include "ureslocs.h" 31 #include "zonemeta.h" 32 #include "tznames_impl.h" 33 #include "olsontz.h" 34 #include "ucln_in.h" 35 36 U_NAMESPACE_BEGIN 37 38 #define ZID_KEY_MAX 128 39 40 static const char gZoneStrings[] = "zoneStrings"; 41 42 static const char gRegionFormatTag[] = "regionFormat"; 43 static const char gFallbackFormatTag[] = "fallbackFormat"; 44 45 static const UChar gEmpty[] = {0x00}; 46 47 static const UChar gDefRegionPattern[] = {0x7B, 0x30, 0x7D, 0x00}; // "{0}" 48 static const UChar gDefFallbackPattern[] = {0x7B, 0x31, 0x7D, 0x20, 0x28, 0x7B, 0x30, 0x7D, 0x29, 0x00}; // "{1} ({0})" 49 50 static const double kDstCheckRange = (double)184*U_MILLIS_PER_DAY; 51 52 53 54 U_CDECL_BEGIN 55 56 typedef struct PartialLocationKey { 57 const UChar* tzID; 58 const UChar* mzID; 59 UBool isLong; 60 } PartialLocationKey; 61 62 /** 63 * Hash function for partial location name hash key 64 */ 65 static int32_t U_CALLCONV 66 hashPartialLocationKey(const UHashTok key) { 67 // <tzID>&<mzID>#[L|S] 68 PartialLocationKey *p = (PartialLocationKey *)key.pointer; 69 UnicodeString str(p->tzID); 70 str.append((UChar)0x26) 71 .append(p->mzID, -1) 72 .append((UChar)0x23) 73 .append((UChar)(p->isLong ? 0x4C : 0x53)); 74 return str.hashCode(); 75 } 76 77 /** 78 * Comparer for partial location name hash key 79 */ 80 static UBool U_CALLCONV 81 comparePartialLocationKey(const UHashTok key1, const UHashTok key2) { 82 PartialLocationKey *p1 = (PartialLocationKey *)key1.pointer; 83 PartialLocationKey *p2 = (PartialLocationKey *)key2.pointer; 84 85 if (p1 == p2) { 86 return TRUE; 87 } 88 if (p1 == NULL || p2 == NULL) { 89 return FALSE; 90 } 91 // We just check identity of tzID/mzID 92 return (p1->tzID == p2->tzID && p1->mzID == p2->mzID && p1->isLong == p2->isLong); 93 } 94 95 /** 96 * Deleter for GNameInfo 97 */ 98 static void U_CALLCONV 99 deleteGNameInfo(void *obj) { 100 uprv_free(obj); 101 } 102 103 /** 104 * GNameInfo stores zone name information in the local trie 105 */ 106 typedef struct GNameInfo { 107 UTimeZoneGenericNameType type; 108 const UChar* tzID; 109 } ZNameInfo; 110 111 /** 112 * GMatchInfo stores zone name match information used by find method 113 */ 114 typedef struct GMatchInfo { 115 const GNameInfo* gnameInfo; 116 int32_t matchLength; 117 UTimeZoneFormatTimeType timeType; 118 } ZMatchInfo; 119 120 U_CDECL_END 121 122 // --------------------------------------------------- 123 // The class stores time zone generic name match information 124 // --------------------------------------------------- 125 class TimeZoneGenericNameMatchInfo : public UMemory { 126 public: 127 TimeZoneGenericNameMatchInfo(UVector* matches); 128 ~TimeZoneGenericNameMatchInfo(); 129 130 int32_t size() const; 131 UTimeZoneGenericNameType getGenericNameType(int32_t index) const; 132 int32_t getMatchLength(int32_t index) const; 133 UnicodeString& getTimeZoneID(int32_t index, UnicodeString& tzID) const; 134 135 private: 136 UVector* fMatches; // vector of MatchEntry 137 }; 138 139 TimeZoneGenericNameMatchInfo::TimeZoneGenericNameMatchInfo(UVector* matches) 140 : fMatches(matches) { 141 } 142 143 TimeZoneGenericNameMatchInfo::~TimeZoneGenericNameMatchInfo() { 144 if (fMatches != NULL) { 145 delete fMatches; 146 } 147 } 148 149 int32_t 150 TimeZoneGenericNameMatchInfo::size() const { 151 if (fMatches == NULL) { 152 return 0; 153 } 154 return fMatches->size(); 155 } 156 157 UTimeZoneGenericNameType 158 TimeZoneGenericNameMatchInfo::getGenericNameType(int32_t index) const { 159 GMatchInfo *minfo = (GMatchInfo *)fMatches->elementAt(index); 160 if (minfo != NULL) { 161 return static_cast<UTimeZoneGenericNameType>(minfo->gnameInfo->type); 162 } 163 return UTZGNM_UNKNOWN; 164 } 165 166 int32_t 167 TimeZoneGenericNameMatchInfo::getMatchLength(int32_t index) const { 168 ZMatchInfo *minfo = (ZMatchInfo *)fMatches->elementAt(index); 169 if (minfo != NULL) { 170 return minfo->matchLength; 171 } 172 return -1; 173 } 174 175 UnicodeString& 176 TimeZoneGenericNameMatchInfo::getTimeZoneID(int32_t index, UnicodeString& tzID) const { 177 GMatchInfo *minfo = (GMatchInfo *)fMatches->elementAt(index); 178 if (minfo != NULL && minfo->gnameInfo->tzID != NULL) { 179 tzID.setTo(TRUE, minfo->gnameInfo->tzID, -1); 180 } else { 181 tzID.setToBogus(); 182 } 183 return tzID; 184 } 185 186 // --------------------------------------------------- 187 // GNameSearchHandler 188 // --------------------------------------------------- 189 class GNameSearchHandler : public TextTrieMapSearchResultHandler { 190 public: 191 GNameSearchHandler(uint32_t types); 192 virtual ~GNameSearchHandler(); 193 194 UBool handleMatch(int32_t matchLength, const CharacterNode *node, UErrorCode &status); 195 UVector* getMatches(int32_t& maxMatchLen); 196 197 private: 198 uint32_t fTypes; 199 UVector* fResults; 200 int32_t fMaxMatchLen; 201 }; 202 203 GNameSearchHandler::GNameSearchHandler(uint32_t types) 204 : fTypes(types), fResults(NULL), fMaxMatchLen(0) { 205 } 206 207 GNameSearchHandler::~GNameSearchHandler() { 208 if (fResults != NULL) { 209 delete fResults; 210 } 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 == NULL) { 223 break; 224 } 225 if ((nameinfo->type & fTypes) != 0) { 226 // matches a requested type 227 if (fResults == NULL) { 228 fResults = new UVector(uprv_free, NULL, status); 229 if (fResults == NULL) { 230 status = U_MEMORY_ALLOCATION_ERROR; 231 } 232 } 233 if (U_SUCCESS(status)) { 234 U_ASSERT(fResults != NULL); 235 GMatchInfo *gmatch = (GMatchInfo *)uprv_malloc(sizeof(GMatchInfo)); 236 if (gmatch == NULL) { 237 status = U_MEMORY_ALLOCATION_ERROR; 238 } else { 239 // add the match to the vector 240 gmatch->gnameInfo = nameinfo; 241 gmatch->matchLength = matchLength; 242 gmatch->timeType = UTZFMT_TIME_TYPE_UNKNOWN; 243 fResults->addElement(gmatch, status); 244 if (U_FAILURE(status)) { 245 uprv_free(gmatch); 246 } else { 247 if (matchLength > fMaxMatchLen) { 248 fMaxMatchLen = matchLength; 249 } 250 } 251 } 252 } 253 } 254 } 255 } 256 return TRUE; 257 } 258 259 UVector* 260 GNameSearchHandler::getMatches(int32_t& maxMatchLen) { 261 // give the ownership to the caller 262 UVector *results = fResults; 263 maxMatchLen = fMaxMatchLen; 264 265 // reset 266 fResults = NULL; 267 fMaxMatchLen = 0; 268 return results; 269 } 270 271 static UMutex gLock = U_MUTEX_INITIALIZER; 272 273 class TZGNCore : public UMemory { 274 public: 275 TZGNCore(const Locale& locale, UErrorCode& status); 276 virtual ~TZGNCore(); 277 278 UnicodeString& getDisplayName(const TimeZone& tz, UTimeZoneGenericNameType type, 279 UDate date, UnicodeString& name) const; 280 281 UnicodeString& getGenericLocationName(const UnicodeString& tzCanonicalID, UnicodeString& name) const; 282 283 int32_t findBestMatch(const UnicodeString& text, int32_t start, uint32_t types, 284 UnicodeString& tzID, UTimeZoneFormatTimeType& timeType, UErrorCode& status) const; 285 286 private: 287 Locale fLocale; 288 const TimeZoneNames* fTimeZoneNames; 289 UHashtable* fLocationNamesMap; 290 UHashtable* fPartialLocationNamesMap; 291 292 SimpleFormatter fRegionFormat; 293 SimpleFormatter fFallbackFormat; 294 295 LocaleDisplayNames* fLocaleDisplayNames; 296 ZNStringPool fStringPool; 297 298 TextTrieMap fGNamesTrie; 299 UBool fGNamesTrieFullyLoaded; 300 301 char fTargetRegion[ULOC_COUNTRY_CAPACITY]; 302 303 void initialize(const Locale& locale, UErrorCode& status); 304 void cleanup(); 305 306 void loadStrings(const UnicodeString& tzCanonicalID); 307 308 const UChar* getGenericLocationName(const UnicodeString& tzCanonicalID); 309 310 UnicodeString& formatGenericNonLocationName(const TimeZone& tz, UTimeZoneGenericNameType type, 311 UDate date, UnicodeString& name) const; 312 313 UnicodeString& getPartialLocationName(const UnicodeString& tzCanonicalID, 314 const UnicodeString& mzID, UBool isLong, const UnicodeString& mzDisplayName, 315 UnicodeString& name) const; 316 317 const UChar* getPartialLocationName(const UnicodeString& tzCanonicalID, 318 const UnicodeString& mzID, UBool isLong, const UnicodeString& mzDisplayName); 319 320 TimeZoneGenericNameMatchInfo* findLocal(const UnicodeString& text, int32_t start, uint32_t types, UErrorCode& status) const; 321 322 TimeZoneNames::MatchInfoCollection* findTimeZoneNames(const UnicodeString& text, int32_t start, uint32_t types, UErrorCode& status) const; 323 }; 324 325 326 // --------------------------------------------------- 327 // TZGNCore - core implmentation of TimeZoneGenericNames 328 // 329 // TimeZoneGenericNames is parallel to TimeZoneNames, 330 // but handles run-time generated time zone names. 331 // This is the main part of this module. 332 // --------------------------------------------------- 333 TZGNCore::TZGNCore(const Locale& locale, UErrorCode& status) 334 : fLocale(locale), 335 fTimeZoneNames(NULL), 336 fLocationNamesMap(NULL), 337 fPartialLocationNamesMap(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.applyPatternMinMaxArguments(rpat, 1, 1, status); 383 fFallbackFormat.applyPatternMinMaxArguments(fpat, 2, 2, status); 384 if (U_FAILURE(status)) { 385 cleanup(); 386 return; 387 } 388 389 // locale display names 390 fLocaleDisplayNames = LocaleDisplayNames::createInstance(locale); 391 392 // hash table for names - no key/value deleters 393 fLocationNamesMap = uhash_open(uhash_hashUChars, uhash_compareUChars, NULL, &status); 394 if (U_FAILURE(status)) { 395 cleanup(); 396 return; 397 } 398 399 fPartialLocationNamesMap = uhash_open(hashPartialLocationKey, comparePartialLocationKey, NULL, &status); 400 if (U_FAILURE(status)) { 401 cleanup(); 402 return; 403 } 404 uhash_setKeyDeleter(fPartialLocationNamesMap, uprv_free); 405 // no value deleter 406 407 // target region 408 const char* region = fLocale.getCountry(); 409 int32_t regionLen = uprv_strlen(region); 410 if (regionLen == 0) { 411 char loc[ULOC_FULLNAME_CAPACITY]; 412 uloc_addLikelySubtags(fLocale.getName(), loc, sizeof(loc), &status); 413 414 regionLen = uloc_getCountry(loc, fTargetRegion, sizeof(fTargetRegion), &status); 415 if (U_SUCCESS(status)) { 416 fTargetRegion[regionLen] = 0; 417 } else { 418 cleanup(); 419 return; 420 } 421 } else if (regionLen < (int32_t)sizeof(fTargetRegion)) { 422 uprv_strcpy(fTargetRegion, region); 423 } else { 424 fTargetRegion[0] = 0; 425 } 426 427 // preload generic names for the default zone 428 TimeZone *tz = TimeZone::createDefault(); 429 const UChar *tzID = ZoneMeta::getCanonicalCLDRID(*tz); 430 if (tzID != NULL) { 431 loadStrings(UnicodeString(TRUE, tzID, -1)); 432 } 433 delete tz; 434 } 435 436 void 437 TZGNCore::cleanup() { 438 if (fLocaleDisplayNames != NULL) { 439 delete fLocaleDisplayNames; 440 } 441 if (fTimeZoneNames != NULL) { 442 delete fTimeZoneNames; 443 } 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 UChar* tzCanonicalID = ZoneMeta::getCanonicalCLDRID(tz); 457 if (tzCanonicalID != NULL) { 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 UChar* tzCanonicalID = ZoneMeta::getCanonicalCLDRID(tz); 467 if (tzCanonicalID != NULL) { 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 UChar *locname = NULL; 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 == NULL) { 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 UChar* 506 TZGNCore::getGenericLocationName(const UnicodeString& tzCanonicalID) { 507 U_ASSERT(!tzCanonicalID.isEmpty()); 508 if (tzCanonicalID.length() > ZID_KEY_MAX) { 509 return NULL; 510 } 511 512 UErrorCode status = U_ZERO_ERROR; 513 UChar 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 UChar *locname = (const UChar *)uhash_get(fLocationNamesMap, tzIDKey); 519 520 if (locname != NULL) { 521 // gEmpty indicate the name is not available 522 if (locname == gEmpty) { 523 return NULL; 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 retur 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 NULL; 559 } 560 } 561 562 locname = name.isEmpty() ? NULL : fStringPool.get(name, status); 563 if (U_SUCCESS(status)) { 564 // Cache the result 565 const UChar* cacheID = ZoneMeta::findTimeZoneID(tzCanonicalID); 566 U_ASSERT(cacheID != NULL); 567 if (locname == NULL) { 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 = NULL; 574 } else { 575 // put the name info into the trie 576 GNameInfo *nameinfo = (ZNameInfo *)uprv_malloc(sizeof(GNameInfo)); 577 if (nameinfo != NULL) { 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 UChar* uID = ZoneMeta::getCanonicalCLDRID(tz); 595 if (uID == NULL) { 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 UChar 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 UChar tmpNameBuf[64]; 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 = NULL; 630 if (dynamic_cast<OlsonTimeZone *>(tmptz) != NULL 631 || dynamic_cast<SimpleTimeZone *>(tmptz) != NULL 632 || dynamic_cast<RuleBasedTimeZone *>(tmptz) != NULL 633 || dynamic_cast<VTimeZone *>(tmptz) != NULL) { 634 btz = (BasicTimeZone*)tmptz; 635 } 636 637 if (btz != NULL) { 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 UChar genNameBuf[64]; 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 UChar idBuf[32]; 702 UnicodeString goldenID(idBuf, 0, UPRV_LENGTHOF(idBuf)); 703 fTimeZoneNames->getReferenceZoneID(mzID, fTargetRegion, 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 UChar *uplname = NULL; 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 == NULL) { 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 UChar* 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 != NULL && key.mzID != NULL); 771 772 const UChar* uplname = (const UChar*)uhash_get(fPartialLocationNamesMap, (void *)&key); 773 if (uplname != NULL) { 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 NULL; 810 } 811 812 uplname = fStringPool.get(name, status); 813 if (U_SUCCESS(status)) { 814 // Add the name to cache 815 PartialLocationKey* cacheKey = (PartialLocationKey *)uprv_malloc(sizeof(PartialLocationKey)); 816 if (cacheKey != NULL) { 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 = (ZNameInfo *)uprv_malloc(sizeof(GNameInfo)); 826 if (nameinfo != NULL) { 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))) { 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, 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 if (mzIDs != NULL) { 878 delete mzIDs; 879 } 880 } 881 882 int32_t 883 TZGNCore::findBestMatch(const UnicodeString& text, int32_t start, uint32_t types, 884 UnicodeString& tzID, UTimeZoneFormatTimeType& timeType, UErrorCode& status) const { 885 timeType = UTZFMT_TIME_TYPE_UNKNOWN; 886 tzID.setToBogus(); 887 888 if (U_FAILURE(status)) { 889 return 0; 890 } 891 892 // Find matches in the TimeZoneNames first 893 TimeZoneNames::MatchInfoCollection *tznamesMatches = findTimeZoneNames(text, start, types, status); 894 if (U_FAILURE(status)) { 895 return 0; 896 } 897 898 int32_t bestMatchLen = 0; 899 UTimeZoneFormatTimeType bestMatchTimeType = UTZFMT_TIME_TYPE_UNKNOWN; 900 UnicodeString bestMatchTzID; 901 // UBool isLongStandard = FALSE; // workaround - see the comments below 902 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 903 904 if (tznamesMatches != NULL) { 905 UnicodeString mzID; 906 for (int32_t i = 0; i < tznamesMatches->size(); i++) { 907 int32_t len = tznamesMatches->getMatchLengthAt(i); 908 if (len > bestMatchLen) { 909 bestMatchLen = len; 910 if (!tznamesMatches->getTimeZoneIDAt(i, bestMatchTzID)) { 911 // name for a meta zone 912 if (tznamesMatches->getMetaZoneIDAt(i, mzID)) { 913 fTimeZoneNames->getReferenceZoneID(mzID, fTargetRegion, bestMatchTzID); 914 } 915 } 916 UTimeZoneNameType nameType = tznamesMatches->getNameTypeAt(i); 917 if (U_FAILURE(status)) { 918 break; 919 } 920 switch (nameType) { 921 case UTZNM_LONG_STANDARD: 922 // isLongStandard = TRUE; 923 case UTZNM_SHORT_STANDARD: // this one is never used for generic, but just in case 924 isStandard = TRUE; // TODO: Remove this later, see the comments above. 925 bestMatchTimeType = UTZFMT_TIME_TYPE_STANDARD; 926 break; 927 case UTZNM_LONG_DAYLIGHT: 928 case UTZNM_SHORT_DAYLIGHT: // this one is never used for generic, but just in case 929 bestMatchTimeType = UTZFMT_TIME_TYPE_DAYLIGHT; 930 break; 931 default: 932 bestMatchTimeType = UTZFMT_TIME_TYPE_UNKNOWN; 933 } 934 } 935 } 936 delete tznamesMatches; 937 if (U_FAILURE(status)) { 938 return 0; 939 } 940 941 if (bestMatchLen == (text.length() - start)) { 942 // Full match 943 944 //tzID.setTo(bestMatchTzID); 945 //timeType = bestMatchTimeType; 946 //return bestMatchLen; 947 948 // TODO Some time zone uses a same name for the long standard name 949 // and the location name. When the match is a long standard name, 950 // then we need to check if the name is same with the location name. 951 // This is probably a data error or a design bug. 952 /* 953 if (!isLongStandard) { 954 tzID.setTo(bestMatchTzID); 955 timeType = bestMatchTimeType; 956 return bestMatchLen; 957 } 958 */ 959 // TODO The deprecation of commonlyUsed flag introduced the name 960 // conflict not only for long standard names, but short standard names too. 961 // These short names (found in zh_Hant) should be gone once we clean 962 // up CLDR time zone display name data. Once the short name conflict 963 // problem (with location name) is resolved, we should change the condition 964 // below back to the original one above. -Yoshito (2011-09-14) 965 if (!isStandard) { 966 tzID.setTo(bestMatchTzID); 967 timeType = bestMatchTimeType; 968 return bestMatchLen; 969 } 970 } 971 } 972 973 // Find matches in the local trie 974 TimeZoneGenericNameMatchInfo *localMatches = findLocal(text, start, types, status); 975 if (U_FAILURE(status)) { 976 return 0; 977 } 978 if (localMatches != NULL) { 979 for (int32_t i = 0; i < localMatches->size(); i++) { 980 int32_t len = localMatches->getMatchLength(i); 981 982 // TODO See the above TODO. We use len >= bestMatchLen 983 // because of the long standard/location name collision 984 // problem. If it is also a location name, carrying 985 // timeType = UTZFMT_TIME_TYPE_STANDARD will cause a 986 // problem in SimpleDateFormat 987 if (len >= bestMatchLen) { 988 bestMatchLen = localMatches->getMatchLength(i); 989 bestMatchTimeType = UTZFMT_TIME_TYPE_UNKNOWN; // because generic 990 localMatches->getTimeZoneID(i, bestMatchTzID); 991 } 992 } 993 delete localMatches; 994 } 995 996 if (bestMatchLen > 0) { 997 timeType = bestMatchTimeType; 998 tzID.setTo(bestMatchTzID); 999 } 1000 return bestMatchLen; 1001 } 1002 1003 TimeZoneGenericNameMatchInfo* 1004 TZGNCore::findLocal(const UnicodeString& text, int32_t start, uint32_t types, UErrorCode& status) const { 1005 GNameSearchHandler handler(types); 1006 1007 TZGNCore *nonConstThis = const_cast<TZGNCore *>(this); 1008 1009 umtx_lock(&gLock); 1010 { 1011 fGNamesTrie.search(text, start, (TextTrieMapSearchResultHandler *)&handler, status); 1012 } 1013 umtx_unlock(&gLock); 1014 1015 if (U_FAILURE(status)) { 1016 return NULL; 1017 } 1018 1019 TimeZoneGenericNameMatchInfo *gmatchInfo = NULL; 1020 1021 int32_t maxLen = 0; 1022 UVector *results = handler.getMatches(maxLen); 1023 if (results != NULL && ((maxLen == (text.length() - start)) || fGNamesTrieFullyLoaded)) { 1024 // perfect match 1025 gmatchInfo = new TimeZoneGenericNameMatchInfo(results); 1026 if (gmatchInfo == NULL) { 1027 status = U_MEMORY_ALLOCATION_ERROR; 1028 delete results; 1029 return NULL; 1030 } 1031 return gmatchInfo; 1032 } 1033 1034 if (results != NULL) { 1035 delete results; 1036 } 1037 1038 // All names are not yet loaded into the local trie. 1039 // Load all available names into the trie. This could be very heavy. 1040 umtx_lock(&gLock); 1041 { 1042 if (!fGNamesTrieFullyLoaded) { 1043 StringEnumeration *tzIDs = TimeZone::createTimeZoneIDEnumeration(UCAL_ZONE_TYPE_CANONICAL, NULL, NULL, status); 1044 if (U_SUCCESS(status)) { 1045 const UnicodeString *tzID; 1046 while ((tzID = tzIDs->snext(status))) { 1047 if (U_FAILURE(status)) { 1048 break; 1049 } 1050 nonConstThis->loadStrings(*tzID); 1051 } 1052 } 1053 if (tzIDs != NULL) { 1054 delete tzIDs; 1055 } 1056 1057 if (U_SUCCESS(status)) { 1058 nonConstThis->fGNamesTrieFullyLoaded = TRUE; 1059 } 1060 } 1061 } 1062 umtx_unlock(&gLock); 1063 1064 if (U_FAILURE(status)) { 1065 return NULL; 1066 } 1067 1068 umtx_lock(&gLock); 1069 { 1070 // now try it again 1071 fGNamesTrie.search(text, start, (TextTrieMapSearchResultHandler *)&handler, status); 1072 } 1073 umtx_unlock(&gLock); 1074 1075 results = handler.getMatches(maxLen); 1076 if (results != NULL && maxLen > 0) { 1077 gmatchInfo = new TimeZoneGenericNameMatchInfo(results); 1078 if (gmatchInfo == NULL) { 1079 status = U_MEMORY_ALLOCATION_ERROR; 1080 delete results; 1081 return NULL; 1082 } 1083 } 1084 1085 return gmatchInfo; 1086 } 1087 1088 TimeZoneNames::MatchInfoCollection* 1089 TZGNCore::findTimeZoneNames(const UnicodeString& text, int32_t start, uint32_t types, UErrorCode& status) const { 1090 // Check if the target name typs is really in the TimeZoneNames 1091 uint32_t nameTypes = 0; 1092 if (types & UTZGNM_LONG) { 1093 nameTypes |= (UTZNM_LONG_GENERIC | UTZNM_LONG_STANDARD); 1094 } 1095 if (types & UTZGNM_SHORT) { 1096 nameTypes |= (UTZNM_SHORT_GENERIC | UTZNM_SHORT_STANDARD); 1097 } 1098 1099 if (types) { 1100 // Find matches in the TimeZoneNames 1101 return fTimeZoneNames->find(text, start, nameTypes, status); 1102 } 1103 1104 return NULL; 1105 } 1106 1107 typedef struct TZGNCoreRef { 1108 TZGNCore* obj; 1109 int32_t refCount; 1110 double lastAccess; 1111 } TZGNCoreRef; 1112 1113 // TZGNCore object cache handling 1114 static UMutex gTZGNLock = U_MUTEX_INITIALIZER; 1115 static UHashtable *gTZGNCoreCache = NULL; 1116 static UBool gTZGNCoreCacheInitialized = FALSE; 1117 1118 // Access count - incremented every time up to SWEEP_INTERVAL, 1119 // then reset to 0 1120 static int32_t gAccessCount = 0; 1121 1122 // Interval for calling the cache sweep function - every 100 times 1123 #define SWEEP_INTERVAL 100 1124 1125 // Cache expiration in millisecond. When a cached entry is no 1126 // longer referenced and exceeding this threshold since last 1127 // access time, then the cache entry will be deleted by the sweep 1128 // function. For now, 3 minutes. 1129 #define CACHE_EXPIRATION 180000.0 1130 1131 U_CDECL_BEGIN 1132 /** 1133 * Cleanup callback func 1134 */ 1135 static UBool U_CALLCONV tzgnCore_cleanup(void) 1136 { 1137 if (gTZGNCoreCache != NULL) { 1138 uhash_close(gTZGNCoreCache); 1139 gTZGNCoreCache = NULL; 1140 } 1141 gTZGNCoreCacheInitialized = FALSE; 1142 return TRUE; 1143 } 1144 1145 /** 1146 * Deleter for TZGNCoreRef 1147 */ 1148 static void U_CALLCONV 1149 deleteTZGNCoreRef(void *obj) { 1150 icu::TZGNCoreRef *entry = (icu::TZGNCoreRef*)obj; 1151 delete (icu::TZGNCore*) entry->obj; 1152 uprv_free(entry); 1153 } 1154 U_CDECL_END 1155 1156 /** 1157 * Function used for removing unreferrenced cache entries exceeding 1158 * the expiration time. This function must be called with in the mutex 1159 * block. 1160 */ 1161 static void sweepCache() { 1162 int32_t pos = UHASH_FIRST; 1163 const UHashElement* elem; 1164 double now = (double)uprv_getUTCtime(); 1165 1166 while ((elem = uhash_nextElement(gTZGNCoreCache, &pos))) { 1167 TZGNCoreRef *entry = (TZGNCoreRef *)elem->value.pointer; 1168 if (entry->refCount <= 0 && (now - entry->lastAccess) > CACHE_EXPIRATION) { 1169 // delete this entry 1170 uhash_removeElement(gTZGNCoreCache, elem); 1171 } 1172 } 1173 } 1174 1175 TimeZoneGenericNames::TimeZoneGenericNames() 1176 : fRef(0) { 1177 } 1178 1179 TimeZoneGenericNames::~TimeZoneGenericNames() { 1180 umtx_lock(&gTZGNLock); 1181 { 1182 U_ASSERT(fRef->refCount > 0); 1183 // Just decrement the reference count 1184 fRef->refCount--; 1185 } 1186 umtx_unlock(&gTZGNLock); 1187 } 1188 1189 TimeZoneGenericNames* 1190 TimeZoneGenericNames::createInstance(const Locale& locale, UErrorCode& status) { 1191 if (U_FAILURE(status)) { 1192 return NULL; 1193 } 1194 TimeZoneGenericNames* instance = new TimeZoneGenericNames(); 1195 if (instance == NULL) { 1196 status = U_MEMORY_ALLOCATION_ERROR; 1197 return NULL; 1198 } 1199 1200 TZGNCoreRef *cacheEntry = NULL; 1201 { 1202 Mutex lock(&gTZGNLock); 1203 1204 if (!gTZGNCoreCacheInitialized) { 1205 // Create empty hashtable 1206 gTZGNCoreCache = uhash_open(uhash_hashChars, uhash_compareChars, NULL, &status); 1207 if (U_SUCCESS(status)) { 1208 uhash_setKeyDeleter(gTZGNCoreCache, uprv_free); 1209 uhash_setValueDeleter(gTZGNCoreCache, deleteTZGNCoreRef); 1210 gTZGNCoreCacheInitialized = TRUE; 1211 ucln_i18n_registerCleanup(UCLN_I18N_TIMEZONEGENERICNAMES, tzgnCore_cleanup); 1212 } 1213 } 1214 if (U_FAILURE(status)) { 1215 return NULL; 1216 } 1217 1218 // Check the cache, if not available, create new one and cache 1219 const char *key = locale.getName(); 1220 cacheEntry = (TZGNCoreRef *)uhash_get(gTZGNCoreCache, key); 1221 if (cacheEntry == NULL) { 1222 TZGNCore *tzgnCore = NULL; 1223 char *newKey = NULL; 1224 1225 tzgnCore = new TZGNCore(locale, status); 1226 if (tzgnCore == NULL) { 1227 status = U_MEMORY_ALLOCATION_ERROR; 1228 } 1229 if (U_SUCCESS(status)) { 1230 newKey = (char *)uprv_malloc(uprv_strlen(key) + 1); 1231 if (newKey == NULL) { 1232 status = U_MEMORY_ALLOCATION_ERROR; 1233 } else { 1234 uprv_strcpy(newKey, key); 1235 } 1236 } 1237 if (U_SUCCESS(status)) { 1238 cacheEntry = (TZGNCoreRef *)uprv_malloc(sizeof(TZGNCoreRef)); 1239 if (cacheEntry == NULL) { 1240 status = U_MEMORY_ALLOCATION_ERROR; 1241 } else { 1242 cacheEntry->obj = tzgnCore; 1243 cacheEntry->refCount = 1; 1244 cacheEntry->lastAccess = (double)uprv_getUTCtime(); 1245 1246 uhash_put(gTZGNCoreCache, newKey, cacheEntry, &status); 1247 } 1248 } 1249 if (U_FAILURE(status)) { 1250 if (tzgnCore != NULL) { 1251 delete tzgnCore; 1252 } 1253 if (newKey != NULL) { 1254 uprv_free(newKey); 1255 } 1256 if (cacheEntry != NULL) { 1257 uprv_free(cacheEntry); 1258 } 1259 cacheEntry = NULL; 1260 } 1261 } else { 1262 // Update the reference count 1263 cacheEntry->refCount++; 1264 cacheEntry->lastAccess = (double)uprv_getUTCtime(); 1265 } 1266 gAccessCount++; 1267 if (gAccessCount >= SWEEP_INTERVAL) { 1268 // sweep 1269 sweepCache(); 1270 gAccessCount = 0; 1271 } 1272 } // End of mutex locked block 1273 1274 if (cacheEntry == NULL) { 1275 delete instance; 1276 return NULL; 1277 } 1278 1279 instance->fRef = cacheEntry; 1280 return instance; 1281 } 1282 1283 UBool 1284 TimeZoneGenericNames::operator==(const TimeZoneGenericNames& other) const { 1285 // Just compare if the other object also use the same 1286 // ref entry 1287 return fRef == other.fRef; 1288 } 1289 1290 TimeZoneGenericNames* 1291 TimeZoneGenericNames::clone() const { 1292 TimeZoneGenericNames* other = new TimeZoneGenericNames(); 1293 if (other) { 1294 umtx_lock(&gTZGNLock); 1295 { 1296 // Just increments the reference count 1297 fRef->refCount++; 1298 other->fRef = fRef; 1299 } 1300 umtx_unlock(&gTZGNLock); 1301 } 1302 return other; 1303 } 1304 1305 UnicodeString& 1306 TimeZoneGenericNames::getDisplayName(const TimeZone& tz, UTimeZoneGenericNameType type, 1307 UDate date, UnicodeString& name) const { 1308 return fRef->obj->getDisplayName(tz, type, date, name); 1309 } 1310 1311 UnicodeString& 1312 TimeZoneGenericNames::getGenericLocationName(const UnicodeString& tzCanonicalID, UnicodeString& name) const { 1313 return fRef->obj->getGenericLocationName(tzCanonicalID, name); 1314 } 1315 1316 int32_t 1317 TimeZoneGenericNames::findBestMatch(const UnicodeString& text, int32_t start, uint32_t types, 1318 UnicodeString& tzID, UTimeZoneFormatTimeType& timeType, UErrorCode& status) const { 1319 return fRef->obj->findBestMatch(text, start, types, tzID, timeType, status); 1320 } 1321 1322 U_NAMESPACE_END 1323 #endif 1324