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