intl/icu/source/i18n/zonemeta.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/intl/icu/source/i18n/zonemeta.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,939 @@
     1.4 +/*
     1.5 +*******************************************************************************
     1.6 +* Copyright (C) 2007-2013, International Business Machines Corporation and
     1.7 +* others. All Rights Reserved.
     1.8 +*******************************************************************************
     1.9 +*/
    1.10 +
    1.11 +#include "unicode/utypes.h"
    1.12 +
    1.13 +#if !UCONFIG_NO_FORMATTING
    1.14 +
    1.15 +#include "zonemeta.h"
    1.16 +
    1.17 +#include "unicode/timezone.h"
    1.18 +#include "unicode/ustring.h"
    1.19 +#include "unicode/putil.h"
    1.20 +#include "unicode/simpletz.h"
    1.21 +
    1.22 +#include "umutex.h"
    1.23 +#include "uvector.h"
    1.24 +#include "cmemory.h"
    1.25 +#include "gregoimp.h"
    1.26 +#include "cstring.h"
    1.27 +#include "ucln_in.h"
    1.28 +#include "uassert.h"
    1.29 +#include "uresimp.h"
    1.30 +#include "uhash.h"
    1.31 +#include "olsontz.h"
    1.32 +
    1.33 +static UMutex gZoneMetaLock = U_MUTEX_INITIALIZER;
    1.34 +
    1.35 +// CLDR Canonical ID mapping table
    1.36 +static UHashtable *gCanonicalIDCache = NULL;
    1.37 +static icu::UInitOnce gCanonicalIDCacheInitOnce = U_INITONCE_INITIALIZER;
    1.38 +
    1.39 +// Metazone mapping table
    1.40 +static UHashtable *gOlsonToMeta = NULL;
    1.41 +static icu::UInitOnce gOlsonToMetaInitOnce = U_INITONCE_INITIALIZER;
    1.42 +
    1.43 +// Available metazone IDs vector and table
    1.44 +static icu::UVector *gMetaZoneIDs = NULL;
    1.45 +static UHashtable *gMetaZoneIDTable = NULL;
    1.46 +static icu::UInitOnce gMetaZoneIDsInitOnce = U_INITONCE_INITIALIZER;
    1.47 +
    1.48 +// Country info vectors
    1.49 +static icu::UVector *gSingleZoneCountries = NULL;
    1.50 +static icu::UVector *gMultiZonesCountries = NULL;
    1.51 +static icu::UInitOnce gCountryInfoVectorsInitOnce = U_INITONCE_INITIALIZER;
    1.52 +
    1.53 +U_CDECL_BEGIN
    1.54 +
    1.55 +/**
    1.56 + * Cleanup callback func
    1.57 + */
    1.58 +static UBool U_CALLCONV zoneMeta_cleanup(void)
    1.59 +{
    1.60 +    if (gCanonicalIDCache != NULL) {
    1.61 +        uhash_close(gCanonicalIDCache);
    1.62 +        gCanonicalIDCache = NULL;
    1.63 +    }
    1.64 +    gCanonicalIDCacheInitOnce.reset();
    1.65 +
    1.66 +    if (gOlsonToMeta != NULL) {
    1.67 +        uhash_close(gOlsonToMeta);
    1.68 +        gOlsonToMeta = NULL;
    1.69 +    }
    1.70 +    gOlsonToMetaInitOnce.reset();
    1.71 +
    1.72 +    if (gMetaZoneIDTable != NULL) {
    1.73 +        uhash_close(gMetaZoneIDTable);
    1.74 +        gMetaZoneIDTable = NULL;
    1.75 +    }
    1.76 +    // delete after closing gMetaZoneIDTable, because it holds
    1.77 +    // value objects held by the hashtable
    1.78 +    delete gMetaZoneIDs;
    1.79 +    gMetaZoneIDs = NULL;
    1.80 +    gMetaZoneIDsInitOnce.reset();
    1.81 +
    1.82 +    delete gSingleZoneCountries;
    1.83 +    gSingleZoneCountries = NULL;
    1.84 +    delete gMultiZonesCountries;
    1.85 +    gMultiZonesCountries = NULL;
    1.86 +    gCountryInfoVectorsInitOnce.reset();
    1.87 +
    1.88 +    return TRUE;
    1.89 +}
    1.90 +
    1.91 +/**
    1.92 + * Deleter for UChar* string
    1.93 + */
    1.94 +static void U_CALLCONV
    1.95 +deleteUCharString(void *obj) {
    1.96 +    UChar *entry = (UChar*)obj;
    1.97 +    uprv_free(entry);
    1.98 +}
    1.99 +
   1.100 +/**
   1.101 + * Deleter for UVector
   1.102 + */
   1.103 +static void U_CALLCONV
   1.104 +deleteUVector(void *obj) {
   1.105 +   delete (icu::UVector*) obj;
   1.106 +}
   1.107 +
   1.108 +/**
   1.109 + * Deleter for OlsonToMetaMappingEntry
   1.110 + */
   1.111 +static void U_CALLCONV
   1.112 +deleteOlsonToMetaMappingEntry(void *obj) {
   1.113 +    icu::OlsonToMetaMappingEntry *entry = (icu::OlsonToMetaMappingEntry*)obj;
   1.114 +    uprv_free(entry);
   1.115 +}
   1.116 +
   1.117 +U_CDECL_END
   1.118 +
   1.119 +U_NAMESPACE_BEGIN
   1.120 +
   1.121 +#define ZID_KEY_MAX 128
   1.122 +
   1.123 +static const char gMetaZones[]          = "metaZones";
   1.124 +static const char gMetazoneInfo[]       = "metazoneInfo";
   1.125 +static const char gMapTimezonesTag[]    = "mapTimezones";
   1.126 +
   1.127 +static const char gKeyTypeData[]        = "keyTypeData";
   1.128 +static const char gTypeAliasTag[]       = "typeAlias";
   1.129 +static const char gTypeMapTag[]         = "typeMap";
   1.130 +static const char gTimezoneTag[]        = "timezone";
   1.131 +
   1.132 +static const char gPrimaryZonesTag[]    = "primaryZones";
   1.133 +
   1.134 +static const char gWorldTag[]           = "001";
   1.135 +
   1.136 +static const UChar gWorld[] = {0x30, 0x30, 0x31, 0x00}; // "001"
   1.137 +
   1.138 +static const UChar gDefaultFrom[] = {0x31, 0x39, 0x37, 0x30, 0x2D, 0x30, 0x31, 0x2D, 0x30, 0x31,
   1.139 +                                     0x20, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x00}; // "1970-01-01 00:00"
   1.140 +static const UChar gDefaultTo[]   = {0x39, 0x39, 0x39, 0x39, 0x2D, 0x31, 0x32, 0x2D, 0x33, 0x31,
   1.141 +                                     0x20, 0x32, 0x33, 0x3A, 0x35, 0x39, 0x00}; // "9999-12-31 23:59"
   1.142 +
   1.143 +static const UChar gCustomTzPrefix[]    = {0x47, 0x4D, 0x54, 0};    // "GMT"
   1.144 +
   1.145 +#define ASCII_DIGIT(c) (((c)>=0x30 && (c)<=0x39) ? (c)-0x30 : -1)
   1.146 +
   1.147 +/*
   1.148 + * Convert a date string used by metazone mappings to UDate.
   1.149 + * The format used by CLDR metazone mapping is "yyyy-MM-dd HH:mm".
   1.150 + */
   1.151 +static UDate
   1.152 +parseDate (const UChar *text, UErrorCode &status) {
   1.153 +    if (U_FAILURE(status)) {
   1.154 +        return 0;
   1.155 +    }
   1.156 +    int32_t len = u_strlen(text);
   1.157 +    if (len != 16 && len != 10) {
   1.158 +        // It must be yyyy-MM-dd HH:mm (length 16) or yyyy-MM-dd (length 10)
   1.159 +        status = U_INVALID_FORMAT_ERROR;
   1.160 +        return 0;
   1.161 +    }
   1.162 +
   1.163 +    int32_t year = 0, month = 0, day = 0, hour = 0, min = 0, n;
   1.164 +    int32_t idx;
   1.165 +
   1.166 +    // "yyyy" (0 - 3)
   1.167 +    for (idx = 0; idx <= 3 && U_SUCCESS(status); idx++) {
   1.168 +        n = ASCII_DIGIT((int32_t)text[idx]);
   1.169 +        if (n >= 0) {
   1.170 +            year = 10*year + n;
   1.171 +        } else {
   1.172 +            status = U_INVALID_FORMAT_ERROR;
   1.173 +        }
   1.174 +    }
   1.175 +    // "MM" (5 - 6)
   1.176 +    for (idx = 5; idx <= 6 && U_SUCCESS(status); idx++) {
   1.177 +        n = ASCII_DIGIT((int32_t)text[idx]);
   1.178 +        if (n >= 0) {
   1.179 +            month = 10*month + n;
   1.180 +        } else {
   1.181 +            status = U_INVALID_FORMAT_ERROR;
   1.182 +        }
   1.183 +    }
   1.184 +    // "dd" (8 - 9)
   1.185 +    for (idx = 8; idx <= 9 && U_SUCCESS(status); idx++) {
   1.186 +        n = ASCII_DIGIT((int32_t)text[idx]);
   1.187 +        if (n >= 0) {
   1.188 +            day = 10*day + n;
   1.189 +        } else {
   1.190 +            status = U_INVALID_FORMAT_ERROR;
   1.191 +        }
   1.192 +    }
   1.193 +    if (len == 16) {
   1.194 +        // "HH" (11 - 12)
   1.195 +        for (idx = 11; idx <= 12 && U_SUCCESS(status); idx++) {
   1.196 +            n = ASCII_DIGIT((int32_t)text[idx]);
   1.197 +            if (n >= 0) {
   1.198 +                hour = 10*hour + n;
   1.199 +            } else {
   1.200 +                status = U_INVALID_FORMAT_ERROR;
   1.201 +            }
   1.202 +        }
   1.203 +        // "mm" (14 - 15)
   1.204 +        for (idx = 14; idx <= 15 && U_SUCCESS(status); idx++) {
   1.205 +            n = ASCII_DIGIT((int32_t)text[idx]);
   1.206 +            if (n >= 0) {
   1.207 +                min = 10*min + n;
   1.208 +            } else {
   1.209 +                status = U_INVALID_FORMAT_ERROR;
   1.210 +            }
   1.211 +        }
   1.212 +    }
   1.213 +
   1.214 +    if (U_SUCCESS(status)) {
   1.215 +        UDate date = Grego::fieldsToDay(year, month - 1, day) * U_MILLIS_PER_DAY
   1.216 +            + hour * U_MILLIS_PER_HOUR + min * U_MILLIS_PER_MINUTE;
   1.217 +        return date;
   1.218 +    }
   1.219 +    return 0;
   1.220 +}
   1.221 +
   1.222 +static void U_CALLCONV initCanonicalIDCache(UErrorCode &status) {
   1.223 +    gCanonicalIDCache = uhash_open(uhash_hashUChars, uhash_compareUChars, NULL, &status);
   1.224 +    if (gCanonicalIDCache == NULL) {
   1.225 +        status = U_MEMORY_ALLOCATION_ERROR;
   1.226 +    }
   1.227 +    if (U_FAILURE(status)) {
   1.228 +        gCanonicalIDCache = NULL;
   1.229 +    }
   1.230 +    // No key/value deleters - keys/values are from a resource bundle
   1.231 +    ucln_i18n_registerCleanup(UCLN_I18N_ZONEMETA, zoneMeta_cleanup);
   1.232 +}
   1.233 +
   1.234 +
   1.235 +const UChar* U_EXPORT2
   1.236 +ZoneMeta::getCanonicalCLDRID(const UnicodeString &tzid, UErrorCode& status) {
   1.237 +    if (U_FAILURE(status)) {
   1.238 +        return NULL;
   1.239 +    }
   1.240 +
   1.241 +    int32_t len = tzid.length();
   1.242 +    if (len > ZID_KEY_MAX) {
   1.243 +        status = U_ILLEGAL_ARGUMENT_ERROR;
   1.244 +        return NULL;
   1.245 +    }
   1.246 +
   1.247 +    // Checking the cached results
   1.248 +    umtx_initOnce(gCanonicalIDCacheInitOnce, &initCanonicalIDCache, status);
   1.249 +    if (U_FAILURE(status)) {
   1.250 +        return NULL;
   1.251 +    }
   1.252 +
   1.253 +    const UChar *canonicalID = NULL;
   1.254 +
   1.255 +    UErrorCode tmpStatus = U_ZERO_ERROR;
   1.256 +    UChar utzid[ZID_KEY_MAX + 1];
   1.257 +    tzid.extract(utzid, ZID_KEY_MAX + 1, tmpStatus);
   1.258 +    U_ASSERT(tmpStatus == U_ZERO_ERROR);    // we checked the length of tzid already
   1.259 +
   1.260 +    // Check if it was already cached
   1.261 +    umtx_lock(&gZoneMetaLock);
   1.262 +    {
   1.263 +        canonicalID = (const UChar *)uhash_get(gCanonicalIDCache, utzid);
   1.264 +    }
   1.265 +    umtx_unlock(&gZoneMetaLock);
   1.266 +
   1.267 +    if (canonicalID != NULL) {
   1.268 +        return canonicalID;
   1.269 +    }
   1.270 +
   1.271 +    // If not, resolve CLDR canonical ID with resource data
   1.272 +    UBool isInputCanonical = FALSE;
   1.273 +    char id[ZID_KEY_MAX + 1];
   1.274 +    const UChar* idChars = tzid.getBuffer();
   1.275 +
   1.276 +    u_UCharsToChars(idChars,id,len);
   1.277 +    id[len] = (char) 0; // Make sure it is null terminated.
   1.278 +
   1.279 +    // replace '/' with ':'
   1.280 +    char *p = id;
   1.281 +    while (*p++) {
   1.282 +        if (*p == '/') {
   1.283 +            *p = ':';
   1.284 +        }
   1.285 +    }
   1.286 +
   1.287 +    UResourceBundle *top = ures_openDirect(NULL, gKeyTypeData, &tmpStatus);
   1.288 +    UResourceBundle *rb = ures_getByKey(top, gTypeMapTag, NULL, &tmpStatus);
   1.289 +    ures_getByKey(rb, gTimezoneTag, rb, &tmpStatus);
   1.290 +    ures_getByKey(rb, id, rb, &tmpStatus);
   1.291 +    if (U_SUCCESS(tmpStatus)) {
   1.292 +        // type entry (canonical) found
   1.293 +        // the input is the canonical ID. resolve to const UChar*
   1.294 +        canonicalID = TimeZone::findID(tzid);
   1.295 +        isInputCanonical = TRUE;
   1.296 +    }
   1.297 +
   1.298 +    if (canonicalID == NULL) {
   1.299 +        // If a map element not found, then look for an alias
   1.300 +        tmpStatus = U_ZERO_ERROR;
   1.301 +        ures_getByKey(top, gTypeAliasTag, rb, &tmpStatus);
   1.302 +        ures_getByKey(rb, gTimezoneTag, rb, &tmpStatus);
   1.303 +        const UChar *canonical = ures_getStringByKey(rb,id,NULL,&tmpStatus);
   1.304 +        if (U_SUCCESS(tmpStatus)) {
   1.305 +            // canonical map found
   1.306 +            canonicalID = canonical;
   1.307 +        }
   1.308 +
   1.309 +        if (canonicalID == NULL) {
   1.310 +            // Dereference the input ID using the tz data
   1.311 +            const UChar *derefer = TimeZone::dereferOlsonLink(tzid);
   1.312 +            if (derefer == NULL) {
   1.313 +                status = U_ILLEGAL_ARGUMENT_ERROR;
   1.314 +            } else {
   1.315 +                len = u_strlen(derefer);
   1.316 +                u_UCharsToChars(derefer,id,len);
   1.317 +                id[len] = (char) 0; // Make sure it is null terminated.
   1.318 +
   1.319 +                // replace '/' with ':'
   1.320 +                char *p = id;
   1.321 +                while (*p++) {
   1.322 +                    if (*p == '/') {
   1.323 +                        *p = ':';
   1.324 +                    }
   1.325 +                }
   1.326 +
   1.327 +                // If a dereference turned something up then look for an alias.
   1.328 +                // rb still points to the alias table, so we don't have to go looking
   1.329 +                // for it.
   1.330 +                tmpStatus = U_ZERO_ERROR;
   1.331 +                canonical = ures_getStringByKey(rb,id,NULL,&tmpStatus);
   1.332 +                if (U_SUCCESS(tmpStatus)) {
   1.333 +                    // canonical map for the dereferenced ID found
   1.334 +                    canonicalID = canonical;
   1.335 +                } else {
   1.336 +                    canonicalID = derefer;
   1.337 +                    isInputCanonical = TRUE;
   1.338 +                }
   1.339 +            }
   1.340 +        }
   1.341 +    }
   1.342 +    ures_close(rb);
   1.343 +    ures_close(top);
   1.344 +
   1.345 +    if (U_SUCCESS(status)) {
   1.346 +        U_ASSERT(canonicalID != NULL);  // canocanilD must be non-NULL here
   1.347 +
   1.348 +        // Put the resolved canonical ID to the cache
   1.349 +        umtx_lock(&gZoneMetaLock);
   1.350 +        {
   1.351 +            const UChar* idInCache = (const UChar *)uhash_get(gCanonicalIDCache, utzid);
   1.352 +            if (idInCache == NULL) {
   1.353 +                const UChar* key = ZoneMeta::findTimeZoneID(tzid);
   1.354 +                U_ASSERT(key != NULL);
   1.355 +                if (key != NULL) {
   1.356 +                    idInCache = (const UChar *)uhash_put(gCanonicalIDCache, (void *)key, (void *)canonicalID, &status);
   1.357 +                    U_ASSERT(idInCache == NULL);
   1.358 +                }
   1.359 +            }
   1.360 +            if (U_SUCCESS(status) && isInputCanonical) {
   1.361 +                // Also put canonical ID itself into the cache if not exist
   1.362 +                const UChar *canonicalInCache = (const UChar*)uhash_get(gCanonicalIDCache, canonicalID);
   1.363 +                if (canonicalInCache == NULL) {
   1.364 +                    canonicalInCache = (const UChar *)uhash_put(gCanonicalIDCache, (void *)canonicalID, (void *)canonicalID, &status);
   1.365 +                    U_ASSERT(canonicalInCache == NULL);
   1.366 +                }
   1.367 +            }
   1.368 +        }
   1.369 +        umtx_unlock(&gZoneMetaLock);
   1.370 +    }
   1.371 +
   1.372 +    return canonicalID;
   1.373 +}
   1.374 +
   1.375 +UnicodeString& U_EXPORT2
   1.376 +ZoneMeta::getCanonicalCLDRID(const UnicodeString &tzid, UnicodeString &systemID, UErrorCode& status) {
   1.377 +    const UChar *canonicalID = getCanonicalCLDRID(tzid, status);
   1.378 +    if (U_FAILURE(status) || canonicalID == NULL) {
   1.379 +        systemID.setToBogus();
   1.380 +        return systemID;
   1.381 +    }
   1.382 +    systemID.setTo(TRUE, canonicalID, -1);
   1.383 +    return systemID;
   1.384 +}
   1.385 +
   1.386 +const UChar* U_EXPORT2
   1.387 +ZoneMeta::getCanonicalCLDRID(const TimeZone& tz) {
   1.388 +    if (dynamic_cast<const OlsonTimeZone *>(&tz) != NULL) {
   1.389 +        // short cut for OlsonTimeZone
   1.390 +        const OlsonTimeZone *otz = (const OlsonTimeZone*)&tz;
   1.391 +        return otz->getCanonicalID();
   1.392 +    }
   1.393 +    UErrorCode status = U_ZERO_ERROR;
   1.394 +    UnicodeString tzID;
   1.395 +    return getCanonicalCLDRID(tz.getID(tzID), status);
   1.396 +}
   1.397 +
   1.398 +static void U_CALLCONV countryInfoVectorsInit(UErrorCode &status) {
   1.399 +    // Create empty vectors
   1.400 +    // No deleters for these UVectors, it's a reference to a resource bundle string.
   1.401 +    gSingleZoneCountries = new UVector(NULL, uhash_compareUChars, status);
   1.402 +    if (gSingleZoneCountries == NULL) {
   1.403 +        status = U_MEMORY_ALLOCATION_ERROR;
   1.404 +    }
   1.405 +    gMultiZonesCountries = new UVector(NULL, uhash_compareUChars, status);
   1.406 +    if (gMultiZonesCountries == NULL) {
   1.407 +        status = U_MEMORY_ALLOCATION_ERROR;
   1.408 +    }
   1.409 +
   1.410 +    if (U_FAILURE(status)) {
   1.411 +        delete gSingleZoneCountries;
   1.412 +        delete gMultiZonesCountries;
   1.413 +        gSingleZoneCountries = NULL;
   1.414 +        gMultiZonesCountries  = NULL;
   1.415 +    }
   1.416 +    ucln_i18n_registerCleanup(UCLN_I18N_ZONEMETA, zoneMeta_cleanup);
   1.417 +}
   1.418 +
   1.419 +
   1.420 +UnicodeString& U_EXPORT2
   1.421 +ZoneMeta::getCanonicalCountry(const UnicodeString &tzid, UnicodeString &country, UBool *isPrimary /* = NULL */) {
   1.422 +    if (isPrimary != NULL) {
   1.423 +        *isPrimary = FALSE;
   1.424 +    }
   1.425 +
   1.426 +    const UChar *region = TimeZone::getRegion(tzid);
   1.427 +    if (region != NULL && u_strcmp(gWorld, region) != 0) {
   1.428 +        country.setTo(region, -1);
   1.429 +    } else {
   1.430 +        country.setToBogus();
   1.431 +        return country;
   1.432 +    }
   1.433 +
   1.434 +    if (isPrimary != NULL) {
   1.435 +        char regionBuf[] = {0, 0, 0};
   1.436 +
   1.437 +        // Checking the cached results
   1.438 +        UErrorCode status = U_ZERO_ERROR;
   1.439 +        umtx_initOnce(gCountryInfoVectorsInitOnce, &countryInfoVectorsInit, status);
   1.440 +        if (U_FAILURE(status)) {
   1.441 +            return country;
   1.442 +        }
   1.443 +
   1.444 +        // Check if it was already cached
   1.445 +        UBool cached = FALSE;
   1.446 +        UBool singleZone = FALSE;
   1.447 +        umtx_lock(&gZoneMetaLock);
   1.448 +        {
   1.449 +            singleZone = cached = gSingleZoneCountries->contains((void*)region);
   1.450 +            if (!cached) {
   1.451 +                cached = gMultiZonesCountries->contains((void*)region);
   1.452 +            }
   1.453 +        }
   1.454 +        umtx_unlock(&gZoneMetaLock);
   1.455 +
   1.456 +        if (!cached) {
   1.457 +            // We need to go through all zones associated with the region.
   1.458 +            // This is relatively heavy operation.
   1.459 +
   1.460 +            U_ASSERT(u_strlen(region) == 2);
   1.461 +
   1.462 +            u_UCharsToChars(region, regionBuf, 2);
   1.463 +
   1.464 +            StringEnumeration *ids = TimeZone::createTimeZoneIDEnumeration(UCAL_ZONE_TYPE_CANONICAL_LOCATION, regionBuf, NULL, status);
   1.465 +            int32_t idsLen = ids->count(status);
   1.466 +            if (U_SUCCESS(status) && idsLen == 1) {
   1.467 +                // only the single zone is available for the region
   1.468 +                singleZone = TRUE;
   1.469 +            }
   1.470 +            delete ids;
   1.471 +
   1.472 +            // Cache the result
   1.473 +            umtx_lock(&gZoneMetaLock);
   1.474 +            {
   1.475 +                UErrorCode ec = U_ZERO_ERROR;
   1.476 +                if (singleZone) {
   1.477 +                    if (!gSingleZoneCountries->contains((void*)region)) {
   1.478 +                        gSingleZoneCountries->addElement((void*)region, ec);
   1.479 +                    }
   1.480 +                } else {
   1.481 +                    if (!gMultiZonesCountries->contains((void*)region)) {
   1.482 +                        gMultiZonesCountries->addElement((void*)region, ec);
   1.483 +                    }
   1.484 +                }
   1.485 +            }
   1.486 +            umtx_unlock(&gZoneMetaLock);
   1.487 +        }
   1.488 +
   1.489 +        if (singleZone) {
   1.490 +            *isPrimary = TRUE;
   1.491 +        } else {
   1.492 +            // Note: We may cache the primary zone map in future.
   1.493 +
   1.494 +            // Even a country has multiple zones, one of them might be
   1.495 +            // dominant and treated as a primary zone
   1.496 +            int32_t idLen = 0;
   1.497 +            if (regionBuf[0] == 0) {
   1.498 +                u_UCharsToChars(region, regionBuf, 2);
   1.499 +            }
   1.500 +
   1.501 +            UResourceBundle *rb = ures_openDirect(NULL, gMetaZones, &status);
   1.502 +            ures_getByKey(rb, gPrimaryZonesTag, rb, &status);
   1.503 +            const UChar *primaryZone = ures_getStringByKey(rb, regionBuf, &idLen, &status);
   1.504 +            if (U_SUCCESS(status)) {
   1.505 +                if (tzid.compare(primaryZone, idLen) == 0) {
   1.506 +                    *isPrimary = TRUE;
   1.507 +                } else {
   1.508 +                    // The given ID might not be a canonical ID
   1.509 +                    UnicodeString canonicalID;
   1.510 +                    TimeZone::getCanonicalID(tzid, canonicalID, status);
   1.511 +                    if (U_SUCCESS(status) && canonicalID.compare(primaryZone, idLen) == 0) {
   1.512 +                        *isPrimary = TRUE;
   1.513 +                    }
   1.514 +                }
   1.515 +            }
   1.516 +            ures_close(rb);
   1.517 +        }
   1.518 +    }
   1.519 +
   1.520 +    return country;
   1.521 +}
   1.522 +
   1.523 +UnicodeString& U_EXPORT2
   1.524 +ZoneMeta::getMetazoneID(const UnicodeString &tzid, UDate date, UnicodeString &result) {
   1.525 +    UBool isSet = FALSE;
   1.526 +    const UVector *mappings = getMetazoneMappings(tzid);
   1.527 +    if (mappings != NULL) {
   1.528 +        for (int32_t i = 0; i < mappings->size(); i++) {
   1.529 +            OlsonToMetaMappingEntry *mzm = (OlsonToMetaMappingEntry*)mappings->elementAt(i);
   1.530 +            if (mzm->from <= date && mzm->to > date) {
   1.531 +                result.setTo(mzm->mzid, -1);
   1.532 +                isSet = TRUE;
   1.533 +                break;
   1.534 +            }
   1.535 +        }
   1.536 +    }
   1.537 +    if (!isSet) {
   1.538 +        result.setToBogus();
   1.539 +    }
   1.540 +    return result;
   1.541 +}
   1.542 +
   1.543 +static void U_CALLCONV olsonToMetaInit(UErrorCode &status) {
   1.544 +    U_ASSERT(gOlsonToMeta == NULL);
   1.545 +    ucln_i18n_registerCleanup(UCLN_I18N_ZONEMETA, zoneMeta_cleanup);
   1.546 +    gOlsonToMeta = uhash_open(uhash_hashUChars, uhash_compareUChars, NULL, &status);
   1.547 +    if (U_FAILURE(status)) {
   1.548 +        gOlsonToMeta = NULL;
   1.549 +    } else {
   1.550 +        uhash_setKeyDeleter(gOlsonToMeta, deleteUCharString);
   1.551 +        uhash_setValueDeleter(gOlsonToMeta, deleteUVector);
   1.552 +    }
   1.553 +}
   1.554 +
   1.555 +
   1.556 +const UVector* U_EXPORT2
   1.557 +ZoneMeta::getMetazoneMappings(const UnicodeString &tzid) {
   1.558 +    UErrorCode status = U_ZERO_ERROR;
   1.559 +    UChar tzidUChars[ZID_KEY_MAX + 1];
   1.560 +    tzid.extract(tzidUChars, ZID_KEY_MAX + 1, status);
   1.561 +    if (U_FAILURE(status) || status == U_STRING_NOT_TERMINATED_WARNING) {
   1.562 +        return NULL;
   1.563 +    }
   1.564 +
   1.565 +    umtx_initOnce(gOlsonToMetaInitOnce, &olsonToMetaInit, status);
   1.566 +    if (U_FAILURE(status)) {
   1.567 +        return NULL;
   1.568 +    }
   1.569 +
   1.570 +    // get the mapping from cache
   1.571 +    const UVector *result = NULL;
   1.572 +
   1.573 +    umtx_lock(&gZoneMetaLock);
   1.574 +    {
   1.575 +        result = (UVector*) uhash_get(gOlsonToMeta, tzidUChars);
   1.576 +    }
   1.577 +    umtx_unlock(&gZoneMetaLock);
   1.578 +
   1.579 +    if (result != NULL) {
   1.580 +        return result;
   1.581 +    }
   1.582 +
   1.583 +    // miss the cache - create new one
   1.584 +    UVector *tmpResult = createMetazoneMappings(tzid);
   1.585 +    if (tmpResult == NULL) {
   1.586 +        // not available
   1.587 +        return NULL;
   1.588 +    }
   1.589 +
   1.590 +    // put the new one into the cache
   1.591 +    umtx_lock(&gZoneMetaLock);
   1.592 +    {
   1.593 +        // make sure it's already created
   1.594 +        result = (UVector*) uhash_get(gOlsonToMeta, tzidUChars);
   1.595 +        if (result == NULL) {
   1.596 +            // add the one just created
   1.597 +            int32_t tzidLen = tzid.length() + 1;
   1.598 +            UChar *key = (UChar*)uprv_malloc(tzidLen * sizeof(UChar));
   1.599 +            if (key == NULL) {
   1.600 +                // memory allocation error..  just return NULL
   1.601 +                result = NULL;
   1.602 +                delete tmpResult;
   1.603 +            } else {
   1.604 +                tzid.extract(key, tzidLen, status);
   1.605 +                uhash_put(gOlsonToMeta, key, tmpResult, &status);
   1.606 +                if (U_FAILURE(status)) {
   1.607 +                    // delete the mapping
   1.608 +                    result = NULL;
   1.609 +                    delete tmpResult;
   1.610 +                } else {
   1.611 +                    result = tmpResult;
   1.612 +                }
   1.613 +            }
   1.614 +        } else {
   1.615 +            // another thread already put the one
   1.616 +            delete tmpResult;
   1.617 +        }
   1.618 +    }
   1.619 +    umtx_unlock(&gZoneMetaLock);
   1.620 +
   1.621 +    return result;
   1.622 +}
   1.623 +
   1.624 +UVector*
   1.625 +ZoneMeta::createMetazoneMappings(const UnicodeString &tzid) {
   1.626 +    UVector *mzMappings = NULL;
   1.627 +    UErrorCode status = U_ZERO_ERROR;
   1.628 +
   1.629 +    UnicodeString canonicalID;
   1.630 +    UResourceBundle *rb = ures_openDirect(NULL, gMetaZones, &status);
   1.631 +    ures_getByKey(rb, gMetazoneInfo, rb, &status);
   1.632 +    getCanonicalCLDRID(tzid, canonicalID, status);
   1.633 +
   1.634 +    if (U_SUCCESS(status)) {
   1.635 +        char tzKey[ZID_KEY_MAX + 1];
   1.636 +        int32_t tzKeyLen = canonicalID.extract(0, canonicalID.length(), tzKey, sizeof(tzKey), US_INV);
   1.637 +        tzKey[tzKeyLen] = 0;
   1.638 +
   1.639 +        // tzid keys are using ':' as separators
   1.640 +        char *p = tzKey;
   1.641 +        while (*p) {
   1.642 +            if (*p == '/') {
   1.643 +                *p = ':';
   1.644 +            }
   1.645 +            p++;
   1.646 +        }
   1.647 +
   1.648 +        ures_getByKey(rb, tzKey, rb, &status);
   1.649 +
   1.650 +        if (U_SUCCESS(status)) {
   1.651 +            UResourceBundle *mz = NULL;
   1.652 +            while (ures_hasNext(rb)) {
   1.653 +                mz = ures_getNextResource(rb, mz, &status);
   1.654 +
   1.655 +                const UChar *mz_name = ures_getStringByIndex(mz, 0, NULL, &status);
   1.656 +                const UChar *mz_from = gDefaultFrom;
   1.657 +                const UChar *mz_to = gDefaultTo;
   1.658 +
   1.659 +                if (ures_getSize(mz) == 3) {
   1.660 +                    mz_from = ures_getStringByIndex(mz, 1, NULL, &status);
   1.661 +                    mz_to   = ures_getStringByIndex(mz, 2, NULL, &status);
   1.662 +                }
   1.663 +
   1.664 +                if(U_FAILURE(status)){
   1.665 +                    status = U_ZERO_ERROR;
   1.666 +                    continue;
   1.667 +                }
   1.668 +                // We do not want to use SimpleDateformat to parse boundary dates,
   1.669 +                // because this code could be triggered by the initialization code
   1.670 +                // used by SimpleDateFormat.
   1.671 +                UDate from = parseDate(mz_from, status);
   1.672 +                UDate to = parseDate(mz_to, status);
   1.673 +                if (U_FAILURE(status)) {
   1.674 +                    status = U_ZERO_ERROR;
   1.675 +                    continue;
   1.676 +                }
   1.677 +
   1.678 +                OlsonToMetaMappingEntry *entry = (OlsonToMetaMappingEntry*)uprv_malloc(sizeof(OlsonToMetaMappingEntry));
   1.679 +                if (entry == NULL) {
   1.680 +                    status = U_MEMORY_ALLOCATION_ERROR;
   1.681 +                    break;
   1.682 +                }
   1.683 +                entry->mzid = mz_name;
   1.684 +                entry->from = from;
   1.685 +                entry->to = to;
   1.686 +
   1.687 +                if (mzMappings == NULL) {
   1.688 +                    mzMappings = new UVector(deleteOlsonToMetaMappingEntry, NULL, status);
   1.689 +                    if (U_FAILURE(status)) {
   1.690 +                        delete mzMappings;
   1.691 +                        deleteOlsonToMetaMappingEntry(entry);
   1.692 +                        uprv_free(entry);
   1.693 +                        break;
   1.694 +                    }
   1.695 +                }
   1.696 +
   1.697 +                mzMappings->addElement(entry, status);
   1.698 +                if (U_FAILURE(status)) {
   1.699 +                    break;
   1.700 +                }
   1.701 +            }
   1.702 +            ures_close(mz);
   1.703 +            if (U_FAILURE(status)) {
   1.704 +                if (mzMappings != NULL) {
   1.705 +                    delete mzMappings;
   1.706 +                    mzMappings = NULL;
   1.707 +                }
   1.708 +            }
   1.709 +        }
   1.710 +    }
   1.711 +    ures_close(rb);
   1.712 +    return mzMappings;
   1.713 +}
   1.714 +
   1.715 +UnicodeString& U_EXPORT2
   1.716 +ZoneMeta::getZoneIdByMetazone(const UnicodeString &mzid, const UnicodeString &region, UnicodeString &result) {
   1.717 +    UErrorCode status = U_ZERO_ERROR;
   1.718 +    const UChar *tzid = NULL;
   1.719 +    int32_t tzidLen = 0;
   1.720 +    char keyBuf[ZID_KEY_MAX + 1];
   1.721 +    int32_t keyLen = 0;
   1.722 +
   1.723 +    if (mzid.length() > ZID_KEY_MAX) {
   1.724 +        result.setToBogus();
   1.725 +        return result;
   1.726 +    }
   1.727 +
   1.728 +    keyLen = mzid.extract(0, mzid.length(), keyBuf, ZID_KEY_MAX + 1, US_INV);
   1.729 +    keyBuf[keyLen] = 0;
   1.730 +
   1.731 +    UResourceBundle *rb = ures_openDirect(NULL, gMetaZones, &status);
   1.732 +    ures_getByKey(rb, gMapTimezonesTag, rb, &status);
   1.733 +    ures_getByKey(rb, keyBuf, rb, &status);
   1.734 +
   1.735 +    if (U_SUCCESS(status)) {
   1.736 +        // check region mapping
   1.737 +        if (region.length() == 2 || region.length() == 3) {
   1.738 +            keyLen = region.extract(0, region.length(), keyBuf, ZID_KEY_MAX + 1, US_INV);
   1.739 +            keyBuf[keyLen] = 0;
   1.740 +            tzid = ures_getStringByKey(rb, keyBuf, &tzidLen, &status);
   1.741 +            if (status == U_MISSING_RESOURCE_ERROR) {
   1.742 +                status = U_ZERO_ERROR;
   1.743 +            }
   1.744 +        }
   1.745 +        if (U_SUCCESS(status) && tzid == NULL) {
   1.746 +            // try "001"
   1.747 +            tzid = ures_getStringByKey(rb, gWorldTag, &tzidLen, &status);
   1.748 +        }
   1.749 +    }
   1.750 +    ures_close(rb);
   1.751 +
   1.752 +    if (tzid == NULL) {
   1.753 +        result.setToBogus();
   1.754 +    } else {
   1.755 +        result.setTo(tzid, tzidLen);
   1.756 +    }
   1.757 +
   1.758 +    return result;
   1.759 +}
   1.760 +
   1.761 +static void U_CALLCONV initAvailableMetaZoneIDs () {
   1.762 +    U_ASSERT(gMetaZoneIDs == NULL);
   1.763 +    U_ASSERT(gMetaZoneIDTable == NULL);
   1.764 +    ucln_i18n_registerCleanup(UCLN_I18N_ZONEMETA, zoneMeta_cleanup);
   1.765 +
   1.766 +    UErrorCode status = U_ZERO_ERROR;
   1.767 +    gMetaZoneIDTable = uhash_open(uhash_hashUnicodeString, uhash_compareUnicodeString, NULL, &status);
   1.768 +    if (U_FAILURE(status) || gMetaZoneIDTable == NULL) {
   1.769 +        gMetaZoneIDTable = NULL;
   1.770 +        return;
   1.771 +    }
   1.772 +    uhash_setKeyDeleter(gMetaZoneIDTable, uprv_deleteUObject);
   1.773 +    // No valueDeleter, because the vector maintain the value objects
   1.774 +    gMetaZoneIDs = new UVector(NULL, uhash_compareUChars, status);
   1.775 +    if (U_FAILURE(status) || gMetaZoneIDs == NULL) {
   1.776 +        gMetaZoneIDs = NULL;
   1.777 +        uhash_close(gMetaZoneIDTable);
   1.778 +        gMetaZoneIDTable = NULL;
   1.779 +        return;
   1.780 +    }
   1.781 +    gMetaZoneIDs->setDeleter(uprv_free);
   1.782 +
   1.783 +    UResourceBundle *rb = ures_openDirect(NULL, gMetaZones, &status);
   1.784 +    UResourceBundle *bundle = ures_getByKey(rb, gMapTimezonesTag, NULL, &status);
   1.785 +    UResourceBundle res;
   1.786 +    ures_initStackObject(&res);
   1.787 +    while (U_SUCCESS(status) && ures_hasNext(bundle)) {
   1.788 +        ures_getNextResource(bundle, &res, &status);
   1.789 +        if (U_FAILURE(status)) {
   1.790 +            break;
   1.791 +        }
   1.792 +        const char *mzID = ures_getKey(&res);
   1.793 +        int32_t len = uprv_strlen(mzID);
   1.794 +        UChar *uMzID = (UChar*)uprv_malloc(sizeof(UChar) * (len + 1));
   1.795 +        if (uMzID == NULL) {
   1.796 +            status = U_MEMORY_ALLOCATION_ERROR;
   1.797 +            break;
   1.798 +        }
   1.799 +        u_charsToUChars(mzID, uMzID, len);
   1.800 +        uMzID[len] = 0;
   1.801 +        UnicodeString *usMzID = new UnicodeString(uMzID);
   1.802 +        if (uhash_get(gMetaZoneIDTable, usMzID) == NULL) {
   1.803 +            gMetaZoneIDs->addElement((void *)uMzID, status);
   1.804 +            uhash_put(gMetaZoneIDTable, (void *)usMzID, (void *)uMzID, &status);
   1.805 +        } else {
   1.806 +            uprv_free(uMzID);
   1.807 +            delete usMzID;
   1.808 +        }
   1.809 +    }
   1.810 +    ures_close(&res);
   1.811 +    ures_close(bundle);
   1.812 +    ures_close(rb);
   1.813 +
   1.814 +    if (U_FAILURE(status)) {
   1.815 +        uhash_close(gMetaZoneIDTable);
   1.816 +        delete gMetaZoneIDs;
   1.817 +        gMetaZoneIDTable = NULL;
   1.818 +        gMetaZoneIDs = NULL;
   1.819 +    }
   1.820 +}
   1.821 +
   1.822 +const UVector*
   1.823 +ZoneMeta::getAvailableMetazoneIDs() {
   1.824 +    umtx_initOnce(gMetaZoneIDsInitOnce, &initAvailableMetaZoneIDs);
   1.825 +    return gMetaZoneIDs;
   1.826 +}
   1.827 +
   1.828 +const UChar*
   1.829 +ZoneMeta::findMetaZoneID(const UnicodeString& mzid) {
   1.830 +    umtx_initOnce(gMetaZoneIDsInitOnce, &initAvailableMetaZoneIDs);
   1.831 +    if (gMetaZoneIDTable == NULL) {
   1.832 +        return NULL;
   1.833 +    }
   1.834 +    return (const UChar*)uhash_get(gMetaZoneIDTable, &mzid);
   1.835 +}
   1.836 +
   1.837 +const UChar*
   1.838 +ZoneMeta::findTimeZoneID(const UnicodeString& tzid) {
   1.839 +    return TimeZone::findID(tzid);
   1.840 +}
   1.841 +
   1.842 +
   1.843 +TimeZone*
   1.844 +ZoneMeta::createCustomTimeZone(int32_t offset) {
   1.845 +    UBool negative = FALSE;
   1.846 +    int32_t tmp = offset;
   1.847 +    if (offset < 0) {
   1.848 +        negative = TRUE;
   1.849 +        tmp = -offset;
   1.850 +    }
   1.851 +    int32_t hour, min, sec;
   1.852 +
   1.853 +    tmp /= 1000;
   1.854 +    sec = tmp % 60;
   1.855 +    tmp /= 60;
   1.856 +    min = tmp % 60;
   1.857 +    hour = tmp / 60;
   1.858 +
   1.859 +    UnicodeString zid;
   1.860 +    formatCustomID(hour, min, sec, negative, zid);
   1.861 +    return new SimpleTimeZone(offset, zid);
   1.862 +}
   1.863 +
   1.864 +UnicodeString&
   1.865 +ZoneMeta::formatCustomID(uint8_t hour, uint8_t min, uint8_t sec, UBool negative, UnicodeString& id) {
   1.866 +    // Create normalized time zone ID - GMT[+|-]HH:mm[:ss]
   1.867 +    id.setTo(gCustomTzPrefix, -1);
   1.868 +    if (hour != 0 || min != 0) {
   1.869 +        if (negative) {
   1.870 +          id.append((UChar)0x2D);    // '-'
   1.871 +        } else {
   1.872 +          id.append((UChar)0x2B);    // '+'
   1.873 +        }
   1.874 +        // Always use US-ASCII digits
   1.875 +        id.append((UChar)(0x30 + (hour%100)/10));
   1.876 +        id.append((UChar)(0x30 + (hour%10)));
   1.877 +        id.append((UChar)0x3A);    // ':'
   1.878 +        id.append((UChar)(0x30 + (min%100)/10));
   1.879 +        id.append((UChar)(0x30 + (min%10)));
   1.880 +        if (sec != 0) {
   1.881 +          id.append((UChar)0x3A);    // ':'
   1.882 +          id.append((UChar)(0x30 + (sec%100)/10));
   1.883 +          id.append((UChar)(0x30 + (sec%10)));
   1.884 +        }
   1.885 +    }
   1.886 +    return id;
   1.887 +}
   1.888 +
   1.889 +const UChar*
   1.890 +ZoneMeta::getShortID(const TimeZone& tz) {
   1.891 +    const UChar* canonicalID = NULL;
   1.892 +    if (dynamic_cast<const OlsonTimeZone *>(&tz) != NULL) {
   1.893 +        // short cut for OlsonTimeZone
   1.894 +        const OlsonTimeZone *otz = (const OlsonTimeZone*)&tz;
   1.895 +        canonicalID = otz->getCanonicalID();
   1.896 +    }
   1.897 +    if (canonicalID == NULL) {
   1.898 +        return NULL;
   1.899 +    }
   1.900 +    return getShortIDFromCanonical(canonicalID);
   1.901 +}
   1.902 +
   1.903 +const UChar*
   1.904 +ZoneMeta::getShortID(const UnicodeString& id) {
   1.905 +    UErrorCode status = U_ZERO_ERROR;
   1.906 +    const UChar* canonicalID = ZoneMeta::getCanonicalCLDRID(id, status);
   1.907 +    if (U_FAILURE(status) || canonicalID == NULL) {
   1.908 +        return NULL;
   1.909 +    }
   1.910 +    return ZoneMeta::getShortIDFromCanonical(canonicalID);
   1.911 +}
   1.912 +
   1.913 +const UChar*
   1.914 +ZoneMeta::getShortIDFromCanonical(const UChar* canonicalID) {
   1.915 +    const UChar* shortID = NULL;
   1.916 +    int32_t len = u_strlen(canonicalID);
   1.917 +    char tzidKey[ZID_KEY_MAX + 1];
   1.918 +
   1.919 +    u_UCharsToChars(canonicalID, tzidKey, len);
   1.920 +    tzidKey[len] = (char) 0; // Make sure it is null terminated.
   1.921 +
   1.922 +    // replace '/' with ':'
   1.923 +    char *p = tzidKey;
   1.924 +    while (*p++) {
   1.925 +        if (*p == '/') {
   1.926 +            *p = ':';
   1.927 +        }
   1.928 +    }
   1.929 +
   1.930 +    UErrorCode status = U_ZERO_ERROR;
   1.931 +    UResourceBundle *rb = ures_openDirect(NULL, gKeyTypeData, &status);
   1.932 +    ures_getByKey(rb, gTypeMapTag, rb, &status);
   1.933 +    ures_getByKey(rb, gTimezoneTag, rb, &status);
   1.934 +    shortID = ures_getStringByKey(rb, tzidKey, NULL, &status);
   1.935 +    ures_close(rb);
   1.936 +
   1.937 +    return shortID;
   1.938 +}
   1.939 +
   1.940 +U_NAMESPACE_END
   1.941 +
   1.942 +#endif /* #if !UCONFIG_NO_FORMATTING */

mercurial