Home | History | Annotate | Download | only in i18n
      1 /*
      2 *******************************************************************************
      3 * Copyright (C) 2007-2014, International Business Machines Corporation and
      4 * others. All Rights Reserved.
      5 *******************************************************************************
      6 */
      7 
      8 #include "unicode/utypes.h"
      9 
     10 #if !UCONFIG_NO_FORMATTING
     11 
     12 #include "zonemeta.h"
     13 
     14 #include "unicode/timezone.h"
     15 #include "unicode/ustring.h"
     16 #include "unicode/putil.h"
     17 #include "unicode/simpletz.h"
     18 
     19 #include "umutex.h"
     20 #include "uvector.h"
     21 #include "cmemory.h"
     22 #include "gregoimp.h"
     23 #include "cstring.h"
     24 #include "ucln_in.h"
     25 #include "uassert.h"
     26 #include "uresimp.h"
     27 #include "uhash.h"
     28 #include "olsontz.h"
     29 
     30 static UMutex gZoneMetaLock = U_MUTEX_INITIALIZER;
     31 
     32 // CLDR Canonical ID mapping table
     33 static UHashtable *gCanonicalIDCache = NULL;
     34 static icu::UInitOnce gCanonicalIDCacheInitOnce = U_INITONCE_INITIALIZER;
     35 
     36 // Metazone mapping table
     37 static UHashtable *gOlsonToMeta = NULL;
     38 static icu::UInitOnce gOlsonToMetaInitOnce = U_INITONCE_INITIALIZER;
     39 
     40 // Available metazone IDs vector and table
     41 static icu::UVector *gMetaZoneIDs = NULL;
     42 static UHashtable *gMetaZoneIDTable = NULL;
     43 static icu::UInitOnce gMetaZoneIDsInitOnce = U_INITONCE_INITIALIZER;
     44 
     45 // Country info vectors
     46 static icu::UVector *gSingleZoneCountries = NULL;
     47 static icu::UVector *gMultiZonesCountries = NULL;
     48 static icu::UInitOnce gCountryInfoVectorsInitOnce = U_INITONCE_INITIALIZER;
     49 
     50 U_CDECL_BEGIN
     51 
     52 /**
     53  * Cleanup callback func
     54  */
     55 static UBool U_CALLCONV zoneMeta_cleanup(void)
     56 {
     57     if (gCanonicalIDCache != NULL) {
     58         uhash_close(gCanonicalIDCache);
     59         gCanonicalIDCache = NULL;
     60     }
     61     gCanonicalIDCacheInitOnce.reset();
     62 
     63     if (gOlsonToMeta != NULL) {
     64         uhash_close(gOlsonToMeta);
     65         gOlsonToMeta = NULL;
     66     }
     67     gOlsonToMetaInitOnce.reset();
     68 
     69     if (gMetaZoneIDTable != NULL) {
     70         uhash_close(gMetaZoneIDTable);
     71         gMetaZoneIDTable = NULL;
     72     }
     73     // delete after closing gMetaZoneIDTable, because it holds
     74     // value objects held by the hashtable
     75     delete gMetaZoneIDs;
     76     gMetaZoneIDs = NULL;
     77     gMetaZoneIDsInitOnce.reset();
     78 
     79     delete gSingleZoneCountries;
     80     gSingleZoneCountries = NULL;
     81     delete gMultiZonesCountries;
     82     gMultiZonesCountries = NULL;
     83     gCountryInfoVectorsInitOnce.reset();
     84 
     85     return TRUE;
     86 }
     87 
     88 /**
     89  * Deleter for UChar* string
     90  */
     91 static void U_CALLCONV
     92 deleteUCharString(void *obj) {
     93     UChar *entry = (UChar*)obj;
     94     uprv_free(entry);
     95 }
     96 
     97 /**
     98  * Deleter for UVector
     99  */
    100 static void U_CALLCONV
    101 deleteUVector(void *obj) {
    102    delete (icu::UVector*) obj;
    103 }
    104 
    105 /**
    106  * Deleter for OlsonToMetaMappingEntry
    107  */
    108 static void U_CALLCONV
    109 deleteOlsonToMetaMappingEntry(void *obj) {
    110     icu::OlsonToMetaMappingEntry *entry = (icu::OlsonToMetaMappingEntry*)obj;
    111     uprv_free(entry);
    112 }
    113 
    114 U_CDECL_END
    115 
    116 U_NAMESPACE_BEGIN
    117 
    118 #define ZID_KEY_MAX 128
    119 
    120 static const char gMetaZones[]          = "metaZones";
    121 static const char gMetazoneInfo[]       = "metazoneInfo";
    122 static const char gMapTimezonesTag[]    = "mapTimezones";
    123 
    124 static const char gKeyTypeData[]        = "keyTypeData";
    125 static const char gTypeAliasTag[]       = "typeAlias";
    126 static const char gTypeMapTag[]         = "typeMap";
    127 static const char gTimezoneTag[]        = "timezone";
    128 
    129 static const char gPrimaryZonesTag[]    = "primaryZones";
    130 
    131 static const char gWorldTag[]           = "001";
    132 
    133 static const UChar gWorld[] = {0x30, 0x30, 0x31, 0x00}; // "001"
    134 
    135 static const UChar gDefaultFrom[] = {0x31, 0x39, 0x37, 0x30, 0x2D, 0x30, 0x31, 0x2D, 0x30, 0x31,
    136                                      0x20, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x00}; // "1970-01-01 00:00"
    137 static const UChar gDefaultTo[]   = {0x39, 0x39, 0x39, 0x39, 0x2D, 0x31, 0x32, 0x2D, 0x33, 0x31,
    138                                      0x20, 0x32, 0x33, 0x3A, 0x35, 0x39, 0x00}; // "9999-12-31 23:59"
    139 
    140 static const UChar gCustomTzPrefix[]    = {0x47, 0x4D, 0x54, 0};    // "GMT"
    141 
    142 #define ASCII_DIGIT(c) (((c)>=0x30 && (c)<=0x39) ? (c)-0x30 : -1)
    143 
    144 /*
    145  * Convert a date string used by metazone mappings to UDate.
    146  * The format used by CLDR metazone mapping is "yyyy-MM-dd HH:mm".
    147  */
    148 static UDate
    149 parseDate (const UChar *text, UErrorCode &status) {
    150     if (U_FAILURE(status)) {
    151         return 0;
    152     }
    153     int32_t len = u_strlen(text);
    154     if (len != 16 && len != 10) {
    155         // It must be yyyy-MM-dd HH:mm (length 16) or yyyy-MM-dd (length 10)
    156         status = U_INVALID_FORMAT_ERROR;
    157         return 0;
    158     }
    159 
    160     int32_t year = 0, month = 0, day = 0, hour = 0, min = 0, n;
    161     int32_t idx;
    162 
    163     // "yyyy" (0 - 3)
    164     for (idx = 0; idx <= 3 && U_SUCCESS(status); idx++) {
    165         n = ASCII_DIGIT((int32_t)text[idx]);
    166         if (n >= 0) {
    167             year = 10*year + n;
    168         } else {
    169             status = U_INVALID_FORMAT_ERROR;
    170         }
    171     }
    172     // "MM" (5 - 6)
    173     for (idx = 5; idx <= 6 && U_SUCCESS(status); idx++) {
    174         n = ASCII_DIGIT((int32_t)text[idx]);
    175         if (n >= 0) {
    176             month = 10*month + n;
    177         } else {
    178             status = U_INVALID_FORMAT_ERROR;
    179         }
    180     }
    181     // "dd" (8 - 9)
    182     for (idx = 8; idx <= 9 && U_SUCCESS(status); idx++) {
    183         n = ASCII_DIGIT((int32_t)text[idx]);
    184         if (n >= 0) {
    185             day = 10*day + n;
    186         } else {
    187             status = U_INVALID_FORMAT_ERROR;
    188         }
    189     }
    190     if (len == 16) {
    191         // "HH" (11 - 12)
    192         for (idx = 11; idx <= 12 && U_SUCCESS(status); idx++) {
    193             n = ASCII_DIGIT((int32_t)text[idx]);
    194             if (n >= 0) {
    195                 hour = 10*hour + n;
    196             } else {
    197                 status = U_INVALID_FORMAT_ERROR;
    198             }
    199         }
    200         // "mm" (14 - 15)
    201         for (idx = 14; idx <= 15 && U_SUCCESS(status); idx++) {
    202             n = ASCII_DIGIT((int32_t)text[idx]);
    203             if (n >= 0) {
    204                 min = 10*min + n;
    205             } else {
    206                 status = U_INVALID_FORMAT_ERROR;
    207             }
    208         }
    209     }
    210 
    211     if (U_SUCCESS(status)) {
    212         UDate date = Grego::fieldsToDay(year, month - 1, day) * U_MILLIS_PER_DAY
    213             + hour * U_MILLIS_PER_HOUR + min * U_MILLIS_PER_MINUTE;
    214         return date;
    215     }
    216     return 0;
    217 }
    218 
    219 static void U_CALLCONV initCanonicalIDCache(UErrorCode &status) {
    220     gCanonicalIDCache = uhash_open(uhash_hashUChars, uhash_compareUChars, NULL, &status);
    221     if (gCanonicalIDCache == NULL) {
    222         status = U_MEMORY_ALLOCATION_ERROR;
    223     }
    224     if (U_FAILURE(status)) {
    225         gCanonicalIDCache = NULL;
    226     }
    227     // No key/value deleters - keys/values are from a resource bundle
    228     ucln_i18n_registerCleanup(UCLN_I18N_ZONEMETA, zoneMeta_cleanup);
    229 }
    230 
    231 
    232 const UChar* U_EXPORT2
    233 ZoneMeta::getCanonicalCLDRID(const UnicodeString &tzid, UErrorCode& status) {
    234     if (U_FAILURE(status)) {
    235         return NULL;
    236     }
    237 
    238     if (tzid.isBogus() || tzid.length() > ZID_KEY_MAX) {
    239         status = U_ILLEGAL_ARGUMENT_ERROR;
    240         return NULL;
    241     }
    242 
    243     // Checking the cached results
    244     umtx_initOnce(gCanonicalIDCacheInitOnce, &initCanonicalIDCache, status);
    245     if (U_FAILURE(status)) {
    246         return NULL;
    247     }
    248 
    249     const UChar *canonicalID = NULL;
    250 
    251     UErrorCode tmpStatus = U_ZERO_ERROR;
    252     UChar utzid[ZID_KEY_MAX + 1];
    253     tzid.extract(utzid, ZID_KEY_MAX + 1, tmpStatus);
    254     U_ASSERT(tmpStatus == U_ZERO_ERROR);    // we checked the length of tzid already
    255 
    256     // Check if it was already cached
    257     umtx_lock(&gZoneMetaLock);
    258     {
    259         canonicalID = (const UChar *)uhash_get(gCanonicalIDCache, utzid);
    260     }
    261     umtx_unlock(&gZoneMetaLock);
    262 
    263     if (canonicalID != NULL) {
    264         return canonicalID;
    265     }
    266 
    267     // If not, resolve CLDR canonical ID with resource data
    268     UBool isInputCanonical = FALSE;
    269     char id[ZID_KEY_MAX + 1];
    270     tzid.extract(0, 0x7fffffff, id, UPRV_LENGTHOF(id), US_INV);
    271 
    272     // replace '/' with ':'
    273     char *p = id;
    274     while (*p++) {
    275         if (*p == '/') {
    276             *p = ':';
    277         }
    278     }
    279 
    280     UResourceBundle *top = ures_openDirect(NULL, gKeyTypeData, &tmpStatus);
    281     UResourceBundle *rb = ures_getByKey(top, gTypeMapTag, NULL, &tmpStatus);
    282     ures_getByKey(rb, gTimezoneTag, rb, &tmpStatus);
    283     ures_getByKey(rb, id, rb, &tmpStatus);
    284     if (U_SUCCESS(tmpStatus)) {
    285         // type entry (canonical) found
    286         // the input is the canonical ID. resolve to const UChar*
    287         canonicalID = TimeZone::findID(tzid);
    288         isInputCanonical = TRUE;
    289     }
    290 
    291     if (canonicalID == NULL) {
    292         // If a map element not found, then look for an alias
    293         tmpStatus = U_ZERO_ERROR;
    294         ures_getByKey(top, gTypeAliasTag, rb, &tmpStatus);
    295         ures_getByKey(rb, gTimezoneTag, rb, &tmpStatus);
    296         const UChar *canonical = ures_getStringByKey(rb,id,NULL,&tmpStatus);
    297         if (U_SUCCESS(tmpStatus)) {
    298             // canonical map found
    299             canonicalID = canonical;
    300         }
    301 
    302         if (canonicalID == NULL) {
    303             // Dereference the input ID using the tz data
    304             const UChar *derefer = TimeZone::dereferOlsonLink(tzid);
    305             if (derefer == NULL) {
    306                 status = U_ILLEGAL_ARGUMENT_ERROR;
    307             } else {
    308                 int32_t len = u_strlen(derefer);
    309                 u_UCharsToChars(derefer,id,len);
    310                 id[len] = (char) 0; // Make sure it is null terminated.
    311 
    312                 // replace '/' with ':'
    313                 char *p = id;
    314                 while (*p++) {
    315                     if (*p == '/') {
    316                         *p = ':';
    317                     }
    318                 }
    319 
    320                 // If a dereference turned something up then look for an alias.
    321                 // rb still points to the alias table, so we don't have to go looking
    322                 // for it.
    323                 tmpStatus = U_ZERO_ERROR;
    324                 canonical = ures_getStringByKey(rb,id,NULL,&tmpStatus);
    325                 if (U_SUCCESS(tmpStatus)) {
    326                     // canonical map for the dereferenced ID found
    327                     canonicalID = canonical;
    328                 } else {
    329                     canonicalID = derefer;
    330                     isInputCanonical = TRUE;
    331                 }
    332             }
    333         }
    334     }
    335     ures_close(rb);
    336     ures_close(top);
    337 
    338     if (U_SUCCESS(status)) {
    339         U_ASSERT(canonicalID != NULL);  // canocanilD must be non-NULL here
    340 
    341         // Put the resolved canonical ID to the cache
    342         umtx_lock(&gZoneMetaLock);
    343         {
    344             const UChar* idInCache = (const UChar *)uhash_get(gCanonicalIDCache, utzid);
    345             if (idInCache == NULL) {
    346                 const UChar* key = ZoneMeta::findTimeZoneID(tzid);
    347                 U_ASSERT(key != NULL);
    348                 if (key != NULL) {
    349                     idInCache = (const UChar *)uhash_put(gCanonicalIDCache, (void *)key, (void *)canonicalID, &status);
    350                     U_ASSERT(idInCache == NULL);
    351                 }
    352             }
    353             if (U_SUCCESS(status) && isInputCanonical) {
    354                 // Also put canonical ID itself into the cache if not exist
    355                 const UChar *canonicalInCache = (const UChar*)uhash_get(gCanonicalIDCache, canonicalID);
    356                 if (canonicalInCache == NULL) {
    357                     canonicalInCache = (const UChar *)uhash_put(gCanonicalIDCache, (void *)canonicalID, (void *)canonicalID, &status);
    358                     U_ASSERT(canonicalInCache == NULL);
    359                 }
    360             }
    361         }
    362         umtx_unlock(&gZoneMetaLock);
    363     }
    364 
    365     return canonicalID;
    366 }
    367 
    368 UnicodeString& U_EXPORT2
    369 ZoneMeta::getCanonicalCLDRID(const UnicodeString &tzid, UnicodeString &systemID, UErrorCode& status) {
    370     const UChar *canonicalID = getCanonicalCLDRID(tzid, status);
    371     if (U_FAILURE(status) || canonicalID == NULL) {
    372         systemID.setToBogus();
    373         return systemID;
    374     }
    375     systemID.setTo(TRUE, canonicalID, -1);
    376     return systemID;
    377 }
    378 
    379 const UChar* U_EXPORT2
    380 ZoneMeta::getCanonicalCLDRID(const TimeZone& tz) {
    381     if (dynamic_cast<const OlsonTimeZone *>(&tz) != NULL) {
    382         // short cut for OlsonTimeZone
    383         const OlsonTimeZone *otz = (const OlsonTimeZone*)&tz;
    384         return otz->getCanonicalID();
    385     }
    386     UErrorCode status = U_ZERO_ERROR;
    387     UnicodeString tzID;
    388     return getCanonicalCLDRID(tz.getID(tzID), status);
    389 }
    390 
    391 static void U_CALLCONV countryInfoVectorsInit(UErrorCode &status) {
    392     // Create empty vectors
    393     // No deleters for these UVectors, it's a reference to a resource bundle string.
    394     gSingleZoneCountries = new UVector(NULL, uhash_compareUChars, status);
    395     if (gSingleZoneCountries == NULL) {
    396         status = U_MEMORY_ALLOCATION_ERROR;
    397     }
    398     gMultiZonesCountries = new UVector(NULL, uhash_compareUChars, status);
    399     if (gMultiZonesCountries == NULL) {
    400         status = U_MEMORY_ALLOCATION_ERROR;
    401     }
    402 
    403     if (U_FAILURE(status)) {
    404         delete gSingleZoneCountries;
    405         delete gMultiZonesCountries;
    406         gSingleZoneCountries = NULL;
    407         gMultiZonesCountries  = NULL;
    408     }
    409     ucln_i18n_registerCleanup(UCLN_I18N_ZONEMETA, zoneMeta_cleanup);
    410 }
    411 
    412 
    413 UnicodeString& U_EXPORT2
    414 ZoneMeta::getCanonicalCountry(const UnicodeString &tzid, UnicodeString &country, UBool *isPrimary /* = NULL */) {
    415     if (isPrimary != NULL) {
    416         *isPrimary = FALSE;
    417     }
    418 
    419     const UChar *region = TimeZone::getRegion(tzid);
    420     if (region != NULL && u_strcmp(gWorld, region) != 0) {
    421         country.setTo(region, -1);
    422     } else {
    423         country.setToBogus();
    424         return country;
    425     }
    426 
    427     if (isPrimary != NULL) {
    428         char regionBuf[] = {0, 0, 0};
    429 
    430         // Checking the cached results
    431         UErrorCode status = U_ZERO_ERROR;
    432         umtx_initOnce(gCountryInfoVectorsInitOnce, &countryInfoVectorsInit, status);
    433         if (U_FAILURE(status)) {
    434             return country;
    435         }
    436 
    437         // Check if it was already cached
    438         UBool cached = FALSE;
    439         UBool singleZone = FALSE;
    440         umtx_lock(&gZoneMetaLock);
    441         {
    442             singleZone = cached = gSingleZoneCountries->contains((void*)region);
    443             if (!cached) {
    444                 cached = gMultiZonesCountries->contains((void*)region);
    445             }
    446         }
    447         umtx_unlock(&gZoneMetaLock);
    448 
    449         if (!cached) {
    450             // We need to go through all zones associated with the region.
    451             // This is relatively heavy operation.
    452 
    453             U_ASSERT(u_strlen(region) == 2);
    454 
    455             u_UCharsToChars(region, regionBuf, 2);
    456 
    457             StringEnumeration *ids = TimeZone::createTimeZoneIDEnumeration(UCAL_ZONE_TYPE_CANONICAL_LOCATION, regionBuf, NULL, status);
    458             int32_t idsLen = ids->count(status);
    459             if (U_SUCCESS(status) && idsLen == 1) {
    460                 // only the single zone is available for the region
    461                 singleZone = TRUE;
    462             }
    463             delete ids;
    464 
    465             // Cache the result
    466             umtx_lock(&gZoneMetaLock);
    467             {
    468                 UErrorCode ec = U_ZERO_ERROR;
    469                 if (singleZone) {
    470                     if (!gSingleZoneCountries->contains((void*)region)) {
    471                         gSingleZoneCountries->addElement((void*)region, ec);
    472                     }
    473                 } else {
    474                     if (!gMultiZonesCountries->contains((void*)region)) {
    475                         gMultiZonesCountries->addElement((void*)region, ec);
    476                     }
    477                 }
    478             }
    479             umtx_unlock(&gZoneMetaLock);
    480         }
    481 
    482         if (singleZone) {
    483             *isPrimary = TRUE;
    484         } else {
    485             // Note: We may cache the primary zone map in future.
    486 
    487             // Even a country has multiple zones, one of them might be
    488             // dominant and treated as a primary zone
    489             int32_t idLen = 0;
    490             if (regionBuf[0] == 0) {
    491                 u_UCharsToChars(region, regionBuf, 2);
    492             }
    493 
    494             UResourceBundle *rb = ures_openDirect(NULL, gMetaZones, &status);
    495             ures_getByKey(rb, gPrimaryZonesTag, rb, &status);
    496             const UChar *primaryZone = ures_getStringByKey(rb, regionBuf, &idLen, &status);
    497             if (U_SUCCESS(status)) {
    498                 if (tzid.compare(primaryZone, idLen) == 0) {
    499                     *isPrimary = TRUE;
    500                 } else {
    501                     // The given ID might not be a canonical ID
    502                     UnicodeString canonicalID;
    503                     TimeZone::getCanonicalID(tzid, canonicalID, status);
    504                     if (U_SUCCESS(status) && canonicalID.compare(primaryZone, idLen) == 0) {
    505                         *isPrimary = TRUE;
    506                     }
    507                 }
    508             }
    509             ures_close(rb);
    510         }
    511     }
    512 
    513     return country;
    514 }
    515 
    516 UnicodeString& U_EXPORT2
    517 ZoneMeta::getMetazoneID(const UnicodeString &tzid, UDate date, UnicodeString &result) {
    518     UBool isSet = FALSE;
    519     const UVector *mappings = getMetazoneMappings(tzid);
    520     if (mappings != NULL) {
    521         for (int32_t i = 0; i < mappings->size(); i++) {
    522             OlsonToMetaMappingEntry *mzm = (OlsonToMetaMappingEntry*)mappings->elementAt(i);
    523             if (mzm->from <= date && mzm->to > date) {
    524                 result.setTo(mzm->mzid, -1);
    525                 isSet = TRUE;
    526                 break;
    527             }
    528         }
    529     }
    530     if (!isSet) {
    531         result.setToBogus();
    532     }
    533     return result;
    534 }
    535 
    536 static void U_CALLCONV olsonToMetaInit(UErrorCode &status) {
    537     U_ASSERT(gOlsonToMeta == NULL);
    538     ucln_i18n_registerCleanup(UCLN_I18N_ZONEMETA, zoneMeta_cleanup);
    539     gOlsonToMeta = uhash_open(uhash_hashUChars, uhash_compareUChars, NULL, &status);
    540     if (U_FAILURE(status)) {
    541         gOlsonToMeta = NULL;
    542     } else {
    543         uhash_setKeyDeleter(gOlsonToMeta, deleteUCharString);
    544         uhash_setValueDeleter(gOlsonToMeta, deleteUVector);
    545     }
    546 }
    547 
    548 
    549 const UVector* U_EXPORT2
    550 ZoneMeta::getMetazoneMappings(const UnicodeString &tzid) {
    551     UErrorCode status = U_ZERO_ERROR;
    552     UChar tzidUChars[ZID_KEY_MAX + 1];
    553     tzid.extract(tzidUChars, ZID_KEY_MAX + 1, status);
    554     if (U_FAILURE(status) || status == U_STRING_NOT_TERMINATED_WARNING) {
    555         return NULL;
    556     }
    557 
    558     umtx_initOnce(gOlsonToMetaInitOnce, &olsonToMetaInit, status);
    559     if (U_FAILURE(status)) {
    560         return NULL;
    561     }
    562 
    563     // get the mapping from cache
    564     const UVector *result = NULL;
    565 
    566     umtx_lock(&gZoneMetaLock);
    567     {
    568         result = (UVector*) uhash_get(gOlsonToMeta, tzidUChars);
    569     }
    570     umtx_unlock(&gZoneMetaLock);
    571 
    572     if (result != NULL) {
    573         return result;
    574     }
    575 
    576     // miss the cache - create new one
    577     UVector *tmpResult = createMetazoneMappings(tzid);
    578     if (tmpResult == NULL) {
    579         // not available
    580         return NULL;
    581     }
    582 
    583     // put the new one into the cache
    584     umtx_lock(&gZoneMetaLock);
    585     {
    586         // make sure it's already created
    587         result = (UVector*) uhash_get(gOlsonToMeta, tzidUChars);
    588         if (result == NULL) {
    589             // add the one just created
    590             int32_t tzidLen = tzid.length() + 1;
    591             UChar *key = (UChar*)uprv_malloc(tzidLen * sizeof(UChar));
    592             if (key == NULL) {
    593                 // memory allocation error..  just return NULL
    594                 result = NULL;
    595                 delete tmpResult;
    596             } else {
    597                 tzid.extract(key, tzidLen, status);
    598                 uhash_put(gOlsonToMeta, key, tmpResult, &status);
    599                 if (U_FAILURE(status)) {
    600                     // delete the mapping
    601                     result = NULL;
    602                     delete tmpResult;
    603                 } else {
    604                     result = tmpResult;
    605                 }
    606             }
    607         } else {
    608             // another thread already put the one
    609             delete tmpResult;
    610         }
    611     }
    612     umtx_unlock(&gZoneMetaLock);
    613 
    614     return result;
    615 }
    616 
    617 UVector*
    618 ZoneMeta::createMetazoneMappings(const UnicodeString &tzid) {
    619     UVector *mzMappings = NULL;
    620     UErrorCode status = U_ZERO_ERROR;
    621 
    622     UnicodeString canonicalID;
    623     UResourceBundle *rb = ures_openDirect(NULL, gMetaZones, &status);
    624     ures_getByKey(rb, gMetazoneInfo, rb, &status);
    625     getCanonicalCLDRID(tzid, canonicalID, status);
    626 
    627     if (U_SUCCESS(status)) {
    628         char tzKey[ZID_KEY_MAX + 1];
    629         int32_t tzKeyLen = canonicalID.extract(0, canonicalID.length(), tzKey, sizeof(tzKey), US_INV);
    630         tzKey[tzKeyLen] = 0;
    631 
    632         // tzid keys are using ':' as separators
    633         char *p = tzKey;
    634         while (*p) {
    635             if (*p == '/') {
    636                 *p = ':';
    637             }
    638             p++;
    639         }
    640 
    641         ures_getByKey(rb, tzKey, rb, &status);
    642 
    643         if (U_SUCCESS(status)) {
    644             UResourceBundle *mz = NULL;
    645             while (ures_hasNext(rb)) {
    646                 mz = ures_getNextResource(rb, mz, &status);
    647 
    648                 const UChar *mz_name = ures_getStringByIndex(mz, 0, NULL, &status);
    649                 const UChar *mz_from = gDefaultFrom;
    650                 const UChar *mz_to = gDefaultTo;
    651 
    652                 if (ures_getSize(mz) == 3) {
    653                     mz_from = ures_getStringByIndex(mz, 1, NULL, &status);
    654                     mz_to   = ures_getStringByIndex(mz, 2, NULL, &status);
    655                 }
    656 
    657                 if(U_FAILURE(status)){
    658                     status = U_ZERO_ERROR;
    659                     continue;
    660                 }
    661                 // We do not want to use SimpleDateformat to parse boundary dates,
    662                 // because this code could be triggered by the initialization code
    663                 // used by SimpleDateFormat.
    664                 UDate from = parseDate(mz_from, status);
    665                 UDate to = parseDate(mz_to, status);
    666                 if (U_FAILURE(status)) {
    667                     status = U_ZERO_ERROR;
    668                     continue;
    669                 }
    670 
    671                 OlsonToMetaMappingEntry *entry = (OlsonToMetaMappingEntry*)uprv_malloc(sizeof(OlsonToMetaMappingEntry));
    672                 if (entry == NULL) {
    673                     status = U_MEMORY_ALLOCATION_ERROR;
    674                     break;
    675                 }
    676                 entry->mzid = mz_name;
    677                 entry->from = from;
    678                 entry->to = to;
    679 
    680                 if (mzMappings == NULL) {
    681                     mzMappings = new UVector(deleteOlsonToMetaMappingEntry, NULL, status);
    682                     if (U_FAILURE(status)) {
    683                         delete mzMappings;
    684                         deleteOlsonToMetaMappingEntry(entry);
    685                         uprv_free(entry);
    686                         break;
    687                     }
    688                 }
    689 
    690                 mzMappings->addElement(entry, status);
    691                 if (U_FAILURE(status)) {
    692                     break;
    693                 }
    694             }
    695             ures_close(mz);
    696             if (U_FAILURE(status)) {
    697                 if (mzMappings != NULL) {
    698                     delete mzMappings;
    699                     mzMappings = NULL;
    700                 }
    701             }
    702         }
    703     }
    704     ures_close(rb);
    705     return mzMappings;
    706 }
    707 
    708 UnicodeString& U_EXPORT2
    709 ZoneMeta::getZoneIdByMetazone(const UnicodeString &mzid, const UnicodeString &region, UnicodeString &result) {
    710     UErrorCode status = U_ZERO_ERROR;
    711     const UChar *tzid = NULL;
    712     int32_t tzidLen = 0;
    713     char keyBuf[ZID_KEY_MAX + 1];
    714     int32_t keyLen = 0;
    715 
    716     if (mzid.isBogus() || mzid.length() > ZID_KEY_MAX) {
    717         result.setToBogus();
    718         return result;
    719     }
    720 
    721     keyLen = mzid.extract(0, mzid.length(), keyBuf, ZID_KEY_MAX + 1, US_INV);
    722     keyBuf[keyLen] = 0;
    723 
    724     UResourceBundle *rb = ures_openDirect(NULL, gMetaZones, &status);
    725     ures_getByKey(rb, gMapTimezonesTag, rb, &status);
    726     ures_getByKey(rb, keyBuf, rb, &status);
    727 
    728     if (U_SUCCESS(status)) {
    729         // check region mapping
    730         if (region.length() == 2 || region.length() == 3) {
    731             keyLen = region.extract(0, region.length(), keyBuf, ZID_KEY_MAX + 1, US_INV);
    732             keyBuf[keyLen] = 0;
    733             tzid = ures_getStringByKey(rb, keyBuf, &tzidLen, &status);
    734             if (status == U_MISSING_RESOURCE_ERROR) {
    735                 status = U_ZERO_ERROR;
    736             }
    737         }
    738         if (U_SUCCESS(status) && tzid == NULL) {
    739             // try "001"
    740             tzid = ures_getStringByKey(rb, gWorldTag, &tzidLen, &status);
    741         }
    742     }
    743     ures_close(rb);
    744 
    745     if (tzid == NULL) {
    746         result.setToBogus();
    747     } else {
    748         result.setTo(tzid, tzidLen);
    749     }
    750 
    751     return result;
    752 }
    753 
    754 static void U_CALLCONV initAvailableMetaZoneIDs () {
    755     U_ASSERT(gMetaZoneIDs == NULL);
    756     U_ASSERT(gMetaZoneIDTable == NULL);
    757     ucln_i18n_registerCleanup(UCLN_I18N_ZONEMETA, zoneMeta_cleanup);
    758 
    759     UErrorCode status = U_ZERO_ERROR;
    760     gMetaZoneIDTable = uhash_open(uhash_hashUnicodeString, uhash_compareUnicodeString, NULL, &status);
    761     if (U_FAILURE(status) || gMetaZoneIDTable == NULL) {
    762         gMetaZoneIDTable = NULL;
    763         return;
    764     }
    765     uhash_setKeyDeleter(gMetaZoneIDTable, uprv_deleteUObject);
    766     // No valueDeleter, because the vector maintain the value objects
    767     gMetaZoneIDs = new UVector(NULL, uhash_compareUChars, status);
    768     if (U_FAILURE(status) || gMetaZoneIDs == NULL) {
    769         gMetaZoneIDs = NULL;
    770         uhash_close(gMetaZoneIDTable);
    771         gMetaZoneIDTable = NULL;
    772         return;
    773     }
    774     gMetaZoneIDs->setDeleter(uprv_free);
    775 
    776     UResourceBundle *rb = ures_openDirect(NULL, gMetaZones, &status);
    777     UResourceBundle *bundle = ures_getByKey(rb, gMapTimezonesTag, NULL, &status);
    778     UResourceBundle res;
    779     ures_initStackObject(&res);
    780     while (U_SUCCESS(status) && ures_hasNext(bundle)) {
    781         ures_getNextResource(bundle, &res, &status);
    782         if (U_FAILURE(status)) {
    783             break;
    784         }
    785         const char *mzID = ures_getKey(&res);
    786         int32_t len = uprv_strlen(mzID);
    787         UChar *uMzID = (UChar*)uprv_malloc(sizeof(UChar) * (len + 1));
    788         if (uMzID == NULL) {
    789             status = U_MEMORY_ALLOCATION_ERROR;
    790             break;
    791         }
    792         u_charsToUChars(mzID, uMzID, len);
    793         uMzID[len] = 0;
    794         UnicodeString *usMzID = new UnicodeString(uMzID);
    795         if (uhash_get(gMetaZoneIDTable, usMzID) == NULL) {
    796             gMetaZoneIDs->addElement((void *)uMzID, status);
    797             uhash_put(gMetaZoneIDTable, (void *)usMzID, (void *)uMzID, &status);
    798         } else {
    799             uprv_free(uMzID);
    800             delete usMzID;
    801         }
    802     }
    803     ures_close(&res);
    804     ures_close(bundle);
    805     ures_close(rb);
    806 
    807     if (U_FAILURE(status)) {
    808         uhash_close(gMetaZoneIDTable);
    809         delete gMetaZoneIDs;
    810         gMetaZoneIDTable = NULL;
    811         gMetaZoneIDs = NULL;
    812     }
    813 }
    814 
    815 const UVector*
    816 ZoneMeta::getAvailableMetazoneIDs() {
    817     umtx_initOnce(gMetaZoneIDsInitOnce, &initAvailableMetaZoneIDs);
    818     return gMetaZoneIDs;
    819 }
    820 
    821 const UChar*
    822 ZoneMeta::findMetaZoneID(const UnicodeString& mzid) {
    823     umtx_initOnce(gMetaZoneIDsInitOnce, &initAvailableMetaZoneIDs);
    824     if (gMetaZoneIDTable == NULL) {
    825         return NULL;
    826     }
    827     return (const UChar*)uhash_get(gMetaZoneIDTable, &mzid);
    828 }
    829 
    830 const UChar*
    831 ZoneMeta::findTimeZoneID(const UnicodeString& tzid) {
    832     return TimeZone::findID(tzid);
    833 }
    834 
    835 
    836 TimeZone*
    837 ZoneMeta::createCustomTimeZone(int32_t offset) {
    838     UBool negative = FALSE;
    839     int32_t tmp = offset;
    840     if (offset < 0) {
    841         negative = TRUE;
    842         tmp = -offset;
    843     }
    844     int32_t hour, min, sec;
    845 
    846     tmp /= 1000;
    847     sec = tmp % 60;
    848     tmp /= 60;
    849     min = tmp % 60;
    850     hour = tmp / 60;
    851 
    852     UnicodeString zid;
    853     formatCustomID(hour, min, sec, negative, zid);
    854     return new SimpleTimeZone(offset, zid);
    855 }
    856 
    857 UnicodeString&
    858 ZoneMeta::formatCustomID(uint8_t hour, uint8_t min, uint8_t sec, UBool negative, UnicodeString& id) {
    859     // Create normalized time zone ID - GMT[+|-]HH:mm[:ss]
    860     id.setTo(gCustomTzPrefix, -1);
    861     if (hour != 0 || min != 0) {
    862         if (negative) {
    863           id.append((UChar)0x2D);    // '-'
    864         } else {
    865           id.append((UChar)0x2B);    // '+'
    866         }
    867         // Always use US-ASCII digits
    868         id.append((UChar)(0x30 + (hour%100)/10));
    869         id.append((UChar)(0x30 + (hour%10)));
    870         id.append((UChar)0x3A);    // ':'
    871         id.append((UChar)(0x30 + (min%100)/10));
    872         id.append((UChar)(0x30 + (min%10)));
    873         if (sec != 0) {
    874           id.append((UChar)0x3A);    // ':'
    875           id.append((UChar)(0x30 + (sec%100)/10));
    876           id.append((UChar)(0x30 + (sec%10)));
    877         }
    878     }
    879     return id;
    880 }
    881 
    882 const UChar*
    883 ZoneMeta::getShortID(const TimeZone& tz) {
    884     const UChar* canonicalID = NULL;
    885     if (dynamic_cast<const OlsonTimeZone *>(&tz) != NULL) {
    886         // short cut for OlsonTimeZone
    887         const OlsonTimeZone *otz = (const OlsonTimeZone*)&tz;
    888         canonicalID = otz->getCanonicalID();
    889     }
    890     if (canonicalID == NULL) {
    891         return NULL;
    892     }
    893     return getShortIDFromCanonical(canonicalID);
    894 }
    895 
    896 const UChar*
    897 ZoneMeta::getShortID(const UnicodeString& id) {
    898     UErrorCode status = U_ZERO_ERROR;
    899     const UChar* canonicalID = ZoneMeta::getCanonicalCLDRID(id, status);
    900     if (U_FAILURE(status) || canonicalID == NULL) {
    901         return NULL;
    902     }
    903     return ZoneMeta::getShortIDFromCanonical(canonicalID);
    904 }
    905 
    906 const UChar*
    907 ZoneMeta::getShortIDFromCanonical(const UChar* canonicalID) {
    908     const UChar* shortID = NULL;
    909     int32_t len = u_strlen(canonicalID);
    910     char tzidKey[ZID_KEY_MAX + 1];
    911 
    912     u_UCharsToChars(canonicalID, tzidKey, len);
    913     tzidKey[len] = (char) 0; // Make sure it is null terminated.
    914 
    915     // replace '/' with ':'
    916     char *p = tzidKey;
    917     while (*p++) {
    918         if (*p == '/') {
    919             *p = ':';
    920         }
    921     }
    922 
    923     UErrorCode status = U_ZERO_ERROR;
    924     UResourceBundle *rb = ures_openDirect(NULL, gKeyTypeData, &status);
    925     ures_getByKey(rb, gTypeMapTag, rb, &status);
    926     ures_getByKey(rb, gTimezoneTag, rb, &status);
    927     shortID = ures_getStringByKey(rb, tzidKey, NULL, &status);
    928     ures_close(rb);
    929 
    930     return shortID;
    931 }
    932 
    933 U_NAMESPACE_END
    934 
    935 #endif /* #if !UCONFIG_NO_FORMATTING */
    936