1 /* 2 ******************************************************************************* 3 * Copyright (C) 2007-2011, 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 18 #include "umutex.h" 19 #include "uvector.h" 20 #include "cmemory.h" 21 #include "gregoimp.h" 22 #include "cstring.h" 23 #include "ucln_in.h" 24 #include "uassert.h" 25 #include "uresimp.h" 26 #include "uhash.h" 27 #include "olsontz.h" 28 29 static UMTX gZoneMetaLock = NULL; 30 31 // CLDR Canonical ID mapping table 32 static UHashtable *gCanonicalIDCache = NULL; 33 static UBool gCanonicalIDCacheInitialized = FALSE; 34 35 // Metazone mapping table 36 static UHashtable *gOlsonToMeta = NULL; 37 static UBool gOlsonToMetaInitialized = FALSE; 38 39 // Available metazone IDs vector and table 40 static U_NAMESPACE_QUALIFIER UVector *gMetaZoneIDs = NULL; 41 static UHashtable *gMetaZoneIDTable = NULL; 42 static UBool gMetaZoneIDsInitialized = FALSE; 43 44 // Country info vectors 45 static U_NAMESPACE_QUALIFIER UVector *gSingleZoneCountries = NULL; 46 static U_NAMESPACE_QUALIFIER UVector *gMultiZonesCountries = NULL; 47 static UBool gCountryInfoVectorsInitialized = FALSE; 48 49 U_CDECL_BEGIN 50 51 /** 52 * Cleanup callback func 53 */ 54 static UBool U_CALLCONV zoneMeta_cleanup(void) 55 { 56 umtx_destroy(&gZoneMetaLock); 57 58 if (gCanonicalIDCache != NULL) { 59 uhash_close(gCanonicalIDCache); 60 gCanonicalIDCache = NULL; 61 } 62 gCanonicalIDCacheInitialized = FALSE; 63 64 if (gOlsonToMeta != NULL) { 65 uhash_close(gOlsonToMeta); 66 gOlsonToMeta = NULL; 67 } 68 gOlsonToMetaInitialized = FALSE; 69 70 if (gMetaZoneIDTable != NULL) { 71 uhash_close(gMetaZoneIDTable); 72 } 73 // delete after closing gMetaZoneIDTable, because it holds 74 // value objects held by the hashtable 75 delete gMetaZoneIDs; 76 gMetaZoneIDsInitialized = FALSE; 77 78 delete gSingleZoneCountries; 79 delete gMultiZonesCountries; 80 gCountryInfoVectorsInitialized = FALSE; 81 82 return TRUE; 83 } 84 85 /** 86 * Deleter for UChar* string 87 */ 88 static void U_CALLCONV 89 deleteUCharString(void *obj) { 90 UChar *entry = (UChar*)obj; 91 uprv_free(entry); 92 } 93 94 /** 95 * Deleter for UVector 96 */ 97 static void U_CALLCONV 98 deleteUVector(void *obj) { 99 delete (U_NAMESPACE_QUALIFIER UVector*) obj; 100 } 101 102 /** 103 * Deleter for OlsonToMetaMappingEntry 104 */ 105 static void U_CALLCONV 106 deleteOlsonToMetaMappingEntry(void *obj) { 107 U_NAMESPACE_QUALIFIER OlsonToMetaMappingEntry *entry = (U_NAMESPACE_QUALIFIER OlsonToMetaMappingEntry*)obj; 108 uprv_free(entry); 109 } 110 111 U_CDECL_END 112 113 U_NAMESPACE_BEGIN 114 115 #define ZID_KEY_MAX 128 116 117 static const char gMetaZones[] = "metaZones"; 118 static const char gMetazoneInfo[] = "metazoneInfo"; 119 static const char gMapTimezonesTag[] = "mapTimezones"; 120 121 static const char gTimeZoneTypes[] = "timezoneTypes"; 122 static const char gTypeAliasTag[] = "typeAlias"; 123 static const char gTypeMapTag[] = "typeMap"; 124 static const char gTimezoneTag[] = "timezone"; 125 126 static const char gWorldTag[] = "001"; 127 128 static const UChar gWorld[] = {0x30, 0x30, 0x31, 0x00}; // "001" 129 130 static const UChar gDefaultFrom[] = {0x31, 0x39, 0x37, 0x30, 0x2D, 0x30, 0x31, 0x2D, 0x30, 0x31, 131 0x20, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x00}; // "1970-01-01 00:00" 132 static const UChar gDefaultTo[] = {0x39, 0x39, 0x39, 0x39, 0x2D, 0x31, 0x32, 0x2D, 0x33, 0x31, 133 0x20, 0x32, 0x33, 0x3A, 0x35, 0x39, 0x00}; // "9999-12-31 23:59" 134 135 #define ASCII_DIGIT(c) (((c)>=0x30 && (c)<=0x39) ? (c)-0x30 : -1) 136 137 /* 138 * Convert a date string used by metazone mappings to UDate. 139 * The format used by CLDR metazone mapping is "yyyy-MM-dd HH:mm". 140 */ 141 static UDate 142 parseDate (const UChar *text, UErrorCode &status) { 143 if (U_FAILURE(status)) { 144 return 0; 145 } 146 int32_t len = u_strlen(text); 147 if (len != 16 && len != 10) { 148 // It must be yyyy-MM-dd HH:mm (length 16) or yyyy-MM-dd (length 10) 149 status = U_INVALID_FORMAT_ERROR; 150 return 0; 151 } 152 153 int32_t year = 0, month = 0, day = 0, hour = 0, min = 0, n; 154 int32_t idx; 155 156 // "yyyy" (0 - 3) 157 for (idx = 0; idx <= 3 && U_SUCCESS(status); idx++) { 158 n = ASCII_DIGIT((int32_t)text[idx]); 159 if (n >= 0) { 160 year = 10*year + n; 161 } else { 162 status = U_INVALID_FORMAT_ERROR; 163 } 164 } 165 // "MM" (5 - 6) 166 for (idx = 5; idx <= 6 && U_SUCCESS(status); idx++) { 167 n = ASCII_DIGIT((int32_t)text[idx]); 168 if (n >= 0) { 169 month = 10*month + n; 170 } else { 171 status = U_INVALID_FORMAT_ERROR; 172 } 173 } 174 // "dd" (8 - 9) 175 for (idx = 8; idx <= 9 && U_SUCCESS(status); idx++) { 176 n = ASCII_DIGIT((int32_t)text[idx]); 177 if (n >= 0) { 178 day = 10*day + n; 179 } else { 180 status = U_INVALID_FORMAT_ERROR; 181 } 182 } 183 if (len == 16) { 184 // "HH" (11 - 12) 185 for (idx = 11; idx <= 12 && U_SUCCESS(status); idx++) { 186 n = ASCII_DIGIT((int32_t)text[idx]); 187 if (n >= 0) { 188 hour = 10*hour + n; 189 } else { 190 status = U_INVALID_FORMAT_ERROR; 191 } 192 } 193 // "mm" (14 - 15) 194 for (idx = 14; idx <= 15 && U_SUCCESS(status); idx++) { 195 n = ASCII_DIGIT((int32_t)text[idx]); 196 if (n >= 0) { 197 min = 10*min + n; 198 } else { 199 status = U_INVALID_FORMAT_ERROR; 200 } 201 } 202 } 203 204 if (U_SUCCESS(status)) { 205 UDate date = Grego::fieldsToDay(year, month - 1, day) * U_MILLIS_PER_DAY 206 + hour * U_MILLIS_PER_HOUR + min * U_MILLIS_PER_MINUTE; 207 return date; 208 } 209 return 0; 210 } 211 212 const UChar* U_EXPORT2 213 ZoneMeta::getCanonicalCLDRID(const UnicodeString &tzid, UErrorCode& status) { 214 if (U_FAILURE(status)) { 215 return NULL; 216 } 217 218 int32_t len = tzid.length(); 219 if (len > ZID_KEY_MAX) { 220 status = U_ILLEGAL_ARGUMENT_ERROR; 221 return NULL; 222 } 223 224 // Checking the cached results 225 UBool initialized; 226 UMTX_CHECK(&gZoneMetaLock, gCanonicalIDCacheInitialized, initialized); 227 if (!initialized) { 228 // Create empty hashtable 229 umtx_lock(&gZoneMetaLock); 230 { 231 if (!gCanonicalIDCacheInitialized) { 232 gCanonicalIDCache = uhash_open(uhash_hashUChars, uhash_compareUChars, NULL, &status); 233 if (gCanonicalIDCache == NULL) { 234 status = U_MEMORY_ALLOCATION_ERROR; 235 } 236 if (U_FAILURE(status)) { 237 gCanonicalIDCache = NULL; 238 return NULL; 239 } 240 // No key/value deleters - keys/values are from a resource bundle 241 gCanonicalIDCacheInitialized = TRUE; 242 ucln_i18n_registerCleanup(UCLN_I18N_ZONEMETA, zoneMeta_cleanup); 243 } 244 } 245 umtx_unlock(&gZoneMetaLock); 246 } 247 248 const UChar *canonicalID = NULL; 249 250 UErrorCode tmpStatus = U_ZERO_ERROR; 251 UChar utzid[ZID_KEY_MAX + 1]; 252 tzid.extract(utzid, ZID_KEY_MAX + 1, tmpStatus); 253 U_ASSERT(tmpStatus == U_ZERO_ERROR); // we checked the length of tzid already 254 255 // Check if it was already cached 256 umtx_lock(&gZoneMetaLock); 257 { 258 canonicalID = (const UChar *)uhash_get(gCanonicalIDCache, utzid); 259 } 260 umtx_unlock(&gZoneMetaLock); 261 262 if (canonicalID != NULL) { 263 return canonicalID; 264 } 265 266 // If not, resolve CLDR canonical ID with resource data 267 UBool isInputCanonical = FALSE; 268 char id[ZID_KEY_MAX + 1]; 269 const UChar* idChars = tzid.getBuffer(); 270 271 u_UCharsToChars(idChars,id,len); 272 id[len] = (char) 0; // Make sure it is null terminated. 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, gTimeZoneTypes, &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 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 394 395 UnicodeString& U_EXPORT2 396 ZoneMeta::getCanonicalCountry(const UnicodeString &tzid, UnicodeString &canonicalCountry) { 397 const UChar *region = TimeZone::getRegion(tzid); 398 if (region != NULL && u_strcmp(gWorld, region) != 0) { 399 canonicalCountry.setTo(region, -1); 400 } else { 401 canonicalCountry.setToBogus(); 402 } 403 return canonicalCountry; 404 } 405 406 UnicodeString& U_EXPORT2 407 ZoneMeta::getSingleCountry(const UnicodeString &tzid, UnicodeString &country) { 408 // Get canonical country for the zone 409 const UChar *region = TimeZone::getRegion(tzid); 410 if (region == NULL || u_strcmp(gWorld, region) == 0) { 411 // special case - unknown or "001" 412 country.setToBogus(); 413 return country; 414 } 415 416 // Checking the cached results 417 UErrorCode status = U_ZERO_ERROR; 418 UBool initialized; 419 UMTX_CHECK(&gZoneMetaLock, gCountryInfoVectorsInitialized, initialized); 420 if (!initialized) { 421 // Create empty vectors 422 umtx_lock(&gZoneMetaLock); 423 { 424 if (!gCountryInfoVectorsInitialized) { 425 // No deleters for these UVectors, it's a reference to a resource bundle string. 426 gSingleZoneCountries = new UVector(NULL, uhash_compareUChars, status); 427 if (gSingleZoneCountries == NULL) { 428 status = U_MEMORY_ALLOCATION_ERROR; 429 } 430 gMultiZonesCountries = new UVector(NULL, uhash_compareUChars, status); 431 if (gMultiZonesCountries == NULL) { 432 status = U_MEMORY_ALLOCATION_ERROR; 433 } 434 435 if (U_SUCCESS(status)) { 436 gCountryInfoVectorsInitialized = TRUE; 437 } else { 438 delete gSingleZoneCountries; 439 delete gMultiZonesCountries; 440 } 441 } 442 } 443 umtx_unlock(&gZoneMetaLock); 444 445 if (U_FAILURE(status)) { 446 country.setToBogus(); 447 return country; 448 } 449 } 450 451 // Check if it was already cached 452 UBool cached = FALSE; 453 UBool multiZones = FALSE; 454 umtx_lock(&gZoneMetaLock); 455 { 456 multiZones = cached = gMultiZonesCountries->contains((void*)region); 457 if (!multiZones) { 458 cached = gSingleZoneCountries->contains((void*)region); 459 } 460 } 461 umtx_unlock(&gZoneMetaLock); 462 463 if (!cached) { 464 // We need to go through all zones associated with the region. 465 // This is relatively heavy operation. 466 467 U_ASSERT(u_strlen(region) == 2); 468 469 char buf[] = {0, 0, 0}; 470 u_UCharsToChars(region, buf, 2); 471 472 StringEnumeration *ids = TimeZone::createTimeZoneIDEnumeration(UCAL_ZONE_TYPE_CANONICAL_LOCATION, buf, NULL, status); 473 int32_t idsLen = ids->count(status); 474 if (U_SUCCESS(status) && idsLen > 1) { 475 // multiple canonical zones are available for the region 476 multiZones = TRUE; 477 } 478 if (U_FAILURE(status)) { 479 // no single country by default for any error cases 480 multiZones = TRUE; 481 } 482 delete ids; 483 484 // Cache the result 485 umtx_lock(&gZoneMetaLock); 486 { 487 UErrorCode ec = U_ZERO_ERROR; 488 if (multiZones) { 489 if (!gMultiZonesCountries->contains((void*)region)) { 490 gMultiZonesCountries->addElement((void*)region, ec); 491 } 492 } else { 493 if (!gSingleZoneCountries->contains((void*)region)) { 494 gSingleZoneCountries->addElement((void*)region, ec); 495 } 496 } 497 } 498 umtx_unlock(&gZoneMetaLock); 499 } 500 501 if (multiZones) { 502 country.setToBogus(); 503 } else { 504 country.setTo(region, -1); 505 } 506 return country; 507 } 508 509 UnicodeString& U_EXPORT2 510 ZoneMeta::getMetazoneID(const UnicodeString &tzid, UDate date, UnicodeString &result) { 511 UBool isSet = FALSE; 512 const UVector *mappings = getMetazoneMappings(tzid); 513 if (mappings != NULL) { 514 for (int32_t i = 0; i < mappings->size(); i++) { 515 OlsonToMetaMappingEntry *mzm = (OlsonToMetaMappingEntry*)mappings->elementAt(i); 516 if (mzm->from <= date && mzm->to > date) { 517 result.setTo(mzm->mzid, -1); 518 isSet = TRUE; 519 break; 520 } 521 } 522 } 523 if (!isSet) { 524 result.setToBogus(); 525 } 526 return result; 527 } 528 529 const UVector* U_EXPORT2 530 ZoneMeta::getMetazoneMappings(const UnicodeString &tzid) { 531 UErrorCode status = U_ZERO_ERROR; 532 UChar tzidUChars[ZID_KEY_MAX + 1]; 533 tzid.extract(tzidUChars, ZID_KEY_MAX + 1, status); 534 if (U_FAILURE(status) || status == U_STRING_NOT_TERMINATED_WARNING) { 535 return NULL; 536 } 537 538 UBool initialized; 539 UMTX_CHECK(&gZoneMetaLock, gOlsonToMetaInitialized, initialized); 540 if (!initialized) { 541 UHashtable *tmpOlsonToMeta = uhash_open(uhash_hashUChars, uhash_compareUChars, NULL, &status); 542 if (U_FAILURE(status)) { 543 return NULL; 544 } 545 uhash_setKeyDeleter(tmpOlsonToMeta, deleteUCharString); 546 uhash_setValueDeleter(tmpOlsonToMeta, deleteUVector); 547 548 umtx_lock(&gZoneMetaLock); 549 { 550 if (!gOlsonToMetaInitialized) { 551 gOlsonToMeta = tmpOlsonToMeta; 552 tmpOlsonToMeta = NULL; 553 gOlsonToMetaInitialized = TRUE; 554 } 555 } 556 umtx_unlock(&gZoneMetaLock); 557 558 // OK to call the following multiple times with the same function 559 ucln_i18n_registerCleanup(UCLN_I18N_ZONEMETA, zoneMeta_cleanup); 560 if (tmpOlsonToMeta != NULL) { 561 uhash_close(tmpOlsonToMeta); 562 } 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.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 void 757 ZoneMeta::initAvailableMetaZoneIDs () { 758 UBool initialized; 759 UMTX_CHECK(&gZoneMetaLock, gMetaZoneIDsInitialized, initialized); 760 if (!initialized) { 761 umtx_lock(&gZoneMetaLock); 762 { 763 if (!gMetaZoneIDsInitialized) { 764 UErrorCode status = U_ZERO_ERROR; 765 UHashtable *metaZoneIDTable = uhash_open(uhash_hashUnicodeString, uhash_compareUnicodeString, NULL, &status); 766 uhash_setKeyDeleter(metaZoneIDTable, uhash_deleteUnicodeString); 767 // No valueDeleter, because the vector maintain the value objects 768 UVector *metaZoneIDs = NULL; 769 if (U_SUCCESS(status)) { 770 metaZoneIDs = new UVector(NULL, uhash_compareUChars, status); 771 if (metaZoneIDs == NULL) { 772 status = U_MEMORY_ALLOCATION_ERROR; 773 } 774 } else { 775 uhash_close(metaZoneIDTable); 776 } 777 if (U_SUCCESS(status)) { 778 metaZoneIDs->setDeleter(uhash_freeBlock); 779 780 UResourceBundle *rb = ures_openDirect(NULL, gMetaZones, &status); 781 UResourceBundle *bundle = ures_getByKey(rb, gMapTimezonesTag, NULL, &status); 782 UResourceBundle res; 783 ures_initStackObject(&res); 784 while (U_SUCCESS(status) && ures_hasNext(bundle)) { 785 ures_getNextResource(bundle, &res, &status); 786 if (U_FAILURE(status)) { 787 break; 788 } 789 const char *mzID = ures_getKey(&res); 790 int32_t len = uprv_strlen(mzID); 791 UChar *uMzID = (UChar*)uprv_malloc(sizeof(UChar) * (len + 1)); 792 if (uMzID == NULL) { 793 status = U_MEMORY_ALLOCATION_ERROR; 794 break; 795 } 796 u_charsToUChars(mzID, uMzID, len); 797 uMzID[len] = 0; 798 UnicodeString *usMzID = new UnicodeString(uMzID); 799 if (uhash_get(metaZoneIDTable, usMzID) == NULL) { 800 metaZoneIDs->addElement((void *)uMzID, status); 801 uhash_put(metaZoneIDTable, (void *)usMzID, (void *)uMzID, &status); 802 } else { 803 uprv_free(uMzID); 804 delete usMzID; 805 } 806 } 807 if (U_SUCCESS(status)) { 808 gMetaZoneIDs = metaZoneIDs; 809 gMetaZoneIDTable = metaZoneIDTable; 810 gMetaZoneIDsInitialized = TRUE; 811 } else { 812 uhash_close(metaZoneIDTable); 813 delete metaZoneIDs; 814 } 815 ures_close(&res); 816 ures_close(bundle); 817 ures_close(rb); 818 } 819 } 820 } 821 umtx_unlock(&gZoneMetaLock); 822 } 823 } 824 825 const UVector* 826 ZoneMeta::getAvailableMetazoneIDs() { 827 initAvailableMetaZoneIDs(); 828 return gMetaZoneIDs; 829 } 830 831 const UChar* 832 ZoneMeta::findMetaZoneID(const UnicodeString& mzid) { 833 initAvailableMetaZoneIDs(); 834 return (const UChar*)uhash_get(gMetaZoneIDTable, &mzid); 835 } 836 837 const UChar* 838 ZoneMeta::findTimeZoneID(const UnicodeString& tzid) { 839 return TimeZone::findID(tzid); 840 } 841 842 U_NAMESPACE_END 843 844 #endif /* #if !UCONFIG_NO_FORMATTING */ 845