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