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