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