• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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