]> git.saurik.com Git - apple/icu.git/blobdiff - icuSources/i18n/tznames_impl.cpp
ICU-64260.0.1.tar.gz
[apple/icu.git] / icuSources / i18n / tznames_impl.cpp
index a699ec8c0b4ead79e2d7735aa72feae60c4d2446..3ebbc115594eb71a1f8c5ffc0d758c274b1e62da 100644 (file)
@@ -1,6 +1,8 @@
+// © 2016 and later: Unicode, Inc. and others.
+// License & terms of use: http://www.unicode.org/copyright.html
 /*
 *******************************************************************************
-* Copyright (C) 2011-2012, International Business Machines Corporation and
+* Copyright (C) 2011-2016, International Business Machines Corporation and
 * others. All Rights Reserved.
 *******************************************************************************
 *
 
 #if !UCONFIG_NO_FORMATTING
 
+#include "unicode/strenum.h"
 #include "unicode/ustring.h"
 #include "unicode/timezone.h"
+#include "unicode/utf16.h"
 
 #include "tznames_impl.h"
 #include "cmemory.h"
 #include "cstring.h"
 #include "uassert.h"
+#include "mutex.h"
+#include "resource.h"
 #include "uresimp.h"
 #include "ureslocs.h"
 #include "zonemeta.h"
@@ -27,7 +33,6 @@
 #include "uvector.h"
 #include "olsontz.h"
 
-
 U_NAMESPACE_BEGIN
 
 #define ZID_KEY_MAX  128
@@ -36,19 +41,77 @@ U_NAMESPACE_BEGIN
 static const char gZoneStrings[]        = "zoneStrings";
 static const char gMZPrefix[]           = "meta:";
 
-static const char* KEYS[]               = {"lg", "ls", "ld", "sg", "ss", "sd"};
-static const int32_t KEYS_SIZE = (sizeof KEYS / sizeof KEYS[0]);
+static const char EMPTY[]               = "<empty>";   // place holder for empty ZNames
+static const char DUMMY_LOADER[]        = "<dummy>";   // place holder for dummy ZNamesLoader
+static const UChar NO_NAME[]            = { 0 };   // for empty no-fallback time zone names
+
+// stuff for TZDBTimeZoneNames
+static const char* TZDBNAMES_KEYS[]               = {"ss", "sd"};
+static const int32_t TZDBNAMES_KEYS_SIZE = UPRV_LENGTHOF(TZDBNAMES_KEYS);
+
+static UMutex *gDataMutex() {
+    static UMutex *m = STATIC_NEW(UMutex);
+    return m;
+}
+
+static UHashtable* gTZDBNamesMap = NULL;
+static icu::UInitOnce gTZDBNamesMapInitOnce = U_INITONCE_INITIALIZER;
+
+static TextTrieMap* gTZDBNamesTrie = NULL;
+static icu::UInitOnce gTZDBNamesTrieInitOnce = U_INITONCE_INITIALIZER;
+
+// The order in which strings are stored may be different than the order in the public enum.
+enum UTimeZoneNameTypeIndex {
+    UTZNM_INDEX_UNKNOWN = -1,
+    UTZNM_INDEX_EXEMPLAR_LOCATION,
+    UTZNM_INDEX_LONG_GENERIC,
+    UTZNM_INDEX_LONG_STANDARD,
+    UTZNM_INDEX_LONG_DAYLIGHT,
+    UTZNM_INDEX_SHORT_GENERIC,
+    UTZNM_INDEX_SHORT_STANDARD,
+    UTZNM_INDEX_SHORT_DAYLIGHT,
+    UTZNM_INDEX_COUNT
+};
+static const UChar* const EMPTY_NAMES[UTZNM_INDEX_COUNT] = {0,0,0,0,0,0,0};
+
+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();
 
-static const char gEcTag[]              = "ec";
+    return TRUE;
+}
+U_CDECL_END
 
-static const char EMPTY[]               = "<empty>";   // place holder for empty ZNames/TZNames
+/**
+ * ZNameInfo stores zone name information in the trie
+ */
+struct ZNameInfo {
+    UTimeZoneNameType   type;
+    const UChar*        tzID;
+    const UChar*        mzID;
+};
 
-static const UTimeZoneNameType ALL_NAME_TYPES[] = {
-    UTZNM_LONG_GENERIC, UTZNM_LONG_STANDARD, UTZNM_LONG_DAYLIGHT,
-    UTZNM_SHORT_GENERIC, UTZNM_SHORT_STANDARD, UTZNM_SHORT_DAYLIGHT,
-    UTZNM_UNKNOWN // unknown as the last one
+/**
+ * ZMatchInfo stores zone name match information used by find method
+ */
+struct ZMatchInfo {
+    const ZNameInfo*    znameInfo;
+    int32_t             matchLength;
 };
 
+// Helper functions
+static void mergeTimeZoneKey(const UnicodeString& mzID, char* result);
+
 #define DEFAULT_CHARACTERNODE_CAPACITY 1
 
 // ---------------------------------------------------
@@ -150,7 +213,7 @@ TextTrieMap::put(const UnicodeString &key, void *value, ZNStringPool &sp, UError
     put(s, value, status);
 }
 
-// This method is for designed for a persistent key, such as string key stored in
+// This method is designed for a persistent key, such as string key stored in
 // resource bundle.
 void
 TextTrieMap::put(const UChar *key, void *value, UErrorCode &status) {
@@ -162,11 +225,22 @@ TextTrieMap::put(const UChar *key, void *value, UErrorCode &status) {
         }
     }
     if (U_FAILURE(status)) {
+        if (fValueDeleter) {
+            fValueDeleter((void*) key);
+        }
         return;
     }
     U_ASSERT(fLazyContents != NULL);
+
     UChar *s = const_cast<UChar *>(key);
     fLazyContents->addElement(s, status);
+    if (U_FAILURE(status)) {
+        if (fValueDeleter) {
+            fValueDeleter((void*) key);
+        }
+        return;
+    }
+
     fLazyContents->addElement(value, status);
 }
 
@@ -175,6 +249,10 @@ TextTrieMap::putImpl(const UnicodeString &key, void *value, UErrorCode &status)
     if (fNodes == NULL) {
         fNodesCapacity = 512;
         fNodes = (CharacterNode *)uprv_malloc(fNodesCapacity * sizeof(CharacterNode));
+        if (fNodes == NULL) {
+            status = U_MEMORY_ALLOCATION_ERROR;
+            return;
+        }
         fNodes[0].clear();  // Init root node.
         fNodesCount = 1;
     }
@@ -281,15 +359,12 @@ TextTrieMap::getChildNode(CharacterNode *parent, UChar c) const {
     return NULL;
 }
 
