]> git.saurik.com Git - apple/icu.git/blobdiff - icuSources/i18n/zonemeta.cpp
ICU-491.11.2.tar.gz
[apple/icu.git] / icuSources / i18n / zonemeta.cpp
index 4a5dcc777600ebfcad55d3303c78703f5637c4b5..e244f9c2f82463cb504e303fb1becd22c81fc552 100644 (file)
@@ -1,6 +1,6 @@
 /*
 *******************************************************************************
-* Copyright (C) 2007-2009, International Business Machines Corporation and    *
+* Copyright (C) 2007-2012, International Business Machines Corporation and    *
 * others. All Rights Reserved.                                                *
 *******************************************************************************
 */
@@ -14,6 +14,7 @@
 #include "unicode/timezone.h"
 #include "unicode/ustring.h"
 #include "unicode/putil.h"
+#include "unicode/simpletz.h"
 
 #include "umutex.h"
 #include "uvector.h"
 #include "gregoimp.h"
 #include "cstring.h"
 #include "ucln_in.h"
+#include "uassert.h"
+#include "uresimp.h"
+#include "uhash.h"
+#include "olsontz.h"
 
-// Metazone mapping tables
 static UMTX gZoneMetaLock = NULL;
-static UHashtable *gCanonicalMap = NULL;
+
+// CLDR Canonical ID mapping table
+static UHashtable *gCanonicalIDCache = NULL;
+static UBool gCanonicalIDCacheInitialized = FALSE;
+
+// Metazone mapping table
 static UHashtable *gOlsonToMeta = NULL;
-static UHashtable *gMetaToOlson = NULL;
-static UBool gCanonicalMapInitialized = FALSE;
 static UBool gOlsonToMetaInitialized = FALSE;
-static UBool gMetaToOlsonInitialized = FALSE;
-static UChar **gUStringTable = NULL;
-static int32_t gUStringCount = 0;
-static int32_t gUStringAlloc = 0;
-
-// Currently (ICU 4.1.3+), gUStringTable only contains strings allocated in the section of
-// createCanonicalMap that iterates over the enumerator created with TimeZone::createEnumeration.
-// And currently, that allocates a total of 22 strings. So USTRING_ALLOC_START is defined to
-// be adequate for that set, and USTRING_ALLOC_INCR is a reasonable expansion increment. In
-// future versions of ICU, these numbers may need adjusting to avoid excessive reallocs, or to
-// avoid allocating unused memory (but in any case the effects are small).
-#define USTRING_ALLOC_START 24
-#define USTRING_ALLOC_INCR 12
-
-U_CDECL_BEGIN
 
-// We have switched CanonicalMap to use const UChar* strings for the key and for the id field of
-// CanonicalMapEntry; that is because for the most part these now point into UChar strings in the
-// shared data file, in order to reduce process-specific dynamically-allocated memory. Consequently,
-// there is no longer a deleter for the key field, and the deleter for CanonicalMapEntry
-// no longer frees the id field. However, for the few strings that are obtained from the
-// TimeZone::createEnumeration() enumerator or from TimeZone::dereferOlsonLink instead of the
-// data file, we do need to allocate copies. In order to ensure that these strings are freed by
-// zoneMeta_cleanup(), we need to create a little memory manager for them; this is in the form of
-// a table that tracks the strings allocated for this purpose. The following three functions
-// (along with the gUStringXxxxx statics) are used to allocate and free such strings.
-
-// The following allocs space for a UChar* string of the specified length, puts a pointer to the string
-// in gUStringTable, and returns either a pointer to the allocated string space, or NULL for failure.
-static UChar * allocUStringInTable(int32_t uStringLen) {
-    UChar * uStringSpace = NULL;
-    // initialize the table if necessary
-    umtx_lock(&gZoneMetaLock);
-    if (gUStringTable == NULL) {
-        gUStringTable = (UChar**)uprv_malloc(USTRING_ALLOC_START*sizeof(UChar*));
-        if (gUStringTable != NULL) {
-            gUStringAlloc = USTRING_ALLOC_START;
-        }
-    }
-    if (gUStringTable != NULL) {
-        // expand the table if necessary
-        if (gUStringCount == gUStringAlloc) {
-            UChar ** newTable = (UChar**)uprv_realloc(gUStringTable, (gUStringAlloc+USTRING_ALLOC_INCR)*sizeof(UChar*));
-            if (newTable != NULL) {
-                gUStringTable = newTable;
-                gUStringAlloc += USTRING_ALLOC_INCR;
-            }
-        }
-        // add the string if possible
-        if (gUStringCount < gUStringAlloc) {
-            uStringSpace = (UChar*)uprv_malloc(uStringLen*sizeof(UChar));
-            if (uStringSpace != NULL) {
-                gUStringTable[gUStringCount++] = uStringSpace;
-            }
-        }
-    }
-    umtx_unlock(&gZoneMetaLock);
-    return uStringSpace;
-}
+// Available metazone IDs vector and table
+static icu::UVector *gMetaZoneIDs = NULL;
+static UHashtable *gMetaZoneIDTable = NULL;
+static UBool gMetaZoneIDsInitialized = FALSE;
 
-static void removeLastUStringFromTable(void) {
-       umtx_lock(&gZoneMetaLock);
-    if (gUStringCount > 0) {
-        free(gUStringTable[--gUStringCount]);
-    }
-    umtx_unlock(&gZoneMetaLock);
-}
+// Country info vectors
+static icu::UVector *gSingleZoneCountries = NULL;
+static icu::UVector *gMultiZonesCountries = NULL;
+static UBool gCountryInfoVectorsInitialized = FALSE;
 
