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