|
1 /* |
|
2 ******************************************************************************* |
|
3 * Copyright (C) 1997-2013, International Business Machines Corporation and |
|
4 * others. All Rights Reserved. |
|
5 ******************************************************************************* |
|
6 * |
|
7 * File TIMEZONE.CPP |
|
8 * |
|
9 * Modification History: |
|
10 * |
|
11 * Date Name Description |
|
12 * 12/05/96 clhuang Creation. |
|
13 * 04/21/97 aliu General clean-up and bug fixing. |
|
14 * 05/08/97 aliu Fixed Hashtable code per code review. |
|
15 * 07/09/97 helena Changed createInstance to createDefault. |
|
16 * 07/29/97 aliu Updated with all-new list of 96 UNIX-derived |
|
17 * TimeZones. Changed mechanism to load from static |
|
18 * array rather than resource bundle. |
|
19 * 07/07/1998 srl Bugfixes from the Java side: UTC GMT CAT NST |
|
20 * Added getDisplayName API |
|
21 * going to add custom parsing. |
|
22 * |
|
23 * ISSUES: |
|
24 * - should getDisplayName cache something? |
|
25 * - should custom time zones be cached? [probably] |
|
26 * 08/10/98 stephen Brought getDisplayName() API in-line w/ conventions |
|
27 * 08/19/98 stephen Changed createTimeZone() to never return 0 |
|
28 * 09/02/98 stephen Added getOffset(monthLen) and hasSameRules() |
|
29 * 09/15/98 stephen Added getStaticClassID() |
|
30 * 02/22/99 stephen Removed character literals for EBCDIC safety |
|
31 * 05/04/99 stephen Changed initDefault() for Mutex issues |
|
32 * 07/12/99 helena HPUX 11 CC Port. |
|
33 * 12/03/99 aliu Moved data out of static table into icudata.dll. |
|
34 * Substantial rewrite of zone lookup, default zone, and |
|
35 * available IDs code. Misc. cleanup. |
|
36 *********************************************************************************/ |
|
37 |
|
38 #include "utypeinfo.h" // for 'typeid' to work |
|
39 |
|
40 #include "unicode/utypes.h" |
|
41 #include "unicode/ustring.h" |
|
42 #include "uassert.h" |
|
43 #include "ustr_imp.h" |
|
44 |
|
45 #ifdef U_DEBUG_TZ |
|
46 # include <stdio.h> |
|
47 # include "uresimp.h" // for debugging |
|
48 |
|
49 static void debug_tz_loc(const char *f, int32_t l) |
|
50 { |
|
51 fprintf(stderr, "%s:%d: ", f, l); |
|
52 } |
|
53 |
|
54 static void debug_tz_msg(const char *pat, ...) |
|
55 { |
|
56 va_list ap; |
|
57 va_start(ap, pat); |
|
58 vfprintf(stderr, pat, ap); |
|
59 fflush(stderr); |
|
60 } |
|
61 static char gStrBuf[256]; |
|
62 #define U_DEBUG_TZ_STR(x) u_austrncpy(gStrBuf,x,sizeof(gStrBuf)-1) |
|
63 // must use double parens, i.e.: U_DEBUG_TZ_MSG(("four is: %d",4)); |
|
64 #define U_DEBUG_TZ_MSG(x) {debug_tz_loc(__FILE__,__LINE__);debug_tz_msg x;} |
|
65 #else |
|
66 #define U_DEBUG_TZ_MSG(x) |
|
67 #endif |
|
68 |
|
69 #if !UCONFIG_NO_FORMATTING |
|
70 |
|
71 #include "unicode/simpletz.h" |
|
72 #include "unicode/calendar.h" |
|
73 #include "unicode/gregocal.h" |
|
74 #include "unicode/ures.h" |
|
75 #include "unicode/tzfmt.h" |
|
76 #include "unicode/numfmt.h" |
|
77 #include "gregoimp.h" |
|
78 #include "uresimp.h" // struct UResourceBundle |
|
79 #include "olsontz.h" |
|
80 #include "mutex.h" |
|
81 #include "unicode/udata.h" |
|
82 #include "ucln_in.h" |
|
83 #include "cstring.h" |
|
84 #include "cmemory.h" |
|
85 #include "unicode/strenum.h" |
|
86 #include "uassert.h" |
|
87 #include "zonemeta.h" |
|
88 |
|
89 #define kZONEINFO "zoneinfo64" |
|
90 #define kREGIONS "Regions" |
|
91 #define kZONES "Zones" |
|
92 #define kRULES "Rules" |
|
93 #define kNAMES "Names" |
|
94 #define kTZVERSION "TZVersion" |
|
95 #define kLINKS "links" |
|
96 #define kMAX_CUSTOM_HOUR 23 |
|
97 #define kMAX_CUSTOM_MIN 59 |
|
98 #define kMAX_CUSTOM_SEC 59 |
|
99 #define MINUS 0x002D |
|
100 #define PLUS 0x002B |
|
101 #define ZERO_DIGIT 0x0030 |
|
102 #define COLON 0x003A |
|
103 |
|
104 // Static data and constants |
|
105 |
|
106 static const UChar WORLD[] = {0x30, 0x30, 0x31, 0x00}; /* "001" */ |
|
107 |
|
108 static const UChar GMT_ID[] = {0x47, 0x4D, 0x54, 0x00}; /* "GMT" */ |
|
109 static const UChar UNKNOWN_ZONE_ID[] = {0x45, 0x74, 0x63, 0x2F, 0x55, 0x6E, 0x6B, 0x6E, 0x6F, 0x77, 0x6E, 0x00}; /* "Etc/Unknown" */ |
|
110 static const int32_t GMT_ID_LENGTH = 3; |
|
111 static const int32_t UNKNOWN_ZONE_ID_LENGTH = 11; |
|
112 |
|
113 static icu::TimeZone* DEFAULT_ZONE = NULL; |
|
114 static icu::UInitOnce gDefaultZoneInitOnce = U_INITONCE_INITIALIZER; |
|
115 |
|
116 static icu::TimeZone* _GMT = NULL; |
|
117 static icu::TimeZone* _UNKNOWN_ZONE = NULL; |
|
118 static icu::UInitOnce gStaticZonesInitOnce = U_INITONCE_INITIALIZER; |
|
119 |
|
120 static char TZDATA_VERSION[16]; |
|
121 static icu::UInitOnce gTZDataVersionInitOnce = U_INITONCE_INITIALIZER; |
|
122 |
|
123 static int32_t* MAP_SYSTEM_ZONES = NULL; |
|
124 static int32_t* MAP_CANONICAL_SYSTEM_ZONES = NULL; |
|
125 static int32_t* MAP_CANONICAL_SYSTEM_LOCATION_ZONES = NULL; |
|
126 |
|
127 static int32_t LEN_SYSTEM_ZONES = 0; |
|
128 static int32_t LEN_CANONICAL_SYSTEM_ZONES = 0; |
|
129 static int32_t LEN_CANONICAL_SYSTEM_LOCATION_ZONES = 0; |
|
130 |
|
131 static icu::UInitOnce gSystemZonesInitOnce = U_INITONCE_INITIALIZER; |
|
132 static icu::UInitOnce gCanonicalZonesInitOnce = U_INITONCE_INITIALIZER; |
|
133 static icu::UInitOnce gCanonicalLocationZonesInitOnce = U_INITONCE_INITIALIZER; |
|
134 |
|
135 U_CDECL_BEGIN |
|
136 static UBool U_CALLCONV timeZone_cleanup(void) |
|
137 { |
|
138 U_NAMESPACE_USE |
|
139 delete DEFAULT_ZONE; |
|
140 DEFAULT_ZONE = NULL; |
|
141 gDefaultZoneInitOnce.reset(); |
|
142 |
|
143 delete _GMT; |
|
144 _GMT = NULL; |
|
145 delete _UNKNOWN_ZONE; |
|
146 _UNKNOWN_ZONE = NULL; |
|
147 gStaticZonesInitOnce.reset(); |
|
148 |
|
149 uprv_memset(TZDATA_VERSION, 0, sizeof(TZDATA_VERSION)); |
|
150 gTZDataVersionInitOnce.reset(); |
|
151 |
|
152 LEN_SYSTEM_ZONES = 0; |
|
153 uprv_free(MAP_SYSTEM_ZONES); |
|
154 MAP_SYSTEM_ZONES = 0; |
|
155 gSystemZonesInitOnce.reset(); |
|
156 |
|
157 LEN_CANONICAL_SYSTEM_ZONES = 0; |
|
158 uprv_free(MAP_CANONICAL_SYSTEM_ZONES); |
|
159 MAP_CANONICAL_SYSTEM_ZONES = 0; |
|
160 gCanonicalZonesInitOnce.reset(); |
|
161 |
|
162 LEN_CANONICAL_SYSTEM_LOCATION_ZONES = 0; |
|
163 uprv_free(MAP_CANONICAL_SYSTEM_LOCATION_ZONES); |
|
164 MAP_CANONICAL_SYSTEM_LOCATION_ZONES = 0; |
|
165 gCanonicalLocationZonesInitOnce.reset(); |
|
166 |
|
167 return TRUE; |
|
168 } |
|
169 U_CDECL_END |
|
170 |
|
171 U_NAMESPACE_BEGIN |
|
172 |
|
173 static int32_t findInStringArray(UResourceBundle* array, const UnicodeString& id, UErrorCode &status) |
|
174 { |
|
175 UnicodeString copy; |
|
176 const UChar *u; |
|
177 int32_t len; |
|
178 |
|
179 int32_t start = 0; |
|
180 int32_t limit = ures_getSize(array); |
|
181 int32_t mid; |
|
182 int32_t lastMid = INT32_MAX; |
|
183 if(U_FAILURE(status) || (limit < 1)) { |
|
184 return -1; |
|
185 } |
|
186 U_DEBUG_TZ_MSG(("fisa: Looking for %s, between %d and %d\n", U_DEBUG_TZ_STR(UnicodeString(id).getTerminatedBuffer()), start, limit)); |
|
187 |
|
188 for (;;) { |
|
189 mid = (int32_t)((start + limit) / 2); |
|
190 if (lastMid == mid) { /* Have we moved? */ |
|
191 break; /* We haven't moved, and it wasn't found. */ |
|
192 } |
|
193 lastMid = mid; |
|
194 u = ures_getStringByIndex(array, mid, &len, &status); |
|
195 if (U_FAILURE(status)) { |
|
196 break; |
|
197 } |
|
198 U_DEBUG_TZ_MSG(("tz: compare to %s, %d .. [%d] .. %d\n", U_DEBUG_TZ_STR(u), start, mid, limit)); |
|
199 copy.setTo(TRUE, u, len); |
|
200 int r = id.compare(copy); |
|
201 if(r==0) { |
|
202 U_DEBUG_TZ_MSG(("fisa: found at %d\n", mid)); |
|
203 return mid; |
|
204 } else if(r<0) { |
|
205 limit = mid; |
|
206 } else { |
|
207 start = mid; |
|
208 } |
|
209 } |
|
210 U_DEBUG_TZ_MSG(("fisa: not found\n")); |
|
211 return -1; |
|
212 } |
|
213 |
|
214 /** |
|
215 * Fetch a specific zone by name. Replaces the getByKey call. |
|
216 * @param top Top timezone resource |
|
217 * @param id Time zone ID |
|
218 * @param oldbundle Bundle for reuse (or NULL). see 'ures_open()' |
|
219 * @return the zone's bundle if found, or undefined if error. Reuses oldbundle. |
|
220 */ |
|
221 static UResourceBundle* getZoneByName(const UResourceBundle* top, const UnicodeString& id, UResourceBundle *oldbundle, UErrorCode& status) { |
|
222 // load the Rules object |
|
223 UResourceBundle *tmp = ures_getByKey(top, kNAMES, NULL, &status); |
|
224 |
|
225 // search for the string |
|
226 int32_t idx = findInStringArray(tmp, id, status); |
|
227 |
|
228 if((idx == -1) && U_SUCCESS(status)) { |
|
229 // not found |
|
230 status = U_MISSING_RESOURCE_ERROR; |
|
231 //ures_close(oldbundle); |
|
232 //oldbundle = NULL; |
|
233 } else { |
|
234 U_DEBUG_TZ_MSG(("gzbn: oldbundle= size %d, type %d, %s\n", ures_getSize(tmp), ures_getType(tmp), u_errorName(status))); |
|
235 tmp = ures_getByKey(top, kZONES, tmp, &status); // get Zones object from top |
|
236 U_DEBUG_TZ_MSG(("gzbn: loaded ZONES, size %d, type %d, path %s %s\n", ures_getSize(tmp), ures_getType(tmp), ures_getPath(tmp), u_errorName(status))); |
|
237 oldbundle = ures_getByIndex(tmp, idx, oldbundle, &status); // get nth Zone object |
|
238 U_DEBUG_TZ_MSG(("gzbn: loaded z#%d, size %d, type %d, path %s, %s\n", idx, ures_getSize(oldbundle), ures_getType(oldbundle), ures_getPath(oldbundle), u_errorName(status))); |
|
239 } |
|
240 ures_close(tmp); |
|
241 if(U_FAILURE(status)) { |
|
242 //ures_close(oldbundle); |
|
243 return NULL; |
|
244 } else { |
|
245 return oldbundle; |
|
246 } |
|
247 } |
|
248 |
|
249 |
|
250 UResourceBundle* TimeZone::loadRule(const UResourceBundle* top, const UnicodeString& ruleid, UResourceBundle* oldbundle, UErrorCode& status) { |
|
251 char key[64]; |
|
252 ruleid.extract(0, sizeof(key)-1, key, (int32_t)sizeof(key)-1, US_INV); |
|
253 U_DEBUG_TZ_MSG(("loadRule(%s)\n", key)); |
|
254 UResourceBundle *r = ures_getByKey(top, kRULES, oldbundle, &status); |
|
255 U_DEBUG_TZ_MSG(("loadRule(%s) -> kRULES [%s]\n", key, u_errorName(status))); |
|
256 r = ures_getByKey(r, key, r, &status); |
|
257 U_DEBUG_TZ_MSG(("loadRule(%s) -> item [%s]\n", key, u_errorName(status))); |
|
258 return r; |
|
259 } |
|
260 |
|
261 /** |
|
262 * Given an ID, open the appropriate resource for the given time zone. |
|
263 * Dereference aliases if necessary. |
|
264 * @param id zone id |
|
265 * @param res resource, which must be ready for use (initialized but not open) |
|
266 * @param ec input-output error code |
|
267 * @return top-level resource bundle |
|
268 */ |
|
269 static UResourceBundle* openOlsonResource(const UnicodeString& id, |
|
270 UResourceBundle& res, |
|
271 UErrorCode& ec) |
|
272 { |
|
273 #if U_DEBUG_TZ |
|
274 char buf[128]; |
|
275 id.extract(0, sizeof(buf)-1, buf, sizeof(buf), ""); |
|
276 #endif |
|
277 UResourceBundle *top = ures_openDirect(0, kZONEINFO, &ec); |
|
278 U_DEBUG_TZ_MSG(("pre: res sz=%d\n", ures_getSize(&res))); |
|
279 /* &res = */ getZoneByName(top, id, &res, ec); |
|
280 // Dereference if this is an alias. Docs say result should be 1 |
|
281 // but it is 0 in 2.8 (?). |
|
282 U_DEBUG_TZ_MSG(("Loading zone '%s' (%s, size %d) - %s\n", buf, ures_getKey((UResourceBundle*)&res), ures_getSize(&res), u_errorName(ec))); |
|
283 if (ures_getType(&res) == URES_INT) { |
|
284 int32_t deref = ures_getInt(&res, &ec) + 0; |
|
285 U_DEBUG_TZ_MSG(("getInt: %s - type is %d\n", u_errorName(ec), ures_getType(&res))); |
|
286 UResourceBundle *ares = ures_getByKey(top, kZONES, NULL, &ec); // dereference Zones section |
|
287 ures_getByIndex(ares, deref, &res, &ec); |
|
288 ures_close(ares); |
|
289 U_DEBUG_TZ_MSG(("alias to #%d (%s) - %s\n", deref, "??", u_errorName(ec))); |
|
290 } else { |
|
291 U_DEBUG_TZ_MSG(("not an alias - size %d\n", ures_getSize(&res))); |
|
292 } |
|
293 U_DEBUG_TZ_MSG(("%s - final status is %s\n", buf, u_errorName(ec))); |
|
294 return top; |
|
295 } |
|
296 |
|
297 // ------------------------------------- |
|
298 |
|
299 namespace { |
|
300 |
|
301 void U_CALLCONV initStaticTimeZones() { |
|
302 // Initialize _GMT independently of other static data; it should |
|
303 // be valid even if we can't load the time zone UDataMemory. |
|
304 ucln_i18n_registerCleanup(UCLN_I18N_TIMEZONE, timeZone_cleanup); |
|
305 _UNKNOWN_ZONE = new SimpleTimeZone(0, UnicodeString(TRUE, UNKNOWN_ZONE_ID, UNKNOWN_ZONE_ID_LENGTH)); |
|
306 _GMT = new SimpleTimeZone(0, UnicodeString(TRUE, GMT_ID, GMT_ID_LENGTH)); |
|
307 } |
|
308 |
|
309 } // anonymous namespace |
|
310 |
|
311 const TimeZone& U_EXPORT2 |
|
312 TimeZone::getUnknown() |
|
313 { |
|
314 umtx_initOnce(gStaticZonesInitOnce, &initStaticTimeZones); |
|
315 return *_UNKNOWN_ZONE; |
|
316 } |
|
317 |
|
318 const TimeZone* U_EXPORT2 |
|
319 TimeZone::getGMT(void) |
|
320 { |
|
321 umtx_initOnce(gStaticZonesInitOnce, &initStaticTimeZones); |
|
322 return _GMT; |
|
323 } |
|
324 |
|
325 // ***************************************************************************** |
|
326 // class TimeZone |
|
327 // ***************************************************************************** |
|
328 |
|
329 UOBJECT_DEFINE_ABSTRACT_RTTI_IMPLEMENTATION(TimeZone) |
|
330 |
|
331 TimeZone::TimeZone() |
|
332 : UObject(), fID() |
|
333 { |
|
334 } |
|
335 |
|
336 // ------------------------------------- |
|
337 |
|
338 TimeZone::TimeZone(const UnicodeString &id) |
|
339 : UObject(), fID(id) |
|
340 { |
|
341 } |
|
342 |
|
343 // ------------------------------------- |
|
344 |
|
345 TimeZone::~TimeZone() |
|
346 { |
|
347 } |
|
348 |
|
349 // ------------------------------------- |
|
350 |
|
351 TimeZone::TimeZone(const TimeZone &source) |
|
352 : UObject(source), fID(source.fID) |
|
353 { |
|
354 } |
|
355 |
|
356 // ------------------------------------- |
|
357 |
|
358 TimeZone & |
|
359 TimeZone::operator=(const TimeZone &right) |
|
360 { |
|
361 if (this != &right) fID = right.fID; |
|
362 return *this; |
|
363 } |
|
364 |
|
365 // ------------------------------------- |
|
366 |
|
367 UBool |
|
368 TimeZone::operator==(const TimeZone& that) const |
|
369 { |
|
370 return typeid(*this) == typeid(that) && |
|
371 fID == that.fID; |
|
372 } |
|
373 |
|
374 // ------------------------------------- |
|
375 |
|
376 namespace { |
|
377 TimeZone* |
|
378 createSystemTimeZone(const UnicodeString& id, UErrorCode& ec) { |
|
379 if (U_FAILURE(ec)) { |
|
380 return NULL; |
|
381 } |
|
382 TimeZone* z = 0; |
|
383 UResourceBundle res; |
|
384 ures_initStackObject(&res); |
|
385 U_DEBUG_TZ_MSG(("pre-err=%s\n", u_errorName(ec))); |
|
386 UResourceBundle *top = openOlsonResource(id, res, ec); |
|
387 U_DEBUG_TZ_MSG(("post-err=%s\n", u_errorName(ec))); |
|
388 if (U_SUCCESS(ec)) { |
|
389 z = new OlsonTimeZone(top, &res, id, ec); |
|
390 if (z == NULL) { |
|
391 U_DEBUG_TZ_MSG(("cstz: olson time zone failed to initialize - err %s\n", u_errorName(ec))); |
|
392 } |
|
393 } |
|
394 ures_close(&res); |
|
395 ures_close(top); |
|
396 if (U_FAILURE(ec)) { |
|
397 U_DEBUG_TZ_MSG(("cstz: failed to create, err %s\n", u_errorName(ec))); |
|
398 delete z; |
|
399 z = 0; |
|
400 } |
|
401 return z; |
|
402 } |
|
403 |
|
404 /** |
|
405 * Lookup the given name in our system zone table. If found, |
|
406 * instantiate a new zone of that name and return it. If not |
|
407 * found, return 0. |
|
408 */ |
|
409 TimeZone* |
|
410 createSystemTimeZone(const UnicodeString& id) { |
|
411 UErrorCode ec = U_ZERO_ERROR; |
|
412 return createSystemTimeZone(id, ec); |
|
413 } |
|
414 |
|
415 } |
|
416 |
|
417 TimeZone* U_EXPORT2 |
|
418 TimeZone::createTimeZone(const UnicodeString& ID) |
|
419 { |
|
420 /* We first try to lookup the zone ID in our system list. If this |
|
421 * fails, we try to parse it as a custom string GMT[+-]hh:mm. If |
|
422 * all else fails, we return GMT, which is probably not what the |
|
423 * user wants, but at least is a functioning TimeZone object. |
|
424 * |
|
425 * We cannot return NULL, because that would break compatibility |
|
426 * with the JDK. |
|
427 */ |
|
428 TimeZone* result = createSystemTimeZone(ID); |
|
429 |
|
430 if (result == 0) { |
|
431 U_DEBUG_TZ_MSG(("failed to load system time zone with id - falling to custom")); |
|
432 result = createCustomTimeZone(ID); |
|
433 } |
|
434 if (result == 0) { |
|
435 U_DEBUG_TZ_MSG(("failed to load time zone with id - falling to Etc/Unknown(GMT)")); |
|
436 result = getUnknown().clone(); |
|
437 } |
|
438 return result; |
|
439 } |
|
440 |
|
441 // ------------------------------------- |
|
442 |
|
443 /** |
|
444 * Initialize DEFAULT_ZONE from the system default time zone. |
|
445 * Upon return, DEFAULT_ZONE will not be NULL, unless operator new() |
|
446 * returns NULL. |
|
447 */ |
|
448 static void U_CALLCONV initDefault() |
|
449 { |
|
450 ucln_i18n_registerCleanup(UCLN_I18N_TIMEZONE, timeZone_cleanup); |
|
451 |
|
452 // If setDefault() has already been called we can skip getting the |
|
453 // default zone information from the system. |
|
454 if (DEFAULT_ZONE != NULL) { |
|
455 return; |
|
456 } |
|
457 |
|
458 // We access system timezone data through TPlatformUtilities, |
|
459 // including tzset(), timezone, and tzname[]. |
|
460 int32_t rawOffset = 0; |
|
461 const char *hostID; |
|
462 |
|
463 // First, try to create a system timezone, based |
|
464 // on the string ID in tzname[0]. |
|
465 |
|
466 // NOTE: this code is safely single threaded, being only |
|
467 // run via umtx_initOnce(). |
|
468 // |
|
469 // Some of the locale/timezone OS functions may not be thread safe, |
|
470 // |
|
471 // The operating system might actually use ICU to implement timezones. |
|
472 // So we may have ICU calling ICU here, like on AIX. |
|
473 // There shouldn't be a problem with this; initOnce does not hold a mutex |
|
474 // while the init function is being run. |
|
475 |
|
476 uprv_tzset(); // Initialize tz... system data |
|
477 |
|
478 // Get the timezone ID from the host. This function should do |
|
479 // any required host-specific remapping; e.g., on Windows this |
|
480 // function maps the Date and Time control panel setting to an |
|
481 // ICU timezone ID. |
|
482 hostID = uprv_tzname(0); |
|
483 |
|
484 // Invert sign because UNIX semantics are backwards |
|
485 rawOffset = uprv_timezone() * -U_MILLIS_PER_SECOND; |
|
486 |
|
487 TimeZone* default_zone = NULL; |
|
488 |
|
489 /* Make sure that the string is NULL terminated to prevent BoundsChecker/Purify warnings. */ |
|
490 UnicodeString hostStrID(hostID, -1, US_INV); |
|
491 hostStrID.append((UChar)0); |
|
492 hostStrID.truncate(hostStrID.length()-1); |
|
493 default_zone = createSystemTimeZone(hostStrID); |
|
494 |
|
495 #if U_PLATFORM_USES_ONLY_WIN32_API |
|
496 // hostID points to a heap-allocated location on Windows. |
|
497 uprv_free(const_cast<char *>(hostID)); |
|
498 #endif |
|
499 |
|
500 int32_t hostIDLen = hostStrID.length(); |
|
501 if (default_zone != NULL && rawOffset != default_zone->getRawOffset() |
|
502 && (3 <= hostIDLen && hostIDLen <= 4)) |
|
503 { |
|
504 // Uh oh. This probably wasn't a good id. |
|
505 // It was probably an ambiguous abbreviation |
|
506 delete default_zone; |
|
507 default_zone = NULL; |
|
508 } |
|
509 |
|
510 // Construct a fixed standard zone with the host's ID |
|
511 // and raw offset. |
|
512 if (default_zone == NULL) { |
|
513 default_zone = new SimpleTimeZone(rawOffset, hostStrID); |
|
514 } |
|
515 |
|
516 // If we _still_ don't have a time zone, use GMT. |
|
517 if (default_zone == NULL) { |
|
518 const TimeZone* temptz = TimeZone::getGMT(); |
|
519 // If we can't use GMT, get out. |
|
520 if (temptz == NULL) { |
|
521 return; |
|
522 } |
|
523 default_zone = temptz->clone(); |
|
524 } |
|
525 |
|
526 // The only way for DEFAULT_ZONE to be non-null at this point is if the user |
|
527 // made a thread-unsafe call to setDefault() or adoptDefault() in another |
|
528 // thread while this thread was doing something that required getting the default. |
|
529 U_ASSERT(DEFAULT_ZONE == NULL); |
|
530 |
|
531 DEFAULT_ZONE = default_zone; |
|
532 } |
|
533 |
|
534 // ------------------------------------- |
|
535 |
|
536 TimeZone* U_EXPORT2 |
|
537 TimeZone::createDefault() |
|
538 { |
|
539 umtx_initOnce(gDefaultZoneInitOnce, initDefault); |
|
540 return (DEFAULT_ZONE != NULL) ? DEFAULT_ZONE->clone() : NULL; |
|
541 } |
|
542 |
|
543 // ------------------------------------- |
|
544 |
|
545 void U_EXPORT2 |
|
546 TimeZone::adoptDefault(TimeZone* zone) |
|
547 { |
|
548 if (zone != NULL) |
|
549 { |
|
550 TimeZone *old = DEFAULT_ZONE; |
|
551 DEFAULT_ZONE = zone; |
|
552 delete old; |
|
553 ucln_i18n_registerCleanup(UCLN_I18N_TIMEZONE, timeZone_cleanup); |
|
554 } |
|
555 } |
|
556 // ------------------------------------- |
|
557 |
|
558 void U_EXPORT2 |
|
559 TimeZone::setDefault(const TimeZone& zone) |
|
560 { |
|
561 adoptDefault(zone.clone()); |
|
562 } |
|
563 |
|
564 //---------------------------------------------------------------------- |
|
565 |
|
566 |
|
567 static void U_CALLCONV initMap(USystemTimeZoneType type, UErrorCode& ec) { |
|
568 ucln_i18n_registerCleanup(UCLN_I18N_TIMEZONE, timeZone_cleanup); |
|
569 |
|
570 UResourceBundle *res = ures_openDirect(0, kZONEINFO, &ec); |
|
571 res = ures_getByKey(res, kNAMES, res, &ec); // dereference Zones section |
|
572 if (U_SUCCESS(ec)) { |
|
573 int32_t size = ures_getSize(res); |
|
574 int32_t *m = (int32_t *)uprv_malloc(size * sizeof(int32_t)); |
|
575 if (m == NULL) { |
|
576 ec = U_MEMORY_ALLOCATION_ERROR; |
|
577 } else { |
|
578 int32_t numEntries = 0; |
|
579 for (int32_t i = 0; i < size; i++) { |
|
580 UnicodeString id = ures_getUnicodeStringByIndex(res, i, &ec); |
|
581 if (U_FAILURE(ec)) { |
|
582 break; |
|
583 } |
|
584 if (0 == id.compare(UNKNOWN_ZONE_ID, UNKNOWN_ZONE_ID_LENGTH)) { |
|
585 // exclude Etc/Unknown |
|
586 continue; |
|
587 } |
|
588 if (type == UCAL_ZONE_TYPE_CANONICAL || type == UCAL_ZONE_TYPE_CANONICAL_LOCATION) { |
|
589 UnicodeString canonicalID; |
|
590 ZoneMeta::getCanonicalCLDRID(id, canonicalID, ec); |
|
591 if (U_FAILURE(ec)) { |
|
592 break; |
|
593 } |
|
594 if (canonicalID != id) { |
|
595 // exclude aliases |
|
596 continue; |
|
597 } |
|
598 } |
|
599 if (type == UCAL_ZONE_TYPE_CANONICAL_LOCATION) { |
|
600 const UChar *region = TimeZone::getRegion(id, ec); |
|
601 if (U_FAILURE(ec)) { |
|
602 break; |
|
603 } |
|
604 if (u_strcmp(region, WORLD) == 0) { |
|
605 // exclude non-location ("001") |
|
606 continue; |
|
607 } |
|
608 } |
|
609 m[numEntries++] = i; |
|
610 } |
|
611 if (U_SUCCESS(ec)) { |
|
612 int32_t *tmp = m; |
|
613 m = (int32_t *)uprv_realloc(tmp, numEntries * sizeof(int32_t)); |
|
614 if (m == NULL) { |
|
615 // realloc failed.. use the original one even it has unused |
|
616 // area at the end |
|
617 m = tmp; |
|
618 } |
|
619 |
|
620 switch(type) { |
|
621 case UCAL_ZONE_TYPE_ANY: |
|
622 U_ASSERT(MAP_SYSTEM_ZONES == NULL); |
|
623 MAP_SYSTEM_ZONES = m; |
|
624 LEN_SYSTEM_ZONES = numEntries; |
|
625 break; |
|
626 case UCAL_ZONE_TYPE_CANONICAL: |
|
627 U_ASSERT(MAP_CANONICAL_SYSTEM_ZONES == NULL); |
|
628 MAP_CANONICAL_SYSTEM_ZONES = m; |
|
629 LEN_CANONICAL_SYSTEM_ZONES = numEntries; |
|
630 break; |
|
631 case UCAL_ZONE_TYPE_CANONICAL_LOCATION: |
|
632 U_ASSERT(MAP_CANONICAL_SYSTEM_LOCATION_ZONES == NULL); |
|
633 MAP_CANONICAL_SYSTEM_LOCATION_ZONES = m; |
|
634 LEN_CANONICAL_SYSTEM_LOCATION_ZONES = numEntries; |
|
635 break; |
|
636 } |
|
637 } |
|
638 } |
|
639 } |
|
640 ures_close(res); |
|
641 } |
|
642 |
|
643 |
|
644 /** |
|
645 * This is the default implementation for subclasses that do not |
|
646 * override this method. This implementation calls through to the |
|
647 * 8-argument getOffset() method after suitable computations, and |
|
648 * correctly adjusts GMT millis to local millis when necessary. |
|
649 */ |
|
650 void TimeZone::getOffset(UDate date, UBool local, int32_t& rawOffset, |
|
651 int32_t& dstOffset, UErrorCode& ec) const { |
|
652 if (U_FAILURE(ec)) { |
|
653 return; |
|
654 } |
|
655 |
|
656 rawOffset = getRawOffset(); |
|
657 if (!local) { |
|
658 date += rawOffset; // now in local standard millis |
|
659 } |
|
660 |
|
661 // When local == TRUE, date might not be in local standard |
|
662 // millis. getOffset taking 7 parameters used here assume |
|
663 // the given time in day is local standard time. |
|
664 // At STD->DST transition, there is a range of time which |
|
665 // does not exist. When 'date' is in this time range |
|
666 // (and local == TRUE), this method interprets the specified |
|
667 // local time as DST. At DST->STD transition, there is a |
|
668 // range of time which occurs twice. In this case, this |
|
669 // method interprets the specified local time as STD. |
|
670 // To support the behavior above, we need to call getOffset |
|
671 // (with 7 args) twice when local == true and DST is |
|
672 // detected in the initial call. |
|
673 for (int32_t pass=0; ; ++pass) { |
|
674 int32_t year, month, dom, dow; |
|
675 double day = uprv_floor(date / U_MILLIS_PER_DAY); |
|
676 int32_t millis = (int32_t) (date - day * U_MILLIS_PER_DAY); |
|
677 |
|
678 Grego::dayToFields(day, year, month, dom, dow); |
|
679 |
|
680 dstOffset = getOffset(GregorianCalendar::AD, year, month, dom, |
|
681 (uint8_t) dow, millis, |
|
682 Grego::monthLength(year, month), |
|
683 ec) - rawOffset; |
|
684 |
|
685 // Recompute if local==TRUE, dstOffset!=0. |
|
686 if (pass!=0 || !local || dstOffset == 0) { |
|
687 break; |
|
688 } |
|
689 // adjust to local standard millis |
|
690 date -= dstOffset; |
|
691 } |
|
692 } |
|
693 |
|
694 // ------------------------------------- |
|
695 |
|
696 // New available IDs API as of ICU 2.4. Uses StringEnumeration API. |
|
697 |
|
698 class TZEnumeration : public StringEnumeration { |
|
699 private: |
|
700 |
|
701 // Map into to zones. Our results are zone[map[i]] for |
|
702 // i=0..len-1, where zone[i] is the i-th Olson zone. If map==NULL |
|
703 // then our results are zone[i] for i=0..len-1. Len will be zero |
|
704 // if the zone data could not be loaded. |
|
705 int32_t* map; |
|
706 int32_t* localMap; |
|
707 int32_t len; |
|
708 int32_t pos; |
|
709 |
|
710 TZEnumeration(int32_t* mapData, int32_t mapLen, UBool adoptMapData) : pos(0) { |
|
711 map = mapData; |
|
712 localMap = adoptMapData ? mapData : NULL; |
|
713 len = mapLen; |
|
714 } |
|
715 |
|
716 UBool getID(int32_t i) { |
|
717 UErrorCode ec = U_ZERO_ERROR; |
|
718 int32_t idLen = 0; |
|
719 const UChar* id = NULL; |
|
720 UResourceBundle *top = ures_openDirect(0, kZONEINFO, &ec); |
|
721 top = ures_getByKey(top, kNAMES, top, &ec); // dereference Zones section |
|
722 id = ures_getStringByIndex(top, i, &idLen, &ec); |
|
723 if(U_FAILURE(ec)) { |
|
724 unistr.truncate(0); |
|
725 } |
|
726 else { |
|
727 unistr.fastCopyFrom(UnicodeString(TRUE, id, idLen)); |
|
728 } |
|
729 ures_close(top); |
|
730 return U_SUCCESS(ec); |
|
731 } |
|
732 |
|
733 static int32_t* getMap(USystemTimeZoneType type, int32_t& len, UErrorCode& ec) { |
|
734 len = 0; |
|
735 if (U_FAILURE(ec)) { |
|
736 return NULL; |
|
737 } |
|
738 int32_t* m = NULL; |
|
739 switch (type) { |
|
740 case UCAL_ZONE_TYPE_ANY: |
|
741 umtx_initOnce(gSystemZonesInitOnce, &initMap, type, ec); |
|
742 m = MAP_SYSTEM_ZONES; |
|
743 len = LEN_SYSTEM_ZONES; |
|
744 break; |
|
745 case UCAL_ZONE_TYPE_CANONICAL: |
|
746 umtx_initOnce(gCanonicalZonesInitOnce, &initMap, type, ec); |
|
747 m = MAP_CANONICAL_SYSTEM_ZONES; |
|
748 len = LEN_CANONICAL_SYSTEM_ZONES; |
|
749 break; |
|
750 case UCAL_ZONE_TYPE_CANONICAL_LOCATION: |
|
751 umtx_initOnce(gCanonicalLocationZonesInitOnce, &initMap, type, ec); |
|
752 m = MAP_CANONICAL_SYSTEM_LOCATION_ZONES; |
|
753 len = LEN_CANONICAL_SYSTEM_LOCATION_ZONES; |
|
754 break; |
|
755 default: |
|
756 ec = U_ILLEGAL_ARGUMENT_ERROR; |
|
757 m = NULL; |
|
758 len = 0; |
|
759 break; |
|
760 } |
|
761 return m; |
|
762 } |
|
763 |
|
764 public: |
|
765 |
|
766 #define DEFAULT_FILTERED_MAP_SIZE 8 |
|
767 #define MAP_INCREMENT_SIZE 8 |
|
768 |
|
769 static TZEnumeration* create(USystemTimeZoneType type, const char* region, const int32_t* rawOffset, UErrorCode& ec) { |
|
770 if (U_FAILURE(ec)) { |
|
771 return NULL; |
|
772 } |
|
773 |
|
774 int32_t baseLen; |
|
775 int32_t *baseMap = getMap(type, baseLen, ec); |
|
776 |
|
777 if (U_FAILURE(ec)) { |
|
778 return NULL; |
|
779 } |
|
780 |
|
781 // If any additional conditions are available, |
|
782 // create instance local map filtered by the conditions. |
|
783 |
|
784 int32_t *filteredMap = NULL; |
|
785 int32_t numEntries = 0; |
|
786 |
|
787 if (region != NULL || rawOffset != NULL) { |
|
788 int32_t filteredMapSize = DEFAULT_FILTERED_MAP_SIZE; |
|
789 filteredMap = (int32_t *)uprv_malloc(filteredMapSize * sizeof(int32_t)); |
|
790 if (filteredMap == NULL) { |
|
791 ec = U_MEMORY_ALLOCATION_ERROR; |
|
792 return NULL; |
|
793 } |
|
794 |
|
795 // Walk through the base map |
|
796 UResourceBundle *res = ures_openDirect(0, kZONEINFO, &ec); |
|
797 res = ures_getByKey(res, kNAMES, res, &ec); // dereference Zones section |
|
798 for (int32_t i = 0; i < baseLen; i++) { |
|
799 int32_t zidx = baseMap[i]; |
|
800 UnicodeString id = ures_getUnicodeStringByIndex(res, zidx, &ec); |
|
801 if (U_FAILURE(ec)) { |
|
802 break; |
|
803 } |
|
804 if (region != NULL) { |
|
805 // Filter by region |
|
806 char tzregion[4]; // max 3 letters + null term |
|
807 TimeZone::getRegion(id, tzregion, sizeof(tzregion), ec); |
|
808 if (U_FAILURE(ec)) { |
|
809 break; |
|
810 } |
|
811 if (uprv_stricmp(tzregion, region) != 0) { |
|
812 // region does not match |
|
813 continue; |
|
814 } |
|
815 } |
|
816 if (rawOffset != NULL) { |
|
817 // Filter by raw offset |
|
818 // Note: This is VERY inefficient |
|
819 TimeZone *z = createSystemTimeZone(id, ec); |
|
820 if (U_FAILURE(ec)) { |
|
821 break; |
|
822 } |
|
823 int32_t tzoffset = z->getRawOffset(); |
|
824 delete z; |
|
825 |
|
826 if (tzoffset != *rawOffset) { |
|
827 continue; |
|
828 } |
|
829 } |
|
830 |
|
831 if (filteredMapSize <= numEntries) { |
|
832 filteredMapSize += MAP_INCREMENT_SIZE; |
|
833 int32_t *tmp = (int32_t *)uprv_realloc(filteredMap, filteredMapSize * sizeof(int32_t)); |
|
834 if (tmp == NULL) { |
|
835 ec = U_MEMORY_ALLOCATION_ERROR; |
|
836 break; |
|
837 } else { |
|
838 filteredMap = tmp; |
|
839 } |
|
840 } |
|
841 |
|
842 filteredMap[numEntries++] = zidx; |
|
843 } |
|
844 |
|
845 if (U_FAILURE(ec)) { |
|
846 uprv_free(filteredMap); |
|
847 filteredMap = NULL; |
|
848 } |
|
849 |
|
850 ures_close(res); |
|
851 } |
|
852 |
|
853 TZEnumeration *result = NULL; |
|
854 if (U_SUCCESS(ec)) { |
|
855 // Finally, create a new enumeration instance |
|
856 if (filteredMap == NULL) { |
|
857 result = new TZEnumeration(baseMap, baseLen, FALSE); |
|
858 } else { |
|
859 result = new TZEnumeration(filteredMap, numEntries, TRUE); |
|
860 filteredMap = NULL; |
|
861 } |
|
862 if (result == NULL) { |
|
863 ec = U_MEMORY_ALLOCATION_ERROR; |
|
864 } |
|
865 } |
|
866 |
|
867 if (filteredMap != NULL) { |
|
868 uprv_free(filteredMap); |
|
869 } |
|
870 |
|
871 return result; |
|
872 } |
|
873 |
|
874 TZEnumeration(const TZEnumeration &other) : StringEnumeration(), map(NULL), localMap(NULL), len(0), pos(0) { |
|
875 if (other.localMap != NULL) { |
|
876 localMap = (int32_t *)uprv_malloc(other.len * sizeof(int32_t)); |
|
877 if (localMap != NULL) { |
|
878 len = other.len; |
|
879 uprv_memcpy(localMap, other.localMap, len * sizeof(int32_t)); |
|
880 pos = other.pos; |
|
881 map = localMap; |
|
882 } else { |
|
883 len = 0; |
|
884 pos = 0; |
|
885 map = NULL; |
|
886 } |
|
887 } else { |
|
888 map = other.map; |
|
889 localMap = NULL; |
|
890 len = other.len; |
|
891 pos = other.pos; |
|
892 } |
|
893 } |
|
894 |
|
895 virtual ~TZEnumeration(); |
|
896 |
|
897 virtual StringEnumeration *clone() const { |
|
898 return new TZEnumeration(*this); |
|
899 } |
|
900 |
|
901 virtual int32_t count(UErrorCode& status) const { |
|
902 return U_FAILURE(status) ? 0 : len; |
|
903 } |
|
904 |
|
905 virtual const UnicodeString* snext(UErrorCode& status) { |
|
906 if (U_SUCCESS(status) && map != NULL && pos < len) { |
|
907 getID(map[pos]); |
|
908 ++pos; |
|
909 return &unistr; |
|
910 } |
|
911 return 0; |
|
912 } |
|
913 |
|
914 virtual void reset(UErrorCode& /*status*/) { |
|
915 pos = 0; |
|
916 } |
|
917 |
|
918 public: |
|
919 static UClassID U_EXPORT2 getStaticClassID(void); |
|
920 virtual UClassID getDynamicClassID(void) const; |
|
921 }; |
|
922 |
|
923 TZEnumeration::~TZEnumeration() { |
|
924 if (localMap != NULL) { |
|
925 uprv_free(localMap); |
|
926 } |
|
927 } |
|
928 |
|
929 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(TZEnumeration) |
|
930 |
|
931 StringEnumeration* U_EXPORT2 |
|
932 TimeZone::createTimeZoneIDEnumeration( |
|
933 USystemTimeZoneType zoneType, |
|
934 const char* region, |
|
935 const int32_t* rawOffset, |
|
936 UErrorCode& ec) { |
|
937 return TZEnumeration::create(zoneType, region, rawOffset, ec); |
|
938 } |
|
939 |
|
940 StringEnumeration* U_EXPORT2 |
|
941 TimeZone::createEnumeration() { |
|
942 UErrorCode ec = U_ZERO_ERROR; |
|
943 return TZEnumeration::create(UCAL_ZONE_TYPE_ANY, NULL, NULL, ec); |
|
944 } |
|
945 |
|
946 StringEnumeration* U_EXPORT2 |
|
947 TimeZone::createEnumeration(int32_t rawOffset) { |
|
948 UErrorCode ec = U_ZERO_ERROR; |
|
949 return TZEnumeration::create(UCAL_ZONE_TYPE_ANY, NULL, &rawOffset, ec); |
|
950 } |
|
951 |
|
952 StringEnumeration* U_EXPORT2 |
|
953 TimeZone::createEnumeration(const char* country) { |
|
954 UErrorCode ec = U_ZERO_ERROR; |
|
955 return TZEnumeration::create(UCAL_ZONE_TYPE_ANY, country, NULL, ec); |
|
956 } |
|
957 |
|
958 // --------------------------------------- |
|
959 |
|
960 int32_t U_EXPORT2 |
|
961 TimeZone::countEquivalentIDs(const UnicodeString& id) { |
|
962 int32_t result = 0; |
|
963 UErrorCode ec = U_ZERO_ERROR; |
|
964 UResourceBundle res; |
|
965 ures_initStackObject(&res); |
|
966 U_DEBUG_TZ_MSG(("countEquivalentIDs..\n")); |
|
967 UResourceBundle *top = openOlsonResource(id, res, ec); |
|
968 if (U_SUCCESS(ec)) { |
|
969 UResourceBundle r; |
|
970 ures_initStackObject(&r); |
|
971 ures_getByKey(&res, kLINKS, &r, &ec); |
|
972 ures_getIntVector(&r, &result, &ec); |
|
973 ures_close(&r); |
|
974 } |
|
975 ures_close(&res); |
|
976 ures_close(top); |
|
977 return result; |
|
978 } |
|
979 |
|
980 // --------------------------------------- |
|
981 |
|
982 const UnicodeString U_EXPORT2 |
|
983 TimeZone::getEquivalentID(const UnicodeString& id, int32_t index) { |
|
984 U_DEBUG_TZ_MSG(("gEI(%d)\n", index)); |
|
985 UnicodeString result; |
|
986 UErrorCode ec = U_ZERO_ERROR; |
|
987 UResourceBundle res; |
|
988 ures_initStackObject(&res); |
|
989 UResourceBundle *top = openOlsonResource(id, res, ec); |
|
990 int32_t zone = -1; |
|
991 if (U_SUCCESS(ec)) { |
|
992 UResourceBundle r; |
|
993 ures_initStackObject(&r); |
|
994 int32_t size; |
|
995 ures_getByKey(&res, kLINKS, &r, &ec); |
|
996 const int32_t* v = ures_getIntVector(&r, &size, &ec); |
|
997 if (U_SUCCESS(ec)) { |
|
998 if (index >= 0 && index < size) { |
|
999 zone = v[index]; |
|
1000 } |
|
1001 } |
|
1002 ures_close(&r); |
|
1003 } |
|
1004 ures_close(&res); |
|
1005 if (zone >= 0) { |
|
1006 UResourceBundle *ares = ures_getByKey(top, kNAMES, NULL, &ec); // dereference Zones section |
|
1007 if (U_SUCCESS(ec)) { |
|
1008 int32_t idLen = 0; |
|
1009 const UChar* id = ures_getStringByIndex(ares, zone, &idLen, &ec); |
|
1010 result.fastCopyFrom(UnicodeString(TRUE, id, idLen)); |
|
1011 U_DEBUG_TZ_MSG(("gei(%d) -> %d, len%d, %s\n", index, zone, result.length(), u_errorName(ec))); |
|
1012 } |
|
1013 ures_close(ares); |
|
1014 } |
|
1015 ures_close(top); |
|
1016 #if defined(U_DEBUG_TZ) |
|
1017 if(result.length() ==0) { |
|
1018 U_DEBUG_TZ_MSG(("equiv [__, #%d] -> 0 (%s)\n", index, u_errorName(ec))); |
|
1019 } |
|
1020 #endif |
|
1021 return result; |
|
1022 } |
|
1023 |
|
1024 // --------------------------------------- |
|
1025 |
|
1026 // These methods are used by ZoneMeta class only. |
|
1027 |
|
1028 const UChar* |
|
1029 TimeZone::findID(const UnicodeString& id) { |
|
1030 const UChar *result = NULL; |
|
1031 UErrorCode ec = U_ZERO_ERROR; |
|
1032 UResourceBundle *rb = ures_openDirect(NULL, kZONEINFO, &ec); |
|
1033 |
|
1034 // resolve zone index by name |
|
1035 UResourceBundle *names = ures_getByKey(rb, kNAMES, NULL, &ec); |
|
1036 int32_t idx = findInStringArray(names, id, ec); |
|
1037 result = ures_getStringByIndex(names, idx, NULL, &ec); |
|
1038 if (U_FAILURE(ec)) { |
|
1039 result = NULL; |
|
1040 } |
|
1041 ures_close(names); |
|
1042 ures_close(rb); |
|
1043 return result; |
|
1044 } |
|
1045 |
|
1046 |
|
1047 const UChar* |
|
1048 TimeZone::dereferOlsonLink(const UnicodeString& id) { |
|
1049 const UChar *result = NULL; |
|
1050 UErrorCode ec = U_ZERO_ERROR; |
|
1051 UResourceBundle *rb = ures_openDirect(NULL, kZONEINFO, &ec); |
|
1052 |
|
1053 // resolve zone index by name |
|
1054 UResourceBundle *names = ures_getByKey(rb, kNAMES, NULL, &ec); |
|
1055 int32_t idx = findInStringArray(names, id, ec); |
|
1056 result = ures_getStringByIndex(names, idx, NULL, &ec); |
|
1057 |
|
1058 // open the zone bundle by index |
|
1059 ures_getByKey(rb, kZONES, rb, &ec); |
|
1060 ures_getByIndex(rb, idx, rb, &ec); |
|
1061 |
|
1062 if (U_SUCCESS(ec)) { |
|
1063 if (ures_getType(rb) == URES_INT) { |
|
1064 // this is a link - dereference the link |
|
1065 int32_t deref = ures_getInt(rb, &ec); |
|
1066 const UChar* tmp = ures_getStringByIndex(names, deref, NULL, &ec); |
|
1067 if (U_SUCCESS(ec)) { |
|
1068 result = tmp; |
|
1069 } |
|
1070 } |
|
1071 } |
|
1072 |
|
1073 ures_close(names); |
|
1074 ures_close(rb); |
|
1075 |
|
1076 return result; |
|
1077 } |
|
1078 |
|
1079 const UChar* |
|
1080 TimeZone::getRegion(const UnicodeString& id) { |
|
1081 UErrorCode status = U_ZERO_ERROR; |
|
1082 return getRegion(id, status); |
|
1083 } |
|
1084 |
|
1085 const UChar* |
|
1086 TimeZone::getRegion(const UnicodeString& id, UErrorCode& status) { |
|
1087 if (U_FAILURE(status)) { |
|
1088 return NULL; |
|
1089 } |
|
1090 const UChar *result = NULL; |
|
1091 UResourceBundle *rb = ures_openDirect(NULL, kZONEINFO, &status); |
|
1092 |
|
1093 // resolve zone index by name |
|
1094 UResourceBundle *res = ures_getByKey(rb, kNAMES, NULL, &status); |
|
1095 int32_t idx = findInStringArray(res, id, status); |
|
1096 |
|
1097 // get region mapping |
|
1098 ures_getByKey(rb, kREGIONS, res, &status); |
|
1099 const UChar *tmp = ures_getStringByIndex(res, idx, NULL, &status); |
|
1100 if (U_SUCCESS(status)) { |
|
1101 result = tmp; |
|
1102 } |
|
1103 |
|
1104 ures_close(res); |
|
1105 ures_close(rb); |
|
1106 |
|
1107 return result; |
|
1108 } |
|
1109 |
|
1110 |
|
1111 // --------------------------------------- |
|
1112 int32_t |
|
1113 TimeZone::getRegion(const UnicodeString& id, char *region, int32_t capacity, UErrorCode& status) |
|
1114 { |
|
1115 int32_t resultLen = 0; |
|
1116 *region = 0; |
|
1117 if (U_FAILURE(status)) { |
|
1118 return 0; |
|
1119 } |
|
1120 |
|
1121 const UChar *uregion = NULL; |
|
1122 // "Etc/Unknown" is not a system zone ID, |
|
1123 // but in the zone data |
|
1124 if (id.compare(UNKNOWN_ZONE_ID, UNKNOWN_ZONE_ID_LENGTH) != 0) { |
|
1125 uregion = getRegion(id); |
|
1126 } |
|
1127 if (uregion == NULL) { |
|
1128 status = U_ILLEGAL_ARGUMENT_ERROR; |
|
1129 return 0; |
|
1130 } |
|
1131 resultLen = u_strlen(uregion); |
|
1132 // A region code is represented by invariant characters |
|
1133 u_UCharsToChars(uregion, region, uprv_min(resultLen, capacity)); |
|
1134 |
|
1135 if (capacity < resultLen) { |
|
1136 status = U_BUFFER_OVERFLOW_ERROR; |
|
1137 return resultLen; |
|
1138 } |
|
1139 |
|
1140 return u_terminateChars(region, capacity, resultLen, &status); |
|
1141 } |
|
1142 |
|
1143 // --------------------------------------- |
|
1144 |
|
1145 |
|
1146 UnicodeString& |
|
1147 TimeZone::getDisplayName(UnicodeString& result) const |
|
1148 { |
|
1149 return getDisplayName(FALSE,LONG,Locale::getDefault(), result); |
|
1150 } |
|
1151 |
|
1152 UnicodeString& |
|
1153 TimeZone::getDisplayName(const Locale& locale, UnicodeString& result) const |
|
1154 { |
|
1155 return getDisplayName(FALSE, LONG, locale, result); |
|
1156 } |
|
1157 |
|
1158 UnicodeString& |
|
1159 TimeZone::getDisplayName(UBool daylight, EDisplayType style, UnicodeString& result) const |
|
1160 { |
|
1161 return getDisplayName(daylight,style, Locale::getDefault(), result); |
|
1162 } |
|
1163 //-------------------------------------- |
|
1164 int32_t |
|
1165 TimeZone::getDSTSavings()const { |
|
1166 if (useDaylightTime()) { |
|
1167 return 3600000; |
|
1168 } |
|
1169 return 0; |
|
1170 } |
|
1171 //--------------------------------------- |
|
1172 UnicodeString& |
|
1173 TimeZone::getDisplayName(UBool daylight, EDisplayType style, const Locale& locale, UnicodeString& result) const |
|
1174 { |
|
1175 UErrorCode status = U_ZERO_ERROR; |
|
1176 UDate date = Calendar::getNow(); |
|
1177 UTimeZoneFormatTimeType timeType; |
|
1178 int32_t offset; |
|
1179 |
|
1180 if (style == GENERIC_LOCATION || style == LONG_GENERIC || style == SHORT_GENERIC) { |
|
1181 LocalPointer<TimeZoneFormat> tzfmt(TimeZoneFormat::createInstance(locale, status)); |
|
1182 if (U_FAILURE(status)) { |
|
1183 result.remove(); |
|
1184 return result; |
|
1185 } |
|
1186 // Generic format |
|
1187 switch (style) { |
|
1188 case GENERIC_LOCATION: |
|
1189 tzfmt->format(UTZFMT_STYLE_GENERIC_LOCATION, *this, date, result, &timeType); |
|
1190 break; |
|
1191 case LONG_GENERIC: |
|
1192 tzfmt->format(UTZFMT_STYLE_GENERIC_LONG, *this, date, result, &timeType); |
|
1193 break; |
|
1194 case SHORT_GENERIC: |
|
1195 tzfmt->format(UTZFMT_STYLE_GENERIC_SHORT, *this, date, result, &timeType); |
|
1196 break; |
|
1197 default: |
|
1198 U_ASSERT(FALSE); |
|
1199 } |
|
1200 // Generic format many use Localized GMT as the final fallback. |
|
1201 // When Localized GMT format is used, the result might not be |
|
1202 // appropriate for the requested daylight value. |
|
1203 if ((daylight && timeType == UTZFMT_TIME_TYPE_STANDARD) || (!daylight && timeType == UTZFMT_TIME_TYPE_DAYLIGHT)) { |
|
1204 offset = daylight ? getRawOffset() + getDSTSavings() : getRawOffset(); |
|
1205 if (style == SHORT_GENERIC) { |
|
1206 tzfmt->formatOffsetShortLocalizedGMT(offset, result, status); |
|
1207 } else { |
|
1208 tzfmt->formatOffsetLocalizedGMT(offset, result, status); |
|
1209 } |
|
1210 } |
|
1211 } else if (style == LONG_GMT || style == SHORT_GMT) { |
|
1212 LocalPointer<TimeZoneFormat> tzfmt(TimeZoneFormat::createInstance(locale, status)); |
|
1213 if (U_FAILURE(status)) { |
|
1214 result.remove(); |
|
1215 return result; |
|
1216 } |
|
1217 offset = daylight && useDaylightTime() ? getRawOffset() + getDSTSavings() : getRawOffset(); |
|
1218 switch (style) { |
|
1219 case LONG_GMT: |
|
1220 tzfmt->formatOffsetLocalizedGMT(offset, result, status); |
|
1221 break; |
|
1222 case SHORT_GMT: |
|
1223 tzfmt->formatOffsetISO8601Basic(offset, FALSE, FALSE, FALSE, result, status); |
|
1224 break; |
|
1225 default: |
|
1226 U_ASSERT(FALSE); |
|
1227 } |
|
1228 |
|
1229 } else { |
|
1230 U_ASSERT(style == LONG || style == SHORT || style == SHORT_COMMONLY_USED); |
|
1231 UTimeZoneNameType nameType = UTZNM_UNKNOWN; |
|
1232 switch (style) { |
|
1233 case LONG: |
|
1234 nameType = daylight ? UTZNM_LONG_DAYLIGHT : UTZNM_LONG_STANDARD; |
|
1235 break; |
|
1236 case SHORT: |
|
1237 case SHORT_COMMONLY_USED: |
|
1238 nameType = daylight ? UTZNM_SHORT_DAYLIGHT : UTZNM_SHORT_STANDARD; |
|
1239 break; |
|
1240 default: |
|
1241 U_ASSERT(FALSE); |
|
1242 } |
|
1243 LocalPointer<TimeZoneNames> tznames(TimeZoneNames::createInstance(locale, status)); |
|
1244 if (U_FAILURE(status)) { |
|
1245 result.remove(); |
|
1246 return result; |
|
1247 } |
|
1248 UnicodeString canonicalID(ZoneMeta::getCanonicalCLDRID(*this)); |
|
1249 tznames->getDisplayName(canonicalID, nameType, date, result); |
|
1250 if (result.isEmpty()) { |
|
1251 // Fallback to localized GMT |
|
1252 LocalPointer<TimeZoneFormat> tzfmt(TimeZoneFormat::createInstance(locale, status)); |
|
1253 offset = daylight && useDaylightTime() ? getRawOffset() + getDSTSavings() : getRawOffset(); |
|
1254 if (style == LONG) { |
|
1255 tzfmt->formatOffsetLocalizedGMT(offset, result, status); |
|
1256 } else { |
|
1257 tzfmt->formatOffsetShortLocalizedGMT(offset, result, status); |
|
1258 } |
|
1259 } |
|
1260 } |
|
1261 if (U_FAILURE(status)) { |
|
1262 result.remove(); |
|
1263 } |
|
1264 return result; |
|
1265 } |
|
1266 |
|
1267 /** |
|
1268 * Parse a custom time zone identifier and return a corresponding zone. |
|
1269 * @param id a string of the form GMT[+-]hh:mm, GMT[+-]hhmm, or |
|
1270 * GMT[+-]hh. |
|
1271 * @return a newly created SimpleTimeZone with the given offset and |
|
1272 * no Daylight Savings Time, or null if the id cannot be parsed. |
|
1273 */ |
|
1274 TimeZone* |
|
1275 TimeZone::createCustomTimeZone(const UnicodeString& id) |
|
1276 { |
|
1277 int32_t sign, hour, min, sec; |
|
1278 if (parseCustomID(id, sign, hour, min, sec)) { |
|
1279 UnicodeString customID; |
|
1280 formatCustomID(hour, min, sec, (sign < 0), customID); |
|
1281 int32_t offset = sign * ((hour * 60 + min) * 60 + sec) * 1000; |
|
1282 return new SimpleTimeZone(offset, customID); |
|
1283 } |
|
1284 return NULL; |
|
1285 } |
|
1286 |
|
1287 UnicodeString& |
|
1288 TimeZone::getCustomID(const UnicodeString& id, UnicodeString& normalized, UErrorCode& status) { |
|
1289 normalized.remove(); |
|
1290 if (U_FAILURE(status)) { |
|
1291 return normalized; |
|
1292 } |
|
1293 int32_t sign, hour, min, sec; |
|
1294 if (parseCustomID(id, sign, hour, min, sec)) { |
|
1295 formatCustomID(hour, min, sec, (sign < 0), normalized); |
|
1296 } |
|
1297 return normalized; |
|
1298 } |
|
1299 |
|
1300 UBool |
|
1301 TimeZone::parseCustomID(const UnicodeString& id, int32_t& sign, |
|
1302 int32_t& hour, int32_t& min, int32_t& sec) { |
|
1303 static const int32_t kParseFailed = -99999; |
|
1304 |
|
1305 NumberFormat* numberFormat = 0; |
|
1306 UnicodeString idUppercase = id; |
|
1307 idUppercase.toUpper(""); |
|
1308 |
|
1309 if (id.length() > GMT_ID_LENGTH && |
|
1310 idUppercase.startsWith(GMT_ID, GMT_ID_LENGTH)) |
|
1311 { |
|
1312 ParsePosition pos(GMT_ID_LENGTH); |
|
1313 sign = 1; |
|
1314 hour = 0; |
|
1315 min = 0; |
|
1316 sec = 0; |
|
1317 |
|
1318 if (id[pos.getIndex()] == MINUS /*'-'*/) { |
|
1319 sign = -1; |
|
1320 } else if (id[pos.getIndex()] != PLUS /*'+'*/) { |
|
1321 return FALSE; |
|
1322 } |
|
1323 pos.setIndex(pos.getIndex() + 1); |
|
1324 |
|
1325 UErrorCode success = U_ZERO_ERROR; |
|
1326 numberFormat = NumberFormat::createInstance(success); |
|
1327 if(U_FAILURE(success)){ |
|
1328 return FALSE; |
|
1329 } |
|
1330 numberFormat->setParseIntegerOnly(TRUE); |
|
1331 //numberFormat->setLenient(TRUE); // TODO: May need to set this, depends on latest timezone parsing |
|
1332 |
|
1333 // Look for either hh:mm, hhmm, or hh |
|
1334 int32_t start = pos.getIndex(); |
|
1335 Formattable n(kParseFailed); |
|
1336 numberFormat->parse(id, n, pos); |
|
1337 if (pos.getIndex() == start) { |
|
1338 delete numberFormat; |
|
1339 return FALSE; |
|
1340 } |
|
1341 hour = n.getLong(); |
|
1342 |
|
1343 if (pos.getIndex() < id.length()) { |
|
1344 if (pos.getIndex() - start > 2 |
|
1345 || id[pos.getIndex()] != COLON) { |
|
1346 delete numberFormat; |
|
1347 return FALSE; |
|
1348 } |
|
1349 // hh:mm |
|
1350 pos.setIndex(pos.getIndex() + 1); |
|
1351 int32_t oldPos = pos.getIndex(); |
|
1352 n.setLong(kParseFailed); |
|
1353 numberFormat->parse(id, n, pos); |
|
1354 if ((pos.getIndex() - oldPos) != 2) { |
|
1355 // must be 2 digits |
|
1356 delete numberFormat; |
|
1357 return FALSE; |
|
1358 } |
|
1359 min = n.getLong(); |
|
1360 if (pos.getIndex() < id.length()) { |
|
1361 if (id[pos.getIndex()] != COLON) { |
|
1362 delete numberFormat; |
|
1363 return FALSE; |
|
1364 } |
|
1365 // [:ss] |
|
1366 pos.setIndex(pos.getIndex() + 1); |
|
1367 oldPos = pos.getIndex(); |
|
1368 n.setLong(kParseFailed); |
|
1369 numberFormat->parse(id, n, pos); |
|
1370 if (pos.getIndex() != id.length() |
|
1371 || (pos.getIndex() - oldPos) != 2) { |
|
1372 delete numberFormat; |
|
1373 return FALSE; |
|
1374 } |
|
1375 sec = n.getLong(); |
|
1376 } |
|
1377 } else { |
|
1378 // Supported formats are below - |
|
1379 // |
|
1380 // HHmmss |
|
1381 // Hmmss |
|
1382 // HHmm |
|
1383 // Hmm |
|
1384 // HH |
|
1385 // H |
|
1386 |
|
1387 int32_t length = pos.getIndex() - start; |
|
1388 if (length <= 0 || 6 < length) { |
|
1389 // invalid length |
|
1390 delete numberFormat; |
|
1391 return FALSE; |
|
1392 } |
|
1393 switch (length) { |
|
1394 case 1: |
|
1395 case 2: |
|
1396 // already set to hour |
|
1397 break; |
|
1398 case 3: |
|
1399 case 4: |
|
1400 min = hour % 100; |
|
1401 hour /= 100; |
|
1402 break; |
|
1403 case 5: |
|
1404 case 6: |
|
1405 sec = hour % 100; |
|
1406 min = (hour/100) % 100; |
|
1407 hour /= 10000; |
|
1408 break; |
|
1409 } |
|
1410 } |
|
1411 |
|
1412 delete numberFormat; |
|
1413 |
|
1414 if (hour > kMAX_CUSTOM_HOUR || min > kMAX_CUSTOM_MIN || sec > kMAX_CUSTOM_SEC) { |
|
1415 return FALSE; |
|
1416 } |
|
1417 return TRUE; |
|
1418 } |
|
1419 return FALSE; |
|
1420 } |
|
1421 |
|
1422 UnicodeString& |
|
1423 TimeZone::formatCustomID(int32_t hour, int32_t min, int32_t sec, |
|
1424 UBool negative, UnicodeString& id) { |
|
1425 // Create time zone ID - GMT[+|-]hhmm[ss] |
|
1426 id.setTo(GMT_ID, GMT_ID_LENGTH); |
|
1427 if (hour | min | sec) { |
|
1428 if (negative) { |
|
1429 id += (UChar)MINUS; |
|
1430 } else { |
|
1431 id += (UChar)PLUS; |
|
1432 } |
|
1433 |
|
1434 if (hour < 10) { |
|
1435 id += (UChar)ZERO_DIGIT; |
|
1436 } else { |
|
1437 id += (UChar)(ZERO_DIGIT + hour/10); |
|
1438 } |
|
1439 id += (UChar)(ZERO_DIGIT + hour%10); |
|
1440 id += (UChar)COLON; |
|
1441 if (min < 10) { |
|
1442 id += (UChar)ZERO_DIGIT; |
|
1443 } else { |
|
1444 id += (UChar)(ZERO_DIGIT + min/10); |
|
1445 } |
|
1446 id += (UChar)(ZERO_DIGIT + min%10); |
|
1447 |
|
1448 if (sec) { |
|
1449 id += (UChar)COLON; |
|
1450 if (sec < 10) { |
|
1451 id += (UChar)ZERO_DIGIT; |
|
1452 } else { |
|
1453 id += (UChar)(ZERO_DIGIT + sec/10); |
|
1454 } |
|
1455 id += (UChar)(ZERO_DIGIT + sec%10); |
|
1456 } |
|
1457 } |
|
1458 return id; |
|
1459 } |
|
1460 |
|
1461 |
|
1462 UBool |
|
1463 TimeZone::hasSameRules(const TimeZone& other) const |
|
1464 { |
|
1465 return (getRawOffset() == other.getRawOffset() && |
|
1466 useDaylightTime() == other.useDaylightTime()); |
|
1467 } |
|
1468 |
|
1469 static void U_CALLCONV initTZDataVersion(UErrorCode &status) { |
|
1470 ucln_i18n_registerCleanup(UCLN_I18N_TIMEZONE, timeZone_cleanup); |
|
1471 int32_t len = 0; |
|
1472 UResourceBundle *bundle = ures_openDirect(NULL, kZONEINFO, &status); |
|
1473 const UChar *tzver = ures_getStringByKey(bundle, kTZVERSION, &len, &status); |
|
1474 |
|
1475 if (U_SUCCESS(status)) { |
|
1476 if (len >= (int32_t)sizeof(TZDATA_VERSION)) { |
|
1477 // Ensure that there is always space for a trailing nul in TZDATA_VERSION |
|
1478 len = sizeof(TZDATA_VERSION) - 1; |
|
1479 } |
|
1480 u_UCharsToChars(tzver, TZDATA_VERSION, len); |
|
1481 } |
|
1482 ures_close(bundle); |
|
1483 |
|
1484 } |
|
1485 |
|
1486 const char* |
|
1487 TimeZone::getTZDataVersion(UErrorCode& status) |
|
1488 { |
|
1489 umtx_initOnce(gTZDataVersionInitOnce, &initTZDataVersion, status); |
|
1490 return (const char*)TZDATA_VERSION; |
|
1491 } |
|
1492 |
|
1493 UnicodeString& |
|
1494 TimeZone::getCanonicalID(const UnicodeString& id, UnicodeString& canonicalID, UErrorCode& status) |
|
1495 { |
|
1496 UBool isSystemID = FALSE; |
|
1497 return getCanonicalID(id, canonicalID, isSystemID, status); |
|
1498 } |
|
1499 |
|
1500 UnicodeString& |
|
1501 TimeZone::getCanonicalID(const UnicodeString& id, UnicodeString& canonicalID, UBool& isSystemID, |
|
1502 UErrorCode& status) |
|
1503 { |
|
1504 canonicalID.remove(); |
|
1505 isSystemID = FALSE; |
|
1506 if (U_FAILURE(status)) { |
|
1507 return canonicalID; |
|
1508 } |
|
1509 if (id.compare(UNKNOWN_ZONE_ID, UNKNOWN_ZONE_ID_LENGTH) == 0) { |
|
1510 // special case - Etc/Unknown is a canonical ID, but not system ID |
|
1511 canonicalID.fastCopyFrom(id); |
|
1512 isSystemID = FALSE; |
|
1513 } else { |
|
1514 ZoneMeta::getCanonicalCLDRID(id, canonicalID, status); |
|
1515 if (U_SUCCESS(status)) { |
|
1516 isSystemID = TRUE; |
|
1517 } else { |
|
1518 // Not a system ID |
|
1519 status = U_ZERO_ERROR; |
|
1520 getCustomID(id, canonicalID, status); |
|
1521 } |
|
1522 } |
|
1523 return canonicalID; |
|
1524 } |
|
1525 |
|
1526 #ifndef U_HIDE_DRAFT_API |
|
1527 UnicodeString& |
|
1528 TimeZone::getWindowsID(const UnicodeString& id, UnicodeString& winid, UErrorCode& status) { |
|
1529 winid.remove(); |
|
1530 if (U_FAILURE(status)) { |
|
1531 return winid; |
|
1532 } |
|
1533 |
|
1534 // canonicalize the input ID |
|
1535 UnicodeString canonicalID; |
|
1536 UBool isSystemID = FALSE; |
|
1537 |
|
1538 getCanonicalID(id, canonicalID, isSystemID, status); |
|
1539 if (U_FAILURE(status) || !isSystemID) { |
|
1540 // mapping data is only applicable to tz database IDs |
|
1541 return winid; |
|
1542 } |
|
1543 |
|
1544 UResourceBundle *mapTimezones = ures_openDirect(NULL, "windowsZones", &status); |
|
1545 ures_getByKey(mapTimezones, "mapTimezones", mapTimezones, &status); |
|
1546 |
|
1547 if (U_FAILURE(status)) { |
|
1548 return winid; |
|
1549 } |
|
1550 |
|
1551 UResourceBundle *winzone = NULL; |
|
1552 UBool found = FALSE; |
|
1553 while (ures_hasNext(mapTimezones) && !found) { |
|
1554 winzone = ures_getNextResource(mapTimezones, winzone, &status); |
|
1555 if (U_FAILURE(status)) { |
|
1556 break; |
|
1557 } |
|
1558 if (ures_getType(winzone) != URES_TABLE) { |
|
1559 continue; |
|
1560 } |
|
1561 UResourceBundle *regionalData = NULL; |
|
1562 while (ures_hasNext(winzone) && !found) { |
|
1563 regionalData = ures_getNextResource(winzone, regionalData, &status); |
|
1564 if (U_FAILURE(status)) { |
|
1565 break; |
|
1566 } |
|
1567 if (ures_getType(regionalData) != URES_STRING) { |
|
1568 continue; |
|
1569 } |
|
1570 int32_t len; |
|
1571 const UChar *tzids = ures_getString(regionalData, &len, &status); |
|
1572 if (U_FAILURE(status)) { |
|
1573 break; |
|
1574 } |
|
1575 |
|
1576 const UChar *start = tzids; |
|
1577 UBool hasNext = TRUE; |
|
1578 while (hasNext) { |
|
1579 const UChar *end = u_strchr(start, (UChar)0x20); |
|
1580 if (end == NULL) { |
|
1581 end = tzids + len; |
|
1582 hasNext = FALSE; |
|
1583 } |
|
1584 if (canonicalID.compare(start, end - start) == 0) { |
|
1585 winid = UnicodeString(ures_getKey(winzone), -1 , US_INV); |
|
1586 found = TRUE; |
|
1587 break; |
|
1588 } |
|
1589 start = end + 1; |
|
1590 } |
|
1591 } |
|
1592 ures_close(regionalData); |
|
1593 } |
|
1594 ures_close(winzone); |
|
1595 ures_close(mapTimezones); |
|
1596 |
|
1597 return winid; |
|
1598 } |
|
1599 |
|
1600 #define MAX_WINDOWS_ID_SIZE 128 |
|
1601 |
|
1602 UnicodeString& |
|
1603 TimeZone::getIDForWindowsID(const UnicodeString& winid, const char* region, UnicodeString& id, UErrorCode& status) { |
|
1604 id.remove(); |
|
1605 if (U_FAILURE(status)) { |
|
1606 return id; |
|
1607 } |
|
1608 |
|
1609 UResourceBundle *zones = ures_openDirect(NULL, "windowsZones", &status); |
|
1610 ures_getByKey(zones, "mapTimezones", zones, &status); |
|
1611 if (U_FAILURE(status)) { |
|
1612 ures_close(zones); |
|
1613 return id; |
|
1614 } |
|
1615 |
|
1616 UErrorCode tmperr = U_ZERO_ERROR; |
|
1617 char winidKey[MAX_WINDOWS_ID_SIZE]; |
|
1618 int32_t winKeyLen = winid.extract(0, winid.length(), winidKey, sizeof(winidKey) - 1, US_INV); |
|
1619 |
|
1620 if (winKeyLen == 0 || winKeyLen >= (int32_t)sizeof(winidKey)) { |
|
1621 ures_close(zones); |
|
1622 return id; |
|
1623 } |
|
1624 winidKey[winKeyLen] = 0; |
|
1625 |
|
1626 ures_getByKey(zones, winidKey, zones, &tmperr); // use tmperr, because windows mapping might not |
|
1627 // be avaiable by design |
|
1628 if (U_FAILURE(tmperr)) { |
|
1629 ures_close(zones); |
|
1630 return id; |
|
1631 } |
|
1632 |
|
1633 const UChar *tzid = NULL; |
|
1634 int32_t len = 0; |
|
1635 UBool gotID = FALSE; |
|
1636 if (region) { |
|
1637 const UChar *tzids = ures_getStringByKey(zones, region, &len, &tmperr); // use tmperr, because |
|
1638 // regional mapping is optional |
|
1639 if (U_SUCCESS(tmperr)) { |
|
1640 // first ID delimited by space is the defasult one |
|
1641 const UChar *end = u_strchr(tzids, (UChar)0x20); |
|
1642 if (end == NULL) { |
|
1643 id.setTo(tzids, -1); |
|
1644 } else { |
|
1645 id.setTo(tzids, end - tzids); |
|
1646 } |
|
1647 gotID = TRUE; |
|
1648 } |
|
1649 } |
|
1650 |
|
1651 if (!gotID) { |
|
1652 tzid = ures_getStringByKey(zones, "001", &len, &status); // using status, because "001" must be |
|
1653 // available at this point |
|
1654 if (U_SUCCESS(status)) { |
|
1655 id.setTo(tzid, len); |
|
1656 } |
|
1657 } |
|
1658 |
|
1659 ures_close(zones); |
|
1660 return id; |
|
1661 } |
|
1662 #endif /* U_HIDE_DRAFT_API */ |
|
1663 |
|
1664 |
|
1665 U_NAMESPACE_END |
|
1666 |
|
1667 #endif /* #if !UCONFIG_NO_FORMATTING */ |
|
1668 |
|
1669 //eof |