Home | History | Annotate | Download | only in i18n
      1 /*
      2 *******************************************************************************
      3 * Copyright (C) 2007-2011, 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 
     18 #include "umutex.h"
     19 #include "uvector.h"
     20 #include "cmemory.h"
     21 #include "gregoimp.h"
     22 #include "cstring.h"
     23 #include "ucln_in.h"
     24 #include "uassert.h"
     25 #include "uresimp.h"
     26 #include "uhash.h"
     27 #include "olsontz.h"
     28 
     29 static UMTX gZoneMetaLock = NULL;
     30 
     31 // CLDR Canonical ID mapping table
     32 static UHashtable *gCanonicalIDCache = NULL;
     33 static UBool gCanonicalIDCacheInitialized = FALSE;
     34 
     35 // Metazone mapping table
     36 static UHashtable *gOlsonToMeta = NULL;
     37 static UBool gOlsonToMetaInitialized = FALSE;
     38 
     39 // Available metazone IDs vector and table
     40 static U_NAMESPACE_QUALIFIER UVector *gMetaZoneIDs = NULL;
     41 static UHashtable *gMetaZoneIDTable = NULL;
     42 static UBool gMetaZoneIDsInitialized = FALSE;
     43 
     44 // Country info vectors
     45 static U_NAMESPACE_QUALIFIER UVector *gSingleZoneCountries = NULL;
     46 static U_NAMESPACE_QUALIFIER UVector *gMultiZonesCountries = NULL;
     47 static UBool gCountryInfoVectorsInitialized = FALSE;
     48 
     49 U_CDECL_BEGIN
     50 
     51 /**
     52  * Cleanup callback func
     53  */
     54 static UBool U_CALLCONV zoneMeta_cleanup(void)
     55 {
     56     umtx_destroy(&gZoneMetaLock);
     57 
     58     if (gCanonicalIDCache != NULL) {
     59         uhash_close(gCanonicalIDCache);
     60         gCanonicalIDCache = NULL;
     61     }
     62     gCanonicalIDCacheInitialized = FALSE;
     63 
     64     if (gOlsonToMeta != NULL) {
     65         uhash_close(gOlsonToMeta);
     66         gOlsonToMeta = NULL;
     67     }
     68     gOlsonToMetaInitialized = FALSE;
     69 
     70     if (gMetaZoneIDTable != NULL) {
     71         uhash_close(gMetaZoneIDTable);
     72     }
     73     // delete after closing gMetaZoneIDTable, because it holds
     74     // value objects held by the hashtable
     75     delete gMetaZoneIDs;
     76     gMetaZoneIDsInitialized = FALSE;
     77 
     78     delete gSingleZoneCountries;
     79     delete gMultiZonesCountries;
     80     gCountryInfoVectorsInitialized = FALSE;
     81 
     82     return TRUE;
     83 }
     84 
     85 /**
     86  * Deleter for UChar* string
     87  */
     88 static void U_CALLCONV
     89 deleteUCharString(void *obj) {
     90     UChar *entry = (UChar*)obj;
     91     uprv_free(entry);
     92 }
     93 
     94 /**
     95  * Deleter for UVector
     96  */
     97 static void U_CALLCONV
     98 deleteUVector(void *obj) {
     99    delete (U_NAMESPACE_QUALIFIER UVector*) obj;
    100 }
    101 
    102 /**
    103  * Deleter for OlsonToMetaMappingEntry
    104  */
    105 static void U_CALLCONV
    106 deleteOlsonToMetaMappingEntry(void *obj) {
    107     U_NAMESPACE_QUALIFIER OlsonToMetaMappingEntry *entry = (U_NAMESPACE_QUALIFIER OlsonToMetaMappingEntry*)obj;
    108     uprv_free(entry);
    109 }
    110 
    111 U_CDECL_END
    112 
    113 U_NAMESPACE_BEGIN
    114 
    115 #define ZID_KEY_MAX 128
    116 
    117 static const char gMetaZones[]          = "metaZones";
    118 static const char gMetazoneInfo[]       = "metazoneInfo";
    119 static const char gMapTimezonesTag[]    = "mapTimezones";
    120 
    121 static const char gTimeZoneTypes[]      = "timezoneTypes";
    122 static const char gTypeAliasTag[]       = "typeAlias";
    123 static const char gTypeMapTag[]         = "typeMap";
    124 static const char gTimezoneTag[]        = "timezone";
    125 
    126 static const char gWorldTag[]           = "001";
    127 
    128 static const UChar gWorld[] = {0x30, 0x30, 0x31, 0x00}; // "001"
    129 
    130 static const UChar gDefaultFrom[] = {0x31, 0x39, 0x37, 0x30, 0x2D, 0x30, 0x31, 0x2D, 0x30, 0x31,
    131                                      0x20, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x00}; // "1970-01-01 00:00"
    132 static const UChar gDefaultTo[]   = {0x39, 0x39, 0x39, 0x39, 0x2D, 0x31, 0x32, 0x2D, 0x33, 0x31,
    133                                      0x20, 0x32, 0x33, 0x3A, 0x35, 0x39, 0x00}; // "9999-12-31 23:59"
    134 
    135 #define ASCII_DIGIT(c) (((c)>=0x30 && (c)<=0x39) ? (c)-0x30 : -1)
    136 
    137 /*
    138  * Convert a date string used by metazone mappings to UDate.
    139  * The format used by CLDR metazone mapping is "yyyy-MM-dd HH:mm".
    140  */
    141 static UDate
    142 parseDate (const UChar *text, UErrorCode &status) {
    143     if (U_FAILURE(status)) {
    144         return 0;
    145     }
    146     int32_t len = u_strlen(text);
    147     if (len != 16 && len != 10) {
    148         // It must be yyyy-MM-dd HH:mm (length 16) or yyyy-MM-dd (length 10)
    149         status = U_INVALID_FORMAT_ERROR;
    150         return 0;
    151     }
    152 
    153     int32_t year = 0, month = 0, day = 0, hour = 0, min = 0, n;
    154     int32_t idx;
    155 
    156     // "yyyy" (0 - 3)
    157     for (idx = 0; idx <= 3 && U_SUCCESS(status); idx++) {
    158         n = ASCII_DIGIT((int32_t)text[idx]);
    159         if (n >= 0) {
    160             year = 10*year + n;
    161         } else {
    162             status = U_INVALID_FORMAT_ERROR;
    163         }
    164     }
    165     // "MM" (5 - 6)
    166     for (idx = 5; idx <= 6 && U_SUCCESS(status); idx++) {
    167         n = ASCII_DIGIT((int32_t)text[idx]);
    168         if (n >= 0) {
    169             month = 10*month + n;
    170         } else {
    171             status = U_INVALID_FORMAT_ERROR;
    172         }
    173     }
    174     // "dd" (8 - 9)
    175     for (idx = 8; idx <= 9 && U_SUCCESS(status); idx++) {
    176         n = ASCII_DIGIT((int32_t)text[idx]);
    177         if (n >= 0) {
    178             day = 10*day + n;
    179         } else {
    180             status = U_INVALID_FORMAT_ERROR;
    181         }
    182     }
    183     if (len == 16) {
    184         // "HH" (11 - 12)
    185         for (idx = 11; idx <= 12 && U_SUCCESS(status); idx++) {
    186             n = ASCII_DIGIT((int32_t)text[idx]);
    187             if (n >= 0) {
    188                 hour = 10*hour + n;
    189             } else {
    190                 status = U_INVALID_FORMAT_ERROR;
    191             }
    192         }
    193         // "mm" (14 - 15)
    194         for (idx = 14; idx <= 15 && U_SUCCESS(status); idx++) {
    195             n = ASCII_DIGIT((int32_t)text[idx]);
    196             if (n >= 0) {
    197                 min = 10*min + n;
    198             } else {
    199                 status = U_INVALID_FORMAT_ERROR;
    200             }
    201         }
    202     }
    203 
    204     if (U_SUCCESS(status)) {
    205         UDate date = Grego::fieldsToDay(year, month - 1, day) * U_MILLIS_PER_DAY
    206             + hour * U_MILLIS_PER_HOUR + min * U_MILLIS_PER_MINUTE;
    207         return date;
    208     }
    209     return 0;
    210 }
    211 
    212 const UChar* U_EXPORT2
    213 ZoneMeta::getCanonicalCLDRID(const UnicodeString &tzid, UErrorCode& status) {
    214     if (U_FAILURE(status)) {
    215         return NULL;
    216     }
    217 
    218     int32_t len = tzid.length();
    219     if (len > ZID_KEY_MAX) {
    220         status = U_ILLEGAL_ARGUMENT_ERROR;
    221         return NULL;
    222     }
    223 
    224     // Checking the cached results
    225     UBool initialized;
    226     UMTX_CHECK(&gZoneMetaLock, gCanonicalIDCacheInitialized, initialized);
    227     if (!initialized) {
    228         // Create empty hashtable
    229         umtx_lock(&gZoneMetaLock);
    230         {
    231             if (!gCanonicalIDCacheInitialized) {
    232                 gCanonicalIDCache = uhash_open(uhash_hashUChars, uhash_compareUChars, NULL, &status);
    233                 if (gCanonicalIDCache == NULL) {
    234                     status = U_MEMORY_ALLOCATION_ERROR;
    235                 }
    236                 if (U_FAILURE(status)) {
    237                     gCanonicalIDCache = NULL;
    238                     return NULL;
    239                 }
    240                 // No key/value deleters - keys/values are from a resource bundle
    241                 gCanonicalIDCacheInitialized = TRUE;
    242                 ucln_i18n_registerCleanup(UCLN_I18N_ZONEMETA, zoneMeta_cleanup);
    243             }
    244         }
    245         umtx_unlock(&gZoneMetaLock);
    246     }
    247 
    248     const UChar *canonicalID = NULL;
    249 
    250     UErrorCode tmpStatus = U_ZERO_ERROR;
    251     UChar utzid[ZID_KEY_MAX + 1];
    252     tzid.extract(utzid, ZID_KEY_MAX + 1, tmpStatus);
    253     U_ASSERT(tmpStatus == U_ZERO_ERROR);    // we checked the length of tzid already
    254 
    255     // Check if it was already cached
    256     umtx_lock(&gZoneMetaLock);
    257     {
    258         canonicalID = (const UChar *)uhash_get(gCanonicalIDCache, utzid);
    259     }
    260     umtx_unlock(&gZoneMetaLock);
    261 
    262     if (canonicalID != NULL) {
    263         return canonicalID;
    264     }
    265 
    266     // If not, resolve CLDR canonical ID with resource data
    267     UBool isInputCanonical = FALSE;
    268     char id[ZID_KEY_MAX + 1];
    269     const UChar* idChars = tzid.getBuffer();
    270 
    271     u_UCharsToChars(idChars,id,len);
    272     id[len] = (char) 0; // Make sure it is null terminated.
    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, gTimeZoneTypes, &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                 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 
    394 
    395 UnicodeString& U_EXPORT2
    396 ZoneMeta::getCanonicalCountry(const UnicodeString &tzid, UnicodeString &canonicalCountry) {
    397     const UChar *region = TimeZone::getRegion(tzid);
    398     if (region != NULL && u_strcmp(gWorld, region) != 0) {
    399         canonicalCountry.setTo(region, -1);
    400     } else {
    401         canonicalCountry.setToBogus();
    402     }
    403     return canonicalCountry;
    404 }
    405 
    406 UnicodeString& U_EXPORT2
    407 ZoneMeta::getSingleCountry(const UnicodeString &tzid, UnicodeString &country) {
    408     // Get canonical country for the zone
    409     const UChar *region = TimeZone::getRegion(tzid);
    410     if (region == NULL || u_strcmp(gWorld, region) == 0) {
    411         // special case - unknown or "001"
    412         country.setToBogus();
    413         return country;
    414     }
    415 
    416     // Checking the cached results
    417     UErrorCode status = U_ZERO_ERROR;
    418     UBool initialized;
    419     UMTX_CHECK(&gZoneMetaLock, gCountryInfoVectorsInitialized, initialized);
    420     if (!initialized) {
    421         // Create empty vectors
    422         umtx_lock(&gZoneMetaLock);
    423         {
    424             if (!gCountryInfoVectorsInitialized) {
    425                 // No deleters for these UVectors, it's a reference to a resource bundle string.
    426                 gSingleZoneCountries = new UVector(NULL, uhash_compareUChars, status);
    427                 if (gSingleZoneCountries == NULL) {
    428                     status = U_MEMORY_ALLOCATION_ERROR;
    429                 }
    430                 gMultiZonesCountries = new UVector(NULL, uhash_compareUChars, status);
    431                 if (gMultiZonesCountries == NULL) {
    432                     status = U_MEMORY_ALLOCATION_ERROR;
    433                 }
    434 
    435                 if (U_SUCCESS(status)) {
    436                     gCountryInfoVectorsInitialized = TRUE;
    437                 } else {
    438                     delete gSingleZoneCountries;
    439                     delete gMultiZonesCountries;
    440                 }
    441             }
    442         }
    443         umtx_unlock(&gZoneMetaLock);
    444 
    445         if (U_FAILURE(status)) {
    446             country.setToBogus();
    447             return country;
    448         }
    449     }
    450 
    451     // Check if it was already cached
    452     UBool cached = FALSE;
    453     UBool multiZones = FALSE;
    454     umtx_lock(&gZoneMetaLock);
    455     {
    456         multiZones = cached = gMultiZonesCountries->contains((void*)region);
    457         if (!multiZones) {
    458             cached = gSingleZoneCountries->contains((void*)region);
    459         }
    460     }
    461     umtx_unlock(&gZoneMetaLock);
    462 
    463     if (!cached) {
    464         // We need to go through all zones associated with the region.
    465         // This is relatively heavy operation.
    466 
    467         U_ASSERT(u_strlen(region) == 2);
    468 
    469         char buf[] = {0, 0, 0};
    470         u_UCharsToChars(region, buf, 2);
    471 
    472         StringEnumeration *ids = TimeZone::createTimeZoneIDEnumeration(UCAL_ZONE_TYPE_CANONICAL_LOCATION, buf, NULL, status);
    473         int32_t idsLen = ids->count(status);
    474         if (U_SUCCESS(status) && idsLen > 1) {
    475             // multiple canonical zones are available for the region
    476             multiZones = TRUE;
    477         }
    478         if (U_FAILURE(status)) {
    479             // no single country by default for any error cases
    480             multiZones = TRUE;
    481         }
    482         delete ids;
    483 
    484         // Cache the result
    485         umtx_lock(&gZoneMetaLock);
    486         {
    487             UErrorCode ec = U_ZERO_ERROR;
    488             if (multiZones) {
    489                 if (!gMultiZonesCountries->contains((void*)region)) {
    490                     gMultiZonesCountries->addElement((void*)region, ec);
    491                 }
    492             } else {
    493                 if (!gSingleZoneCountries->contains((void*)region)) {
    494                     gSingleZoneCountries->addElement((void*)region, ec);
    495                 }
    496             }
    497         }
    498         umtx_unlock(&gZoneMetaLock);
    499     }
    500 
    501     if (multiZones) {
    502         country.setToBogus();
    503     } else {
    504         country.setTo(region, -1);
    505     }
    506     return country;
    507 }
    508 
    509 UnicodeString& U_EXPORT2
    510 ZoneMeta::getMetazoneID(const UnicodeString &tzid, UDate date, UnicodeString &result) {
    511     UBool isSet = FALSE;
    512     const UVector *mappings = getMetazoneMappings(tzid);
    513     if (mappings != NULL) {
    514         for (int32_t i = 0; i < mappings->size(); i++) {
    515             OlsonToMetaMappingEntry *mzm = (OlsonToMetaMappingEntry*)mappings->elementAt(i);
    516             if (mzm->from <= date && mzm->to > date) {
    517                 result.setTo(mzm->mzid, -1);
    518                 isSet = TRUE;
    519                 break;
    520             }
    521         }
    522     }
    523     if (!isSet) {
    524         result.setToBogus();
    525     }
    526     return result;
    527 }
    528 
    529 const UVector* U_EXPORT2
    530 ZoneMeta::getMetazoneMappings(const UnicodeString &tzid) {
    531     UErrorCode status = U_ZERO_ERROR;
    532     UChar tzidUChars[ZID_KEY_MAX + 1];
    533     tzid.extract(tzidUChars, ZID_KEY_MAX + 1, status);
    534     if (U_FAILURE(status) || status == U_STRING_NOT_TERMINATED_WARNING) {
    535         return NULL;
    536     }
    537 
    538     UBool initialized;
    539     UMTX_CHECK(&gZoneMetaLock, gOlsonToMetaInitialized, initialized);
    540     if (!initialized) {
    541         UHashtable *tmpOlsonToMeta = uhash_open(uhash_hashUChars, uhash_compareUChars, NULL, &status);
    542         if (U_FAILURE(status)) {
    543             return NULL;
    544         }
    545         uhash_setKeyDeleter(tmpOlsonToMeta, deleteUCharString);
    546         uhash_setValueDeleter(tmpOlsonToMeta, deleteUVector);
    547 
    548         umtx_lock(&gZoneMetaLock);
    549         {
    550             if (!gOlsonToMetaInitialized) {
    551                 gOlsonToMeta = tmpOlsonToMeta;
    552                 tmpOlsonToMeta = NULL;
    553                 gOlsonToMetaInitialized = TRUE;
    554             }
    555         }
    556         umtx_unlock(&gZoneMetaLock);
    557 
    558         // OK to call the following multiple times with the same function
    559         ucln_i18n_registerCleanup(UCLN_I18N_ZONEMETA, zoneMeta_cleanup);
    560         if (tmpOlsonToMeta != NULL) {
    561             uhash_close(tmpOlsonToMeta);
    562         }
    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.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 void
    757 ZoneMeta::initAvailableMetaZoneIDs () {
    758     UBool initialized;
    759     UMTX_CHECK(&gZoneMetaLock, gMetaZoneIDsInitialized, initialized);
    760     if (!initialized) {
    761         umtx_lock(&gZoneMetaLock);
    762         {
    763             if (!gMetaZoneIDsInitialized) {
    764                 UErrorCode status = U_ZERO_ERROR;
    765                 UHashtable *metaZoneIDTable = uhash_open(uhash_hashUnicodeString, uhash_compareUnicodeString, NULL, &status);
    766                 uhash_setKeyDeleter(metaZoneIDTable, uhash_deleteUnicodeString);
    767                 // No valueDeleter, because the vector maintain the value objects
    768                 UVector *metaZoneIDs = NULL;
    769                 if (U_SUCCESS(status)) {
    770                     metaZoneIDs = new UVector(NULL, uhash_compareUChars, status);
    771                     if (metaZoneIDs == NULL) {
    772                         status = U_MEMORY_ALLOCATION_ERROR;
    773                     }
    774                 } else {
    775                     uhash_close(metaZoneIDTable);
    776                 }
    777                 if (U_SUCCESS(status)) {
    778                     metaZoneIDs->setDeleter(uhash_freeBlock);
    779 
    780                     UResourceBundle *rb = ures_openDirect(NULL, gMetaZones, &status);
    781                     UResourceBundle *bundle = ures_getByKey(rb, gMapTimezonesTag, NULL, &status);
    782                     UResourceBundle res;
    783                     ures_initStackObject(&res);
    784                     while (U_SUCCESS(status) && ures_hasNext(bundle)) {
    785                         ures_getNextResource(bundle, &res, &status);
    786                         if (U_FAILURE(status)) {
    787                             break;
    788                         }
    789                         const char *mzID = ures_getKey(&res);
    790                         int32_t len = uprv_strlen(mzID);
    791                         UChar *uMzID = (UChar*)uprv_malloc(sizeof(UChar) * (len + 1));
    792                         if (uMzID == NULL) {
    793                             status = U_MEMORY_ALLOCATION_ERROR;
    794                             break;
    795                         }
    796                         u_charsToUChars(mzID, uMzID, len);
    797                         uMzID[len] = 0;
    798                         UnicodeString *usMzID = new UnicodeString(uMzID);
    799                         if (uhash_get(metaZoneIDTable, usMzID) == NULL) {
    800                             metaZoneIDs->addElement((void *)uMzID, status);
    801                             uhash_put(metaZoneIDTable, (void *)usMzID, (void *)uMzID, &status);
    802                         } else {
    803                             uprv_free(uMzID);
    804                             delete usMzID;
    805                         }
    806                     }
    807                     if (U_SUCCESS(status)) {
    808                         gMetaZoneIDs = metaZoneIDs;
    809                         gMetaZoneIDTable = metaZoneIDTable;
    810                         gMetaZoneIDsInitialized = TRUE;
    811                     } else {
    812                         uhash_close(metaZoneIDTable);
    813                         delete metaZoneIDs;
    814                     }
    815                     ures_close(&res);
    816                     ures_close(bundle);
    817                     ures_close(rb);
    818                 }
    819             }
    820         }
    821         umtx_unlock(&gZoneMetaLock);
    822     }
    823 }
    824 
    825 const UVector*
    826 ZoneMeta::getAvailableMetazoneIDs() {
    827     initAvailableMetaZoneIDs();
    828     return gMetaZoneIDs;
    829 }
    830 
    831 const UChar*
    832 ZoneMeta::findMetaZoneID(const UnicodeString& mzid) {
    833     initAvailableMetaZoneIDs();
    834     return (const UChar*)uhash_get(gMetaZoneIDTable, &mzid);
    835 }
    836 
    837 const UChar*
    838 ZoneMeta::findTimeZoneID(const UnicodeString& tzid) {
    839     return TimeZone::findID(tzid);
    840 }
    841 
    842 U_NAMESPACE_END
    843 
    844 #endif /* #if !UCONFIG_NO_FORMATTING */
    845