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