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