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