]> git.saurik.com Git - apple/icu.git/blob - icuSources/i18n/tzgnames.cpp
ICU-57166.0.1.tar.gz
[apple/icu.git] / icuSources / i18n / tzgnames.cpp
1 /*
2 *******************************************************************************
3 * Copyright (C) 2011-2016, 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/rbtz.h"
17 #include "unicode/simpleformatter.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 SimpleFormatter fRegionFormat;
291 SimpleFormatter 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 fLocaleDisplayNames(NULL),
337 fStringPool(status),
338 fGNamesTrie(TRUE, deleteGNameInfo),
339 fGNamesTrieFullyLoaded(FALSE) {
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 UChar *regionPattern = ures_getStringByKeyWithFallback(zoneStrings, gRegionFormatTag, NULL, &tmpsts);
369 if (U_SUCCESS(tmpsts) && u_strlen(regionPattern) > 0) {
370 rpat.setTo(regionPattern, -1);
371 }
372 tmpsts = U_ZERO_ERROR;
373 const UChar *fallbackPattern = ures_getStringByKeyWithFallback(zoneStrings, gFallbackFormatTag, NULL, &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, NULL, &status);
392 if (U_FAILURE(status)) {
393 cleanup();
394 return;
395 }
396
397 fPartialLocationNamesMap = uhash_open(hashPartialLocationKey, comparePartialLocationKey, NULL, &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 = uprv_strlen(region);
408 if (regionLen == 0) {
409 char loc[ULOC_FULLNAME_CAPACITY];
410 uloc_addLikelySubtags(fLocale.getName(), loc, sizeof(loc), &status);
411
412 regionLen = uloc_getCountry(loc, fTargetRegion, sizeof(fTargetRegion), &status);
413 if (U_SUCCESS(status)) {
414 fTargetRegion[regionLen] = 0;
415 } else {
416 cleanup();
417 return;
418 }
419 } else if (regionLen < (int32_t)sizeof(fTargetRegion)) {
420 uprv_strcpy(fTargetRegion, region);
421 } else {
422 fTargetRegion[0] = 0;
423 }
424
425 // preload generic names for the default zone
426 TimeZone *tz = TimeZone::createDefault();
427 const UChar *tzID = ZoneMeta::getCanonicalCLDRID(*tz);
428 if (tzID != NULL) {
429 loadStrings(UnicodeString(TRUE, tzID, -1));
430 }
431 delete tz;
432 }
433
434 void
435 TZGNCore::cleanup() {
436 if (fLocaleDisplayNames != NULL) {
437 delete fLocaleDisplayNames;
438 }
439 if (fTimeZoneNames != NULL) {
440 delete fTimeZoneNames;
441 }
442
443 uhash_close(fLocationNamesMap);
444 uhash_close(fPartialLocationNamesMap);
445 }
446
447
448 UnicodeString&
449 TZGNCore::getDisplayName(const TimeZone& tz, UTimeZoneGenericNameType type, UDate date, UnicodeString& name) const {
450 name.setToBogus();
451 switch (type) {
452 case UTZGNM_LOCATION:
453 {
454 const UChar* tzCanonicalID = ZoneMeta::getCanonicalCLDRID(tz);
455 if (tzCanonicalID != NULL) {
456 getGenericLocationName(UnicodeString(TRUE, tzCanonicalID, -1), name);
457 }
458 }
459 break;
460 case UTZGNM_LONG:
461 case UTZGNM_SHORT:
462 formatGenericNonLocationName(tz, type, date, name);
463 if (name.isEmpty()) {
464 const UChar* tzCanonicalID = ZoneMeta::getCanonicalCLDRID(tz);
465 if (tzCanonicalID != NULL) {
466 getGenericLocationName(UnicodeString(TRUE, tzCanonicalID, -1), name);
467 }
468 }
469 break;
470 default:
471 break;
472 }
473 return name;
474 }
475
476 UnicodeString&
477 TZGNCore::getGenericLocationName(const UnicodeString& tzCanonicalID, UnicodeString& name) const {
478 if (tzCanonicalID.isEmpty()) {
479 name.setToBogus();
480 return name;
481 }
482
483 const UChar *locname = NULL;
484 TZGNCore *nonConstThis = const_cast<TZGNCore *>(this);
485 umtx_lock(&gLock);
486 {
487 locname = nonConstThis->getGenericLocationName(tzCanonicalID);
488 }
489 umtx_unlock(&gLock);
490
491 if (locname == NULL) {
492 name.setToBogus();
493 } else {
494 name.setTo(locname, u_strlen(locname));
495 }
496
497 return name;
498 }
499
500 /*
501 * This method updates the cache and must be called with a lock
502 */
503 const UChar*
504 TZGNCore::getGenericLocationName(const UnicodeString& tzCanonicalID) {
505 U_ASSERT(!tzCanonicalID.isEmpty());
506 if (tzCanonicalID.length() > ZID_KEY_MAX) {
507 return NULL;
508 }
509
510 UErrorCode status = U_ZERO_ERROR;
511 UChar tzIDKey[ZID_KEY_MAX + 1];
512 int32_t tzIDKeyLen = tzCanonicalID.extract(tzIDKey, ZID_KEY_MAX + 1, status);
513 U_ASSERT(status == U_ZERO_ERROR); // already checked length above
514 tzIDKey[tzIDKeyLen] = 0;
515
516 const UChar *locname = (const UChar *)uhash_get(fLocationNamesMap, tzIDKey);
517
518 if (locname != NULL) {
519 // gEmpty indicate the name is not available
520 if (locname == gEmpty) {
521 return NULL;
522 }
523 return locname;
524 }
525
526 // Construct location name
527 UnicodeString name;
528 UnicodeString usCountryCode;
529 UBool isPrimary = FALSE;
530
531 ZoneMeta::getCanonicalCountry(tzCanonicalID, usCountryCode, &isPrimary);
532
533 if (!usCountryCode.isEmpty()) {
534 if (isPrimary) {
535 // If this is the primary zone in the country, use the country name.
536 char countryCode[ULOC_COUNTRY_CAPACITY];
537 U_ASSERT(usCountryCode.length() < ULOC_COUNTRY_CAPACITY);
538 int32_t ccLen = usCountryCode.extract(0, usCountryCode.length(), countryCode, sizeof(countryCode), US_INV);
539 countryCode[ccLen] = 0;
540
541 UnicodeString country;
542 fLocaleDisplayNames->regionDisplayName(countryCode, country);
543 fRegionFormat.format(country, name, status);
544 } else {
545 // If this is not the primary zone in the country,
546 // use the exemplar city name.
547
548 // getExemplarLocationName should retur non-empty string
549 // if the time zone is associated with a region
550
551 UnicodeString city;
552 fTimeZoneNames->getExemplarLocationName(tzCanonicalID, city);
553 fRegionFormat.format(city, name, status);
554 }
555 if (U_FAILURE(status)) {
556 return NULL;
557 }
558 }
559
560 locname = name.isEmpty() ? NULL : fStringPool.get(name, status);
561 if (U_SUCCESS(status)) {
562 // Cache the result
563 const UChar* cacheID = ZoneMeta::findTimeZoneID(tzCanonicalID);
564 U_ASSERT(cacheID != NULL);
565 if (locname == NULL) {
566 // gEmpty to indicate - no location name available
567 uhash_put(fLocationNamesMap, (void *)cacheID, (void *)gEmpty, &status);
568 } else {
569 uhash_put(fLocationNamesMap, (void *)cacheID, (void *)locname, &status);
570 if (U_FAILURE(status)) {
571 locname = NULL;
572 } else {
573 // put the name info into the trie
574 GNameInfo *nameinfo = (ZNameInfo *)uprv_malloc(sizeof(GNameInfo));
575 if (nameinfo != NULL) {
576 nameinfo->type = UTZGNM_LOCATION;
577 nameinfo->tzID = cacheID;
578 fGNamesTrie.put(locname, nameinfo, status);
579 }
580 }
581 }
582 }
583
584 return locname;
585 }
586
587 UnicodeString&
588 TZGNCore::formatGenericNonLocationName(const TimeZone& tz, UTimeZoneGenericNameType type, UDate date, UnicodeString& name) const {
589 U_ASSERT(type == UTZGNM_LONG || type == UTZGNM_SHORT);
590 name.setToBogus();
591
592 const UChar* uID = ZoneMeta::getCanonicalCLDRID(tz);
593 if (uID == NULL) {
594 return name;
595 }
596
597 UnicodeString tzID(TRUE, uID, -1);
598
599 // Try to get a name from time zone first
600 UTimeZoneNameType nameType = (type == UTZGNM_LONG) ? UTZNM_LONG_GENERIC : UTZNM_SHORT_GENERIC;
601 fTimeZoneNames->getTimeZoneDisplayName(tzID, nameType, name);
602
603 if (!name.isEmpty()) {
604 return name;
605 }
606
607 // Try meta zone
608 UChar mzIDBuf[32];
609 UnicodeString mzID(mzIDBuf, 0, UPRV_LENGTHOF(mzIDBuf));
610 fTimeZoneNames->getMetaZoneID(tzID, date, mzID);
611 if (!mzID.isEmpty()) {
612 UErrorCode status = U_ZERO_ERROR;
613 UBool useStandard = FALSE;
614 int32_t raw, sav;
615 UChar tmpNameBuf[64];
616
617 tz.getOffset(date, FALSE, raw, sav, status);
618 if (U_FAILURE(status)) {
619 return name;
620 }
621
622 if (sav == 0) {
623 useStandard = TRUE;
624
625 TimeZone *tmptz = tz.clone();
626 // Check if the zone actually uses daylight saving time around the time
627 BasicTimeZone *btz = NULL;
628 if (dynamic_cast<OlsonTimeZone *>(tmptz) != NULL
629 || dynamic_cast<SimpleTimeZone *>(tmptz) != NULL
630 || dynamic_cast<RuleBasedTimeZone *>(tmptz) != NULL
631 || dynamic_cast<VTimeZone *>(tmptz) != NULL) {
632 btz = (BasicTimeZone*)tmptz;
633 }
634
635 if (btz != NULL) {
636 TimeZoneTransition before;
637 UBool beforTrs = btz->getPreviousTransition(date, TRUE, before);
638 if (beforTrs
639 && (date - before.getTime() < kDstCheckRange)
640 && before.getFrom()->getDSTSavings() != 0) {
641 useStandard = FALSE;
642 } else {
643 TimeZoneTransition after;
644 UBool afterTrs = btz->getNextTransition(date, FALSE, after);
645 if (afterTrs
646 && (after.getTime() - date < kDstCheckRange)
647 && after.getTo()->getDSTSavings() != 0) {
648 useStandard = FALSE;
649 }
650 }
651 } else {
652 // If not BasicTimeZone... only if the instance is not an ICU's implementation.
653 // We may get a wrong answer in edge case, but it should practically work OK.
654 tmptz->getOffset(date - kDstCheckRange, FALSE, raw, sav, status);
655 if (sav != 0) {
656 useStandard = FALSE;
657 } else {
658 tmptz->getOffset(date + kDstCheckRange, FALSE, raw, sav, status);
659 if (sav != 0){
660 useStandard = FALSE;
661 }
662 }
663 if (U_FAILURE(status)) {
664 delete tmptz;
665 return name;
666 }
667 }
668 delete tmptz;
669 }
670 if (useStandard) {
671 UTimeZoneNameType stdNameType = (nameType == UTZNM_LONG_GENERIC)
672 ? UTZNM_LONG_STANDARD : UTZNM_SHORT_STANDARD;
673 UnicodeString stdName(tmpNameBuf, 0, UPRV_LENGTHOF(tmpNameBuf));
674 fTimeZoneNames->getDisplayName(tzID, stdNameType, date, stdName);
675 if (!stdName.isEmpty()) {
676 name.setTo(stdName);
677
678 // TODO: revisit this issue later
679 // In CLDR, a same display name is used for both generic and standard
680 // for some meta zones in some locales. This looks like a data bugs.
681 // For now, we check if the standard name is different from its generic
682 // name below.
683 UChar genNameBuf[64];
684 UnicodeString mzGenericName(genNameBuf, 0, UPRV_LENGTHOF(genNameBuf));
685 fTimeZoneNames->getMetaZoneDisplayName(mzID, nameType, mzGenericName);
686 if (stdName.caseCompare(mzGenericName, 0) == 0) {
687 name.setToBogus();
688 }
689 }
690 }
691 if (name.isEmpty()) {
692 // Get a name from meta zone
693 UnicodeString mzName(tmpNameBuf, 0, UPRV_LENGTHOF(tmpNameBuf));
694 fTimeZoneNames->getMetaZoneDisplayName(mzID, nameType, mzName);
695 if (!mzName.isEmpty()) {
696 // Check if we need to use a partial location format.
697 // This check is done by comparing offset with the meta zone's
698 // golden zone at the given date.
699 UChar idBuf[32];
700 UnicodeString goldenID(idBuf, 0, UPRV_LENGTHOF(idBuf));
701 fTimeZoneNames->getReferenceZoneID(mzID, fTargetRegion, goldenID);
702 if (!goldenID.isEmpty() && goldenID != tzID) {
703 TimeZone *goldenZone = TimeZone::createTimeZone(goldenID);
704 int32_t raw1, sav1;
705
706 // Check offset in the golden zone with wall time.
707 // With getOffset(date, false, offsets1),
708 // you may get incorrect results because of time overlap at DST->STD
709 // transition.
710 goldenZone->getOffset(date + raw + sav, TRUE, raw1, sav1, status);
711 delete goldenZone;
712 if (U_SUCCESS(status)) {
713 if (raw != raw1 || sav != sav1) {
714 // Now we need to use a partial location format
715 getPartialLocationName(tzID, mzID, (nameType == UTZNM_LONG_GENERIC), mzName, name);
716 } else {
717 name.setTo(mzName);
718 }
719 }
720 } else {
721 name.setTo(mzName);
722 }
723 }
724 }
725 }
726 return name;
727 }
728
729 UnicodeString&
730 TZGNCore::getPartialLocationName(const UnicodeString& tzCanonicalID,
731 const UnicodeString& mzID, UBool isLong, const UnicodeString& mzDisplayName,
732 UnicodeString& name) const {
733 name.setToBogus();
734 if (tzCanonicalID.isEmpty() || mzID.isEmpty() || mzDisplayName.isEmpty()) {
735 return name;
736 }
737
738 const UChar *uplname = NULL;
739 TZGNCore *nonConstThis = const_cast<TZGNCore *>(this);
740 umtx_lock(&gLock);
741 {
742 uplname = nonConstThis->getPartialLocationName(tzCanonicalID, mzID, isLong, mzDisplayName);
743 }
744 umtx_unlock(&gLock);
745
746 if (uplname == NULL) {
747 name.setToBogus();
748 } else {
749 name.setTo(TRUE, uplname, -1);
750 }
751 return name;
752 }
753
754 /*
755 * This method updates the cache and must be called with a lock
756 */
757 const UChar*
758 TZGNCore::getPartialLocationName(const UnicodeString& tzCanonicalID,
759 const UnicodeString& mzID, UBool isLong, const UnicodeString& mzDisplayName) {
760 U_ASSERT(!tzCanonicalID.isEmpty());
761 U_ASSERT(!mzID.isEmpty());
762 U_ASSERT(!mzDisplayName.isEmpty());
763
764 PartialLocationKey key;
765 key.tzID = ZoneMeta::findTimeZoneID(tzCanonicalID);
766 key.mzID = ZoneMeta::findMetaZoneID(mzID);
767 key.isLong = isLong;
768 U_ASSERT(key.tzID != NULL && key.mzID != NULL);
769
770 const UChar* uplname = (const UChar*)uhash_get(fPartialLocationNamesMap, (void *)&key);
771 if (uplname != NULL) {
772 return uplname;
773 }
774
775 UnicodeString location;
776 UnicodeString usCountryCode;
777 ZoneMeta::getCanonicalCountry(tzCanonicalID, usCountryCode);
778 if (!usCountryCode.isEmpty()) {
779 char countryCode[ULOC_COUNTRY_CAPACITY];
780 U_ASSERT(usCountryCode.length() < ULOC_COUNTRY_CAPACITY);
781 int32_t ccLen = usCountryCode.extract(0, usCountryCode.length(), countryCode, sizeof(countryCode), US_INV);
782 countryCode[ccLen] = 0;
783
784 UnicodeString regionalGolden;
785 fTimeZoneNames->getReferenceZoneID(mzID, countryCode, regionalGolden);
786 if (tzCanonicalID == regionalGolden) {
787 // Use country name
788 fLocaleDisplayNames->regionDisplayName(countryCode, location);
789 } else {
790 // Otherwise, use exemplar city name
791 fTimeZoneNames->getExemplarLocationName(tzCanonicalID, location);
792 }
793 } else {
794 fTimeZoneNames->getExemplarLocationName(tzCanonicalID, location);
795 if (location.isEmpty()) {
796 // This could happen when the time zone is not associated with a country,
797 // and its ID is not hierarchical, for example, CST6CDT.
798 // We use the canonical ID itself as the location for this case.
799 location.setTo(tzCanonicalID);
800 }
801 }
802
803 UErrorCode status = U_ZERO_ERROR;
804 UnicodeString name;
805 fFallbackFormat.format(location, mzDisplayName, name, status);
806 if (U_FAILURE(status)) {
807 return NULL;
808 }
809
810 uplname = fStringPool.get(name, status);
811 if (U_SUCCESS(status)) {
812 // Add the name to cache
813 PartialLocationKey* cacheKey = (PartialLocationKey *)uprv_malloc(sizeof(PartialLocationKey));
814 if (cacheKey != NULL) {
815 cacheKey->tzID = key.tzID;
816 cacheKey->mzID = key.mzID;
817 cacheKey->isLong = key.isLong;
818 uhash_put(fPartialLocationNamesMap, (void *)cacheKey, (void *)uplname, &status);
819 if (U_FAILURE(status)) {
820 uprv_free(cacheKey);
821 } else {
822 // put the name to the local trie as well
823 GNameInfo *nameinfo = (ZNameInfo *)uprv_malloc(sizeof(GNameInfo));
824 if (nameinfo != NULL) {
825 nameinfo->type = isLong ? UTZGNM_LONG : UTZGNM_SHORT;
826 nameinfo->tzID = key.tzID;
827 fGNamesTrie.put(uplname, nameinfo, status);
828 }
829 }
830 }
831 }
832 return uplname;
833 }
834
835 /*
836 * This method updates the cache and must be called with a lock,
837 * except initializer.
838 */
839 void
840 TZGNCore::loadStrings(const UnicodeString& tzCanonicalID) {
841 // load the generic location name
842 getGenericLocationName(tzCanonicalID);
843
844 // partial location names
845 UErrorCode status = U_ZERO_ERROR;
846
847 const UnicodeString *mzID;
848 UnicodeString goldenID;
849 UnicodeString mzGenName;
850 UTimeZoneNameType genNonLocTypes[] = {
851 UTZNM_LONG_GENERIC, UTZNM_SHORT_GENERIC,
852 UTZNM_UNKNOWN /*terminator*/
853 };
854
855 StringEnumeration *mzIDs = fTimeZoneNames->getAvailableMetaZoneIDs(tzCanonicalID, status);
856 while ((mzID = mzIDs->snext(status))) {
857 if (U_FAILURE(status)) {
858 break;
859 }
860 // if this time zone is not the golden zone of the meta zone,
861 // partial location name (such as "PT (Los Angeles)") might be
862 // available.
863 fTimeZoneNames->getReferenceZoneID(*mzID, fTargetRegion, goldenID);
864 if (tzCanonicalID != goldenID) {
865 for (int32_t i = 0; genNonLocTypes[i] != UTZNM_UNKNOWN; i++) {
866 fTimeZoneNames->getMetaZoneDisplayName(*mzID, genNonLocTypes[i], mzGenName);
867 if (!mzGenName.isEmpty()) {
868 // getPartialLocationName formats a name and put it into the trie
869 getPartialLocationName(tzCanonicalID, *mzID,
870 (genNonLocTypes[i] == UTZNM_LONG_GENERIC), mzGenName);
871 }
872 }
873 }
874 }
875 if (mzIDs != NULL) {
876 delete mzIDs;
877 }
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 != NULL) {
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, 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 != NULL) {
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 NULL;
1015 }
1016
1017 TimeZoneGenericNameMatchInfo *gmatchInfo = NULL;
1018
1019 int32_t maxLen = 0;
1020 UVector *results = handler.getMatches(maxLen);
1021 if (results != NULL && ((maxLen == (text.length() - start)) || fGNamesTrieFullyLoaded)) {
1022 // perfect match
1023 gmatchInfo = new TimeZoneGenericNameMatchInfo(results);
1024 if (gmatchInfo == NULL) {
1025 status = U_MEMORY_ALLOCATION_ERROR;
1026 delete results;
1027 return NULL;
1028 }
1029 return gmatchInfo;
1030 }
1031
1032 if (results != NULL) {
1033 delete results;
1034 }
1035
1036 // All names are not yet loaded into the local trie.
1037 // Load all available names into the trie. This could be very heavy.
1038 umtx_lock(&gLock);
1039 {
1040 if (!fGNamesTrieFullyLoaded) {
1041 StringEnumeration *tzIDs = TimeZone::createTimeZoneIDEnumeration(UCAL_ZONE_TYPE_CANONICAL, NULL, NULL, status);
1042 if (U_SUCCESS(status)) {
1043 const UnicodeString *tzID;
1044 while ((tzID = tzIDs->snext(status))) {
1045 if (U_FAILURE(status)) {
1046 break;
1047 }
1048 nonConstThis->loadStrings(*tzID);
1049 }
1050 }
1051 if (tzIDs != NULL) {
1052 delete tzIDs;
1053 }
1054
1055 if (U_SUCCESS(status)) {
1056 nonConstThis->fGNamesTrieFullyLoaded = TRUE;
1057 }
1058 }
1059 }
1060 umtx_unlock(&gLock);
1061
1062 if (U_FAILURE(status)) {
1063 return NULL;
1064 }
1065
1066 umtx_lock(&gLock);
1067 {
1068 // now try it again
1069 fGNamesTrie.search(text, start, (TextTrieMapSearchResultHandler *)&handler, status);
1070 }
1071 umtx_unlock(&gLock);
1072
1073 results = handler.getMatches(maxLen);
1074 if (results != NULL && maxLen > 0) {
1075 gmatchInfo = new TimeZoneGenericNameMatchInfo(results);
1076 if (gmatchInfo == NULL) {
1077 status = U_MEMORY_ALLOCATION_ERROR;
1078 delete results;
1079 return NULL;
1080 }
1081 }
1082
1083 return gmatchInfo;
1084 }
1085
1086 TimeZoneNames::MatchInfoCollection*
1087 TZGNCore::findTimeZoneNames(const UnicodeString& text, int32_t start, uint32_t types, UErrorCode& status) const {
1088 // Check if the target name typs is really in the TimeZoneNames
1089 uint32_t nameTypes = 0;
1090 if (types & UTZGNM_LONG) {
1091 nameTypes |= (UTZNM_LONG_GENERIC | UTZNM_LONG_STANDARD);
1092 }
1093 if (types & UTZGNM_SHORT) {
1094 nameTypes |= (UTZNM_SHORT_GENERIC | UTZNM_SHORT_STANDARD);
1095 }
1096
1097 if (types) {
1098 // Find matches in the TimeZoneNames
1099 return fTimeZoneNames->find(text, start, nameTypes, status);
1100 }
1101
1102 return NULL;
1103 }
1104
1105 typedef struct TZGNCoreRef {
1106 TZGNCore* obj;
1107 int32_t refCount;
1108 double lastAccess;
1109 } TZGNCoreRef;
1110
1111 // TZGNCore object cache handling
1112 static UMutex gTZGNLock = U_MUTEX_INITIALIZER;
1113 static UHashtable *gTZGNCoreCache = NULL;
1114 static UBool gTZGNCoreCacheInitialized = FALSE;
1115
1116 // Access count - incremented every time up to SWEEP_INTERVAL,
1117 // then reset to 0
1118 static int32_t gAccessCount = 0;
1119
1120 // Interval for calling the cache sweep function - every 100 times
1121 #define SWEEP_INTERVAL 100
1122
1123 // Cache expiration in millisecond. When a cached entry is no
1124 // longer referenced and exceeding this threshold since last
1125 // access time, then the cache entry will be deleted by the sweep
1126 // function. For now, 3 minutes.
1127 #define CACHE_EXPIRATION 180000.0
1128
1129 U_CDECL_BEGIN
1130 /**
1131 * Cleanup callback func
1132 */
1133 static UBool U_CALLCONV tzgnCore_cleanup(void)
1134 {
1135 if (gTZGNCoreCache != NULL) {
1136 uhash_close(gTZGNCoreCache);
1137 gTZGNCoreCache = NULL;
1138 }
1139 gTZGNCoreCacheInitialized = FALSE;
1140 return TRUE;
1141 }
1142
1143 /**
1144 * Deleter for TZGNCoreRef
1145 */
1146 static void U_CALLCONV
1147 deleteTZGNCoreRef(void *obj) {
1148 icu::TZGNCoreRef *entry = (icu::TZGNCoreRef*)obj;
1149 delete (icu::TZGNCore*) entry->obj;
1150 uprv_free(entry);
1151 }
1152 U_CDECL_END
1153
1154 /**
1155 * Function used for removing unreferrenced cache entries exceeding
1156 * the expiration time. This function must be called with in the mutex
1157 * block.
1158 */
1159 static void sweepCache() {
1160 int32_t pos = UHASH_FIRST;
1161 const UHashElement* elem;
1162 double now = (double)uprv_getUTCtime();
1163
1164 while ((elem = uhash_nextElement(gTZGNCoreCache, &pos))) {
1165 TZGNCoreRef *entry = (TZGNCoreRef *)elem->value.pointer;
1166 if (entry->refCount <= 0 && (now - entry->lastAccess) > CACHE_EXPIRATION) {
1167 // delete this entry
1168 uhash_removeElement(gTZGNCoreCache, elem);
1169 }
1170 }
1171 }
1172
1173 TimeZoneGenericNames::TimeZoneGenericNames()
1174 : fRef(0) {
1175 }
1176
1177 TimeZoneGenericNames::~TimeZoneGenericNames() {
1178 umtx_lock(&gTZGNLock);
1179 {
1180 U_ASSERT(fRef->refCount > 0);
1181 // Just decrement the reference count
1182 fRef->refCount--;
1183 }
1184 umtx_unlock(&gTZGNLock);
1185 }
1186
1187 TimeZoneGenericNames*
1188 TimeZoneGenericNames::createInstance(const Locale& locale, UErrorCode& status) {
1189 if (U_FAILURE(status)) {
1190 return NULL;
1191 }
1192 TimeZoneGenericNames* instance = new TimeZoneGenericNames();
1193 if (instance == NULL) {
1194 status = U_MEMORY_ALLOCATION_ERROR;
1195 return NULL;
1196 }
1197
1198 TZGNCoreRef *cacheEntry = NULL;
1199 {
1200 Mutex lock(&gTZGNLock);
1201
1202 if (!gTZGNCoreCacheInitialized) {
1203 // Create empty hashtable
1204 gTZGNCoreCache = uhash_open(uhash_hashChars, uhash_compareChars, NULL, &status);
1205 if (U_SUCCESS(status)) {
1206 uhash_setKeyDeleter(gTZGNCoreCache, uprv_free);
1207 uhash_setValueDeleter(gTZGNCoreCache, deleteTZGNCoreRef);
1208 gTZGNCoreCacheInitialized = TRUE;
1209 ucln_i18n_registerCleanup(UCLN_I18N_TIMEZONEGENERICNAMES, tzgnCore_cleanup);
1210 }
1211 }
1212 if (U_FAILURE(status)) {
1213 return NULL;
1214 }
1215
1216 // Check the cache, if not available, create new one and cache
1217 const char *key = locale.getName();
1218 cacheEntry = (TZGNCoreRef *)uhash_get(gTZGNCoreCache, key);
1219 if (cacheEntry == NULL) {
1220 TZGNCore *tzgnCore = NULL;
1221 char *newKey = NULL;
1222
1223 tzgnCore = new TZGNCore(locale, status);
1224 if (tzgnCore == NULL) {
1225 status = U_MEMORY_ALLOCATION_ERROR;
1226 }
1227 if (U_SUCCESS(status)) {
1228 newKey = (char *)uprv_malloc(uprv_strlen(key) + 1);
1229 if (newKey == NULL) {
1230 status = U_MEMORY_ALLOCATION_ERROR;
1231 } else {
1232 uprv_strcpy(newKey, key);
1233 }
1234 }
1235 if (U_SUCCESS(status)) {
1236 cacheEntry = (TZGNCoreRef *)uprv_malloc(sizeof(TZGNCoreRef));
1237 if (cacheEntry == NULL) {
1238 status = U_MEMORY_ALLOCATION_ERROR;
1239 } else {
1240 cacheEntry->obj = tzgnCore;
1241 cacheEntry->refCount = 1;
1242 cacheEntry->lastAccess = (double)uprv_getUTCtime();
1243
1244 uhash_put(gTZGNCoreCache, newKey, cacheEntry, &status);
1245 }
1246 }
1247 if (U_FAILURE(status)) {
1248 if (tzgnCore != NULL) {
1249 delete tzgnCore;
1250 }
1251 if (newKey != NULL) {
1252 uprv_free(newKey);
1253 }
1254 if (cacheEntry != NULL) {
1255 uprv_free(cacheEntry);
1256 }
1257 cacheEntry = NULL;
1258 }
1259 } else {
1260 // Update the reference count
1261 cacheEntry->refCount++;
1262 cacheEntry->lastAccess = (double)uprv_getUTCtime();
1263 }
1264 gAccessCount++;
1265 if (gAccessCount >= SWEEP_INTERVAL) {
1266 // sweep
1267 sweepCache();
1268 gAccessCount = 0;
1269 }
1270 } // End of mutex locked block
1271
1272 if (cacheEntry == NULL) {
1273 delete instance;
1274 return NULL;
1275 }
1276
1277 instance->fRef = cacheEntry;
1278 return instance;
1279 }
1280
1281 UBool
1282 TimeZoneGenericNames::operator==(const TimeZoneGenericNames& other) const {
1283 // Just compare if the other object also use the same
1284 // ref entry
1285 return fRef == other.fRef;
1286 }
1287
1288 TimeZoneGenericNames*
1289 TimeZoneGenericNames::clone() const {
1290 TimeZoneGenericNames* other = new TimeZoneGenericNames();
1291 if (other) {
1292 umtx_lock(&gTZGNLock);
1293 {
1294 // Just increments the reference count
1295 fRef->refCount++;
1296 other->fRef = fRef;
1297 }
1298 umtx_unlock(&gTZGNLock);
1299 }
1300 return other;
1301 }
1302
1303 UnicodeString&
1304 TimeZoneGenericNames::getDisplayName(const TimeZone& tz, UTimeZoneGenericNameType type,
1305 UDate date, UnicodeString& name) const {
1306 return fRef->obj->getDisplayName(tz, type, date, name);
1307 }
1308
1309 UnicodeString&
1310 TimeZoneGenericNames::getGenericLocationName(const UnicodeString& tzCanonicalID, UnicodeString& name) const {
1311 return fRef->obj->getGenericLocationName(tzCanonicalID, name);
1312 }
1313
1314 int32_t
1315 TimeZoneGenericNames::findBestMatch(const UnicodeString& text, int32_t start, uint32_t types,
1316 UnicodeString& tzID, UTimeZoneFormatTimeType& timeType, UErrorCode& status) const {
1317 return fRef->obj->findBestMatch(text, start, types, tzID, timeType, status);
1318 }
1319
1320 U_NAMESPACE_END
1321 #endif