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
36U_NAMESPACE_BEGIN
37
38#define ZID_KEY_MAX  128
39
40static const char gZoneStrings[]                = "zoneStrings";
41
42static const char gRegionFormatTag[]            = "regionFormat";
43static const char gFallbackFormatTag[]          = "fallbackFormat";
44
45static const UChar gEmpty[]                     = {0x00};
46
47static const UChar gDefRegionPattern[]          = {0x7B, 0x30, 0x7D, 0x00}; // "{0}"
48static const UChar gDefFallbackPattern[]        = {0x7B, 0x31, 0x7D, 0x20, 0x28, 0x7B, 0x30, 0x7D, 0x29, 0x00}; // "{1} ({0})"
49
50static const double kDstCheckRange      = (double)184*U_MILLIS_PER_DAY;
51
52
53
54U_CDECL_BEGIN
55
56typedef 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 */
65static int32_t U_CALLCONV
66hashPartialLocationKey(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 */
80static UBool U_CALLCONV
81comparePartialLocationKey(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 */
98static void U_CALLCONV
99deleteGNameInfo(void *obj) {
100    uprv_free(obj);
101}
102
103/**
104 * GNameInfo stores zone name information in the local trie
105 */
106typedef 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 */
114typedef struct GMatchInfo {
115    const GNameInfo*    gnameInfo;
116    int32_t             matchLength;
117    UTimeZoneFormatTimeType   timeType;
118} ZMatchInfo;
119
120U_CDECL_END
121
122// ---------------------------------------------------
123// The class stores time zone generic name match information
124// ---------------------------------------------------
125class TimeZoneGenericNameMatchInfo : public UMemory {
126public:
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
135private:
136    UVector* fMatches;  // vector of MatchEntry
137};
138
139TimeZoneGenericNameMatchInfo::TimeZoneGenericNameMatchInfo(UVector* matches)
140: fMatches(matches) {
141}
142
143TimeZoneGenericNameMatchInfo::~TimeZoneGenericNameMatchInfo() {
144    if (fMatches != NULL) {
145        delete fMatches;
146    }
147}
148
149int32_t
150TimeZoneGenericNameMatchInfo::size() const {
151    if (fMatches == NULL) {
152        return 0;
153    }
154    return fMatches->size();
155}
156
157UTimeZoneGenericNameType
158TimeZoneGenericNameMatchInfo::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
166int32_t
167TimeZoneGenericNameMatchInfo::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
175UnicodeString&
176TimeZoneGenericNameMatchInfo::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// ---------------------------------------------------
189class GNameSearchHandler : public TextTrieMapSearchResultHandler {
190public:
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
197private:
198    uint32_t fTypes;
199    UVector* fResults;
200    int32_t fMaxMatchLen;
201};
202
203GNameSearchHandler::GNameSearchHandler(uint32_t types)
204: fTypes(types), fResults(NULL), fMaxMatchLen(0) {
205}
206
207GNameSearchHandler::~GNameSearchHandler() {
208    if (fResults != NULL) {
209        delete fResults;
210    }
211}
212
213UBool
214GNameSearchHandler::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
259UVector*
260GNameSearchHandler::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
271static UMutex gLock = U_MUTEX_INITIALIZER;
272
273class TZGNCore : public UMemory {
274public:
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
286private:
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// ---------------------------------------------------
333TZGNCore::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
345TZGNCore::~TZGNCore() {
346    cleanup();
347}
348
349void
350TZGNCore::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
436void
437TZGNCore::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
450UnicodeString&
451TZGNCore::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
478UnicodeString&
479TZGNCore::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 */
505const UChar*
506TZGNCore::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
589UnicodeString&
590TZGNCore::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
731UnicodeString&
732TZGNCore::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 */
759const UChar*
760TZGNCore::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 */
841void
842TZGNCore::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
882int32_t
883TZGNCore::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
1003TimeZoneGenericNameMatchInfo*
1004TZGNCore::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
1088TimeZoneNames::MatchInfoCollection*
1089TZGNCore::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
1107typedef struct TZGNCoreRef {
1108    TZGNCore*       obj;
1109    int32_t         refCount;
1110    double          lastAccess;
1111} TZGNCoreRef;
1112
1113// TZGNCore object cache handling
1114static UMutex gTZGNLock = U_MUTEX_INITIALIZER;
1115static UHashtable *gTZGNCoreCache = NULL;
1116static UBool gTZGNCoreCacheInitialized = FALSE;
1117
1118// Access count - incremented every time up to SWEEP_INTERVAL,
1119// then reset to 0
1120static 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
1131U_CDECL_BEGIN
1132/**
1133 * Cleanup callback func
1134 */
1135static 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 */
1148static void U_CALLCONV
1149deleteTZGNCoreRef(void *obj) {
1150    icu::TZGNCoreRef *entry = (icu::TZGNCoreRef*)obj;
1151    delete (icu::TZGNCore*) entry->obj;
1152    uprv_free(entry);
1153}
1154U_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 */
1161static 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
1175TimeZoneGenericNames::TimeZoneGenericNames()
1176: fRef(0) {
1177}
1178
1179TimeZoneGenericNames::~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
1189TimeZoneGenericNames*
1190TimeZoneGenericNames::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
1283UBool
1284TimeZoneGenericNames::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
1290TimeZoneGenericNames*
1291TimeZoneGenericNames::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
1305UnicodeString&
1306TimeZoneGenericNames::getDisplayName(const TimeZone& tz, UTimeZoneGenericNameType type,
1307                        UDate date, UnicodeString& name) const {
1308    return fRef->obj->getDisplayName(tz, type, date, name);
1309}
1310
1311UnicodeString&
1312TimeZoneGenericNames::getGenericLocationName(const UnicodeString& tzCanonicalID, UnicodeString& name) const {
1313    return fRef->obj->getGenericLocationName(tzCanonicalID, name);
1314}
1315
1316int32_t
1317TimeZoneGenericNames::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
1322U_NAMESPACE_END
1323#endif
1324