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