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