Coverage Report

Created: 2025-06-24 06:43

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