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