Coverage Report

Created: 2026-02-05 06:34

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