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