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