-static void freeUStringTable(void) {
-    int32_t uStringCount = gUStringCount;
-    gUStringCount = 0;
-    gUStringAlloc = 0;
-    if (gUStringTable != NULL) {
-        while (uStringCount > 0) {
-            free(gUStringTable[--uStringCount]);
-        }
-        free(gUStringTable);
-        gUStringTable = NULL;
-    }
-}
+U_CDECL_BEGIN
 
 /**
  * Cleanup callback func
  */
 static UBool U_CALLCONV zoneMeta_cleanup(void)
 {
-     umtx_destroy(&gZoneMetaLock);
+    umtx_destroy(&gZoneMetaLock);
 
-    if (gCanonicalMap != NULL) {
-        uhash_close(gCanonicalMap);
-        gCanonicalMap = NULL;
+    if (gCanonicalIDCache != NULL) {
+        uhash_close(gCanonicalIDCache);
+        gCanonicalIDCache = NULL;
     }
-    gCanonicalMapInitialized = FALSE;
+    gCanonicalIDCacheInitialized = FALSE;
 
     if (gOlsonToMeta != NULL) {
         uhash_close(gOlsonToMeta);
@@ -129,13 +68,17 @@ static UBool U_CALLCONV zoneMeta_cleanup(void)
     }
     gOlsonToMetaInitialized = FALSE;
 
-    if (gMetaToOlson != NULL) {
-        uhash_close(gMetaToOlson);
-        gMetaToOlson = NULL;
+    if (gMetaZoneIDTable != NULL) {
+        uhash_close(gMetaZoneIDTable);
     }
-    gMetaToOlsonInitialized = FALSE;
-    
-    freeUStringTable();
+    // delete after closing gMetaZoneIDTable, because it holds
+    // value objects held by the hashtable
+    delete gMetaZoneIDs;
+    gMetaZoneIDsInitialized = FALSE;
+
+    delete gSingleZoneCountries;
+    delete gMultiZonesCountries;
+    gCountryInfoVectorsInitialized = FALSE;
 
     return TRUE;
 }
@@ -154,16 +97,7 @@ deleteUCharString(void *obj) {
  */
 static void U_CALLCONV
 deleteUVector(void *obj) {
-   delete (U_NAMESPACE_QUALIFIER UVector*) obj;
-}
-
-/**
- * Deleter for CanonicalMapEntry
- */
-static void U_CALLCONV
-deleteCanonicalMapEntry(void *obj) {
-    U_NAMESPACE_QUALIFIER CanonicalMapEntry *entry = (U_NAMESPACE_QUALIFIER CanonicalMapEntry*)obj;
-    uprv_free(entry);
+   delete (icu::UVector*) obj;
 }
 
 /**
@@ -171,44 +105,36 @@ deleteCanonicalMapEntry(void *obj) {
  */
 static void U_CALLCONV
 deleteOlsonToMetaMappingEntry(void *obj) {
-    U_NAMESPACE_QUALIFIER OlsonToMetaMappingEntry *entry = (U_NAMESPACE_QUALIFIER OlsonToMetaMappingEntry*)obj;
+    icu::OlsonToMetaMappingEntry *entry = (icu::OlsonToMetaMappingEntry*)obj;
     uprv_free(entry);
 }
 
-/**
- * Deleter for MetaToOlsonMappingEntry
- */
-static void U_CALLCONV
-deleteMetaToOlsonMappingEntry(void *obj) {
-    U_NAMESPACE_QUALIFIER MetaToOlsonMappingEntry *entry = (U_NAMESPACE_QUALIFIER MetaToOlsonMappingEntry*)obj;
-    uprv_free(entry->territory);
-    uprv_free(entry);
-}
 U_CDECL_END
 
 U_NAMESPACE_BEGIN
 
 #define ZID_KEY_MAX 128
-static const char gZoneStringsTag[]     = "zoneStrings";
-static const char gUseMetazoneTag[]     = "um";
 
-static const char gSupplementalData[]   = "supplementalData";
+static const char gMetaZones[]          = "metaZones";
+static const char gMetazoneInfo[]       = "metazoneInfo";
 static const char gMapTimezonesTag[]    = "mapTimezones";
-static const char gMetazonesTag[]       = "metazones";
-static const char gZoneFormattingTag[]  = "zoneFormatting";
-static const char gCanonicalTag[]       = "canonical";
-static const char gTerritoryTag[]       = "territory";
-static const char gAliasesTag[]         = "aliases";
-static const char gMultizoneTag[]       = "multizone";
 
-static const char gMetazoneInfo[]       = "metazoneInfo";
-static const char gMetazoneMappings[]   = "metazoneMappings";
+static const char gTimeZoneTypes[]      = "timezoneTypes";
+static const char gTypeAliasTag[]       = "typeAlias";
+static const char gTypeMapTag[]         = "typeMap";
+static const char gTimezoneTag[]        = "timezone";
 
-#define MZID_PREFIX_LEN 5
-static const char gMetazoneIdPrefix[]   = "meta:";
+static const char gWorldTag[]           = "001";
 
 static const UChar gWorld[] = {0x30, 0x30, 0x31, 0x00}; // "001"
 
+static const UChar gDefaultFrom[] = {0x31, 0x39, 0x37, 0x30, 0x2D, 0x30, 0x31, 0x2D, 0x30, 0x31,
+                                     0x20, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x00}; // "1970-01-01 00:00"
+static const UChar gDefaultTo[]   = {0x39, 0x39, 0x39, 0x39, 0x2D, 0x31, 0x32, 0x2D, 0x33, 0x31,
+                                     0x20, 0x32, 0x33, 0x3A, 0x35, 0x39, 0x00}; // "9999-12-31 23:59"
+
+static const UChar gCustomTzPrefix[]    = {0x47, 0x4D, 0x54, 0};    // "GMT"
+
 #define ASCII_DIGIT(c) (((c)>=0x30 && (c)<=0x39) ? (c)-0x30 : -1)
 
 /*
@@ -286,697 +212,684 @@ parseDate (const UChar *text, UErrorCode &status) {
     return 0;
 }
 
-UHashtable*
-ZoneMeta::createCanonicalMap(void) {
-    UErrorCode status = U_ZERO_ERROR;
-
-    UHashtable *canonicalMap = NULL;
-    UResourceBundle *zoneFormatting = NULL;
-    UResourceBundle *tzitem = NULL;
-    UResourceBundle *aliases = NULL;
-
-    StringEnumeration* tzenum = NULL;
-    int32_t numZones;
-
-    canonicalMap = uhash_open(uhash_hashUChars, uhash_compareUChars, NULL, &status);
+const UChar* U_EXPORT2
+ZoneMeta::getCanonicalCLDRID(const UnicodeString &tzid, UErrorCode& status) {
     if (U_FAILURE(status)) {
         return NULL;
     }
-    // no key deleter
-    uhash_setValueDeleter(canonicalMap, deleteCanonicalMapEntry);
 
-    zoneFormatting = ures_openDirect(NULL, gSupplementalData, &status);
-    zoneFormatting = ures_getByKey(zoneFormatting, gZoneFormattingTag, zoneFormatting, &status);
-    if (U_FAILURE(status)) {
-        goto error_cleanup;
+    int32_t len = tzid.length();
+    if (len > ZID_KEY_MAX) {
+        status = U_ILLEGAL_ARGUMENT_ERROR;
+        return NULL;
     }
 
-    while (ures_hasNext(zoneFormatting)) {
-        tzitem = ures_getNextResource(zoneFormatting, tzitem, &status);
-        if (U_FAILURE(status)) {
-            status = U_ZERO_ERROR;
-            continue;
-        }
-        if (ures_getType(tzitem) != URES_TABLE) {
-            continue;
+    // Checking the cached results
+    UBool initialized;
+    UMTX_CHECK(&gZoneMetaLock, gCanonicalIDCacheInitialized, initialized);
+    if (!initialized) {
+        // Create empty hashtable
+        umtx_lock(&gZoneMetaLock);
+        {
+            if (!gCanonicalIDCacheInitialized) {
+                gCanonicalIDCache = uhash_open(uhash_hashUChars, uhash_compareUChars, NULL, &status);
+                if (gCanonicalIDCache == NULL) {
+                    status = U_MEMORY_ALLOCATION_ERROR;
+                }
+                if (U_FAILURE(status)) {
+                    gCanonicalIDCache = NULL;
+                    return NULL;
+                }
+                // No key/value deleters - keys/values are from a resource bundle
+                gCanonicalIDCacheInitialized = TRUE;
+                ucln_i18n_registerCleanup(UCLN_I18N_ZONEMETA, zoneMeta_cleanup);
+            }
         }
+        umtx_unlock(&gZoneMetaLock);
+    }
 
-        int32_t canonicalLen;
-        const UChar *canonical = ures_getStringByKey(tzitem, gCanonicalTag, &canonicalLen, &status);
-        if (U_FAILURE(status)) {
-            status = U_ZERO_ERROR;
-            continue;
-        }
+    const UChar *canonicalID = NULL;
 
-        int32_t territoryLen;
-        const UChar *territory = ures_getStringByKey(tzitem, gTerritoryTag, &territoryLen, &status);
-        if (U_FAILURE(status)) {
-            territory = NULL;
-            status = U_ZERO_ERROR;
-        }
+    UErrorCode tmpStatus = U_ZERO_ERROR;
+    UChar utzid[ZID_KEY_MAX + 1];
+    tzid.extract(utzid, ZID_KEY_MAX + 1, tmpStatus);
+    U_ASSERT(tmpStatus == U_ZERO_ERROR);    // we checked the length of tzid already
 
-        // Create canonical map entry
-        CanonicalMapEntry *entry = (CanonicalMapEntry*)uprv_malloc(sizeof(CanonicalMapEntry));
-        if (entry == NULL) {
-            status = U_MEMORY_ALLOCATION_ERROR;
-            goto error_cleanup;
-        }
-        entry->id = canonical;
-        if (territory == NULL || u_strcmp(territory, gWorld) == 0) {
-            entry->country = NULL;
-        } else {
-            entry->country = territory;
-        }
+    // Check if it was already cached
+    umtx_lock(&gZoneMetaLock);
+    {
+        canonicalID = (const UChar *)uhash_get(gCanonicalIDCache, utzid);
+    }
+    umtx_unlock(&gZoneMetaLock);
 
-        // Put this entry in the hashtable. Since this hashtable has no key deleter,
-        // key is treated as const, but must be passed as non-const.
-        uhash_put(canonicalMap, (UChar*)canonical, entry, &status);
-        if (U_FAILURE(status)) {
-            goto error_cleanup;
+    if (canonicalID != NULL) {
+        return canonicalID;
+    }
+
+    // If not, resolve CLDR canonical ID with resource data
+    UBool isInputCanonical = FALSE;
+    char id[ZID_KEY_MAX + 1];
+    const UChar* idChars = tzid.getBuffer();
+
+    u_UCharsToChars(idChars,id,len);
+    id[len] = (char) 0; // Make sure it is null terminated.
+
+    // replace '/' with ':'
+    char *p = id;
+    while (*p++) {
+        if (*p == '/') {
+            *p = ':';
         }
+    }
 
-        // Get aliases
-        aliases = ures_getByKey(tzitem, gAliasesTag, aliases, &status);
-        if (U_FAILURE(status)) {
-            // No aliases
-            status = U_ZERO_ERROR;
-            continue;
+    UResourceBundle *top = ures_openDirect(NULL, gTimeZoneTypes, &tmpStatus);
+    UResourceBundle *rb = ures_getByKey(top, gTypeMapTag, NULL, &tmpStatus);
+    ures_getByKey(rb, gTimezoneTag, rb, &tmpStatus);
+    ures_getByKey(rb, id, rb, &tmpStatus);
+    if (U_SUCCESS(tmpStatus)) {
+        // type entry (canonical) found
+        // the input is the canonical ID. resolve to const UChar*
+        canonicalID = TimeZone::findID(tzid);
+        isInputCanonical = TRUE;
+    }
+
+    if (canonicalID == NULL) {
+        // If a map element not found, then look for an alias
+        tmpStatus = U_ZERO_ERROR;
+        ures_getByKey(top, gTypeAliasTag, rb, &tmpStatus);
+        ures_getByKey(rb, gTimezoneTag, rb, &tmpStatus);
+        const UChar *canonical = ures_getStringByKey(rb,id,NULL,&tmpStatus);
+        if (U_SUCCESS(tmpStatus)) {
+            // canonical map found
+            canonicalID = canonical;
         }
 
-        while (ures_hasNext(aliases)) {
-            const UChar* alias = ures_getNextString(aliases, NULL, NULL, &status);
-            if (U_FAILURE(status)) {
-                status = U_ZERO_ERROR;
-                continue;
-            }
-            // Create canonical map entry for this alias
-            entry = (CanonicalMapEntry*)uprv_malloc(sizeof(CanonicalMapEntry));
-            if (entry == NULL) {
-                status = U_MEMORY_ALLOCATION_ERROR;
-                goto error_cleanup;
-            }
-            entry->id = canonical;
-            if (territory  == NULL || u_strcmp(territory, gWorld) == 0) {
-                entry->country = NULL;
+        if (canonicalID == NULL) {
+            // Dereference the input ID using the tz data
+            const UChar *derefer = TimeZone::dereferOlsonLink(tzid);
+            if (derefer == NULL) {
+                status = U_ILLEGAL_ARGUMENT_ERROR;
             } else {
-                entry->country = territory;
-            }
+                len = u_strlen(derefer);
+                u_UCharsToChars(derefer,id,len);
+                id[len] = (char) 0; // Make sure it is null terminated.
+
+                // replace '/' with ':'
+                char *p = id;
+                while (*p++) {
+                    if (*p == '/') {
+                        *p = ':';
+                    }
+                }
 
-            // Put this entry in the hashtable. Since this hashtable has no key deleter,
-            // key is treated as const, but must be passed as non-const.
-            uhash_put(canonicalMap, (UChar*)alias, entry, &status);
-            if (U_FAILURE(status)) {
-                goto error_cleanup;
+                // If a dereference turned something up then look for an alias.
+                // rb still points to the alias table, so we don't have to go looking
+                // for it.
+                tmpStatus = U_ZERO_ERROR;
+                canonical = ures_getStringByKey(rb,id,NULL,&tmpStatus);
+                if (U_SUCCESS(tmpStatus)) {
+                    // canonical map for the dereferenced ID found
+                    canonicalID = canonical;
+                } else {
+                    canonicalID = derefer;
+                    isInputCanonical = TRUE;
+                }
             }
         }
     }
+    ures_close(rb);
+    ures_close(top);
 
-    // Some available Olson zones are not included in CLDR data (such as Asia/Riyadh87).
-    // Also, when we update Olson tzdata, new zones may be added.
-    // This code scans all available zones in zoneinfo.res, and if any of them are
-    // missing, add them to the map.
-    tzenum = TimeZone::createEnumeration();
-    numZones = tzenum->count(status);
     if (U_SUCCESS(status)) {
-        int32_t i;
-        for (i = 0; i < numZones; i++) {
-            const UnicodeString *zone = tzenum->snext(status);
-            if (U_FAILURE(status)) {
-                // We should not get here.
-                status = U_ZERO_ERROR;
-                continue;
-            }
-            UChar zoneUChars[ZID_KEY_MAX];
-            int32_t zoneUCharsLen = zone->extract(zoneUChars, ZID_KEY_MAX, status) + 1; // Add one for NUL termination
-            if (U_FAILURE(status) || status==U_STRING_NOT_TERMINATED_WARNING) {
-                status = U_ZERO_ERROR;
-                continue; // zone id is too long to extract
-            }
-            CanonicalMapEntry *entry = (CanonicalMapEntry*)uhash_get(canonicalMap, zoneUChars);
-            if (entry) {
-                // Already included in CLDR data
-                continue;
-            }
-            // Not in CLDR data, but it could be new one whose alias is available
-            // in CLDR.
-            int32_t nTzdataEquivalent = TimeZone::countEquivalentIDs(*zone);
-            int32_t j;
-            for (j = 0; j < nTzdataEquivalent; j++) {
-                UnicodeString alias = TimeZone::getEquivalentID(*zone, j);
-                if (alias == *zone) {
-                    continue;
-                }
-                UChar aliasUChars[ZID_KEY_MAX];
-                alias.extract(aliasUChars, ZID_KEY_MAX, status);
-                if (U_FAILURE(status) || status==U_STRING_NOT_TERMINATED_WARNING) {
-                    status = U_ZERO_ERROR;
-                    continue; // zone id is too long to extract
-                }
-                entry = (CanonicalMapEntry*)uhash_get(canonicalMap, aliasUChars);
-                if (entry != NULL) {
-                    break;
+        U_ASSERT(canonicalID != NULL);  // canocanilD must be non-NULL here
+
+        // Put the resolved canonical ID to the cache
+        umtx_lock(&gZoneMetaLock);
+        {
+            const UChar* idInCache = (const UChar *)uhash_get(gCanonicalIDCache, utzid);
+            if (idInCache == NULL) {
+                const UChar* key = ZoneMeta::findTimeZoneID(tzid);
+                U_ASSERT(key != NULL);
+                if (key != NULL) {
+                    idInCache = (const UChar *)uhash_put(gCanonicalIDCache, (void *)key, (void *)canonicalID, &status);
+                    U_ASSERT(idInCache == NULL);
                 }
             }
-            // Create a new map entry
-            CanonicalMapEntry* newEntry = (CanonicalMapEntry*)uprv_malloc(sizeof(CanonicalMapEntry));
-            int32_t idLen;
-            if (newEntry == NULL) {
-                status = U_MEMORY_ALLOCATION_ERROR;
-                goto error_cleanup;
-            }
-            if (entry == NULL) {
-                // Set dereferenced zone ID as the canonical ID
-                UnicodeString derefZone;
-                TimeZone::dereferOlsonLink(*zone, derefZone);
-                if (derefZone.length() == 0) {
-                    // It should never happen.. but just in case
-                    derefZone = *zone;
-                }
-                idLen = derefZone.length() + 1;
-                newEntry->id = allocUStringInTable(idLen);
-                if (newEntry->id == NULL) {
-                    status = U_MEMORY_ALLOCATION_ERROR;
-                    uprv_free(newEntry);
-                    goto error_cleanup;
+            if (U_SUCCESS(status) && isInputCanonical) {
+                // Also put canonical ID itself into the cache if not exist
+                const UChar *canonicalInCache = (const UChar*)uhash_get(gCanonicalIDCache, canonicalID);
+                if (canonicalInCache == NULL) {
+                    canonicalInCache = (const UChar *)uhash_put(gCanonicalIDCache, (void *)canonicalID, (void *)canonicalID, &status);
+                    U_ASSERT(canonicalInCache == NULL);
                 }
-                // Copy NULL terminated string
-                derefZone.extract((UChar*)(newEntry->id), idLen, status);
-                if (U_FAILURE(status)) {
-                    removeLastUStringFromTable();
-                    uprv_free(newEntry);
-                    goto error_cleanup;
-                }
-                // No territory information available
-                newEntry->country = NULL;
-            } else {
-                // Duplicate the entry
-                newEntry->id = entry->id;
-                newEntry->country = entry->country;
-            }
-
-            // Put this entry in the hashtable
-            UChar *key = allocUStringInTable(zoneUCharsLen);
-            if (key == NULL) {
-                status = U_MEMORY_ALLOCATION_ERROR;
-                deleteCanonicalMapEntry(newEntry);
-                goto error_cleanup;
-            }
-            u_strncpy(key, zoneUChars, zoneUCharsLen);
-            uhash_put(canonicalMap, key, newEntry, &status);
-            if (U_FAILURE(status)) {
-                goto error_cleanup;
             }
         }
+        umtx_unlock(&gZoneMetaLock);
     }
 
-normal_cleanup:
-    ures_close(aliases);
-    ures_close(tzitem);
-    ures_close(zoneFormatting);
-    delete tzenum;
-    return canonicalMap;
+    return canonicalID;
+}
 
-error_cleanup:
-    if (canonicalMap != NULL) {
-        uhash_close(canonicalMap);
-        canonicalMap = NULL;
+UnicodeString& U_EXPORT2
+ZoneMeta::getCanonicalCLDRID(const UnicodeString &tzid, UnicodeString &systemID, UErrorCode& status) {
+    const UChar *canonicalID = getCanonicalCLDRID(tzid, status);
+    if (U_FAILURE(status) || canonicalID == NULL) {
+        systemID.setToBogus();
+        return systemID;
     }
-    goto normal_cleanup;
+    systemID.setTo(TRUE, canonicalID, -1);
+    return systemID;
 }
 
-/*
- * Creating Olson tzid to metazone mappings from resource (3.8.1 and beyond)
- */
-UHashtable*
-ZoneMeta::createOlsonToMetaMap(void) {
+const UChar* U_EXPORT2
+ZoneMeta::getCanonicalCLDRID(const TimeZone& tz) {
+    if (dynamic_cast<const OlsonTimeZone *>(&tz) != NULL) {
+        // short cut for OlsonTimeZone
+        const OlsonTimeZone *otz = (const OlsonTimeZone*)&tz;
+        return otz->getCanonicalID();
+    }
     UErrorCode status = U_ZERO_ERROR;
+    UnicodeString tzID;
+    return getCanonicalCLDRID(tz.getID(tzID), status);
+}
 
-    UHashtable *olsonToMeta = NULL;
-    UResourceBundle *metazoneMappings = NULL;
-    UResourceBundle *zoneItem = NULL;
-    UResourceBundle *mz = NULL;
-    StringEnumeration *tzids = NULL;
 
-    olsonToMeta = uhash_open(uhash_hashUChars, uhash_compareUChars, NULL, &status);
-    if (U_FAILURE(status)) {
-        return NULL;
+
+UnicodeString& U_EXPORT2
+ZoneMeta::getCanonicalCountry(const UnicodeString &tzid, UnicodeString &canonicalCountry) {
+    const UChar *region = TimeZone::getRegion(tzid);
+    if (region != NULL && u_strcmp(gWorld, region) != 0) {
+        canonicalCountry.setTo(region, -1);
+    } else {
+        canonicalCountry.setToBogus();
     }
-    uhash_setKeyDeleter(olsonToMeta, deleteUCharString);
-    uhash_setValueDeleter(olsonToMeta, deleteUVector);
+    return canonicalCountry;
+}
 
-    // Read metazone mappings from metazoneInfo bundle
-    metazoneMappings = ures_openDirect(NULL, gMetazoneInfo, &status);
-    metazoneMappings = ures_getByKey(metazoneMappings, gMetazoneMappings, metazoneMappings, &status);
-    if (U_FAILURE(status)) {
-        goto error_cleanup;
+UnicodeString& U_EXPORT2
+ZoneMeta::getSingleCountry(const UnicodeString &tzid, UnicodeString &country) {
+    // Get canonical country for the zone
+    const UChar *region = TimeZone::getRegion(tzid);
+    if (region == NULL || u_strcmp(gWorld, region) == 0) {
+        // special case - unknown or "001"
+        country.setToBogus();
+        return country;
     }
 
-    // Walk through all canonical tzids
-    char zidkey[ZID_KEY_MAX];
+    // Checking the cached results
+    UErrorCode status = U_ZERO_ERROR;
+    UBool initialized;
+    UMTX_CHECK(&gZoneMetaLock, gCountryInfoVectorsInitialized, initialized);
+    if (!initialized) {
+        // Create empty vectors
+        umtx_lock(&gZoneMetaLock);
+        {
+            if (!gCountryInfoVectorsInitialized) {
+                // No deleters for these UVectors, it's a reference to a resource bundle string.
+                gSingleZoneCountries = new UVector(NULL, uhash_compareUChars, status);
+                if (gSingleZoneCountries == NULL) {
+                    status = U_MEMORY_ALLOCATION_ERROR;
+                }
+                gMultiZonesCountries = new UVector(NULL, uhash_compareUChars, status);
+                if (gMultiZonesCountries == NULL) {
+                    status = U_MEMORY_ALLOCATION_ERROR;
+                }
 
-    tzids = TimeZone::createEnumeration();
-    const UnicodeString *tzid;
-    while ((tzid = tzids->snext(status))) {
-        if (U_FAILURE(status)) {
-            goto error_cleanup;
-        }
-        // We may skip aliases, because the bundle
-        // contains only canonical IDs.  For now, try
-        // all of them.
-        tzid->extract(0, tzid->length(), zidkey, sizeof(zidkey), US_INV);
-        zidkey[sizeof(zidkey)-1] = 0; // NULL terminate just in case.
-
-        // Replace '/' with ':'
-        UBool foundSep = FALSE;
-        char *p = zidkey;
-        while (*p) {
-            if (*p == '/') {
-                *p = ':';
-                foundSep = TRUE;
+                if (U_SUCCESS(status)) {
+                    gCountryInfoVectorsInitialized = TRUE;
+                } else {
+                    delete gSingleZoneCountries;
+                    delete gMultiZonesCountries;
+                }
             }
-            p++;
-        }
-        if (!foundSep) {
-            // A valid time zone key has at least one separator
-            continue;
         }
+        umtx_unlock(&gZoneMetaLock);
 
-        zoneItem = ures_getByKey(metazoneMappings, zidkey, zoneItem, &status);
         if (U_FAILURE(status)) {
-            status = U_ZERO_ERROR;
-            continue;
+            country.setToBogus();
+            return country;
         }
+        U_ASSERT(gSingleZoneCountries != NULL);
+        U_ASSERT(gMultiZonesCountries != NULL);
+    }
 
-        UVector *mzMappings = NULL;
-        while (ures_hasNext(zoneItem)) {
-            mz = ures_getNextResource(zoneItem, mz, &status);
-            const UChar *mz_name = ures_getStringByIndex(mz, 0, NULL, &status);
-            const UChar *mz_from = ures_getStringByIndex(mz, 1, NULL, &status);
-            const UChar *mz_to   = ures_getStringByIndex(mz, 2, NULL, &status);
+    // Check if it was already cached
+    UBool cached = FALSE;
+    UBool multiZones = FALSE;
+    umtx_lock(&gZoneMetaLock);
+    {
+        multiZones = cached = gMultiZonesCountries->contains((void*)region);
+        if (!multiZones) {
+            cached = gSingleZoneCountries->contains((void*)region);
+        }
+    }
+    umtx_unlock(&gZoneMetaLock);
 
-            if(U_FAILURE(status)){
-                status = U_ZERO_ERROR;
-                continue;
-            }
-            // We do not want to use SimpleDateformat to parse boundary dates,
-            // because this code could be triggered by the initialization code
-            // used by SimpleDateFormat.
-            UDate from = parseDate(mz_from, status);
-            UDate to = parseDate(mz_to, status);
-            if (U_FAILURE(status)) {
-                status = U_ZERO_ERROR;
-                continue;
-            }
+    if (!cached) {
+        // We need to go through all zones associated with the region.
+        // This is relatively heavy operation.
 
-            OlsonToMetaMappingEntry *entry = (OlsonToMetaMappingEntry*)uprv_malloc(sizeof(OlsonToMetaMappingEntry));
-            if (entry == NULL) {
-                status = U_MEMORY_ALLOCATION_ERROR;
-                break;
-            }
-            entry->mzid = mz_name;
-            entry->from = from;
-            entry->to = to;
+        U_ASSERT(u_strlen(region) == 2);
 
-            if (mzMappings == NULL) {
-                mzMappings = new UVector(deleteOlsonToMetaMappingEntry, NULL, status);
-                if (U_FAILURE(status)) {
-                    delete mzMappings;
-                    deleteOlsonToMetaMappingEntry(entry);
-                    uprv_free(entry);
-                    break;
-                }
-            }
+        char buf[] = {0, 0, 0};
+        u_UCharsToChars(region, buf, 2);
 
-            mzMappings->addElement(entry, status);
-            if (U_FAILURE(status)) {
-                break;
-            }
+        StringEnumeration *ids = TimeZone::createTimeZoneIDEnumeration(UCAL_ZONE_TYPE_CANONICAL_LOCATION, buf, NULL, status);
+        int32_t idsLen = ids->count(status);
+        if (U_SUCCESS(status) && idsLen > 1) {
+            // multiple canonical zones are available for the region
+            multiZones = TRUE;
         }
-
         if (U_FAILURE(status)) {
-            if (mzMappings != NULL) {
-                delete mzMappings;
-            }
-            goto error_cleanup;
-        }
-        if (mzMappings != NULL) {
-            // Add to hashtable
-            int32_t tzidLen = tzid->length() + 1; // Add one for NUL terminator
-            UChar *key = (UChar*)uprv_malloc(tzidLen * sizeof(UChar));
-            if (key == NULL) {
-                status = U_MEMORY_ALLOCATION_ERROR;
-                delete mzMappings;
-                goto error_cleanup;
-            }
-            tzid->extract(key, tzidLen, status);
-            uhash_put(olsonToMeta, key, mzMappings, &status);
-            if (U_FAILURE(status)) {
-                goto error_cleanup;
+            // no single country by default for any error cases
+            multiZones = TRUE;
+        }
+        delete ids;
+
+        // Cache the result
+        umtx_lock(&gZoneMetaLock);
+        {
+            UErrorCode ec = U_ZERO_ERROR;
+            if (multiZones) {
+                if (!gMultiZonesCountries->contains((void*)region)) {
+                    gMultiZonesCountries->addElement((void*)region, ec);
+                }
+            } else {
+                if (!gSingleZoneCountries->contains((void*)region)) {
+                    gSingleZoneCountries->addElement((void*)region, ec);
+                }
             }
         }
+        umtx_unlock(&gZoneMetaLock);
     }
 
-normal_cleanup:
-    if (tzids != NULL) {
-        delete tzids;
+    if (multiZones) {
+        country.setToBogus();
+    } else {
+        country.setTo(region, -1);
     }
-    ures_close(zoneItem);
-    ures_close(mz);
-    ures_close(metazoneMappings);
-    return olsonToMeta;
+    return country;
+}
 
-error_cleanup:
-    if (olsonToMeta != NULL) {
-        uhash_close(olsonToMeta);
-        olsonToMeta = NULL;
+UnicodeString& U_EXPORT2
+ZoneMeta::getMetazoneID(const UnicodeString &tzid, UDate date, UnicodeString &result) {
+    UBool isSet = FALSE;
+    const UVector *mappings = getMetazoneMappings(tzid);
+    if (mappings != NULL) {
+        for (int32_t i = 0; i < mappings->size(); i++) {
+            OlsonToMetaMappingEntry *mzm = (OlsonToMetaMappingEntry*)mappings->elementAt(i);
+            if (mzm->from <= date && mzm->to > date) {
+                result.setTo(mzm->mzid, -1);
+                isSet = TRUE;
+                break;
+            }
+        }
     }
-    goto normal_cleanup;
+    if (!isSet) {
+        result.setToBogus();
+    }
+    return result;
 }
 
-UHashtable*
-ZoneMeta::createMetaToOlsonMap(void) {
+const UVector* U_EXPORT2
+ZoneMeta::getMetazoneMappings(const UnicodeString &tzid) {
     UErrorCode status = U_ZERO_ERROR;
-
-    UHashtable *metaToOlson = NULL;
-    UResourceBundle *metazones = NULL;
-    UResourceBundle *mz = NULL;
-
-    metaToOlson = uhash_open(uhash_hashUChars, uhash_compareUChars, NULL, &status);
-    if (U_FAILURE(status)) {
+    UChar tzidUChars[ZID_KEY_MAX + 1];
+    tzid.extract(tzidUChars, ZID_KEY_MAX + 1, status);
+    if (U_FAILURE(status) || status == U_STRING_NOT_TERMINATED_WARNING) {
         return NULL;
     }
-    uhash_setKeyDeleter(metaToOlson, deleteUCharString);
-    uhash_setValueDeleter(metaToOlson, deleteUVector);
-
-    metazones = ures_openDirect(NULL, gSupplementalData, &status);
-    metazones = ures_getByKey(metazones, gMapTimezonesTag, metazones, &status);
-    metazones = ures_getByKey(metazones, gMetazonesTag, metazones, &status);
-    if (U_FAILURE(status)) {
-        goto error_cleanup;
-    }
 
-    while (ures_hasNext(metazones)) {
-        mz = ures_getNextResource(metazones, mz, &status);
+    UBool initialized;
+    UMTX_CHECK(&gZoneMetaLock, gOlsonToMetaInitialized, initialized);
+    if (!initialized) {
+        UHashtable *tmpOlsonToMeta = uhash_open(uhash_hashUChars, uhash_compareUChars, NULL, &status);
         if (U_FAILURE(status)) {
-            status = U_ZERO_ERROR;
-            continue;
+            return NULL;
         }
-        const char *mzkey = ures_getKey(mz);
-        if (uprv_strncmp(mzkey, gMetazoneIdPrefix, MZID_PREFIX_LEN) == 0) {
-            const char *mzid = mzkey + MZID_PREFIX_LEN;
-            const char *territory = uprv_strrchr(mzid, '_');
-            int32_t mzidLen = 0;
-            int32_t territoryLen = 0;
-            if (territory) {
-                mzidLen = territory - mzid;
-                territory++;
-                territoryLen = uprv_strlen(territory);
-            }
-            if (mzidLen > 0 && territoryLen > 0) {
-                int32_t tzidLen;
-                const UChar *tzid = ures_getStringByIndex(mz, 0, &tzidLen, &status);
-                if (U_SUCCESS(status)) {
-                    // Create MetaToOlsonMappingEntry
-                    MetaToOlsonMappingEntry *entry = (MetaToOlsonMappingEntry*)uprv_malloc(sizeof(MetaToOlsonMappingEntry));
-                    if (entry == NULL) {
-                        status = U_MEMORY_ALLOCATION_ERROR;
-                        goto error_cleanup;
-                    }
-                    entry->id = tzid;
-                    entry->territory = (UChar*)uprv_malloc((territoryLen + 1) * sizeof(UChar));
-                    if (entry->territory == NULL) {
-                        status = U_MEMORY_ALLOCATION_ERROR;
-                        uprv_free(entry);
-                        goto error_cleanup;
-                    }
-                    u_charsToUChars(territory, entry->territory, territoryLen + 1);
-
-                    // Check if mapping entries for metazone is already available
-                    if (mzidLen < ZID_KEY_MAX) {
-                        UChar mzidUChars[ZID_KEY_MAX];
-                        u_charsToUChars(mzid, mzidUChars, mzidLen);
-                        mzidUChars[mzidLen++] = 0; // Add NUL terminator
-                        UVector *tzMappings = (UVector*)uhash_get(metaToOlson, mzidUChars);
-                        if (tzMappings == NULL) {
-                            // Create new UVector and put it into the hashtable
-                            tzMappings = new UVector(deleteMetaToOlsonMappingEntry, NULL, status);
-                            if (U_FAILURE(status)) {
-                                deleteMetaToOlsonMappingEntry(entry);
-                                goto error_cleanup;
-                            }
-                            UChar *key = (UChar*)uprv_malloc(mzidLen * sizeof(UChar));
-                            if (key == NULL) {
-                                status = U_MEMORY_ALLOCATION_ERROR;
-                                delete tzMappings;
-                                deleteMetaToOlsonMappingEntry(entry);
-                                goto error_cleanup;
-                            }
-                            u_strncpy(key, mzidUChars, mzidLen);
-                            uhash_put(metaToOlson, key, tzMappings, &status);
-                            if (U_FAILURE(status)) {
-                                goto error_cleanup;
-                            }
-                        }
-                        tzMappings->addElement(entry, status);
-                        if (U_FAILURE(status)) {
-                            goto error_cleanup;
-                        }
-                    } else {
-                        deleteMetaToOlsonMappingEntry(entry);
-                    }
-                } else {
-                    status = U_ZERO_ERROR;
-                }
+        uhash_setKeyDeleter(tmpOlsonToMeta, deleteUCharString);
+        uhash_setValueDeleter(tmpOlsonToMeta, deleteUVector);
+
+        umtx_lock(&gZoneMetaLock);
+        {
+            if (!gOlsonToMetaInitialized) {
+                gOlsonToMeta = tmpOlsonToMeta;
+                tmpOlsonToMeta = NULL;
+                gOlsonToMetaInitialized = TRUE;
             }
         }
-    }
+        umtx_unlock(&gZoneMetaLock);
 
-normal_cleanup:
-    ures_close(mz);
-    ures_close(metazones);
-    return metaToOlson;
-
-error_cleanup:
-    if (metaToOlson != NULL) {
-        uhash_close(metaToOlson);
-        metaToOlson = NULL;
+        // OK to call the following multiple times with the same function
+        ucln_i18n_registerCleanup(UCLN_I18N_ZONEMETA, zoneMeta_cleanup);
+        if (tmpOlsonToMeta != NULL) {
+            uhash_close(tmpOlsonToMeta);
+        }
     }
-    goto normal_cleanup;
-}
 
- /*
- * Initialize global objects
- */
-void
-ZoneMeta::initializeCanonicalMap(void) {
-    UBool initialized;
-    UMTX_CHECK(&gZoneMetaLock, gCanonicalMapInitialized, initialized);
-    if (initialized) {
-        return;
-    }
-    // Initialize hash table
-    UHashtable *tmpCanonicalMap = createCanonicalMap();
+    // get the mapping from cache
+    const UVector *result = NULL;
 
     umtx_lock(&gZoneMetaLock);
-    if (!gCanonicalMapInitialized) {
-        gCanonicalMap = tmpCanonicalMap;
-        tmpCanonicalMap = NULL;
-        gCanonicalMapInitialized = TRUE;
+    {
+        result = (UVector*) uhash_get(gOlsonToMeta, tzidUChars);
     }
     umtx_unlock(&gZoneMetaLock);
 
-    // OK to call the following multiple times with the same function
-    ucln_i18n_registerCleanup(UCLN_I18N_ZONEMETA, zoneMeta_cleanup);
-    if (tmpCanonicalMap != NULL) {
-        uhash_close(tmpCanonicalMap);
+    if (result != NULL) {
+        return result;
     }
-}
 
-void
-ZoneMeta::initializeOlsonToMeta(void) {
-    UBool initialized;
-    UMTX_CHECK(&gZoneMetaLock, gOlsonToMetaInitialized, initialized);
-    if (initialized) {
-        return;
+    // miss the cache - create new one
+    UVector *tmpResult = createMetazoneMappings(tzid);
+    if (tmpResult == NULL) {
+        // not available
+        return NULL;
     }
-    // Initialize hash tables
-    UHashtable *tmpOlsonToMeta = createOlsonToMetaMap();
 
+    // put the new one into the cache
     umtx_lock(&gZoneMetaLock);
-    if (!gOlsonToMetaInitialized) {
-        gOlsonToMeta = tmpOlsonToMeta;
-        tmpOlsonToMeta = NULL;
-        gOlsonToMetaInitialized = TRUE;
+    {
+        // make sure it's already created
+        result = (UVector*) uhash_get(gOlsonToMeta, tzidUChars);
+        if (result == NULL) {
+            // add the one just created
+            int32_t tzidLen = tzid.length() + 1;
+            UChar *key = (UChar*)uprv_malloc(tzidLen * sizeof(UChar));
+            if (key == NULL) {
+                // memory allocation error..  just return NULL
+                result = NULL;
+                delete tmpResult;
+            } else {
+                tzid.extract(key, tzidLen, status);
+                uhash_put(gOlsonToMeta, key, tmpResult, &status);
+                if (U_FAILURE(status)) {
+                    // delete the mapping
+                    result = NULL;
+                    delete tmpResult;
+                } else {
+                    result = tmpResult;
+                }
+            }
+        } else {
+            // another thread already put the one
+            delete tmpResult;
+        }
     }
     umtx_unlock(&gZoneMetaLock);
-    
-    // OK to call the following multiple times with the same function
-    ucln_i18n_registerCleanup(UCLN_I18N_ZONEMETA, zoneMeta_cleanup);
-    if (tmpOlsonToMeta != NULL) {
-        uhash_close(tmpOlsonToMeta);
-    }
-}
-
-void
-ZoneMeta::initializeMetaToOlson(void) {
-    UBool initialized;
-    UMTX_CHECK(&gZoneMetaLock, gMetaToOlsonInitialized, initialized);
-    if (initialized) {
-        return;
-    }
-    // Initialize hash table
-    UHashtable *tmpMetaToOlson = createMetaToOlsonMap();
 
-    umtx_lock(&gZoneMetaLock);
-    if (!gMetaToOlsonInitialized) {
-        gMetaToOlson = tmpMetaToOlson;
-        tmpMetaToOlson = NULL;
-        gMetaToOlsonInitialized = TRUE;
-    }
-    umtx_unlock(&gZoneMetaLock);
-    
-    // OK to call the following multiple times with the same function
-    ucln_i18n_registerCleanup(UCLN_I18N_ZONEMETA, zoneMeta_cleanup);
-    if (tmpMetaToOlson != NULL) {
-        uhash_close(tmpMetaToOlson);
-    }
+    return result;
 }
 
-UnicodeString& U_EXPORT2
-ZoneMeta::getCanonicalSystemID(const UnicodeString &tzid, UnicodeString &systemID, UErrorCode& status) {
-    const CanonicalMapEntry *entry = getCanonicalInfo(tzid);
-    if (entry != NULL) {
-        systemID.setTo(entry->id);
-    } else {
-        status = U_ILLEGAL_ARGUMENT_ERROR;
-    }
-    return systemID;
-}
+UVector*
+ZoneMeta::createMetazoneMappings(const UnicodeString &tzid) {
+    UVector *mzMappings = NULL;
+    UErrorCode status = U_ZERO_ERROR;
 
-UnicodeString& U_EXPORT2
-ZoneMeta::getCanonicalCountry(const UnicodeString &tzid, UnicodeString &canonicalCountry) {
-    const CanonicalMapEntry *entry = getCanonicalInfo(tzid);
-    if (entry != NULL && entry->country != NULL) {
-        canonicalCountry.setTo(entry->country);
-    } else {
-        // Use the input tzid
-        canonicalCountry.remove();
-    }
-    return canonicalCountry;
-}
+    UnicodeString canonicalID;
+    UResourceBundle *rb = ures_openDirect(NULL, gMetaZones, &status);
+    ures_getByKey(rb, gMetazoneInfo, rb, &status);
+    getCanonicalCLDRID(tzid, canonicalID, status);
 
-const CanonicalMapEntry* U_EXPORT2
-ZoneMeta::getCanonicalInfo(const UnicodeString &tzid) {
-    initializeCanonicalMap();
-    CanonicalMapEntry *entry = NULL;
-    if (gCanonicalMap != NULL) {
-        UErrorCode status = U_ZERO_ERROR;
-        UChar tzidUChars[ZID_KEY_MAX];
-        tzid.extract(tzidUChars, ZID_KEY_MAX, status);
-        if (U_SUCCESS(status) && status!=U_STRING_NOT_TERMINATED_WARNING) {
-            entry = (CanonicalMapEntry*)uhash_get(gCanonicalMap, tzidUChars);
+    if (U_SUCCESS(status)) {
+        char tzKey[ZID_KEY_MAX + 1];
+        int32_t tzKeyLen = canonicalID.extract(0, canonicalID.length(), tzKey, sizeof(tzKey), US_INV);
+        tzKey[tzKeyLen] = 0;
+
+        // tzid keys are using ':' as separators
+        char *p = tzKey;
+        while (*p) {
+            if (*p == '/') {
+                *p = ':';
+            }
+            p++;
         }
-    }
-    return entry;
-}
 
-UnicodeString& U_EXPORT2
-ZoneMeta::getSingleCountry(const UnicodeString &tzid, UnicodeString &country) {
-    UErrorCode status = U_ZERO_ERROR;
+        ures_getByKey(rb, tzKey, rb, &status);
 
-    // Get canonical country for the zone
-    getCanonicalCountry(tzid, country);
+        if (U_SUCCESS(status)) {
+            UResourceBundle *mz = NULL;
+            while (ures_hasNext(rb)) {
+                mz = ures_getNextResource(rb, mz, &status);
 
-    if (!country.isEmpty()) { 
-        UResourceBundle *supplementalDataBundle = ures_openDirect(NULL, gSupplementalData, &status);
-        UResourceBundle *zoneFormatting = ures_getByKey(supplementalDataBundle, gZoneFormattingTag, NULL, &status);
-        UResourceBundle *multizone = ures_getByKey(zoneFormatting, gMultizoneTag, NULL, &status);
+                const UChar *mz_name = ures_getStringByIndex(mz, 0, NULL, &status);
+                const UChar *mz_from = gDefaultFrom;
+                const UChar *mz_to = gDefaultTo;
 
-        if (U_SUCCESS(status)) {
-            while (ures_hasNext(multizone)) {
-                int32_t len;
-                const UChar* multizoneCountry = ures_getNextString(multizone, &len, NULL, &status);
-                if (country.compare(multizoneCountry, len) == 0) {
-                    // Included in the multizone country list
-                    country.remove();
+                if (ures_getSize(mz) == 3) {
+                    mz_from = ures_getStringByIndex(mz, 1, NULL, &status);
+                    mz_to   = ures_getStringByIndex(mz, 2, NULL, &status);
+                }
+
+                if(U_FAILURE(status)){
+                    status = U_ZERO_ERROR;
+                    continue;
+                }
+                // We do not want to use SimpleDateformat to parse boundary dates,
+                // because this code could be triggered by the initialization code
+                // used by SimpleDateFormat.
+                UDate from = parseDate(mz_from, status);
+                UDate to = parseDate(mz_to, status);
+                if (U_FAILURE(status)) {
+                    status = U_ZERO_ERROR;
+                    continue;
+                }
+
+                OlsonToMetaMappingEntry *entry = (OlsonToMetaMappingEntry*)uprv_malloc(sizeof(OlsonToMetaMappingEntry));
+                if (entry == NULL) {
+                    status = U_MEMORY_ALLOCATION_ERROR;
+                    break;
+                }
+                entry->mzid = mz_name;
+                entry->from = from;
+                entry->to = to;
+
+                if (mzMappings == NULL) {
+                    mzMappings = new UVector(deleteOlsonToMetaMappingEntry, NULL, status);
+                    if (U_FAILURE(status)) {
+                        delete mzMappings;
+                        deleteOlsonToMetaMappingEntry(entry);
+                        uprv_free(entry);
+                        break;
+                    }
+                }
+
+                mzMappings->addElement(entry, status);
+                if (U_FAILURE(status)) {
                     break;
                 }
             }
+            ures_close(mz);
+            if (U_FAILURE(status)) {
+                if (mzMappings != NULL) {
+                    delete mzMappings;
+                    mzMappings = NULL;
+                }
+            }
         }
-
-        ures_close(multizone);
-        ures_close(zoneFormatting);
-        ures_close(supplementalDataBundle);
     }
-
-    return country;
+    ures_close(rb);
+    return mzMappings;
 }
 
 UnicodeString& U_EXPORT2
-ZoneMeta::getMetazoneID(const UnicodeString &tzid, UDate date, UnicodeString &result) {
-    UBool isSet = FALSE;
-    const UVector *mappings = getMetazoneMappings(tzid);
-    if (mappings != NULL) {
-        for (int32_t i = 0; i < mappings->size(); i++) {
-            OlsonToMetaMappingEntry *mzm = (OlsonToMetaMappingEntry*)mappings->elementAt(i);
-            if (mzm->from <= date && mzm->to > date) {
-                result.setTo(mzm->mzid, -1);
-                isSet = TRUE;
-                break;
+ZoneMeta::getZoneIdByMetazone(const UnicodeString &mzid, const UnicodeString &region, UnicodeString &result) {
+    UErrorCode status = U_ZERO_ERROR;
+    const UChar *tzid = NULL;
+    int32_t tzidLen = 0;
+    char keyBuf[ZID_KEY_MAX + 1];
+    int32_t keyLen = 0;
+
+    if (mzid.length() > ZID_KEY_MAX) {
+        result.setToBogus();
+        return result;
+    }
+
+    keyLen = mzid.extract(0, mzid.length(), keyBuf, ZID_KEY_MAX + 1, US_INV);
+    keyBuf[keyLen] = 0;
+
+    UResourceBundle *rb = ures_openDirect(NULL, gMetaZones, &status);
+    ures_getByKey(rb, gMapTimezonesTag, rb, &status);
+    ures_getByKey(rb, keyBuf, rb, &status);
+
+    if (U_SUCCESS(status)) {
+        // check region mapping
+        if (region.length() == 2 || region.length() == 3) {
+            keyLen = region.extract(0, region.length(), keyBuf, ZID_KEY_MAX + 1, US_INV);
+            keyBuf[keyLen] = 0;
+            tzid = ures_getStringByKey(rb, keyBuf, &tzidLen, &status);
+            if (status == U_MISSING_RESOURCE_ERROR) {
+                status = U_ZERO_ERROR;
             }
         }
+        if (U_SUCCESS(status) && tzid == NULL) {
+            // try "001"
+            tzid = ures_getStringByKey(rb, gWorldTag, &tzidLen, &status);
+        }
     }
-    if (!isSet) {
-        result.remove();
-    }
-    return result;
-}
+    ures_close(rb);
 
-const UVector* U_EXPORT2
-ZoneMeta::getMetazoneMappings(const UnicodeString &tzid) {
-    initializeOlsonToMeta();
-    const UVector *result = NULL;
-    if (gOlsonToMeta != NULL) {
-        UErrorCode status = U_ZERO_ERROR;
-        UChar tzidUChars[ZID_KEY_MAX];
-        tzid.extract(tzidUChars, ZID_KEY_MAX, status);
-        if (U_SUCCESS(status) && status!=U_STRING_NOT_TERMINATED_WARNING) {
-            result = (UVector*)uhash_get(gOlsonToMeta, tzidUChars);
-        }
+    if (tzid == NULL) {
+        result.setToBogus();
+    } else {
+        result.setTo(tzid, tzidLen);
     }
+
     return result;
 }
 
-UnicodeString& U_EXPORT2
-ZoneMeta::getZoneIdByMetazone(const UnicodeString &mzid, const UnicodeString &region, UnicodeString &result) {
-    initializeMetaToOlson();
-    UBool isSet = FALSE;
-    if (gMetaToOlson != NULL) {
-        UErrorCode status = U_ZERO_ERROR;
-        UChar mzidUChars[ZID_KEY_MAX];
-        mzid.extract(mzidUChars, ZID_KEY_MAX, status);
-        if (U_SUCCESS(status) && status!=U_STRING_NOT_TERMINATED_WARNING) {
-            UVector *mappings = (UVector*)uhash_get(gMetaToOlson, mzidUChars);
-            if (mappings != NULL) {
-                // Find a preferred time zone for the given region.
-                for (int32_t i = 0; i < mappings->size(); i++) {
-                    MetaToOlsonMappingEntry *olsonmap = (MetaToOlsonMappingEntry*)mappings->elementAt(i);
-                    if (region.compare(olsonmap->territory, -1) == 0) {
-                        result.setTo(olsonmap->id);
-                        isSet = TRUE;
-                        break;
-                    } else if (u_strcmp(olsonmap->territory, gWorld) == 0) {
-                        result.setTo(olsonmap->id);
-                        isSet = TRUE;
+void
+ZoneMeta::initAvailableMetaZoneIDs () {
+    UBool initialized;
+    UMTX_CHECK(&gZoneMetaLock, gMetaZoneIDsInitialized, initialized);
+    if (!initialized) {
+        umtx_lock(&gZoneMetaLock);
+        {
+            if (!gMetaZoneIDsInitialized) {
+                UErrorCode status = U_ZERO_ERROR;
+                UHashtable *metaZoneIDTable = uhash_open(uhash_hashUnicodeString, uhash_compareUnicodeString, NULL, &status);
+                uhash_setKeyDeleter(metaZoneIDTable, uprv_deleteUObject);
+                // No valueDeleter, because the vector maintain the value objects
+                UVector *metaZoneIDs = NULL;
+                if (U_SUCCESS(status)) {
+                    metaZoneIDs = new UVector(NULL, uhash_compareUChars, status);
+                    if (metaZoneIDs == NULL) {
+                        status = U_MEMORY_ALLOCATION_ERROR;
+                    }
+                } else {
+                    uhash_close(metaZoneIDTable);
+                }
+                if (U_SUCCESS(status)) {
+                    U_ASSERT(metaZoneIDs != NULL);
+                    metaZoneIDs->setDeleter(uprv_free);
+
+                    UResourceBundle *rb = ures_openDirect(NULL, gMetaZones, &status);
+                    UResourceBundle *bundle = ures_getByKey(rb, gMapTimezonesTag, NULL, &status);
+                    UResourceBundle res;
+                    ures_initStackObject(&res);
+                    while (U_SUCCESS(status) && ures_hasNext(bundle)) {
+                        ures_getNextResource(bundle, &res, &status);
+                        if (U_FAILURE(status)) {
+                            break;
+                        }
+                        const char *mzID = ures_getKey(&res);
+                        int32_t len = uprv_strlen(mzID);
+                        UChar *uMzID = (UChar*)uprv_malloc(sizeof(UChar) * (len + 1));
+                        if (uMzID == NULL) {
+                            status = U_MEMORY_ALLOCATION_ERROR;
+                            break;
+                        }
+                        u_charsToUChars(mzID, uMzID, len);
+                        uMzID[len] = 0;
+                        UnicodeString *usMzID = new UnicodeString(uMzID);
+                        if (uhash_get(metaZoneIDTable, usMzID) == NULL) {
+                            metaZoneIDs->addElement((void *)uMzID, status);
+                            uhash_put(metaZoneIDTable, (void *)usMzID, (void *)uMzID, &status);
+                        } else {
+                            uprv_free(uMzID);
+                            delete usMzID;
+                        }
+                    }
+                    if (U_SUCCESS(status)) {
+                        gMetaZoneIDs = metaZoneIDs;
+                        gMetaZoneIDTable = metaZoneIDTable;
+                        gMetaZoneIDsInitialized = TRUE;
+                    } else {
+                        uhash_close(metaZoneIDTable);
+                        delete metaZoneIDs;
                     }
+                    ures_close(&res);
+                    ures_close(bundle);
+                    ures_close(rb);
                 }
             }
         }
+        umtx_unlock(&gZoneMetaLock);
     }
-    if (!isSet) {
-        result.remove();
+}
+
+const UVector*
+ZoneMeta::getAvailableMetazoneIDs() {
+    initAvailableMetaZoneIDs();
+    return gMetaZoneIDs;
+}
+
+const UChar*
+ZoneMeta::findMetaZoneID(const UnicodeString& mzid) {
+    initAvailableMetaZoneIDs();
+    return (const UChar*)uhash_get(gMetaZoneIDTable, &mzid);
+}
+
+const UChar*
+ZoneMeta::findTimeZoneID(const UnicodeString& tzid) {
+    return TimeZone::findID(tzid);
+}
+
+
+TimeZone*
+ZoneMeta::createCustomTimeZone(int32_t offset) {
+    UBool negative = FALSE;
+    int32_t tmp = offset;
+    if (offset < 0) {
+        negative = TRUE;
+        tmp = -offset;
     }
-    return result;
+    int32_t hour, min, sec;
+
+    tmp /= 1000;
+    sec = tmp % 60;
+    tmp /= 60;
+    min = tmp % 60;
+    hour = tmp / 60;
+
+    UnicodeString zid;
+    formatCustomID(hour, min, sec, negative, zid);
+    return new SimpleTimeZone(offset, zid);
+}
+
+UnicodeString&
+ZoneMeta::formatCustomID(uint8_t hour, uint8_t min, uint8_t sec, UBool negative, UnicodeString& id) {
+    // Create normalized time zone ID - GMT[+|-]HH:mm[:ss]
+    id.setTo(gCustomTzPrefix, -1);
+    if (hour != 0 || min != 0) {
+        if (negative) {
+            id.append(0x2D);    // '-'
+        } else {
+            id.append(0x2B);    // '+'
+        }
+        // Always use US-ASCII digits
+        id.append(0x30 + (hour%100)/10);
+        id.append(0x30 + (hour%10));
+        id.append(0x3A);    // ':'
+        id.append(0x30 + (min%100)/10);
+        id.append(0x30 + (min%10));
+        if (sec != 0) {
+            id.append(0x3A);    // ':'
+            id.append(0x30 + (sec%100)/10);
+            id.append(0x30 + (sec%10));
+        }
+    }
+    return id;
 }