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
34U_NAMESPACE_BEGIN
35
36#define ZID_KEY_MAX  128
37
38static const char gZoneStrings[]                = "zoneStrings";
39
40static const char gRegionFormatTag[]            = "regionFormat";
41static const char gFallbackFormatTag[]          = "fallbackFormat";
42
43static const UChar gEmpty[]                     = {0x00};
44
45static const UChar gDefRegionPattern[]          = {0x7B, 0x30, 0x7D, 0x00}; // "{0}"
46static const UChar gDefFallbackPattern[]        = {0x7B, 0x31, 0x7D, 0x20, 0x28, 0x7B, 0x30, 0x7D, 0x29, 0x00}; // "{1} ({0})"
47
48static const double kDstCheckRange      = (double)184*U_MILLIS_PER_DAY;
49
50
51
52U_CDECL_BEGIN
53
54typedef 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 */
63static int32_t U_CALLCONV
64hashPartialLocationKey(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 */
78static UBool U_CALLCONV
79comparePartialLocationKey(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 */
96static void U_CALLCONV
97deleteGNameInfo(void *obj) {
98    uprv_free(obj);
99}
100
101/**
102 * GNameInfo stores zone name information in the local trie
103 */
104typedef 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 */
112typedef struct GMatchInfo {
113    const GNameInfo*    gnameInfo;
114    int32_t             matchLength;
115    UTimeZoneFormatTimeType   timeType;
116} ZMatchInfo;
117
118U_CDECL_END
119
120// ---------------------------------------------------
121// The class stores time zone generic name match information
122// ---------------------------------------------------
123class TimeZoneGenericNameMatchInfo : public UMemory {
124public:
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
133private:
134    UVector* fMatches;  // vector of MatchEntry
135};
136
137TimeZoneGenericNameMatchInfo::TimeZoneGenericNameMatchInfo(UVector* matches)
138: fMatches(matches) {
139}
140
141TimeZoneGenericNameMatchInfo::~TimeZoneGenericNameMatchInfo() {
142    if (fMatches != NULL) {
143        delete fMatches;
144    }
145}
146
147int32_t
148TimeZoneGenericNameMatchInfo::size() const {
149    if (fMatches == NULL) {
150        return 0;
151    }
152    return fMatches->size();
153}
154
155UTimeZoneGenericNameType
156TimeZoneGenericNameMatchInfo::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
164int32_t
165TimeZoneGenericNameMatchInfo::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
173UnicodeString&
174TimeZoneGenericNameMatchInfo::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// ---------------------------------------------------
187class GNameSearchHandler : public TextTrieMapSearchResultHandler {
188public:
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
195private:
196    uint32_t fTypes;
197    UVector* fResults;
198    int32_t fMaxMatchLen;
199};
200
201GNameSearchHandler::GNameSearchHandler(uint32_t types)
202: fTypes(types), fResults(NULL), fMaxMatchLen(0) {
203}
204
205GNameSearchHandler::~GNameSearchHandler() {
206    if (fResults != NULL) {
207        delete fResults;
208    }
209}
210
211UBool
212GNameSearchHandler::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
257UVector*
258GNameSearchHandler::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
269static UMutex gLock = U_MUTEX_INITIALIZER;
270
271class TZGNCore : public UMemory {
272public:
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
284private:
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// ---------------------------------------------------
331TZGNCore::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
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 = 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
442void
443TZGNCore::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
462UnicodeString&
463TZGNCore::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
490UnicodeString&
491TZGNCore::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 */
517const UChar*
518TZGNCore::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
613UnicodeString&
614TZGNCore::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
751UnicodeString&
752TZGNCore::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 */
779const UChar*
780TZGNCore::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 */
867void
868TZGNCore::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
908int32_t
909TZGNCore::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
1029TimeZoneGenericNameMatchInfo*
1030TZGNCore::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
1114TimeZoneNames::MatchInfoCollection*
1115TZGNCore::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
1133typedef struct TZGNCoreRef {
1134    TZGNCore*       obj;
1135    int32_t         refCount;
1136    double          lastAccess;
1137} TZGNCoreRef;
1138
1139// TZGNCore object cache handling
1140static UMutex gTZGNLock = U_MUTEX_INITIALIZER;
1141static UHashtable *gTZGNCoreCache = NULL;
1142static UBool gTZGNCoreCacheInitialized = FALSE;
1143
1144// Access count - incremented every time up to SWEEP_INTERVAL,
1145// then reset to 0
1146static 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
1157U_CDECL_BEGIN
1158/**
1159 * Cleanup callback func
1160 */
1161static 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 */
1174static void U_CALLCONV
1175deleteTZGNCoreRef(void *obj) {
1176    icu::TZGNCoreRef *entry = (icu::TZGNCoreRef*)obj;
1177    delete (icu::TZGNCore*) entry->obj;
1178    uprv_free(entry);
1179}
1180U_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 */
1187static 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
1201TimeZoneGenericNames::TimeZoneGenericNames()
1202: fRef(0) {
1203}
1204
1205TimeZoneGenericNames::~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
1215TimeZoneGenericNames*
1216TimeZoneGenericNames::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
1309UBool
1310TimeZoneGenericNames::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
1316TimeZoneGenericNames*
1317TimeZoneGenericNames::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
1331UnicodeString&
1332TimeZoneGenericNames::getDisplayName(const TimeZone& tz, UTimeZoneGenericNameType type,
1333                        UDate date, UnicodeString& name) const {
1334    return fRef->obj->getDisplayName(tz, type, date, name);
1335}
1336
1337UnicodeString&
1338TimeZoneGenericNames::getGenericLocationName(const UnicodeString& tzCanonicalID, UnicodeString& name) const {
1339    return fRef->obj->getGenericLocationName(tzCanonicalID, name);
1340}
1341
1342int32_t
1343TimeZoneGenericNames::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
1348U_NAMESPACE_END
1349#endif
1350