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 ®ion, 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 */