]> git.saurik.com Git - apple/icu.git/blobdiff - icuSources/i18n/tznames_impl.cpp
ICU-551.51.4.tar.gz
[apple/icu.git] / icuSources / i18n / tznames_impl.cpp
index d11d787cfc7758756d11fa20a7b76d207ba43980..1595d89c549472c01f56da3e3aa7bc63226b7a01 100644 (file)
@@ -1,6 +1,6 @@
 /*
 *******************************************************************************
-* Copyright (C) 2011-2013, International Business Machines Corporation and
+* Copyright (C) 2011-2014, International Business Machines Corporation and
 * others. All Rights Reserved.
 *******************************************************************************
 *
@@ -51,6 +51,36 @@ static const UTimeZoneNameType ALL_NAME_TYPES[] = {
     UTZNM_UNKNOWN // unknown as the last one
 };
 
+// stuff for TZDBTimeZoneNames
+static const char* TZDBNAMES_KEYS[]               = {"ss", "sd"};
+static const int32_t TZDBNAMES_KEYS_SIZE = (sizeof TZDBNAMES_KEYS / sizeof TZDBNAMES_KEYS[0]);
+
+static UMutex gTZDBNamesMapLock = U_MUTEX_INITIALIZER;
+
+static UHashtable* gTZDBNamesMap = NULL;
+static icu::UInitOnce gTZDBNamesMapInitOnce = U_INITONCE_INITIALIZER;
+
+static TextTrieMap* gTZDBNamesTrie = NULL;
+static icu::UInitOnce gTZDBNamesTrieInitOnce = U_INITONCE_INITIALIZER;
+
+U_CDECL_BEGIN
+static UBool U_CALLCONV tzdbTimeZoneNames_cleanup(void) {
+    if (gTZDBNamesMap != NULL) {
+        uhash_close(gTZDBNamesMap);
+        gTZDBNamesMap = NULL;
+    }
+    gTZDBNamesMapInitOnce.reset();
+
+    if (gTZDBNamesTrie != NULL) {
+        delete gTZDBNamesTrie;
+        gTZDBNamesTrie = NULL;
+    }
+    gTZDBNamesTrieInitOnce.reset();
+
+    return TRUE;
+}
+U_CDECL_END
+
 #define DEFAULT_CHARACTERNODE_CAPACITY 1
 
 // ---------------------------------------------------
@@ -799,7 +829,7 @@ ZNameSearchHandler::handleMatch(int32_t matchLength, const CharacterNode *node,
         for (int32_t i = 0; i < valuesCount; i++) {
             ZNameInfo *nameinfo = (ZNameInfo *)node->getValue(i);
             if (nameinfo == NULL) {
-                break;
+                continue;
             }
             if ((nameinfo->type & fTypes) != 0) {
                 // matches a requested type
@@ -986,6 +1016,12 @@ TimeZoneNamesImpl::clone() const {
 
 StringEnumeration*
 TimeZoneNamesImpl::getAvailableMetaZoneIDs(UErrorCode& status) const {
+    return TimeZoneNamesImpl::_getAvailableMetaZoneIDs(status);
+}
+
+// static implementation of getAvailableMetaZoneIDs(UErrorCode&)
+StringEnumeration*
+TimeZoneNamesImpl::_getAvailableMetaZoneIDs(UErrorCode& status) {
     if (U_FAILURE(status)) {
         return NULL;
     }
@@ -998,6 +1034,12 @@ TimeZoneNamesImpl::getAvailableMetaZoneIDs(UErrorCode& status) const {
 
 StringEnumeration*
 TimeZoneNamesImpl::getAvailableMetaZoneIDs(const UnicodeString& tzID, UErrorCode& status) const {
+    return TimeZoneNamesImpl::_getAvailableMetaZoneIDs(tzID, status);
+}
+
+// static implementation of getAvailableMetaZoneIDs(const UnicodeString&, UErrorCode&)
+StringEnumeration*
+TimeZoneNamesImpl::_getAvailableMetaZoneIDs(const UnicodeString& tzID, UErrorCode& status) {
     if (U_FAILURE(status)) {
         return NULL;
     }
@@ -1032,16 +1074,29 @@ TimeZoneNamesImpl::getAvailableMetaZoneIDs(const UnicodeString& tzID, UErrorCode
 
 UnicodeString&
 TimeZoneNamesImpl::getMetaZoneID(const UnicodeString& tzID, UDate date, UnicodeString& mzID) const {
+    return TimeZoneNamesImpl::_getMetaZoneID(tzID, date, mzID);
+}
+
+// static implementation of getMetaZoneID
+UnicodeString&
+TimeZoneNamesImpl::_getMetaZoneID(const UnicodeString& tzID, UDate date, UnicodeString& mzID) {
     ZoneMeta::getMetazoneID(tzID, date, mzID);
     return mzID;
 }
 
 UnicodeString&
 TimeZoneNamesImpl::getReferenceZoneID(const UnicodeString& mzID, const char* region, UnicodeString& tzID) const {
+    return TimeZoneNamesImpl::_getReferenceZoneID(mzID, region, tzID);
+}
+
+// static implementaion of getReferenceZoneID
+UnicodeString&
+TimeZoneNamesImpl::_getReferenceZoneID(const UnicodeString& mzID, const char* region, UnicodeString& tzID) {
     ZoneMeta::getZoneIdByMetazone(mzID, UnicodeString(region, -1, US_INV), tzID);
     return tzID;
 }
 
+
 UnicodeString&
 TimeZoneNamesImpl::getMetaZoneDisplayName(const UnicodeString& mzID,
                                           UTimeZoneNameType type,
@@ -1170,6 +1225,7 @@ TimeZoneNamesImpl::loadMetaZoneNames(const UnicodeString& mzID) {
             if (U_FAILURE(status)) {
                 if (znames != NULL) {
                     delete znames;
+                    znames = NULL;
                 }
             } else if (znames != NULL) {
                 // put the name info into the trie
@@ -1247,6 +1303,7 @@ TimeZoneNamesImpl::loadTimeZoneNames(const UnicodeString& tzID) {
             if (U_FAILURE(status)) {
                 if (tznames != NULL) {
                     delete tznames;
+                    tznames = NULL;
                 }
             } else if (tznames != NULL) {
                 // put the name info into the trie
@@ -1371,6 +1428,589 @@ TimeZoneNamesImpl::getDefaultExemplarLocationName(const UnicodeString& tzID, Uni
     return name;
 }
 
+// ---------------------------------------------------
+// TZDBTimeZoneNames and its supporting classes
+//
+// TZDBTimeZoneNames is an implementation class of
+// TimeZoneNames holding the IANA tz database abbreviations.
+// ---------------------------------------------------
+
+class TZDBNames : public UMemory {
+public:
+    virtual ~TZDBNames();
+
+    static TZDBNames* createInstance(UResourceBundle* rb, const char* key);
+    const UChar* getName(UTimeZoneNameType type) const;
+    const char** getParseRegions(int32_t& numRegions) const;
+
+protected:
+    TZDBNames(const UChar** names, char** regions, int32_t numRegions);
+
+private:
+    const UChar** fNames;
+    char** fRegions;
+    int32_t fNumRegions;
+};
+
+TZDBNames::TZDBNames(const UChar** names, char** regions, int32_t numRegions)
+    :   fNames(names),
+        fRegions(regions),
+        fNumRegions(numRegions) {
+}
+
+TZDBNames::~TZDBNames() {
+    if (fNames != NULL) {
+        uprv_free(fNames);
+    }
+    if (fRegions != NULL) {
+        char **p = fRegions;
+        for (int32_t i = 0; i < fNumRegions; p++, i++) {
+            uprv_free(*p);
+        }
+        uprv_free(fRegions);
+    }
+}
+
+TZDBNames*
+TZDBNames::createInstance(UResourceBundle* rb, const char* key) {
+    if (rb == NULL || key == NULL || *key == 0) {
+        return NULL;
+    }
+
+    UErrorCode status = U_ZERO_ERROR;
+
+    const UChar **names = NULL;
+    char** regions = NULL;
+    int32_t numRegions = 0;
+
+    int32_t len = 0;
+
+    UResourceBundle* rbTable = NULL;
+    rbTable = ures_getByKey(rb, key, rbTable, &status);
+    if (U_FAILURE(status)) {
+        return NULL;
+    }
+
+    names = (const UChar **)uprv_malloc(sizeof(const UChar*) * TZDBNAMES_KEYS_SIZE);
+    UBool isEmpty = TRUE;
+    if (names != NULL) {
+        for (int32_t i = 0; i < TZDBNAMES_KEYS_SIZE; i++) {
+            status = U_ZERO_ERROR;
+            const UChar *value = ures_getStringByKey(rbTable, TZDBNAMES_KEYS[i], &len, &status);
+            if (U_FAILURE(status) || len == 0) {
+                names[i] = NULL;
+            } else {
+                names[i] = value;
+                isEmpty = FALSE;
+            }
+        }
+    }
+
+    if (isEmpty) {
+        if (names != NULL) {
+            uprv_free(names);
+        }
+        return NULL;
+    }
+
+    UResourceBundle *regionsRes = ures_getByKey(rbTable, "parseRegions", NULL, &status);
+    UBool regionError = FALSE;
+    if (U_SUCCESS(status)) {
+        numRegions = ures_getSize(regionsRes);
+        if (numRegions > 0) {
+            regions = (char**)uprv_malloc(sizeof(char*) * numRegions);
+            if (regions != NULL) {
+                char **pRegion = regions;
+                for (int32_t i = 0; i < numRegions; i++, pRegion++) {
+                    *pRegion = NULL;
+                }
+                // filling regions
+                pRegion = regions;
+                for (int32_t i = 0; i < numRegions; i++, pRegion++) {
+                    status = U_ZERO_ERROR;
+                    const UChar *uregion = ures_getStringByIndex(regionsRes, i, &len, &status);
+                    if (U_FAILURE(status)) {
+                        regionError = TRUE;
+                        break;
+                    }
+                    *pRegion = (char*)uprv_malloc(sizeof(char) * (len + 1));
+                    if (*pRegion == NULL) {
+                        regionError = TRUE;
+                        break;
+                    }
+                    u_UCharsToChars(uregion, *pRegion, len);
+                    (*pRegion)[len] = 0;
+                }
+            }
+        }
+    }
+    ures_close(regionsRes);
+    ures_close(rbTable);
+
+    if (regionError) {
+        if (names != NULL) {
+            uprv_free(names);
+        }
+        if (regions != NULL) {
+            char **p = regions;
+            for (int32_t i = 0; i < numRegions; p++, i++) {
+                uprv_free(p);
+            }
+            uprv_free(regions);
+        }
+        return NULL;
+    }
+
+    return new TZDBNames(names, regions, numRegions);
+}
+
+const UChar*
+TZDBNames::getName(UTimeZoneNameType type) const {
+    if (fNames == NULL) {
+        return NULL;
+    }
+    const UChar *name = NULL;
+    switch(type) {
+    case UTZNM_SHORT_STANDARD:
+        name = fNames[0];
+        break;
+    case UTZNM_SHORT_DAYLIGHT:
+        name = fNames[1];
+        break;
+    default:
+        name = NULL;
+    }
+    return name;
+}
+
+const char**
+TZDBNames::getParseRegions(int32_t& numRegions) const {
+    if (fRegions == NULL) {
+        numRegions = 0;
+    } else {
+        numRegions = fNumRegions;
+    }
+    return (const char**)fRegions;
+}
+
+U_CDECL_BEGIN
+/**
+ * TZDBNameInfo stores metazone name information for the IANA abbreviations
+ * in the trie
+ */
+typedef struct TZDBNameInfo {
+    const UChar*        mzID;
+    UTimeZoneNameType   type;
+    UBool               ambiguousType;
+    const char**        parseRegions;
+    int32_t             nRegions;
+} TZDBNameInfo;
+U_CDECL_END
+
+
+class TZDBNameSearchHandler : public TextTrieMapSearchResultHandler {
+public:
+    TZDBNameSearchHandler(uint32_t types, const char* region);
+    virtual ~TZDBNameSearchHandler();
+
+    UBool handleMatch(int32_t matchLength, const CharacterNode *node, UErrorCode &status);
+    TimeZoneNames::MatchInfoCollection* getMatches(int32_t& maxMatchLen);
+
+private:
+    uint32_t fTypes;
+    int32_t fMaxMatchLen;
+    TimeZoneNames::MatchInfoCollection* fResults;
+    const char* fRegion;
+};
+
+TZDBNameSearchHandler::TZDBNameSearchHandler(uint32_t types, const char* region) 
+: fTypes(types), fMaxMatchLen(0), fResults(NULL), fRegion(region) {
+}
+
+TZDBNameSearchHandler::~TZDBNameSearchHandler() {
+    if (fResults != NULL) {
+        delete fResults;
+    }
+}
+
+UBool
+TZDBNameSearchHandler::handleMatch(int32_t matchLength, const CharacterNode *node, UErrorCode &status) {
+    if (U_FAILURE(status)) {
+        return FALSE;
+    }
+
+    TZDBNameInfo *match = NULL;
+    TZDBNameInfo *defaultRegionMatch = NULL;
+
+    if (node->hasValues()) {
+        int32_t valuesCount = node->countValues();
+        for (int32_t i = 0; i < valuesCount; i++) {
+            TZDBNameInfo *ninfo = (TZDBNameInfo *)node->getValue(i);
+            if (ninfo == NULL) {
+                continue;
+            }
+            if ((ninfo->type & fTypes) != 0) {
+                // Some tz database abbreviations are ambiguous. For example,
+                // CST means either Central Standard Time or China Standard Time.
+                // Unlike CLDR time zone display names, this implementation
+                // does not use unique names. And TimeZoneFormat does not expect
+                // multiple results returned for the same time zone type.
+                // For this reason, this implementation resolve one among same
+                // zone type with a same name at this level.
+                if (ninfo->parseRegions == NULL) {
+                    // parseRegions == null means this is the default metazone
+                    // mapping for the abbreviation.
+                    if (defaultRegionMatch == NULL) {
+                        match = defaultRegionMatch = ninfo;
+                    }
+                } else {
+                    UBool matchRegion = FALSE;
+                    // non-default metazone mapping for an abbreviation
+                    // comes with applicable regions. For example, the default
+                    // metazone mapping for "CST" is America_Central,
+                    // but if region is one of CN/MO/TW, "CST" is parsed
+                    // as metazone China (China Standard Time).
+                    for (int32_t i = 0; i < ninfo->nRegions; i++) {
+                        const char *region = ninfo->parseRegions[i];
+                        if (uprv_strcmp(fRegion, region) == 0) {
+                            match = ninfo;
+                            matchRegion = TRUE;
+                            break;
+                        }
+                    }
+                    if (matchRegion) {
+                        break;
+                    }
+                    if (match == NULL) {
+                        match = ninfo;
+                    }
+                }
+            }
+        }
+
+        if (match != NULL) {
+            UTimeZoneNameType ntype = match->type;
+            // Note: Workaround for duplicated standard/daylight names
+            // The tz database contains a few zones sharing a
+            // same name for both standard time and daylight saving
+            // time. For example, Australia/Sydney observes DST,
+            // but "EST" is used for both standard and daylight.
+            // When both SHORT_STANDARD and SHORT_DAYLIGHT are included
+            // in the find operation, we cannot tell which one was
+            // actually matched.
+            // TimeZoneFormat#parse returns a matched name type (standard
+            // or daylight) and DateFormat implementation uses the info to
+            // to adjust actual time. To avoid false type information,
+            // this implementation replaces the name type with SHORT_GENERIC.
+            if (match->ambiguousType
+                    && (ntype == UTZNM_SHORT_STANDARD || ntype == UTZNM_SHORT_DAYLIGHT)
+                    && (fTypes & UTZNM_SHORT_STANDARD) != 0
+                    && (fTypes & UTZNM_SHORT_DAYLIGHT) != 0) {
+                ntype = UTZNM_SHORT_GENERIC;
+            }
+
+            if (fResults == NULL) {
+                fResults = new TimeZoneNames::MatchInfoCollection();
+                if (fResults == NULL) {
+                    status = U_MEMORY_ALLOCATION_ERROR;
+                }
+            }
+            if (U_SUCCESS(status)) {
+                U_ASSERT(fResults != NULL);
+                U_ASSERT(match->mzID != NULL);
+                fResults->addMetaZone(ntype, matchLength, UnicodeString(match->mzID, -1), status);
+                if (U_SUCCESS(status) && matchLength > fMaxMatchLen) {
+                    fMaxMatchLen = matchLength;
+                }
+            }
+        }
+    }
+    return TRUE;
+}
+
+TimeZoneNames::MatchInfoCollection*
+TZDBNameSearchHandler::getMatches(int32_t& maxMatchLen) {
+    // give the ownership to the caller
+    TimeZoneNames::MatchInfoCollection* results = fResults;
+    maxMatchLen = fMaxMatchLen;
+
+    // reset
+    fResults = NULL;
+    fMaxMatchLen = 0;
+    return results;
+}
+
+U_CDECL_BEGIN
+/**
+ * Deleter for TZDBNames
+ */
+static void U_CALLCONV
+deleteTZDBNames(void *obj) {
+    if (obj != EMPTY) {
+        delete (TZDBNames *)obj;
+    }
+}
+
+static void U_CALLCONV initTZDBNamesMap(UErrorCode &status) {
+    gTZDBNamesMap = uhash_open(uhash_hashUChars, uhash_compareUChars, NULL, &status);
+    if (U_FAILURE(status)) {
+        gTZDBNamesMap = NULL;
+        return;
+    }
+    // no key deleters for tzdb name maps
+    uhash_setValueDeleter(gTZDBNamesMap, deleteTZDBNames);
+    ucln_i18n_registerCleanup(UCLN_I18N_TZDBTIMEZONENAMES, tzdbTimeZoneNames_cleanup);
+}
+
+/**
+ * Deleter for TZDBNameInfo
+ */
+static void U_CALLCONV
+deleteTZDBNameInfo(void *obj) {
+    if (obj != NULL) {
+        uprv_free(obj);
+    }
+}
+
+static void U_CALLCONV prepareFind(UErrorCode &status) {
+    if (U_FAILURE(status)) {
+        return;
+    }
+    gTZDBNamesTrie = new TextTrieMap(TRUE, deleteTZDBNameInfo);
+    if (gTZDBNamesTrie == NULL) {
+        status = U_MEMORY_ALLOCATION_ERROR;
+        return;
+    }
+
+    const UnicodeString *mzID;
+    StringEnumeration *mzIDs = TimeZoneNamesImpl::_getAvailableMetaZoneIDs(status);
+    if (U_SUCCESS(status)) {
+        while ((mzID = mzIDs->snext(status)) && U_SUCCESS(status)) {
+            const TZDBNames *names = TZDBTimeZoneNames::getMetaZoneNames(*mzID, status);
+            if (names == NULL) {
+                continue;
+            }
+            const UChar *std = names->getName(UTZNM_SHORT_STANDARD);
+            const UChar *dst = names->getName(UTZNM_SHORT_DAYLIGHT);
+            if (std == NULL && dst == NULL) {
+                continue;
+            }
+            int32_t numRegions = 0;
+            const char **parseRegions = names->getParseRegions(numRegions);
+
+            // The tz database contains a few zones sharing a
+            // same name for both standard time and daylight saving
+            // time. For example, Australia/Sydney observes DST,
+            // but "EST" is used for both standard and daylight.
+            // we need to store the information for later processing.
+            UBool ambiguousType = (std != NULL && dst != NULL && u_strcmp(std, dst) == 0);
+
+            const UChar *uMzID = ZoneMeta::findMetaZoneID(*mzID);
+            if (std != NULL) {
+                TZDBNameInfo *stdInf = (TZDBNameInfo *)uprv_malloc(sizeof(TZDBNameInfo));
+                if (stdInf == NULL) {
+                    status = U_MEMORY_ALLOCATION_ERROR;
+                    break;
+                }
+                stdInf->mzID = uMzID;
+                stdInf->type = UTZNM_SHORT_STANDARD;
+                stdInf->ambiguousType = ambiguousType;
+                stdInf->parseRegions = parseRegions;
+                stdInf->nRegions = numRegions;
+                gTZDBNamesTrie->put(std, stdInf, status);
+            }
+            if (U_SUCCESS(status) && dst != NULL) {
+                TZDBNameInfo *dstInf = (TZDBNameInfo *)uprv_malloc(sizeof(TZDBNameInfo));
+                if (dstInf == NULL) {
+                    status = U_MEMORY_ALLOCATION_ERROR;
+                    break;
+                }
+                dstInf->mzID = uMzID;
+                dstInf->type = UTZNM_SHORT_DAYLIGHT;
+                dstInf->ambiguousType = ambiguousType;
+                dstInf->parseRegions = parseRegions;
+                dstInf->nRegions = numRegions;
+                gTZDBNamesTrie->put(dst, dstInf, status);
+            }
+        }
+    }
+    delete mzIDs;
+
+    if (U_FAILURE(status)) {
+        delete gTZDBNamesTrie;
+        gTZDBNamesTrie = NULL;
+        return;
+    }
+
+    ucln_i18n_registerCleanup(UCLN_I18N_TZDBTIMEZONENAMES, tzdbTimeZoneNames_cleanup);
+}
+
+U_CDECL_END
+
+TZDBTimeZoneNames::TZDBTimeZoneNames(const Locale& locale)
+: fLocale(locale) {
+    UBool useWorld = TRUE;
+    const char* region = fLocale.getCountry();
+    int32_t regionLen = uprv_strlen(region);
+    if (regionLen == 0) {
+        UErrorCode status = U_ZERO_ERROR;
+        char loc[ULOC_FULLNAME_CAPACITY];
+        uloc_addLikelySubtags(fLocale.getName(), loc, sizeof(loc), &status);
+        regionLen = uloc_getCountry(loc, fRegion, sizeof(fRegion), &status);
+        if (U_SUCCESS(status) && regionLen < (int32_t)sizeof(fRegion)) {
+            useWorld = FALSE;
+        }
+    } else if (regionLen < (int32_t)sizeof(fRegion)) {
+        uprv_strcpy(fRegion, region);
+        useWorld = FALSE;
+    }
+    if (useWorld) {
+        uprv_strcpy(fRegion, "001");
+    }
+}
+
+TZDBTimeZoneNames::~TZDBTimeZoneNames() {
+}
+
+UBool
+TZDBTimeZoneNames::operator==(const TimeZoneNames& other) const {
+    if (this == &other) {
+        return TRUE;
+    }
+    // No implementation for now
+    return FALSE;
+}
+
+TimeZoneNames*
+TZDBTimeZoneNames::clone() const {
+    return new TZDBTimeZoneNames(fLocale);
+}
+
+StringEnumeration*
+TZDBTimeZoneNames::getAvailableMetaZoneIDs(UErrorCode& status) const {
+    return TimeZoneNamesImpl::_getAvailableMetaZoneIDs(status);
+}
+
+StringEnumeration*
+TZDBTimeZoneNames::getAvailableMetaZoneIDs(const UnicodeString& tzID, UErrorCode& status) const {
+    return TimeZoneNamesImpl::_getAvailableMetaZoneIDs(tzID, status);
+}
+
+UnicodeString&
+TZDBTimeZoneNames::getMetaZoneID(const UnicodeString& tzID, UDate date, UnicodeString& mzID) const {
+    return TimeZoneNamesImpl::_getMetaZoneID(tzID, date, mzID);
+}
+
+UnicodeString&
+TZDBTimeZoneNames::getReferenceZoneID(const UnicodeString& mzID, const char* region, UnicodeString& tzID) const {
+    return TimeZoneNamesImpl::_getReferenceZoneID(mzID, region, tzID);
+}
+
+UnicodeString&
+TZDBTimeZoneNames::getMetaZoneDisplayName(const UnicodeString& mzID,
+                                          UTimeZoneNameType type,
+                                          UnicodeString& name) const {
+    name.setToBogus();
+    if (mzID.isEmpty()) {
+        return name;
+    }
+
+    UErrorCode status = U_ZERO_ERROR;
+    const TZDBNames *tzdbNames = TZDBTimeZoneNames::getMetaZoneNames(mzID, status);
+    if (U_SUCCESS(status)) {
+        const UChar *s = tzdbNames->getName(type);
+        if (s != NULL) {
+            name.setTo(TRUE, s, -1);
+        }
+    }
+
+    return name;
+}
+
+UnicodeString&
+TZDBTimeZoneNames::getTimeZoneDisplayName(const UnicodeString& /* tzID */, UTimeZoneNameType /* type */, UnicodeString& name) const {
+    // No abbreviations associated a zone directly for now.
+    name.setToBogus();
+    return name;
+}
+
+TZDBTimeZoneNames::MatchInfoCollection*
+TZDBTimeZoneNames::find(const UnicodeString& text, int32_t start, uint32_t types, UErrorCode& status) const {
+    umtx_initOnce(gTZDBNamesTrieInitOnce, &prepareFind, status);
+    if (U_FAILURE(status)) {
+        return NULL;
+    }
+
+    TZDBNameSearchHandler handler(types, fRegion);
+    gTZDBNamesTrie->search(text, start, (TextTrieMapSearchResultHandler *)&handler, status);
+    if (U_FAILURE(status)) {
+        return NULL;
+    }
+    int32_t maxLen = 0;
+    return handler.getMatches(maxLen);
+}
+
+const TZDBNames*
+TZDBTimeZoneNames::getMetaZoneNames(const UnicodeString& mzID, UErrorCode& status) {
+    umtx_initOnce(gTZDBNamesMapInitOnce, &initTZDBNamesMap, status);
+    if (U_FAILURE(status)) {
+        return NULL;
+    }
+
+    TZDBNames* tzdbNames = NULL;
+
+    UChar mzIDKey[ZID_KEY_MAX + 1];
+    mzID.extract(mzIDKey, ZID_KEY_MAX + 1, status);
+    U_ASSERT(status == U_ZERO_ERROR);   // already checked length above
+    mzIDKey[mzID.length()] = 0;
+
+    umtx_lock(&gTZDBNamesMapLock);
+    {
+        void *cacheVal = uhash_get(gTZDBNamesMap, mzIDKey);
+        if (cacheVal == NULL) {
+            UResourceBundle *zoneStringsRes = ures_openDirect(U_ICUDATA_ZONE, "tzdbNames", &status);
+            zoneStringsRes = ures_getByKey(zoneStringsRes, gZoneStrings, zoneStringsRes, &status);
+            if (U_SUCCESS(status)) {
+                char key[ZID_KEY_MAX + 1];
+                mergeTimeZoneKey(mzID, key);
+                tzdbNames = TZDBNames::createInstance(zoneStringsRes, key);
+
+                if (tzdbNames == NULL) {
+                    cacheVal = (void *)EMPTY;
+                } else {
+                    cacheVal = tzdbNames;
+                }
+                // Use the persistent ID as the resource key, so we can
+                // avoid duplications.
+                const UChar* newKey = ZoneMeta::findMetaZoneID(mzID);
+                if (newKey != NULL) {
+                    uhash_put(gTZDBNamesMap, (void *)newKey, cacheVal, &status);
+                    if (U_FAILURE(status)) {
+                        if (tzdbNames != NULL) {
+                            delete tzdbNames;
+                            tzdbNames = NULL;
+                        }
+                    }
+                } else {
+                    // Should never happen with a valid input
+                    if (tzdbNames != NULL) {
+                        // It's not possible that we get a valid tzdbNames with unknown ID.
+                        // But just in case..
+                        delete tzdbNames;
+                        tzdbNames = NULL;
+                    }
+                }
+            }
+            ures_close(zoneStringsRes);
+        } else if (cacheVal != EMPTY) {
+            tzdbNames = (TZDBNames *)cacheVal;
+        }
+    }
+    umtx_unlock(&gTZDBNamesMapLock);
+
+    return tzdbNames;
+}
+
 U_NAMESPACE_END