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