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