LCOV - code coverage report
Current view: top level - intl/icu/source/i18n - zonemeta.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 0 495 0.0 %
Date: 2017-07-14 16:53:18 Functions: 0 25 0.0 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.13