-// Mutex for protecting the lazy creation of the Trie node structure on the first call to search().
-static UMTX TextTrieMutex;
 
 // buildTrie() - The Trie node structure is needed.  Create it from the data that was
 //               saved at the time the ZoneStringFormatter was created.  The Trie is only
 //               needed for parsing operations, which are less common than formatting,
 //               and the Trie is big, which is why its creation is deferred until first use.
 void TextTrieMap::buildTrie(UErrorCode &status) {
-    umtx_lock(&TextTrieMutex);
     if (fLazyContents != NULL) {
         for (int32_t i=0; i<fLazyContents->size(); i+=2) {
             const UChar *key = (UChar *)fLazyContents->elementAt(i);
@@ -300,17 +375,26 @@ void TextTrieMap::buildTrie(UErrorCode &status) {
         delete fLazyContents;
         fLazyContents = NULL; 
     }
-    umtx_unlock(&TextTrieMutex);
 }
 
 void
 TextTrieMap::search(const UnicodeString &text, int32_t start,
                   TextTrieMapSearchResultHandler *handler, UErrorCode &status) const {
-    UBool trieNeedsInitialization = FALSE;
-    UMTX_CHECK(&TextTrieMutex, fLazyContents != NULL, trieNeedsInitialization);
-    if (trieNeedsInitialization) {
-        TextTrieMap *nonConstThis = const_cast<TextTrieMap *>(this);
-        nonConstThis->buildTrie(status);
+    {
+        // TODO: if locking the mutex for each check proves to be a performance problem,
+        //       add a flag of type atomic_int32_t to class TextTrieMap, and use only
+        //       the ICU atomic safe functions for assigning and testing.
+        //       Don't test the pointer fLazyContents.
+        //       Don't do unless it's really required.
+
+        // Mutex for protecting the lazy creation of the Trie node structure on the first call to search().
+        static UMutex *TextTrieMutex = STATIC_NEW(UMutex);
+
+        Mutex lock(TextTrieMutex);
+        if (fLazyContents != NULL) {
+            TextTrieMap *nonConstThis = const_cast<TextTrieMap *>(this);
+            nonConstThis->buildTrie(status);
+        }
     }
     if (fNodes == NULL) {
         return;
@@ -332,25 +416,29 @@ TextTrieMap::search(CharacterNode *node, const UnicodeString &text, int32_t star
             return;
         }
     }
-    UChar32 c = text.char32At(index);
     if (fIgnoreCase) {
-        // size of character may grow after fold operation
-        UnicodeString tmp(c);
+        // for folding we need to get a complete code point.
+        // size of character may grow after fold operation;
+        // then we need to get result as UTF16 code units.
+        UChar32 c32 = text.char32At(index);
+        index += U16_LENGTH(c32);
+        UnicodeString tmp(c32);
         tmp.foldCase();
         int32_t tmpidx = 0;
         while (tmpidx < tmp.length()) {
-            c = tmp.char32At(tmpidx);
+            UChar c = tmp.charAt(tmpidx++);
             node = getChildNode(node, c);
             if (node == NULL) {
                 break;
             }
-            tmpidx = tmp.moveIndex32(tmpidx, 1);
         }
     } else {
+        // here we just get the next UTF16 code unit
+        UChar c = text.charAt(index++);
         node = getChildNode(node, c);
     }
     if (node != NULL) {
-        search(node, text, start, index+1, handler, status);
+        search(node, text, start, index, handler, status);
     }
 }
 
@@ -481,163 +569,283 @@ void ZNStringPool::freeze() {
 }
 
 
-// ---------------------------------------------------
-// ZNames - names common for time zone and meta zone
-// ---------------------------------------------------
+/**
+ * This class stores name data for a meta zone or time zone.
+ */
 class ZNames : public UMemory {
-public:
-    virtual ~ZNames();
+private:
+    friend class TimeZoneNamesImpl;
+
+    static UTimeZoneNameTypeIndex getTZNameTypeIndex(UTimeZoneNameType type) {
+        switch(type) {
+        case UTZNM_EXEMPLAR_LOCATION: return UTZNM_INDEX_EXEMPLAR_LOCATION;
+        case UTZNM_LONG_GENERIC: return UTZNM_INDEX_LONG_GENERIC;
+        case UTZNM_LONG_STANDARD: return UTZNM_INDEX_LONG_STANDARD;
+        case UTZNM_LONG_DAYLIGHT: return UTZNM_INDEX_LONG_DAYLIGHT;
+        case UTZNM_SHORT_GENERIC: return UTZNM_INDEX_SHORT_GENERIC;
+        case UTZNM_SHORT_STANDARD: return UTZNM_INDEX_SHORT_STANDARD;
+        case UTZNM_SHORT_DAYLIGHT: return UTZNM_INDEX_SHORT_DAYLIGHT;
+        default: return UTZNM_INDEX_UNKNOWN;
+        }
+    }
+    static UTimeZoneNameType getTZNameType(UTimeZoneNameTypeIndex index) {
+        switch(index) {
+        case UTZNM_INDEX_EXEMPLAR_LOCATION: return UTZNM_EXEMPLAR_LOCATION;
+        case UTZNM_INDEX_LONG_GENERIC: return UTZNM_LONG_GENERIC;
+        case UTZNM_INDEX_LONG_STANDARD: return UTZNM_LONG_STANDARD;
+        case UTZNM_INDEX_LONG_DAYLIGHT: return UTZNM_LONG_DAYLIGHT;
+        case UTZNM_INDEX_SHORT_GENERIC: return UTZNM_SHORT_GENERIC;
+        case UTZNM_INDEX_SHORT_STANDARD: return UTZNM_SHORT_STANDARD;
+        case UTZNM_INDEX_SHORT_DAYLIGHT: return UTZNM_SHORT_DAYLIGHT;
+        default: return UTZNM_UNKNOWN;
+        }
+    }
 
-    static ZNames* createInstance(UResourceBundle* rb, const char* key);
-    const UChar* getName(UTimeZoneNameType type);
+    const UChar* fNames[UTZNM_INDEX_COUNT];
+    UBool fDidAddIntoTrie;
 
-protected:
-    ZNames(const UChar** names);
-    static const UChar** loadData(UResourceBundle* rb, const char* key);
+    // Whether we own the location string, if computed rather than loaded from a bundle.
+    // A meta zone names instance never has an exemplar location string.
+    UBool fOwnsLocationName;
+
+    ZNames(const UChar* names[], const UChar* locationName)
+            : fDidAddIntoTrie(FALSE) {
+        uprv_memcpy(fNames, names, sizeof(fNames));
+        if (locationName != NULL) {
+            fOwnsLocationName = TRUE;
+            fNames[UTZNM_INDEX_EXEMPLAR_LOCATION] = locationName;
+        } else {
+            fOwnsLocationName = FALSE;
+        }
+    }
+
+public:
+    ~ZNames() {
+        if (fOwnsLocationName) {
+            const UChar* locationName = fNames[UTZNM_INDEX_EXEMPLAR_LOCATION];
+            U_ASSERT(locationName != NULL);
+            uprv_free((void*) locationName);
+        }
+    }
 
 private:
-    const UChar** fNames;
-};
+    static void* createMetaZoneAndPutInCache(UHashtable* cache, const UChar* names[],
+            const UnicodeString& mzID, UErrorCode& status) {
+        if (U_FAILURE(status)) { return NULL; }
+        U_ASSERT(names != NULL);
 
-ZNames::ZNames(const UChar** names)
-: fNames(names) {
-}
+        // Use the persistent ID as the resource key, so we can
+        // avoid duplications.
+        // TODO: Is there a more efficient way, like intern() in Java?
+        void* key = (void*) ZoneMeta::findMetaZoneID(mzID);
+        void* value;
+        if (uprv_memcmp(names, EMPTY_NAMES, sizeof(EMPTY_NAMES)) == 0) {
+            value = (void*) EMPTY;
+        } else {
+            value = (void*) (new ZNames(names, NULL));
+            if (value == NULL) {
+                status = U_MEMORY_ALLOCATION_ERROR;
+                return NULL;
+            }
+        }
+        uhash_put(cache, key, value, &status);
+        return value;
+    }
+
+    static void* createTimeZoneAndPutInCache(UHashtable* cache, const UChar* names[],
+            const UnicodeString& tzID, UErrorCode& status) {
+        if (U_FAILURE(status)) { return NULL; }
+        U_ASSERT(names != NULL);
+
+        // If necessary, compute the location name from the time zone name.
+        UChar* locationName = NULL;
+        if (names[UTZNM_INDEX_EXEMPLAR_LOCATION] == NULL) {
+            UnicodeString locationNameUniStr;
+            TimeZoneNamesImpl::getDefaultExemplarLocationName(tzID, locationNameUniStr);
+
+            // Copy the computed location name to the heap
+            if (locationNameUniStr.length() > 0) {
+                const UChar* buff = locationNameUniStr.getTerminatedBuffer();
+                int32_t len = sizeof(UChar) * (locationNameUniStr.length() + 1);
+                locationName = (UChar*) uprv_malloc(len);
+                if (locationName == NULL) {
+                    status = U_MEMORY_ALLOCATION_ERROR;
+                    return NULL;
+                }
+                uprv_memcpy(locationName, buff, len);
+            }
+        }
 
-ZNames::~ZNames() {
-    if (fNames != NULL) {
-        uprv_free(fNames);
+        // Use the persistent ID as the resource key, so we can
+        // avoid duplications.
+        // TODO: Is there a more efficient way, like intern() in Java?
+        void* key = (void*) ZoneMeta::findTimeZoneID(tzID);
+        void* value = (void*) (new ZNames(names, locationName));
+        if (value == NULL) {
+            status = U_MEMORY_ALLOCATION_ERROR;
+            return NULL;
+        }
+        uhash_put(cache, key, value, &status);
+        return value;
     }
-}
 
-ZNames*
-ZNames::createInstance(UResourceBundle* rb, const char* key) {
-    const UChar** names = loadData(rb, key);
-    if (names == NULL) {
-        // No names data available
-        return NULL; 
+    const UChar* getName(UTimeZoneNameType type) const {
+        UTimeZoneNameTypeIndex index = getTZNameTypeIndex(type);
+        return index >= 0 ? fNames[index] : NULL;
     }
-    return new ZNames(names);
-}
 
-const UChar*
-ZNames::getName(UTimeZoneNameType type) {
-    if (fNames == NULL) {
-        return NULL;
-    }
-    const UChar *name = NULL;
-    switch(type) {
-    case UTZNM_LONG_GENERIC:
-        name = fNames[0];
-        break;
-    case UTZNM_LONG_STANDARD:
-        name = fNames[1];
-        break;
-    case UTZNM_LONG_DAYLIGHT:
-        name = fNames[2];
-        break;
-    case UTZNM_SHORT_GENERIC:
-        name = fNames[3];
-        break;
-    case UTZNM_SHORT_STANDARD:
-        name = fNames[4];
-        break;
-    case UTZNM_SHORT_DAYLIGHT:
-        name = fNames[5];
-        break;
-    default:
-        name = NULL;
+    void addAsMetaZoneIntoTrie(const UChar* mzID, TextTrieMap& trie, UErrorCode& status) {
+        addNamesIntoTrie(mzID, NULL, trie, status);
     }
-    return name;
-}
-
-const UChar**
-ZNames::loadData(UResourceBundle* rb, const char* key) {
-    if (rb == NULL || key == NULL || *key == 0) {
-        return NULL;
+    void addAsTimeZoneIntoTrie(const UChar* tzID, TextTrieMap& trie, UErrorCode& status) {
+        addNamesIntoTrie(NULL, tzID, trie, status);
     }
 
-    UErrorCode status = U_ZERO_ERROR;
-    const UChar **names = NULL;
+    void addNamesIntoTrie(const UChar* mzID, const UChar* tzID, TextTrieMap& trie,
+            UErrorCode& status) {
+        if (U_FAILURE(status)) { return; }
+        if (fDidAddIntoTrie) { return; }
+        fDidAddIntoTrie = TRUE;
 
-    UResourceBundle* rbTable = NULL;
-    rbTable = ures_getByKeyWithFallback(rb, key, rbTable, &status);
-    if (U_SUCCESS(status)) {
-        names = (const UChar **)uprv_malloc(sizeof(const UChar*) * KEYS_SIZE);
-        if (names != NULL) {
-            UBool isEmpty = TRUE;
-            for (int32_t i = 0; i < KEYS_SIZE; i++) {
-                status = U_ZERO_ERROR;
-                int32_t len = 0;
-                const UChar *value = ures_getStringByKeyWithFallback(rbTable, KEYS[i], &len, &status);
-                if (U_FAILURE(status) || len == 0) {
-                    names[i] = NULL;
-                } else {
-                    names[i] = value;
-                    isEmpty = FALSE;
+        for (int32_t i = 0; i < UTZNM_INDEX_COUNT; i++) {
+            const UChar* name = fNames[i];
+            if (name != NULL) {
+                ZNameInfo *nameinfo = (ZNameInfo *)uprv_malloc(sizeof(ZNameInfo));
+                if (nameinfo == NULL) {
+                    status = U_MEMORY_ALLOCATION_ERROR;
+                    return;
+                }
+                nameinfo->mzID = mzID;
+                nameinfo->tzID = tzID;
+                nameinfo->type = getTZNameType((UTimeZoneNameTypeIndex)i);
+                trie.put(name, nameinfo, status); // trie.put() takes ownership of the key
+                if (U_FAILURE(status)) {
+                    return;
                 }
-            }
-            if (isEmpty) {
-                // No need to keep the names array
-                uprv_free(names);
-                names = NULL;
             }
         }
     }
-    ures_close(rbTable);
-    return names;
-}
 
-// ---------------------------------------------------
-// TZNames - names for a time zone
-// ---------------------------------------------------
-class TZNames : public ZNames {
 public:
-    virtual ~TZNames();
+    struct ZNamesLoader;
+};
 
-    static TZNames* createInstance(UResourceBundle* rb, const char* key);
-    const UChar* getLocationName(void);
+struct ZNames::ZNamesLoader : public ResourceSink {
+    const UChar *names[UTZNM_INDEX_COUNT];
 
-private:
-    TZNames(const UChar** names, const UChar* locationName);
-    const UChar* fLocationName;
-};
+    ZNamesLoader() {
+        clear();
+    }
+    virtual ~ZNamesLoader();
 
-TZNames::TZNames(const UChar** names, const UChar* locationName)
-: ZNames(names), fLocationName(locationName) {
-}
+    /** Reset for loading another set of names. */
+    void clear() {
+        uprv_memcpy(names, EMPTY_NAMES, sizeof(names));
+    }
 
-TZNames::~TZNames() {
-}
+    void loadMetaZone(const UResourceBundle* zoneStrings, const UnicodeString& mzID, UErrorCode& errorCode) {
+        if (U_FAILURE(errorCode)) { return; }
 
-const UChar*
-TZNames::getLocationName() {
-    return fLocationName;
-}
+        char key[ZID_KEY_MAX + 1];
+        mergeTimeZoneKey(mzID, key);
 
-TZNames*
-TZNames::createInstance(UResourceBundle* rb, const char* key) {
-    if (rb == NULL || key == NULL || *key == 0) {
-        return NULL;
+        loadNames(zoneStrings, key, errorCode);
     }
-    TZNames* tznames = NULL;
-    UErrorCode status = U_ZERO_ERROR;
-    UResourceBundle* rbTable = ures_getByKeyWithFallback(rb, key, NULL, &status);
-    if (U_SUCCESS(status)) {
-        int32_t len = 0;
-        const UChar* locationName = ures_getStringByKeyWithFallback(rbTable, gEcTag, &len, &status);
-        if (U_FAILURE(status) || len == 0) {
-            locationName = NULL;
+
+    void loadTimeZone(const UResourceBundle* zoneStrings, const UnicodeString& tzID, UErrorCode& errorCode) {
+        // Replace "/" with ":".
+        UnicodeString uKey(tzID);
+        for (int32_t i = 0; i < uKey.length(); i++) {
+            if (uKey.charAt(i) == (UChar)0x2F) {
+                uKey.setCharAt(i, (UChar)0x3A);
+            }
         }
 
-        const UChar** names = loadData(rb, key);
+        char key[ZID_KEY_MAX + 1];
+        uKey.extract(0, uKey.length(), key, sizeof(key), US_INV);
+
+        loadNames(zoneStrings, key, errorCode);
+    }
 
-        if (locationName != NULL || names != NULL) {
-            tznames = new TZNames(names, locationName);
+    void loadNames(const UResourceBundle* zoneStrings, const char* key, UErrorCode& errorCode) {
+        U_ASSERT(zoneStrings != NULL);
+        U_ASSERT(key != NULL);
+        U_ASSERT(key[0] != '\0');
+
+        UErrorCode localStatus = U_ZERO_ERROR;
+        clear();
+        ures_getAllItemsWithFallback(zoneStrings, key, *this, localStatus);
+
+        // Ignore errors, but propogate possible warnings.
+        if (U_SUCCESS(localStatus)) {
+            errorCode = localStatus;
         }
     }
-    ures_close(rbTable);
-    return tznames;
-}
+
+    void setNameIfEmpty(const char* key, const ResourceValue* value, UErrorCode& errorCode) {
+        UTimeZoneNameTypeIndex type = nameTypeFromKey(key);
+        if (type == UTZNM_INDEX_UNKNOWN) { return; }
+        if (names[type] == NULL) {
+            int32_t length;
+            // 'NO_NAME' indicates internally that this field should remain empty.  It will be
+            // replaced by 'NULL' in getNames()
+            names[type] = (value == NULL) ? NO_NAME : value->getString(length, errorCode);
+        }
+    }
+
+    virtual void put(const char* key, ResourceValue& value, UBool /*noFallback*/,
+            UErrorCode &errorCode) {
+        ResourceTable namesTable = value.getTable(errorCode);
+        if (U_FAILURE(errorCode)) { return; }
+        for (int32_t i = 0; namesTable.getKeyAndValue(i, key, value); ++i) {
+            if (value.isNoInheritanceMarker()) {
+                setNameIfEmpty(key, NULL, errorCode);
+            } else {
+                setNameIfEmpty(key, &value, errorCode);
+            }
+        }
+    }
+
+    static UTimeZoneNameTypeIndex nameTypeFromKey(const char *key) {
+        char c0, c1;
+        if ((c0 = key[0]) == 0 || (c1 = key[1]) == 0 || key[2] != 0) {
+            return UTZNM_INDEX_UNKNOWN;
+        }
+        if (c0 == 'l') {
+            return c1 == 'g' ? UTZNM_INDEX_LONG_GENERIC :
+                    c1 == 's' ? UTZNM_INDEX_LONG_STANDARD :
+                        c1 == 'd' ? UTZNM_INDEX_LONG_DAYLIGHT : UTZNM_INDEX_UNKNOWN;
+        } else if (c0 == 's') {
+            return c1 == 'g' ? UTZNM_INDEX_SHORT_GENERIC :
+                    c1 == 's' ? UTZNM_INDEX_SHORT_STANDARD :
+                        c1 == 'd' ? UTZNM_INDEX_SHORT_DAYLIGHT : UTZNM_INDEX_UNKNOWN;
+        } else if (c0 == 'e' && c1 == 'c') {
+            return UTZNM_INDEX_EXEMPLAR_LOCATION;
+        }
+        return UTZNM_INDEX_UNKNOWN;
+    }
+
+    /**
+    * Returns an array of names.  It is the caller's responsibility to copy the data into a
+    * permanent location, as the returned array is owned by the loader instance and may be
+    * cleared or leave scope.
+    *
+    * This is different than Java, where the array will no longer be modified and null
+    * may be returned.
+    */
+    const UChar** getNames() {
+        // Remove 'NO_NAME' references in the array and replace with 'NULL'
+        for (int32_t i = 0; i < UTZNM_INDEX_COUNT; ++i) {
+            if (names[i] == NO_NAME) {
+                names[i] = NULL;
+            }
+        }
+        return names;
+    }
+};
+
+ZNames::ZNamesLoader::~ZNamesLoader() {}
+
 
 // ---------------------------------------------------
 // The meta zone ID enumeration class
@@ -703,25 +911,6 @@ MetaZoneIDsEnumeration::~MetaZoneIDsEnumeration() {
     }
 }
 
-U_CDECL_BEGIN
-/**
- * ZNameInfo stores zone name information in the trie
- */
-typedef struct ZNameInfo {
-    UTimeZoneNameType   type;
-    const UChar*        tzID;
-    const UChar*        mzID;
-} ZNameInfo;
-
-/**
- * ZMatchInfo stores zone name match information used by find method
- */
-typedef struct ZMatchInfo {
-    const ZNameInfo*    znameInfo;
-    int32_t             matchLength;
-} ZMatchInfo;
-U_CDECL_END
-
 
 // ---------------------------------------------------
 // ZNameSearchHandler
@@ -760,7 +949,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
@@ -814,16 +1003,7 @@ U_CDECL_BEGIN
 static void U_CALLCONV
 deleteZNames(void *obj) {
     if (obj != EMPTY) {
-        delete (ZNames *)obj;
-    }
-}
-/**
- * Deleter for TZNames
- */
-static void U_CALLCONV
-deleteTZNames(void *obj) {
-    if (obj != EMPTY) {
-        delete (TZNames *)obj;
+        delete (ZNames*) obj;
     }
 }
 
@@ -839,11 +1019,11 @@ U_CDECL_END
 
 TimeZoneNamesImpl::TimeZoneNamesImpl(const Locale& locale, UErrorCode& status)
 : fLocale(locale),
-  fLock(NULL),
   fZoneStrings(NULL),
   fTZNamesMap(NULL),
   fMZNamesMap(NULL),
   fNamesTrieFullyLoaded(FALSE),
+  fNamesFullyLoaded(FALSE),
   fNamesTrie(TRUE, deleteZNameInfo) {
     initialize(locale, status);
 }
@@ -873,14 +1053,14 @@ TimeZoneNamesImpl::initialize(const Locale& locale, UErrorCode& status) {
     }
 
     uhash_setValueDeleter(fMZNamesMap, deleteZNames);
-    uhash_setValueDeleter(fTZNamesMap, deleteTZNames);
+    uhash_setValueDeleter(fTZNamesMap, deleteZNames);
     // no key deleters for name maps
 
     // preload zone strings for the default zone
     TimeZone *tz = TimeZone::createDefault();
     const UChar *tzID = ZoneMeta::getCanonicalCLDRID(*tz);
     if (tzID != NULL) {
-        loadStrings(UnicodeString(tzID));
+        loadStrings(UnicodeString(tzID), status);
     }
     delete tz;
 
@@ -892,26 +1072,20 @@ TimeZoneNamesImpl::initialize(const Locale& locale, UErrorCode& status) {
  * except initializer.
  */
 void
-TimeZoneNamesImpl::loadStrings(const UnicodeString& tzCanonicalID) {
-    loadTimeZoneNames(tzCanonicalID);
+TimeZoneNamesImpl::loadStrings(const UnicodeString& tzCanonicalID, UErrorCode& status) {
+    loadTimeZoneNames(tzCanonicalID, status);
+    LocalPointer<StringEnumeration> mzIDs(getAvailableMetaZoneIDs(tzCanonicalID, status));
+    if (U_FAILURE(status)) { return; }
+    U_ASSERT(!mzIDs.isNull());
 
-    UErrorCode status = U_ZERO_ERROR;
-    StringEnumeration *mzIDs = getAvailableMetaZoneIDs(tzCanonicalID, status);
-    if (U_SUCCESS(status) && mzIDs != NULL) {
-        const UnicodeString *mzID;
-        while ((mzID = mzIDs->snext(status))) {
-            if (U_FAILURE(status)) {
-                break;
-            }
-            loadMetaZoneNames(*mzID);
-        }
-        delete mzIDs;
+    const UnicodeString *mzID;
+    while (((mzID = mzIDs->snext(status)) != NULL) && U_SUCCESS(status)) {
+        loadMetaZoneNames(*mzID, status);
     }
 }
 
 TimeZoneNamesImpl::~TimeZoneNamesImpl() {
     cleanup();
-    umtx_destroy(&fLock);
 }
 
 void
@@ -947,6 +1121,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;
     }
@@ -959,6 +1139,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;
     }
@@ -993,12 +1179,24 @@ 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;
 }
@@ -1015,11 +1213,12 @@ TimeZoneNamesImpl::getMetaZoneDisplayName(const UnicodeString& mzID,
     ZNames *znames = NULL;
     TimeZoneNamesImpl *nonConstThis = const_cast<TimeZoneNamesImpl *>(this);
 
-    umtx_lock(&nonConstThis->fLock);
     {
-        znames = nonConstThis->loadMetaZoneNames(mzID);
+        Mutex lock(gDataMutex());
+        UErrorCode status = U_ZERO_ERROR;
+        znames = nonConstThis->loadMetaZoneNames(mzID, status);
+        if (U_FAILURE(status)) { return name; }
     }
-    umtx_unlock(&nonConstThis->fLock);
 
     if (znames != NULL) {
         const UChar* s = znames->getName(type);
@@ -1037,14 +1236,15 @@ TimeZoneNamesImpl::getTimeZoneDisplayName(const UnicodeString& tzID, UTimeZoneNa
         return name;
     }
 
-    TZNames *tznames = NULL;
+    ZNames *tznames = NULL;
     TimeZoneNamesImpl *nonConstThis = const_cast<TimeZoneNamesImpl *>(this);
 
-    umtx_lock(&nonConstThis->fLock);
     {
-        tznames = nonConstThis->loadTimeZoneNames(tzID);
+        Mutex lock(gDataMutex());
+        UErrorCode status = U_ZERO_ERROR;
+        tznames = nonConstThis->loadTimeZoneNames(tzID, status);
+        if (U_FAILURE(status)) { return name; }
     }
-    umtx_unlock(&nonConstThis->fLock);
 
     if (tznames != NULL) {
         const UChar *s = tznames->getName(type);
@@ -1057,25 +1257,26 @@ TimeZoneNamesImpl::getTimeZoneDisplayName(const UnicodeString& tzID, UTimeZoneNa
 
 UnicodeString&
 TimeZoneNamesImpl::getExemplarLocationName(const UnicodeString& tzID, UnicodeString& name) const {
+    name.setToBogus();  // cleanup result.
     const UChar* locName = NULL;
-    TZNames *tznames = NULL;
+    ZNames *tznames = NULL;
     TimeZoneNamesImpl *nonConstThis = const_cast<TimeZoneNamesImpl *>(this);
 
-    umtx_lock(&nonConstThis->fLock);
     {
-        tznames = nonConstThis->loadTimeZoneNames(tzID);
+        Mutex lock(gDataMutex());
+        UErrorCode status = U_ZERO_ERROR;
+        tznames = nonConstThis->loadTimeZoneNames(tzID, status);
+        if (U_FAILURE(status)) { return name; }
     }
-    umtx_unlock(&nonConstThis->fLock);
 
     if (tznames != NULL) {
-        locName = tznames->getLocationName();
+        locName = tznames->getName(UTZNM_EXEMPLAR_LOCATION);
     }
     if (locName != NULL) {
         name.setTo(TRUE, locName, -1);
-        return name;
     }
 
-    return TimeZoneNames::getExemplarLocationName(tzID, name);
+    return name;
 }
 
 
@@ -1088,7 +1289,7 @@ static void mergeTimeZoneKey(const UnicodeString& mzID, char* result) {
 
     char mzIdChar[ZID_KEY_MAX + 1];
     int32_t keyLen;
-    int32_t prefixLen = uprv_strlen(gMZPrefix);
+    int32_t prefixLen = static_cast<int32_t>(uprv_strlen(gMZPrefix));
     keyLen = mzID.extract(0, mzID.length(), mzIdChar, ZID_KEY_MAX + 1, US_INV);
     uprv_memcpy((void *)result, (void *)gMZPrefix, prefixLen);
     uprv_memcpy((void *)(result + prefixLen), (void *)mzIdChar, keyLen);
@@ -1099,211 +1300,1000 @@ static void mergeTimeZoneKey(const UnicodeString& mzID, char* result) {
  * This method updates the cache and must be called with a lock
  */
 ZNames*
-TimeZoneNamesImpl::loadMetaZoneNames(const UnicodeString& mzID) {
-    if (mzID.length() > (ZID_KEY_MAX - MZ_PREFIX_LEN)) {
-        return NULL;
-    }
-
-    ZNames *znames = NULL;
+TimeZoneNamesImpl::loadMetaZoneNames(const UnicodeString& mzID, UErrorCode& status) {
+    if (U_FAILURE(status)) { return NULL; }
+    U_ASSERT(mzID.length() <= ZID_KEY_MAX - MZ_PREFIX_LEN);
 
-    UErrorCode status = U_ZERO_ERROR;
     UChar mzIDKey[ZID_KEY_MAX + 1];
     mzID.extract(mzIDKey, ZID_KEY_MAX + 1, status);
-    U_ASSERT(status == U_ZERO_ERROR);   // already checked length above
+    U_ASSERT(U_SUCCESS(status));   // already checked length above
     mzIDKey[mzID.length()] = 0;
 
-    void *cacheVal = uhash_get(fMZNamesMap, mzIDKey);
-    if (cacheVal == NULL) {
-        char key[ZID_KEY_MAX + 1];
-        mergeTimeZoneKey(mzID, key);
-        znames = ZNames::createInstance(fZoneStrings, key);
-
-        if (znames == NULL) {
-            cacheVal = (void *)EMPTY;
-        } else {
-            cacheVal = znames;
-        }
-        // Use the persistent ID as the resource key, so we can
-        // avoid duplications.
-        const UChar* newKey = ZoneMeta::findMetaZoneID(mzID);
-        if (newKey != NULL) {
-            uhash_put(fMZNamesMap, (void *)newKey, cacheVal, &status);
-            if (U_FAILURE(status)) {
-                if (znames != NULL) {
-                    delete znames;
-                }
-            } else if (znames != NULL) {
-                // put the name info into the trie
-                for (int32_t i = 0; ALL_NAME_TYPES[i] != UTZNM_UNKNOWN; i++) {
-                    const UChar* name = znames->getName(ALL_NAME_TYPES[i]);
-                    if (name != NULL) {
-                        ZNameInfo *nameinfo = (ZNameInfo *)uprv_malloc(sizeof(ZNameInfo));
-                        if (nameinfo != NULL) {
-                            nameinfo->type = ALL_NAME_TYPES[i];
-                            nameinfo->tzID = NULL;
-                            nameinfo->mzID = newKey;
-                            fNamesTrie.put(name, nameinfo, status);
-                        }
-                    }
-                }
-            }
-
-        } else {
-            // Should never happen with a valid input
-            if (znames != NULL) {
-                // It's not possible that we get a valid ZNames with unknown ID.
-                // But just in case..
-                delete znames;
-                znames = NULL;
-            }
-        }
-    } else if (cacheVal != EMPTY) {
-        znames = (ZNames *)cacheVal;
+    void* mznames = uhash_get(fMZNamesMap, mzIDKey);
+    if (mznames == NULL) {
+        ZNames::ZNamesLoader loader;
+        loader.loadMetaZone(fZoneStrings, mzID, status);
+        mznames = ZNames::createMetaZoneAndPutInCache(fMZNamesMap, loader.getNames(), mzID, status);
+        if (U_FAILURE(status)) { return NULL; }
     }
 
-    return znames;
+    if (mznames != EMPTY) {
+        return (ZNames*)mznames;
+    } else {
+        return NULL;
+    }
 }
 
 /*
  * This method updates the cache and must be called with a lock
  */
-TZNames*
-TimeZoneNamesImpl::loadTimeZoneNames(const UnicodeString& tzID) {
-    if (tzID.length() > ZID_KEY_MAX) {
-        return NULL;
-    }
-
-    TZNames *tznames = NULL;
+ZNames*
+TimeZoneNamesImpl::loadTimeZoneNames(const UnicodeString& tzID, UErrorCode& status) {
+    if (U_FAILURE(status)) { return NULL; }
+    U_ASSERT(tzID.length() <= ZID_KEY_MAX);
 
-    UErrorCode status = U_ZERO_ERROR;
     UChar tzIDKey[ZID_KEY_MAX + 1];
     int32_t tzIDKeyLen = tzID.extract(tzIDKey, ZID_KEY_MAX + 1, status);
-    U_ASSERT(status == U_ZERO_ERROR);   // already checked length above
+    U_ASSERT(U_SUCCESS(status));   // already checked length above
     tzIDKey[tzIDKeyLen] = 0;
 
-    void *cacheVal = uhash_get(fTZNamesMap, tzIDKey);
-    if (cacheVal == NULL) {
-        char key[ZID_KEY_MAX + 1];
-        UErrorCode status = U_ZERO_ERROR;
-        // Replace "/" with ":".
-        UnicodeString uKey(tzID);
-        for (int32_t i = 0; i < uKey.length(); i++) {
-            if (uKey.charAt(i) == (UChar)0x2F) {
-                uKey.setCharAt(i, (UChar)0x3A);
-            }
+    void *tznames = uhash_get(fTZNamesMap, tzIDKey);
+    if (tznames == NULL) {
+        ZNames::ZNamesLoader loader;
+        loader.loadTimeZone(fZoneStrings, tzID, status);
+        tznames = ZNames::createTimeZoneAndPutInCache(fTZNamesMap, loader.getNames(), tzID, status);
+        if (U_FAILURE(status)) { return NULL; }
+    }
+
+    // tznames is never EMPTY
+    return (ZNames*)tznames;
+}
+
+TimeZoneNames::MatchInfoCollection*
+TimeZoneNamesImpl::find(const UnicodeString& text, int32_t start, uint32_t types, UErrorCode& status) const {
+    ZNameSearchHandler handler(types);
+    TimeZoneNames::MatchInfoCollection* matches;
+    TimeZoneNamesImpl* nonConstThis = const_cast<TimeZoneNamesImpl*>(this);
+
+    // Synchronize so that data is not loaded multiple times.
+    // TODO: Consider more fine-grained synchronization.
+    {
+        Mutex lock(gDataMutex());
+
+        // First try of lookup.
+        matches = doFind(handler, text, start, status);
+        if (U_FAILURE(status)) { return NULL; }
+        if (matches != NULL) {
+            return matches;
         }
-        uKey.extract(0, uKey.length(), key, sizeof(key), US_INV);
-        tznames = TZNames::createInstance(fZoneStrings, key);
 
-        if (tznames == NULL) {
-            cacheVal = (void *)EMPTY;
-        } else {
-            cacheVal = tznames;
+        // All names are not yet loaded into the trie.
+        // We may have loaded names for formatting several time zones,
+        // and might be parsing one of those.
+        // Populate the parsing trie from all of the already-loaded names.
+        nonConstThis->addAllNamesIntoTrie(status);
+
+        // Second try of lookup.
+        matches = doFind(handler, text, start, status);
+        if (U_FAILURE(status)) { return NULL; }
+        if (matches != NULL) {
+            return matches;
         }
-        // Use the persistent ID as the resource key, so we can
-        // avoid duplications.
-        const UChar* newKey = ZoneMeta::findTimeZoneID(tzID);
-        if (newKey != NULL) {
-            uhash_put(fTZNamesMap, (void *)newKey, cacheVal, &status);
-            if (U_FAILURE(status)) {
-                if (tznames != NULL) {
-                    delete tznames;
+
+        // There are still some names we haven't loaded into the trie yet.
+        // Load everything now.
+        nonConstThis->internalLoadAllDisplayNames(status);
+        nonConstThis->addAllNamesIntoTrie(status);
+        nonConstThis->fNamesTrieFullyLoaded = TRUE;
+        if (U_FAILURE(status)) { return NULL; }
+
+        // Third try: we must return this one.
+        return doFind(handler, text, start, status);
+    }
+}
+
+TimeZoneNames::MatchInfoCollection*
+TimeZoneNamesImpl::doFind(ZNameSearchHandler& handler,
+        const UnicodeString& text, int32_t start, UErrorCode& status) const {
+
+    fNamesTrie.search(text, start, (TextTrieMapSearchResultHandler *)&handler, status);
+    if (U_FAILURE(status)) { return NULL; }
+
+    int32_t maxLen = 0;
+    TimeZoneNames::MatchInfoCollection* matches = handler.getMatches(maxLen);
+    if (matches != NULL && ((maxLen == (text.length() - start)) || fNamesTrieFullyLoaded)) {
+        // perfect match, or no more names available
+        return matches;
+    }
+    delete matches;
+    return NULL;
+}
+
+// Caller must synchronize.
+void TimeZoneNamesImpl::addAllNamesIntoTrie(UErrorCode& status) {
+    if (U_FAILURE(status)) return;
+    int32_t pos;
+    const UHashElement* element;
+
+    pos = UHASH_FIRST;
+    while ((element = uhash_nextElement(fMZNamesMap, &pos)) != NULL) {
+        if (element->value.pointer == EMPTY) { continue; }
+        UChar* mzID = (UChar*) element->key.pointer;
+        ZNames* znames = (ZNames*) element->value.pointer;
+        znames->addAsMetaZoneIntoTrie(mzID, fNamesTrie, status);
+        if (U_FAILURE(status)) { return; }
+    }
+
+    pos = UHASH_FIRST;
+    while ((element = uhash_nextElement(fTZNamesMap, &pos)) != NULL) {
+        if (element->value.pointer == EMPTY) { continue; }
+        UChar* tzID = (UChar*) element->key.pointer;
+        ZNames* znames = (ZNames*) element->value.pointer;
+        znames->addAsTimeZoneIntoTrie(tzID, fNamesTrie, status);
+        if (U_FAILURE(status)) { return; }
+    }
+}
+
+U_CDECL_BEGIN
+static void U_CALLCONV
+deleteZNamesLoader(void* obj) {
+    if (obj == DUMMY_LOADER) { return; }
+    const ZNames::ZNamesLoader* loader = (const ZNames::ZNamesLoader*) obj;
+    delete loader;
+}
+U_CDECL_END
+
+struct TimeZoneNamesImpl::ZoneStringsLoader : public ResourceSink {
+    TimeZoneNamesImpl& tzn;
+    UHashtable* keyToLoader;
+
+    ZoneStringsLoader(TimeZoneNamesImpl& _tzn, UErrorCode& status)
+            : tzn(_tzn) {
+        keyToLoader = uhash_open(uhash_hashChars, uhash_compareChars, NULL, &status);
+        if (U_FAILURE(status)) { return; }
+        uhash_setKeyDeleter(keyToLoader, uprv_free);
+        uhash_setValueDeleter(keyToLoader, deleteZNamesLoader);
+    }
+    virtual ~ZoneStringsLoader();
+
+    void* createKey(const char* key, UErrorCode& status) {
+        int32_t len = sizeof(char) * (static_cast<int32_t>(uprv_strlen(key)) + 1);
+        char* newKey = (char*) uprv_malloc(len);
+        if (newKey == NULL) {
+            status = U_MEMORY_ALLOCATION_ERROR;
+            return NULL;
+        }
+        uprv_memcpy(newKey, key, len);
+        newKey[len-1] = '\0';
+        return (void*) newKey;
+    }
+
+    UBool isMetaZone(const char* key) {
+        return (uprv_strlen(key) >= MZ_PREFIX_LEN && uprv_memcmp(key, gMZPrefix, MZ_PREFIX_LEN) == 0);
+    }
+
+    UnicodeString mzIDFromKey(const char* key) {
+        return UnicodeString(key + MZ_PREFIX_LEN, static_cast<int32_t>(uprv_strlen(key)) - MZ_PREFIX_LEN, US_INV);
+    }
+
+    UnicodeString tzIDFromKey(const char* key) {
+        UnicodeString tzID(key, -1, US_INV);
+        // Replace all colons ':' with slashes '/'
+        for (int i=0; i<tzID.length(); i++) {
+            if (tzID.charAt(i) == 0x003A) {
+                tzID.setCharAt(i, 0x002F);
+            }
+        }
+        return tzID;
+    }
+
+    void load(UErrorCode& status) {
+        ures_getAllItemsWithFallback(tzn.fZoneStrings, "", *this, status);
+        if (U_FAILURE(status)) { return; }
+
+        int32_t pos = UHASH_FIRST;
+        const UHashElement* element;
+        while ((element = uhash_nextElement(keyToLoader, &pos)) != NULL) {
+            if (element->value.pointer == DUMMY_LOADER) { continue; }
+            ZNames::ZNamesLoader* loader = (ZNames::ZNamesLoader*) element->value.pointer;
+            char* key = (char*) element->key.pointer;
+
+            if (isMetaZone(key)) {
+                UnicodeString mzID = mzIDFromKey(key);
+                ZNames::createMetaZoneAndPutInCache(tzn.fMZNamesMap, loader->getNames(), mzID, status);
+            } else {
+                UnicodeString tzID = tzIDFromKey(key);
+                ZNames::createTimeZoneAndPutInCache(tzn.fTZNamesMap, loader->getNames(), tzID, status);
+            }
+            if (U_FAILURE(status)) { return; }
+        }
+    }
+
+    void consumeNamesTable(const char *key, ResourceValue &value, UBool noFallback,
+            UErrorCode &status) {
+        if (U_FAILURE(status)) { return; }
+
+        void* loader = uhash_get(keyToLoader, key);
+        if (loader == NULL) {
+            if (isMetaZone(key)) {
+                UnicodeString mzID = mzIDFromKey(key);
+                void* cacheVal = uhash_get(tzn.fMZNamesMap, mzID.getTerminatedBuffer());
+                if (cacheVal != NULL) {
+                    // We have already loaded the names for this meta zone.
+                    loader = (void*) DUMMY_LOADER;
+                } else {
+                    loader = (void*) new ZNames::ZNamesLoader();
+                    if (loader == NULL) {
+                        status = U_MEMORY_ALLOCATION_ERROR;
+                        return;
+                    }
                 }
-            } else if (tznames != NULL) {
-                // put the name info into the trie
-                for (int32_t i = 0; ALL_NAME_TYPES[i] != UTZNM_UNKNOWN; i++) {
-                    const UChar* name = tznames->getName(ALL_NAME_TYPES[i]);
-                    if (name != NULL) {
-                        ZNameInfo *nameinfo = (ZNameInfo *)uprv_malloc(sizeof(ZNameInfo));
-                        if (nameinfo != NULL) {
-                            nameinfo->type = ALL_NAME_TYPES[i];
-                            nameinfo->tzID = newKey;
-                            nameinfo->mzID = NULL;
-                            fNamesTrie.put(name, nameinfo, status);
-                        }
+            } else {
+                UnicodeString tzID = tzIDFromKey(key);
+                void* cacheVal = uhash_get(tzn.fTZNamesMap, tzID.getTerminatedBuffer());
+                if (cacheVal != NULL) {
+                    // We have already loaded the names for this time zone.
+                    loader = (void*) DUMMY_LOADER;
+                } else {
+                    loader = (void*) new ZNames::ZNamesLoader();
+                    if (loader == NULL) {
+                        status = U_MEMORY_ALLOCATION_ERROR;
+                        return;
                     }
                 }
             }
-        } else {
-            // Should never happen with a valid input
-            if (tznames != NULL) {
-                // It's not possible that we get a valid TZNames with unknown ID.
-                // But just in case..
-                delete tznames;
-                tznames = NULL;
+
+            void* newKey = createKey(key, status);
+            if (U_FAILURE(status)) {
+                deleteZNamesLoader(loader);
+                return;
+            }
+
+            uhash_put(keyToLoader, newKey, loader, &status);
+            if (U_FAILURE(status)) { return; }
+        }
+
+        if (loader != DUMMY_LOADER) {
+            // Let the ZNamesLoader consume the names table.
+            ((ZNames::ZNamesLoader*)loader)->put(key, value, noFallback, status);
+        }
+    }
+
+    virtual void put(const char *key, ResourceValue &value, UBool noFallback,
+            UErrorCode &status) {
+        ResourceTable timeZonesTable = value.getTable(status);
+        if (U_FAILURE(status)) { return; }
+        for (int32_t i = 0; timeZonesTable.getKeyAndValue(i, key, value); ++i) {
+            U_ASSERT(!value.isNoInheritanceMarker());
+            if (value.getType() == URES_TABLE) {
+                consumeNamesTable(key, value, noFallback, status);
+            } else {
+                // Ignore fields that aren't tables (e.g., fallbackFormat and regionFormatStandard).
+                // All time zone fields are tables.
             }
+            if (U_FAILURE(status)) { return; }
         }
-    } else if (cacheVal != EMPTY) {
-        tznames = (TZNames *)cacheVal;
     }
+};
 
-    return tznames;
+// Virtual destructors must be defined out of line.
+TimeZoneNamesImpl::ZoneStringsLoader::~ZoneStringsLoader() {
+    uhash_close(keyToLoader);
 }
 
-TimeZoneNames::MatchInfoCollection*
-TimeZoneNamesImpl::find(const UnicodeString& text, int32_t start, uint32_t types, UErrorCode& status) const {
-    ZNameSearchHandler handler(types);
+void TimeZoneNamesImpl::loadAllDisplayNames(UErrorCode& status) {
+    if (U_FAILURE(status)) return;
 
-    TimeZoneNamesImpl *nonConstThis = const_cast<TimeZoneNamesImpl *>(this);
+    {
+        Mutex lock(gDataMutex());
+        internalLoadAllDisplayNames(status);
+    }
+}
 
-    umtx_lock(&nonConstThis->fLock);
+void TimeZoneNamesImpl::getDisplayNames(const UnicodeString& tzID,
+        const UTimeZoneNameType types[], int32_t numTypes,
+        UDate date, UnicodeString dest[], UErrorCode& status) const {
+    if (U_FAILURE(status)) return;
+
+    if (tzID.isEmpty()) { return; }
+    void* tznames = NULL;
+    void* mznames = NULL;
+    TimeZoneNamesImpl *nonConstThis = const_cast<TimeZoneNamesImpl*>(this);
+
+    // Load the time zone strings
     {
-        fNamesTrie.search(text, start, (TextTrieMapSearchResultHandler *)&handler, status);
+        Mutex lock(gDataMutex());
+        tznames = (void*) nonConstThis->loadTimeZoneNames(tzID, status);
+        if (U_FAILURE(status)) { return; }
+    }
+    U_ASSERT(tznames != NULL);
+
+    // Load the values into the dest array
+    for (int i = 0; i < numTypes; i++) {
+        UTimeZoneNameType type = types[i];
+        const UChar* name = ((ZNames*)tznames)->getName(type);
+        if (name == NULL) {
+            if (mznames == NULL) {
+                // Load the meta zone name
+                UnicodeString mzID;
+                getMetaZoneID(tzID, date, mzID);
+                if (mzID.isEmpty()) {
+                    mznames = (void*) EMPTY;
+                } else {
+                    // Load the meta zone strings
+                    // Mutex is scoped to the "else" statement
+                    Mutex lock(gDataMutex());
+                    mznames = (void*) nonConstThis->loadMetaZoneNames(mzID, status);
+                    if (U_FAILURE(status)) { return; }
+                    // Note: when the metazone doesn't exist, in Java, loadMetaZoneNames returns
+                    // a dummy object instead of NULL.
+                    if (mznames == NULL) {
+                        mznames = (void*) EMPTY;
+                    }
+                }
+            }
+            U_ASSERT(mznames != NULL);
+            if (mznames != EMPTY) {
+                name = ((ZNames*)mznames)->getName(type);
+            }
+        }
+        if (name != NULL) {
+            dest[i].setTo(TRUE, name, -1);
+        } else {
+            dest[i].setToBogus();
+        }
     }
-    umtx_unlock(&nonConstThis->fLock);
+}
 
-    if (U_FAILURE(status)) {
+// Caller must synchronize.
+void TimeZoneNamesImpl::internalLoadAllDisplayNames(UErrorCode& status) {
+    if (!fNamesFullyLoaded) {
+        fNamesFullyLoaded = TRUE;
+
+        ZoneStringsLoader loader(*this, status);
+        loader.load(status);
+        if (U_FAILURE(status)) { return; }
+
+        const UnicodeString *id;
+
+        // load strings for all zones
+        StringEnumeration *tzIDs = TimeZone::createTimeZoneIDEnumeration(
+            UCAL_ZONE_TYPE_CANONICAL, NULL, NULL, status);
+        if (U_SUCCESS(status)) {
+            while ((id = tzIDs->snext(status)) != NULL) {
+                if (U_FAILURE(status)) {
+                    break;
+                }
+                UnicodeString copy(*id);
+                void* value = uhash_get(fTZNamesMap, copy.getTerminatedBuffer());
+                if (value == NULL) {
+                    // loadStrings also loads related metazone strings
+                    loadStrings(*id, status);
+                }
+            }
+        }
+        if (tzIDs != NULL) {
+            delete tzIDs;
+        }
+    }
+}
+
+
+
+static const UChar gEtcPrefix[]         = { 0x45, 0x74, 0x63, 0x2F }; // "Etc/"
+static const int32_t gEtcPrefixLen      = 4;
+static const UChar gSystemVPrefix[]     = { 0x53, 0x79, 0x73, 0x74, 0x65, 0x6D, 0x56, 0x2F }; // "SystemV/
+static const int32_t gSystemVPrefixLen  = 8;
+static const UChar gRiyadh8[]           = { 0x52, 0x69, 0x79, 0x61, 0x64, 0x68, 0x38 }; // "Riyadh8"
+static const int32_t gRiyadh8Len       = 7;
+
+UnicodeString& U_EXPORT2
+TimeZoneNamesImpl::getDefaultExemplarLocationName(const UnicodeString& tzID, UnicodeString& name) {
+    if (tzID.isEmpty() || tzID.startsWith(gEtcPrefix, gEtcPrefixLen)
+        || tzID.startsWith(gSystemVPrefix, gSystemVPrefixLen) || tzID.indexOf(gRiyadh8, gRiyadh8Len, 0) > 0) {
+        name.setToBogus();
+        return name;
+    }
+
+    int32_t sep = tzID.lastIndexOf((UChar)0x2F /* '/' */);
+    if (sep > 0 && sep + 1 < tzID.length()) {
+        name.setTo(tzID, sep + 1);
+        name.findAndReplace(UnicodeString((UChar)0x5f /* _ */),
+                            UnicodeString((UChar)0x20 /* space */));
+    } else {
+        name.setToBogus();
+    }
+    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;
     }
 
-    int32_t maxLen = 0;
-    TimeZoneNames::MatchInfoCollection* matches = handler.getMatches(maxLen);
-    if (matches != NULL && ((maxLen == (text.length() - start)) || fNamesTrieFullyLoaded)) {
-        // perfect match
-        return matches;
+    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;
     }
 
-    delete matches;
+    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;
+            }
+        }
+    }
 
-    // All names are not yet loaded into the trie
-    umtx_lock(&nonConstThis->fLock);
-    {
-        if (!fNamesTrieFullyLoaded) {
-            const UnicodeString *id;
+    if (isEmpty) {
+        if (names != NULL) {
+            uprv_free(names);
+        }
+        return NULL;
+    }
 
-            // load strings for all zones
-            StringEnumeration *tzIDs = TimeZone::createTimeZoneIDEnumeration(UCAL_ZONE_TYPE_CANONICAL, NULL, NULL, status);
-            if (U_SUCCESS(status)) {
-                while ((id = tzIDs->snext(status))) {
+    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;
                     }
-                    // loadStrings also load related metazone strings
-                    nonConstThis->loadStrings(*id);
+                    u_UCharsToChars(uregion, *pRegion, len);
+                    (*pRegion)[len] = 0;
                 }
             }
-            if (tzIDs != NULL) {
-                delete tzIDs;
+        }
+    }
+    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 j = 0; j < ninfo->nRegions; j++) {
+                        const char *region = ninfo->parseRegions[j];
+                        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)) {
-                nonConstThis->fNamesTrieFullyLoaded = TRUE;
+                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)) != 0 && U_SUCCESS(status)) {
+            const TZDBNames *names = TZDBTimeZoneNames::getMetaZoneNames(*mzID, status);
+            if (U_FAILURE(status)) {
+                break;
+            }
+            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 = static_cast<int32_t>(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)) {
+        if (tzdbNames != NULL) {
+            const UChar *s = tzdbNames->getName(type);
+            if (s != NULL) {
+                name.setTo(TRUE, s, -1);
             }
         }
     }
-    umtx_unlock(&nonConstThis->fLock);
 
+    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;
     }
 
-    umtx_lock(&nonConstThis->fLock);
+    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;
+
+    static UMutex *gTZDBNamesMapLock = STATIC_NEW(UMutex);
+    umtx_lock(gTZDBNamesMapLock);
     {
-        // now try it again
-        fNamesTrie.search(text, start, (TextTrieMapSearchResultHandler *)&handler, status);
+        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.
+                // TODO: Is there a more efficient way, like intern() in Java?
+                void* newKey = (void*) ZoneMeta::findMetaZoneID(mzID);
+                if (newKey != NULL) {
+                    uhash_put(gTZDBNamesMap, 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(&nonConstThis->fLock);
+    umtx_unlock(gTZDBNamesMapLock);
 
-    return handler.getMatches(maxLen);
+    return tzdbNames;
 }
 
 U_NAMESPACE_END