Coverage Report

Created: 2025-06-13 06:34

/src/icu/icu4c/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 "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.36M
#define kZONEINFO "zoneinfo64"
93
0
#define kREGIONS  "Regions"
94
6.82k
#define kZONES    "Zones"
95
1.70k
#define kRULES    "Rules"
96
1.36M
#define kNAMES    "Names"
97
0
#define kTZVERSION  "TZVersion"
98
0
#define kLINKS    "links"
99
0
#define kMAX_CUSTOM_HOUR    23
100
0
#define kMAX_CUSTOM_MIN     59
101
0
#define kMAX_CUSTOM_SEC     59
102
0
#define MINUS 0x002D
103
0
#define PLUS 0x002B
104
0
#define ZERO_DIGIT 0x0030
105
0
#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
5.70k
{
184
5.70k
    UnicodeString copy;
185
5.70k
    const char16_t *u;
186
5.70k
    int32_t len;
187
188
5.70k
    int32_t start = 0;
189
5.70k
    int32_t limit = ures_getSize(array);
190
5.70k
    int32_t mid;
191
5.70k
    int32_t lastMid = INT32_MAX;
192
5.70k
    if(U_FAILURE(status) || (limit < 1)) {
193
0
        return -1;
194
0
    }
195
5.70k
    U_DEBUG_TZ_MSG(("fisa: Looking for %s, between %d and %d\n", U_DEBUG_TZ_STR(UnicodeString(id).getTerminatedBuffer()), start, limit));
196
197
48.1k
    for (;;) {
198
48.1k
        mid = static_cast<int32_t>((start + limit) / 2);
199
48.1k
        if (lastMid == mid) {   /* Have we moved? */
200
0
            break;  /* We haven't moved, and it wasn't found. */
201
0
        }
202
48.1k
        lastMid = mid;
203
48.1k
        u = ures_getStringByIndex(array, mid, &len, &status);
204
48.1k
        if (U_FAILURE(status)) {
205
0
            break;
206
0
        }
207
48.1k
        U_DEBUG_TZ_MSG(("tz: compare to %s, %d .. [%d] .. %d\n", U_DEBUG_TZ_STR(u), start, mid, limit));
208
48.1k
        copy.setTo(true, u, len);
209
48.1k
        int r = id.compare(copy);
210
48.1k
        if(r==0) {
211
5.70k
            U_DEBUG_TZ_MSG(("fisa: found at %d\n", mid));
212
5.70k
            return mid;
213
42.4k
        } else if(r<0) {
214
21.5k
            limit = mid;
215
21.5k
        } else {
216
20.8k
            start = mid;
217
20.8k
        }
218
48.1k
    }
219
0
    U_DEBUG_TZ_MSG(("fisa: not found\n"));
220
0
    return -1;
221
5.70k
}
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
4.63k
static UResourceBundle* getZoneByName(const UResourceBundle* top, const UnicodeString& id, UResourceBundle *oldbundle, UErrorCode& status) {
231
    // load the Rules object
232
4.63k
    UResourceBundle *tmp = ures_getByKey(top, kNAMES, nullptr, &status);
233
234
    // search for the string
235
4.63k
    int32_t idx = findInStringArray(tmp, id, status);
236
237
4.63k
    if((idx == -1) && U_SUCCESS(status)) {
238
        // not found
239
0
        status = U_MISSING_RESOURCE_ERROR;
240
        //ures_close(oldbundle);
241
        //oldbundle = nullptr;
242
4.63k
    } else {
243
4.63k
        U_DEBUG_TZ_MSG(("gzbn: oldbundle= size %d, type %d, %s\n", ures_getSize(tmp), ures_getType(tmp), u_errorName(status)));
244
4.63k
        tmp = ures_getByKey(top, kZONES, tmp, &status); // get Zones object from top
245
4.63k
        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.63k
        oldbundle = ures_getByIndex(tmp, idx, oldbundle, &status); // get nth Zone object
247
4.63k
        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.63k
    }
249
4.63k
    ures_close(tmp);
250
4.63k
    if(U_FAILURE(status)) {
251
        //ures_close(oldbundle);
252
0
        return nullptr;
253
4.63k
    } else {
254
4.63k
        return oldbundle;
255
4.63k
    }
256
4.63k
}
257
258
259
1.70k
UResourceBundle* TimeZone::loadRule(const UResourceBundle* top, const UnicodeString& ruleid, UResourceBundle* oldbundle, UErrorCode& status) {
260
1.70k
    char key[64];
261
1.70k
    ruleid.extract(0, sizeof(key) - 1, key, static_cast<int32_t>(sizeof(key)) - 1, US_INV);
262
1.70k
    U_DEBUG_TZ_MSG(("loadRule(%s)\n", key));
263
1.70k
    UResourceBundle *r = ures_getByKey(top, kRULES, oldbundle, &status);
264
1.70k
    U_DEBUG_TZ_MSG(("loadRule(%s) -> kRULES [%s]\n", key, u_errorName(status)));
265
1.70k
    r = ures_getByKey(r, key, r, &status);
266
1.70k
    U_DEBUG_TZ_MSG(("loadRule(%s) -> item [%s]\n", key, u_errorName(status)));
267
1.70k
    return r;
268
1.70k
}
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
4.63k
{
282
#ifdef U_DEBUG_TZ
283
    char buf[128];
284
    id.extract(0, sizeof(buf)-1, buf, sizeof(buf), "");
285
#endif
286
4.63k
    UResourceBundle *top = ures_openDirect(nullptr, kZONEINFO, &ec);
287
4.63k
    U_DEBUG_TZ_MSG(("pre: res sz=%d\n", ures_getSize(&res)));
288
4.63k
    /* &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
4.63k
    U_DEBUG_TZ_MSG(("Loading zone '%s' (%s, size %d) - %s\n", buf, ures_getKey((UResourceBundle*)&res), ures_getSize(&res), u_errorName(ec)));
292
4.63k
    if (ures_getType(&res) == URES_INT) {
293
2.14k
        int32_t deref = ures_getInt(&res, &ec) + 0;
294
2.14k
        U_DEBUG_TZ_MSG(("getInt: %s - type is %d\n", u_errorName(ec), ures_getType(&res)));
295
2.14k
        UResourceBundle *ares = ures_getByKey(top, kZONES, nullptr, &ec); // dereference Zones section
296
2.14k
        ures_getByIndex(ares, deref, &res, &ec);
297
2.14k
        ures_close(ares);
298
2.14k
        U_DEBUG_TZ_MSG(("alias to #%d (%s) - %s\n", deref, "??", u_errorName(ec)));
299
2.48k
    } else {
300
2.48k
        U_DEBUG_TZ_MSG(("not an alias - size %d\n", ures_getSize(&res)));
301
2.48k
    }
302
4.63k
    U_DEBUG_TZ_MSG(("%s - final status is %s\n", buf, u_errorName(ec)));
303
4.63k
    return top;
304
4.63k
}
305
306
// -------------------------------------
307
308
namespace {
309
310
0
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
0
    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
0
    new(gRawGMT) SimpleTimeZone(0, UnicodeString(true, GMT_ID, GMT_ID_LENGTH));
317
0
    new(gRawUNKNOWN) SimpleTimeZone(0, UnicodeString(true, UNKNOWN_ZONE_ID, UNKNOWN_ZONE_ID_LENGTH));
318
319
0
    gStaticZonesInitialized = true;
320
0
}
321
322
}  // anonymous namespace
323
324
const TimeZone& U_EXPORT2
325
TimeZone::getUnknown()
326
0
{
327
0
    umtx_initOnce(gStaticZonesInitOnce, &initStaticTimeZones);
328
0
    return *reinterpret_cast<SimpleTimeZone*>(gRawUNKNOWN);
329
0
}
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
0
    :   UObject(), fID()
346
0
{
347
0
}
348
349
// -------------------------------------
350
351
TimeZone::TimeZone(const UnicodeString &id)
352
6.34k
    :   UObject(), fID(id)
353
6.34k
{
354
6.34k
}
355
356
// -------------------------------------
357
358
TimeZone::~TimeZone()
359
694k
{
360
694k
}
361
362
// -------------------------------------
363
364
TimeZone::TimeZone(const TimeZone &source)
365
689k
    :   UObject(source), fID(source.fID)
366
689k
{
367
689k
}
368
369
// -------------------------------------
370
371
TimeZone &
372
TimeZone::operator=(const TimeZone &right)
373
174k
{
374
174k
    if (this != &right) fID = right.fID;
375
174k
    return *this;
376
174k
}
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
4.63k
createSystemTimeZone(const UnicodeString& id, UErrorCode& ec) {
392
4.63k
    if (U_FAILURE(ec)) {
393
0
        return nullptr;
394
0
    }
395
4.63k
    TimeZone* z = nullptr;
396
4.63k
    StackUResourceBundle res;
397
4.63k
    U_DEBUG_TZ_MSG(("pre-err=%s\n", u_errorName(ec)));
398
4.63k
    UResourceBundle *top = openOlsonResource(id, res.ref(), ec);
399
4.63k
    U_DEBUG_TZ_MSG(("post-err=%s\n", u_errorName(ec)));
400
4.63k
    if (U_SUCCESS(ec)) {
401
4.63k
        z = new OlsonTimeZone(top, res.getAlias(), id, ec);
402
4.63k
        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.63k
    }
407
4.63k
    ures_close(top);
408
4.63k
    if (U_FAILURE(ec)) {
409
0
        U_DEBUG_TZ_MSG(("cstz: failed to create, err %s\n", u_errorName(ec)));
410
0
        delete z;
411
0
        z = nullptr;
412
0
    }
413
4.63k
    return z;
414
4.63k
}
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
4.63k
createSystemTimeZone(const UnicodeString& id) {
423
4.63k
    UErrorCode ec = U_ZERO_ERROR;
424
4.63k
    return createSystemTimeZone(id, ec);
425
4.63k
}
426
427
}
428
429
TimeZone* U_EXPORT2
430
TimeZone::createTimeZone(const UnicodeString& ID)
431
4.63k
{
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
4.63k
    TimeZone* result = createSystemTimeZone(ID);
441
442
4.63k
    if (result == nullptr) {
443
0
        U_DEBUG_TZ_MSG(("failed to load system time zone with id - falling to custom"));
444
0
        result = createCustomTimeZone(ID);
445
0
    }
446
4.63k
    if (result == nullptr) {
447
0
        U_DEBUG_TZ_MSG(("failed to load time zone with id - falling to Etc/Unknown(GMT)"));
448
0
        const TimeZone& unknown = getUnknown();
449
        // Unknown zone uses statically allocated memory, so creation of it can never fail due to OOM.
450
0
        result = unknown.clone();
451
0
    }
452
4.63k
    return result;
453
4.63k
}
454
455
// -------------------------------------
456
457
TimeZone* U_EXPORT2
458
TimeZone::detectHostTimeZone()
459
1
{
460
    // We access system timezone data through uprv_tzset(), uprv_tzname(), and others,
461
    // which have platform specific implementations in putil.cpp
462
1
    int32_t rawOffset = 0;
463
1
    const char *hostID;
464
1
    UBool hostDetectionSucceeded = true;
465
466
    // First, try to create a system timezone, based
467
    // on the string ID in tzname[0].
468
469
1
    uprv_tzset(); // Initialize tz... system data
470
471
1
    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
1
    hostID = uprv_tzname(0);
477
478
    // Invert sign because UNIX semantics are backwards
479
1
    rawOffset = uprv_timezone() * -U_MILLIS_PER_SECOND;
480
481
1
    TimeZone* hostZone = nullptr;
482
483
1
    UnicodeString hostStrID(hostID, -1, US_INV);
484
485
1
    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
1
    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
1
    int32_t hostIDLen = hostStrID.length();
500
1
    if (hostZone != nullptr && rawOffset != hostZone->getRawOffset()
501
1
        && (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
1
    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
1
    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
1
    return hostZone;
527
1
}
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
1
{
540
1
    ucln_i18n_registerCleanup(UCLN_I18N_TIMEZONE, timeZone_cleanup);
541
542
1
    Mutex lock(&gDefaultZoneMutex);
543
    // If setDefault() has already been called we can skip getting the
544
    // default zone information from the system.
545
1
    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
1
    TimeZone *default_zone = TimeZone::detectHostTimeZone();
563
564
1
    U_ASSERT(DEFAULT_ZONE == nullptr);
565
566
1
    DEFAULT_ZONE = default_zone;
567
1
}
568
569
// -------------------------------------
570
571
TimeZone* U_EXPORT2
572
TimeZone::createDefault()
573
7.49k
{
574
7.49k
    umtx_initOnce(gDefaultZoneInitOnce, initDefault);
575
7.49k
    {
576
7.49k
        Mutex lock(&gDefaultZoneMutex);
577
7.49k
        return (DEFAULT_ZONE != nullptr) ? DEFAULT_ZONE->clone() : nullptr;
578
7.49k
    }
579
7.49k
}
580
581
// -------------------------------------
582
583
TimeZone* U_EXPORT2
584
TimeZone::forLocaleOrDefault(const Locale& locale)
585
7.49k
{
586
7.49k
    char buffer[ULOC_KEYWORDS_CAPACITY] = "";
587
7.49k
    UErrorCode localStatus = U_ZERO_ERROR;
588
7.49k
    int32_t count = locale.getKeywordValue("timezone", buffer, sizeof(buffer), localStatus);
589
7.49k
    if (U_FAILURE(localStatus) || localStatus == U_STRING_NOT_TERMINATED_WARNING) {
590
        // the "timezone" keyword exceeds ULOC_KEYWORDS_CAPACITY; ignore and use default.
591
0
        count = 0;
592
0
    }
593
7.49k
    if (count > 0) {
594
0
        return TimeZone::createTimeZone(UnicodeString(buffer, count, US_INV));
595
0
    }
596
7.49k
    return TimeZone::createDefault();
597
7.49k
}
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
1
static void U_CALLCONV initMap(USystemTimeZoneType type, UErrorCode& ec) {
627
1
    ucln_i18n_registerCleanup(UCLN_I18N_TIMEZONE, timeZone_cleanup);
628
629
1
    UResourceBundle *res = ures_openDirect(nullptr, kZONEINFO, &ec);
630
1
    res = ures_getByKey(res, kNAMES, res, &ec); // dereference Zones section
631
1
    if (U_SUCCESS(ec)) {
632
1
        int32_t size = ures_getSize(res);
633
1
        int32_t* m = static_cast<int32_t*>(uprv_malloc(size * sizeof(int32_t)));
634
1
        if (m == nullptr) {
635
0
            ec = U_MEMORY_ALLOCATION_ERROR;
636
1
        } else {
637
1
            int32_t numEntries = 0;
638
640
            for (int32_t i = 0; i < size; i++) {
639
639
                UnicodeString id = ures_getUnicodeStringByIndex(res, i, &ec);
640
639
                if (U_FAILURE(ec)) {
641
0
                    break;
642
0
                }
643
639
                if (0 == id.compare(UNKNOWN_ZONE_ID, UNKNOWN_ZONE_ID_LENGTH)) {
644
                    // exclude Etc/Unknown
645
1
                    continue;
646
1
                }
647
638
                if (type == UCAL_ZONE_TYPE_CANONICAL || type == UCAL_ZONE_TYPE_CANONICAL_LOCATION) {
648
0
                    UnicodeString canonicalID;
649
0
                    ZoneMeta::getCanonicalCLDRID(id, canonicalID, ec);
650
0
                    if (U_FAILURE(ec)) {
651
0
                        break;
652
0
                    }
653
0
                    if (canonicalID != id) {
654
                        // exclude aliases
655
0
                        continue;
656
0
                    }
657
0
                }
658
638
                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
638
                m[numEntries++] = i;
669
638
            }
670
1
            if (U_SUCCESS(ec)) {
671
1
                int32_t *tmp = m;
672
1
                m = static_cast<int32_t*>(uprv_realloc(tmp, numEntries * sizeof(int32_t)));
673
1
                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
1
                switch(type) {
680
1
                case UCAL_ZONE_TYPE_ANY:
681
1
                    U_ASSERT(MAP_SYSTEM_ZONES == nullptr);
682
1
                    MAP_SYSTEM_ZONES = m;
683
1
                    LEN_SYSTEM_ZONES = numEntries;
684
1
                    break;
685
0
                case UCAL_ZONE_TYPE_CANONICAL:
686
0
                    U_ASSERT(MAP_CANONICAL_SYSTEM_ZONES == nullptr);
687
0
                    MAP_CANONICAL_SYSTEM_ZONES = m;
688
0
                    LEN_CANONICAL_SYSTEM_ZONES = numEntries;
689
0
                    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
1
                }
696
1
            }
697
1
        }
698
1
    }
699
1
    ures_close(res);
700
1
}
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
854k
                         int32_t& dstOffset, UErrorCode& ec) const {
711
854k
    if (U_FAILURE(ec)) {
712
0
        return;
713
0
    }
714
715
854k
    rawOffset = getRawOffset();
716
854k
    if (!local) {
717
854k
        date += rawOffset; // now in local standard millis
718
854k
    }
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
854k
    for (int32_t pass=0; ; ++pass) {
733
854k
        int32_t year, millis;
734
854k
        int8_t month, dom, dow;
735
854k
        Grego::timeToFields(date, year, month, dom, dow, millis, ec);
736
854k
        if (U_FAILURE(ec)) return;
737
738
854k
        dstOffset = getOffset(GregorianCalendar::AD, year, month, dom,
739
854k
                              static_cast<uint8_t>(dow), millis,
740
854k
                              Grego::monthLength(year, month),
741
854k
                              ec) - rawOffset;
742
743
        // Recompute if local==true, dstOffset!=0.
744
854k
        if (pass!=0 || !local || dstOffset == 0) {
745
854k
            break;
746
854k
        }
747
        // adjust to local standard millis
748
0
        date -= dstOffset;
749
0
    }
750
854k
}
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==nullptr
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
4.63k
    TZEnumeration(int32_t* mapData, int32_t mapLen, UBool adoptMapData) : pos(0) {
769
4.63k
        map = mapData;
770
4.63k
        localMap = adoptMapData ? mapData : nullptr;
771
4.63k
        len = mapLen;
772
4.63k
    }
773
774
1.36M
    UBool getID(int32_t i, UErrorCode& ec) {
775
1.36M
        int32_t idLen = 0;
776
1.36M
        const char16_t* id = nullptr;
777
1.36M
        UResourceBundle *top = ures_openDirect(nullptr, kZONEINFO, &ec);
778
1.36M
        top = ures_getByKey(top, kNAMES, top, &ec); // dereference Zones section
779
1.36M
        id = ures_getStringByIndex(top, i, &idLen, &ec);
780
1.36M
        if(U_FAILURE(ec)) {
781
0
            unistr.truncate(0);
782
0
        }
783
1.36M
        else {
784
1.36M
            unistr.fastCopyFrom(UnicodeString(true, id, idLen));
785
1.36M
        }
786
1.36M
        ures_close(top);
787
1.36M
        return U_SUCCESS(ec);
788
1.36M
    }
789
790
4.63k
    static int32_t* getMap(USystemTimeZoneType type, int32_t& len, UErrorCode& ec) {
791
4.63k
        len = 0;
792
4.63k
        if (U_FAILURE(ec)) {
793
0
            return nullptr;
794
0
        }
795
4.63k
        int32_t* m = nullptr;
796
4.63k
        switch (type) {
797
4.63k
        case UCAL_ZONE_TYPE_ANY:
798
4.63k
            umtx_initOnce(gSystemZonesInitOnce, &initMap, type, ec);
799
4.63k
            m = MAP_SYSTEM_ZONES;
800
4.63k
            len = LEN_SYSTEM_ZONES;
801
4.63k
            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 = nullptr;
815
0
            len = 0;
816
0
            break;
817
4.63k
        }
818
4.63k
        return m;
819
4.63k
    }
820
821
public:
822
823
0
#define DEFAULT_FILTERED_MAP_SIZE 8
824
0
#define MAP_INCREMENT_SIZE 8
825
826
4.63k
    static TZEnumeration* create(USystemTimeZoneType type, const char* region, const int32_t* rawOffset, UErrorCode& ec) {
827
4.63k
        if (U_FAILURE(ec)) {
828
0
            return nullptr;
829
0
        }
830
831
4.63k
        int32_t baseLen;
832
4.63k
        int32_t *baseMap = getMap(type, baseLen, ec);
833
834
4.63k
        if (U_FAILURE(ec)) {
835
0
            return nullptr;
836
0
        }
837
838
        // If any additional conditions are available,
839
        // create instance local map filtered by the conditions.
840
841
4.63k
        int32_t *filteredMap = nullptr;
842
4.63k
        int32_t numEntries = 0;
843
844
4.63k
        if (region != nullptr || rawOffset != nullptr) {
845
0
            int32_t filteredMapSize = DEFAULT_FILTERED_MAP_SIZE;
846
0
            filteredMap = static_cast<int32_t*>(uprv_malloc(filteredMapSize * sizeof(int32_t)));
847
0
            if (filteredMap == nullptr) {
848
0
                ec = U_MEMORY_ALLOCATION_ERROR;
849
0
                return nullptr;
850
0
            }
851
852
            // Walk through the base map
853
0
            UResourceBundle *res = ures_openDirect(nullptr, 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 != nullptr) {
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 != nullptr) {
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 = static_cast<int32_t*>(uprv_realloc(filteredMap, filteredMapSize * sizeof(int32_t)));
891
0
                    if (tmp == nullptr) {
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 = nullptr;
905
0
            }
906
907
0
            ures_close(res);
908
0
        }
909
910
4.63k
        TZEnumeration *result = nullptr;
911
4.63k
        if (U_SUCCESS(ec)) {
912
            // Finally, create a new enumeration instance
913
4.63k
            if (filteredMap == nullptr) {
914
4.63k
                result = new TZEnumeration(baseMap, baseLen, false);
915
4.63k
            } else {
916
0
                result = new TZEnumeration(filteredMap, numEntries, true);
917
0
                filteredMap = nullptr;
918
0
            }
919
4.63k
            if (result == nullptr) {
920
0
                ec = U_MEMORY_ALLOCATION_ERROR;
921
0
            }
922
4.63k
        }
923
924
4.63k
        if (filteredMap != nullptr) {
925
0
            uprv_free(filteredMap);
926
0
        }
927
928
4.63k
        return result;
929
4.63k
    }
930
931
0
    TZEnumeration(const TZEnumeration &other) : StringEnumeration(), map(nullptr), localMap(nullptr), len(0), pos(0) {
932
0
        if (other.localMap != nullptr) {
933
0
            localMap = static_cast<int32_t*>(uprv_malloc(other.len * sizeof(int32_t)));
934
0
            if (localMap != nullptr) {
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 = nullptr;
943
0
            }
944
0
        } else {
945
0
            map = other.map;
946
0
            localMap = nullptr;
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 override {
955
0
        return new TZEnumeration(*this);
956
0
    }
957
958
4.63k
    virtual int32_t count(UErrorCode& status) const override {
959
4.63k
        return U_FAILURE(status) ? 0 : len;
960
4.63k
    }
961
962
1.36M
    virtual const UnicodeString* snext(UErrorCode& status) override {
963
1.36M
        if (U_SUCCESS(status) && map != nullptr && pos < len) {
964
1.36M
            getID(map[pos], status);
965
1.36M
            ++pos;
966
1.36M
            return &unistr;
967
1.36M
        }
968
0
        return nullptr;
969
1.36M
    }
970
971
0
    virtual void reset(UErrorCode& /*status*/) override {
972
0
        pos = 0;
973
0
    }
974
975
public:
976
    static UClassID U_EXPORT2 getStaticClassID();
977
    virtual UClassID getDynamicClassID() const override;
978
};
979
980
4.63k
TZEnumeration::~TZEnumeration() {
981
4.63k
    if (localMap != nullptr) {
982
0
        uprv_free(localMap);
983
0
    }
984
4.63k
}
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
4.63k
TimeZone::createEnumeration(UErrorCode& status) {
999
4.63k
    return TZEnumeration::create(UCAL_ZONE_TYPE_ANY, nullptr, nullptr, status);
1000
4.63k
}
1001
1002
StringEnumeration* U_EXPORT2
1003
0
TimeZone::createEnumerationForRawOffset(int32_t rawOffset, UErrorCode& status) {
1004
0
    return TZEnumeration::create(UCAL_ZONE_TYPE_ANY, nullptr, &rawOffset, status);
1005
0
}
1006
1007
StringEnumeration* U_EXPORT2
1008
0
TimeZone::createEnumerationForRegion(const char* region, UErrorCode& status) {
1009
0
    return TZEnumeration::create(UCAL_ZONE_TYPE_ANY, region, nullptr, status);
1010
0
}
1011
1012
//
1013
// Next 3 methods are equivalent to above, but ignores UErrorCode.
1014
// These methods were deprecated in ICU 70.
1015
1016
StringEnumeration* U_EXPORT2
1017
0
TimeZone::createEnumeration() {
1018
0
    UErrorCode ec = U_ZERO_ERROR;
1019
0
    return createEnumeration(ec);
1020
0
}
1021
1022
StringEnumeration* U_EXPORT2
1023
0
TimeZone::createEnumeration(int32_t rawOffset) {
1024
0
    UErrorCode ec = U_ZERO_ERROR;
1025
0
    return createEnumerationForRawOffset(rawOffset, ec);
1026
0
}
1027
1028
StringEnumeration* U_EXPORT2
1029
0
TimeZone::createEnumeration(const char* region) {
1030
0
    UErrorCode ec = U_ZERO_ERROR;
1031
0
    return createEnumerationForRegion(region, ec);
1032
0
}
1033
1034
// ---------------------------------------
1035
1036
int32_t U_EXPORT2
1037
0
TimeZone::countEquivalentIDs(const UnicodeString& id) {
1038
0
    int32_t result = 0;
1039
0
    UErrorCode ec = U_ZERO_ERROR;
1040
0
    StackUResourceBundle res;
1041
0
    U_DEBUG_TZ_MSG(("countEquivalentIDs..\n"));
1042
0
    UResourceBundle *top = openOlsonResource(id, res.ref(), ec);
1043
0
    if (U_SUCCESS(ec)) {
1044
0
        StackUResourceBundle r;
1045
0
        ures_getByKey(res.getAlias(), kLINKS, r.getAlias(), &ec);
1046
0
        ures_getIntVector(r.getAlias(), &result, &ec);
1047
0
    }
1048
0
    ures_close(top);
1049
0
    return result;
1050
0
}
1051
1052
// ---------------------------------------
1053
1054
UnicodeString U_EXPORT2
1055
0
TimeZone::getEquivalentID(const UnicodeString& id, int32_t index) {
1056
0
    U_DEBUG_TZ_MSG(("gEI(%d)\n", index));
1057
0
    UnicodeString result;
1058
0
    UErrorCode ec = U_ZERO_ERROR;
1059
0
    StackUResourceBundle res;
1060
0
    UResourceBundle *top = openOlsonResource(id, res.ref(), ec);
1061
0
    int32_t zone = -1;
1062
0
    if (U_SUCCESS(ec)) {
1063
0
        StackUResourceBundle r;
1064
0
        int32_t size;
1065
0
        ures_getByKey(res.getAlias(), kLINKS, r.getAlias(), &ec);
1066
0
        const int32_t *v = ures_getIntVector(r.getAlias(), &size, &ec);
1067
0
        if (U_SUCCESS(ec)) {
1068
0
            if (index >= 0 && index < size) {
1069
0
                zone = v[index];
1070
0
            }
1071
0
        }
1072
0
    }
1073
0
    if (zone >= 0) {
1074
0
        UResourceBundle *ares = ures_getByKey(top, kNAMES, nullptr, &ec); // dereference Zones section
1075
0
        if (U_SUCCESS(ec)) {
1076
0
            int32_t idLen = 0;
1077
0
            const char16_t* id2 = ures_getStringByIndex(ares, zone, &idLen, &ec);
1078
0
            result.fastCopyFrom(UnicodeString(true, id2, idLen));
1079
0
            U_DEBUG_TZ_MSG(("gei(%d) -> %d, len%d, %s\n", index, zone, result.length(), u_errorName(ec)));
1080
0
        }
1081
0
        ures_close(ares);
1082
0
    }
1083
0
    ures_close(top);
1084
#if defined(U_DEBUG_TZ)
1085
    if(result.length() ==0) {
1086
      U_DEBUG_TZ_MSG(("equiv [__, #%d] -> 0 (%s)\n", index, u_errorName(ec)));
1087
    }
1088
#endif
1089
0
    return result;
1090
0
}
1091
1092
// ---------------------------------------
1093
1094
// These methods are used by ZoneMeta class only.
1095
1096
const char16_t*
1097
1.03k
TimeZone::findID(const UnicodeString& id) {
1098
1.03k
    const char16_t *result = nullptr;
1099
1.03k
    UErrorCode ec = U_ZERO_ERROR;
1100
1.03k
    UResourceBundle *rb = ures_openDirect(nullptr, kZONEINFO, &ec);
1101
1102
    // resolve zone index by name
1103
1.03k
    UResourceBundle *names = ures_getByKey(rb, kNAMES, nullptr, &ec);
1104
1.03k
    int32_t idx = findInStringArray(names, id, ec);
1105
1.03k
    result = ures_getStringByIndex(names, idx, nullptr, &ec);
1106
1.03k
    if (U_FAILURE(ec)) {
1107
0
        result = nullptr;
1108
0
    }
1109
1.03k
    ures_close(names);
1110
1.03k
    ures_close(rb);
1111
1.03k
    return result;
1112
1.03k
}
1113
1114
1115
const char16_t*
1116
37
TimeZone::dereferOlsonLink(const UnicodeString& id) {
1117
37
    const char16_t *result = nullptr;
1118
37
    UErrorCode ec = U_ZERO_ERROR;
1119
37
    UResourceBundle *rb = ures_openDirect(nullptr, kZONEINFO, &ec);
1120
1121
    // resolve zone index by name
1122
37
    UResourceBundle *names = ures_getByKey(rb, kNAMES, nullptr, &ec);
1123
37
    int32_t idx = findInStringArray(names, id, ec);
1124
37
    result = ures_getStringByIndex(names, idx, nullptr, &ec);
1125
1126
    // open the zone bundle by index
1127
37
    ures_getByKey(rb, kZONES, rb, &ec);
1128
37
    ures_getByIndex(rb, idx, rb, &ec); 
1129
1130
37
    if (U_SUCCESS(ec)) {
1131
37
        if (ures_getType(rb) == URES_INT) {
1132
            // this is a link - dereference the link
1133
25
            int32_t deref = ures_getInt(rb, &ec);
1134
25
            const char16_t* tmp = ures_getStringByIndex(names, deref, nullptr, &ec);
1135
25
            if (U_SUCCESS(ec)) {
1136
25
                result = tmp;
1137
25
            }
1138
25
        }
1139
37
    }
1140
1141
37
    ures_close(names);
1142
37
    ures_close(rb);
1143
1144
37
    return result;
1145
37
}
1146
1147
const char16_t*
1148
0
TimeZone::getRegion(const UnicodeString& id) {
1149
0
    UErrorCode status = U_ZERO_ERROR;
1150
0
    return getRegion(id, status);
1151
0
}
1152
1153
const char16_t*
1154
0
TimeZone::getRegion(const UnicodeString& id, UErrorCode& status) {
1155
0
    if (U_FAILURE(status)) {
1156
0
        return nullptr;
1157
0
    }
1158
0
    const char16_t *result = nullptr;
1159
0
    UResourceBundle *rb = ures_openDirect(nullptr, kZONEINFO, &status);
1160
1161
    // resolve zone index by name
1162
0
    UResourceBundle *res = ures_getByKey(rb, kNAMES, nullptr, &status);
1163
0
    int32_t idx = findInStringArray(res, id, status);
1164
1165
    // get region mapping
1166
0
    ures_getByKey(rb, kREGIONS, res, &status);
1167
0
    const char16_t *tmp = ures_getStringByIndex(res, idx, nullptr, &status);
1168
0
    if (U_SUCCESS(status)) {
1169
0
        result = tmp;
1170
0
    }
1171
1172
0
    ures_close(res);
1173
0
    ures_close(rb);
1174
1175
0
    return result;
1176
0
}
1177
1178
1179
// ---------------------------------------
1180
int32_t
1181
TimeZone::getRegion(const UnicodeString& id, char *region, int32_t capacity, UErrorCode& status)
1182
0
{
1183
0
    int32_t resultLen = 0;
1184
0
    *region = 0;
1185
0
    if (U_FAILURE(status)) {
1186
0
        return 0;
1187
0
    }
1188
1189
0
    const char16_t *uregion = nullptr;
1190
    // "Etc/Unknown" is not a system zone ID,
1191
    // but in the zone data
1192
0
    if (id.compare(UNKNOWN_ZONE_ID, UNKNOWN_ZONE_ID_LENGTH) != 0) {
1193
0
        uregion = getRegion(id);
1194
0
    }
1195
0
    if (uregion == nullptr) {
1196
0
        status = U_ILLEGAL_ARGUMENT_ERROR;
1197
0
        return 0;
1198
0
    }
1199
0
    resultLen = u_strlen(uregion);
1200
    // A region code is represented by invariant characters
1201
0
    u_UCharsToChars(uregion, region, uprv_min(resultLen, capacity));
1202
1203
0
    if (capacity < resultLen) {
1204
0
        status = U_BUFFER_OVERFLOW_ERROR;
1205
0
        return resultLen;
1206
0
    }
1207
1208
0
    return u_terminateChars(region, capacity, resultLen, &status);
1209
0
}
1210
1211
// ---------------------------------------
1212
1213
1214
UnicodeString&
1215
TimeZone::getDisplayName(UnicodeString& result) const
1216
0
{
1217
0
    return getDisplayName(false,LONG,Locale::getDefault(), result);
1218
0
}
1219
1220
UnicodeString&
1221
TimeZone::getDisplayName(const Locale& locale, UnicodeString& result) const
1222
0
{
1223
0
    return getDisplayName(false, LONG, locale, result);
1224
0
}
1225
1226
UnicodeString&
1227
TimeZone::getDisplayName(UBool inDaylight, EDisplayType style, UnicodeString& result)  const
1228
0
{
1229
0
    return getDisplayName(inDaylight,style, Locale::getDefault(), result);
1230
0
}
1231
//--------------------------------------
1232
int32_t
1233
0
TimeZone::getDSTSavings()const {
1234
0
    if (useDaylightTime()) {
1235
0
        return 3600000;
1236
0
    }
1237
0
    return 0;
1238
0
}
1239
//---------------------------------------
1240
UnicodeString&
1241
TimeZone::getDisplayName(UBool inDaylight, EDisplayType style, const Locale& locale, UnicodeString& result) const
1242
0
{
1243
0
    UErrorCode status = U_ZERO_ERROR;
1244
0
    UDate date = Calendar::getNow();
1245
0
    UTimeZoneFormatTimeType timeType = UTZFMT_TIME_TYPE_UNKNOWN;
1246
0
    int32_t offset;
1247
1248
0
    if (style == GENERIC_LOCATION || style == LONG_GENERIC || style == SHORT_GENERIC) {
1249
0
        LocalPointer<TimeZoneFormat> tzfmt(TimeZoneFormat::createInstance(locale, status));
1250
0
        if (U_FAILURE(status)) {
1251
0
            result.remove();
1252
0
            return result;
1253
0
        }
1254
        // Generic format
1255
0
        switch (style) {
1256
0
        case GENERIC_LOCATION:
1257
0
            tzfmt->format(UTZFMT_STYLE_GENERIC_LOCATION, *this, date, result, &timeType);
1258
0
            break;
1259
0
        case LONG_GENERIC:
1260
0
            tzfmt->format(UTZFMT_STYLE_GENERIC_LONG, *this, date, result, &timeType);
1261
0
            break;
1262
0
        case SHORT_GENERIC:
1263
0
            tzfmt->format(UTZFMT_STYLE_GENERIC_SHORT, *this, date, result, &timeType);
1264
0
            break;
1265
0
        default:
1266
0
            UPRV_UNREACHABLE_EXIT;
1267
0
        }
1268
        // Generic format many use Localized GMT as the final fallback.
1269
        // When Localized GMT format is used, the result might not be
1270
        // appropriate for the requested daylight value.
1271
0
        if ((inDaylight && timeType == UTZFMT_TIME_TYPE_STANDARD) || (!inDaylight && timeType == UTZFMT_TIME_TYPE_DAYLIGHT)) {
1272
0
            offset = inDaylight ? getRawOffset() + getDSTSavings() : getRawOffset();
1273
0
            if (style == SHORT_GENERIC) {
1274
0
                tzfmt->formatOffsetShortLocalizedGMT(offset, result, status);
1275
0
            } else {
1276
0
                tzfmt->formatOffsetLocalizedGMT(offset, result, status);
1277
0
            }
1278
0
        }
1279
0
    } else if (style == LONG_GMT || style == SHORT_GMT) {
1280
0
        LocalPointer<TimeZoneFormat> tzfmt(TimeZoneFormat::createInstance(locale, status));
1281
0
        if (U_FAILURE(status)) {
1282
0
            result.remove();
1283
0
            return result;
1284
0
        }
1285
0
        offset = inDaylight && useDaylightTime() ? getRawOffset() + getDSTSavings() : getRawOffset();
1286
0
        switch (style) {
1287
0
        case LONG_GMT:
1288
0
            tzfmt->formatOffsetLocalizedGMT(offset, result, status);
1289
0
            break;
1290
0
        case SHORT_GMT:
1291
0
            tzfmt->formatOffsetISO8601Basic(offset, false, false, false, result, status);
1292
0
            break;
1293
0
        default:
1294
0
            UPRV_UNREACHABLE_EXIT;
1295
0
        }
1296
1297
0
    } else {
1298
0
        U_ASSERT(style == LONG || style == SHORT || style == SHORT_COMMONLY_USED);
1299
0
        UTimeZoneNameType nameType = UTZNM_UNKNOWN;
1300
0
        switch (style) {
1301
0
        case LONG:
1302
0
            nameType = inDaylight ? UTZNM_LONG_DAYLIGHT : UTZNM_LONG_STANDARD;
1303
0
            break;
1304
0
        case SHORT:
1305
0
        case SHORT_COMMONLY_USED:
1306
0
            nameType = inDaylight ? UTZNM_SHORT_DAYLIGHT : UTZNM_SHORT_STANDARD;
1307
0
            break;
1308
0
        default:
1309
0
            UPRV_UNREACHABLE_EXIT;
1310
0
        }
1311
0
        LocalPointer<TimeZoneNames> tznames(TimeZoneNames::createInstance(locale, status));
1312
0
        if (U_FAILURE(status)) {
1313
0
            result.remove();
1314
0
            return result;
1315
0
        }
1316
0
        UnicodeString canonicalID(ZoneMeta::getCanonicalCLDRID(*this));
1317
0
        tznames->getDisplayName(canonicalID, nameType, date, result);
1318
0
        if (result.isEmpty()) {
1319
            // Fallback to localized GMT
1320
0
            LocalPointer<TimeZoneFormat> tzfmt(TimeZoneFormat::createInstance(locale, status));
1321
0
            offset = inDaylight && useDaylightTime() ? getRawOffset() + getDSTSavings() : getRawOffset();
1322
0
            if (style == LONG) {
1323
0
                tzfmt->formatOffsetLocalizedGMT(offset, result, status);
1324
0
            } else {
1325
0
                tzfmt->formatOffsetShortLocalizedGMT(offset, result, status);
1326
0
            }
1327
0
        }
1328
0
    }
1329
0
    if (U_FAILURE(status)) {
1330
0
        result.remove();
1331
0
    }
1332
0
    return  result;
1333
0
}
1334
1335
/**
1336
 * Parse a custom time zone identifier and return a corresponding zone.
1337
 * @param id a string of the form GMT[+-]hh:mm, GMT[+-]hhmm, or
1338
 * GMT[+-]hh.
1339
 * @return a newly created SimpleTimeZone with the given offset and
1340
 * no Daylight Savings Time, or null if the id cannot be parsed.
1341
*/
1342
TimeZone*
1343
TimeZone::createCustomTimeZone(const UnicodeString& id)
1344
0
{
1345
0
    int32_t sign, hour, min, sec;
1346
0
    if (parseCustomID(id, sign, hour, min, sec)) {
1347
0
        UnicodeString customID;
1348
0
        formatCustomID(hour, min, sec, (sign < 0), customID);
1349
0
        int32_t offset = sign * ((hour * 60 + min) * 60 + sec) * 1000;
1350
0
        return new SimpleTimeZone(offset, customID);
1351
0
    }
1352
0
    return nullptr;
1353
0
}
1354
1355
UnicodeString&
1356
0
TimeZone::getCustomID(const UnicodeString& id, UnicodeString& normalized, UErrorCode& status) {
1357
0
    normalized.remove();
1358
0
    if (U_FAILURE(status)) {
1359
0
        return normalized;
1360
0
    }
1361
0
    int32_t sign, hour, min, sec;
1362
0
    if (parseCustomID(id, sign, hour, min, sec)) {
1363
0
        formatCustomID(hour, min, sec, (sign < 0), normalized);
1364
0
    } else {
1365
0
        status = U_ILLEGAL_ARGUMENT_ERROR;
1366
0
    }
1367
0
    return normalized;
1368
0
}
1369
1370
UBool
1371
TimeZone::parseCustomID(const UnicodeString& id, int32_t& sign,
1372
0
                        int32_t& hour, int32_t& min, int32_t& sec) {
1373
0
    if (id.length() < GMT_ID_LENGTH) {
1374
0
      return false;
1375
0
    }
1376
0
    if (0 != u_strncasecmp(id.getBuffer(), GMT_ID, GMT_ID_LENGTH, 0)) {
1377
0
        return false;
1378
0
    }
1379
0
    sign = 1;
1380
0
    hour = 0;
1381
0
    min = 0;
1382
0
    sec = 0;
1383
1384
0
    if (id[GMT_ID_LENGTH] == MINUS /*'-'*/) {
1385
0
        sign = -1;
1386
0
    } else if (id[GMT_ID_LENGTH] != PLUS /*'+'*/) {
1387
0
        return false;
1388
0
    }
1389
1390
0
    int32_t start = GMT_ID_LENGTH + 1;
1391
0
    int32_t pos = start;
1392
0
    hour = ICU_Utility::parseNumber(id, pos, 10);
1393
0
    if (pos == id.length()) {
1394
        // Handle the following cases
1395
        // HHmmss
1396
        // Hmmss
1397
        // HHmm
1398
        // Hmm
1399
        // HH
1400
        // H
1401
1402
        // Get all digits
1403
        // Should be 1 to 6 digits.
1404
0
        int32_t length = pos - start;
1405
0
        switch (length) {
1406
0
            case 1:  // H
1407
0
            case 2:  // HH
1408
                // already set to hour
1409
0
                break;
1410
0
            case 3:  // Hmm
1411
0
            case 4:  // HHmm
1412
0
                min = hour % 100;
1413
0
                hour /= 100;
1414
0
                break;
1415
0
            case 5:  // Hmmss
1416
0
            case 6:  // HHmmss
1417
0
                sec = hour % 100;
1418
0
                min = (hour/100) % 100;
1419
0
                hour /= 10000;
1420
0
                break;
1421
0
            default:
1422
                // invalid range
1423
0
                return false;
1424
0
        }
1425
0
    } else {
1426
        // Handle the following cases
1427
        // HH:mm:ss
1428
        // H:mm:ss
1429
        // HH:mm
1430
        // H:mm
1431
0
        if (pos - start < 1 || pos - start > 2 || id[pos] != COLON) {
1432
0
            return false;
1433
0
        }
1434
0
        pos++; // skip : after H or HH
1435
0
        if (id.length() == pos) {
1436
0
            return false;
1437
0
        }
1438
0
        start = pos;
1439
0
        min = ICU_Utility::parseNumber(id, pos, 10);
1440
0
        if (pos - start != 2) {
1441
0
            return false;
1442
0
        }
1443
0
        if (id.length() > pos) {
1444
0
            if (id[pos] != COLON) {
1445
0
                return false;
1446
0
            }
1447
0
            pos++; // skip : after mm
1448
0
            start = pos;
1449
0
            sec = ICU_Utility::parseNumber(id, pos, 10);
1450
0
            if (pos - start != 2 || id.length() > pos) {
1451
0
                return false;
1452
0
            }
1453
0
        }
1454
0
    }
1455
0
    if (hour > kMAX_CUSTOM_HOUR ||
1456
0
        min > kMAX_CUSTOM_MIN ||
1457
0
        sec > kMAX_CUSTOM_SEC) {
1458
0
        return false;
1459
0
    }
1460
0
    return true;
1461
0
}
1462
1463
UnicodeString&
1464
TimeZone::formatCustomID(int32_t hour, int32_t min, int32_t sec,
1465
0
                         UBool negative, UnicodeString& id) {
1466
    // Create time zone ID - GMT[+|-]hhmm[ss]
1467
0
    id.setTo(GMT_ID, GMT_ID_LENGTH);
1468
0
    if (hour | min | sec) {
1469
0
        if (negative) {
1470
0
            id += static_cast<char16_t>(MINUS);
1471
0
        } else {
1472
0
            id += static_cast<char16_t>(PLUS);
1473
0
        }
1474
1475
0
        if (hour < 10) {
1476
0
            id += static_cast<char16_t>(ZERO_DIGIT);
1477
0
        } else {
1478
0
            id += static_cast<char16_t>(ZERO_DIGIT + hour / 10);
1479
0
        }
1480
0
        id += static_cast<char16_t>(ZERO_DIGIT + hour % 10);
1481
0
        id += static_cast<char16_t>(COLON);
1482
0
        if (min < 10) {
1483
0
            id += static_cast<char16_t>(ZERO_DIGIT);
1484
0
        } else {
1485
0
            id += static_cast<char16_t>(ZERO_DIGIT + min / 10);
1486
0
        }
1487
0
        id += static_cast<char16_t>(ZERO_DIGIT + min % 10);
1488
1489
0
        if (sec) {
1490
0
            id += static_cast<char16_t>(COLON);
1491
0
            if (sec < 10) {
1492
0
                id += static_cast<char16_t>(ZERO_DIGIT);
1493
0
            } else {
1494
0
                id += static_cast<char16_t>(ZERO_DIGIT + sec / 10);
1495
0
            }
1496
0
            id += static_cast<char16_t>(ZERO_DIGIT + sec % 10);
1497
0
        }
1498
0
    }
1499
0
    return id;
1500
0
}
1501
1502
1503
UBool
1504
TimeZone::hasSameRules(const TimeZone& other) const
1505
0
{
1506
0
    return (getRawOffset() == other.getRawOffset() &&
1507
0
            useDaylightTime() == other.useDaylightTime());
1508
0
}
1509
1510
0
static void U_CALLCONV initTZDataVersion(UErrorCode &status) {
1511
0
    ucln_i18n_registerCleanup(UCLN_I18N_TIMEZONE, timeZone_cleanup);
1512
0
    int32_t len = 0;
1513
0
    StackUResourceBundle bundle;
1514
0
    ures_openDirectFillIn(bundle.getAlias(), nullptr, kZONEINFO, &status);
1515
0
    const char16_t *tzver = ures_getStringByKey(bundle.getAlias(), kTZVERSION, &len, &status);
1516
1517
0
    if (U_SUCCESS(status)) {
1518
0
        if (len >= static_cast<int32_t>(sizeof(TZDATA_VERSION))) {
1519
            // Ensure that there is always space for a trailing nul in TZDATA_VERSION
1520
0
            len = sizeof(TZDATA_VERSION) - 1;
1521
0
        }
1522
0
        u_UCharsToChars(tzver, TZDATA_VERSION, len);
1523
0
    }
1524
0
}
1525
1526
const char*
1527
TimeZone::getTZDataVersion(UErrorCode& status)
1528
0
{
1529
0
    umtx_initOnce(gTZDataVersionInitOnce, &initTZDataVersion, status);
1530
0
    return (const char*)TZDATA_VERSION;
1531
0
}
1532
1533
UnicodeString&
1534
TimeZone::getCanonicalID(const UnicodeString& id, UnicodeString& canonicalID, UErrorCode& status)
1535
0
{
1536
0
    UBool isSystemID = false;
1537
0
    return getCanonicalID(id, canonicalID, isSystemID, status);
1538
0
}
1539
1540
UnicodeString&
1541
TimeZone::getCanonicalID(const UnicodeString& id, UnicodeString& canonicalID, UBool& isSystemID,
1542
                         UErrorCode& status)
1543
0
{
1544
0
    canonicalID.remove();
1545
0
    isSystemID = false;
1546
0
    if (U_FAILURE(status)) {
1547
0
        return canonicalID;
1548
0
    }
1549
0
    if (id.compare(UNKNOWN_ZONE_ID, UNKNOWN_ZONE_ID_LENGTH) == 0) {
1550
        // special case - Etc/Unknown is a canonical ID, but not system ID
1551
0
        canonicalID.fastCopyFrom(id);
1552
0
        isSystemID = false;
1553
0
    } else {
1554
0
        ZoneMeta::getCanonicalCLDRID(id, canonicalID, status);
1555
0
        if (U_SUCCESS(status)) {
1556
0
            isSystemID = true;
1557
0
        } else {
1558
            // Not a system ID
1559
0
            status = U_ZERO_ERROR;
1560
0
            getCustomID(id, canonicalID, status);
1561
0
        }
1562
0
    }
1563
0
    return canonicalID;
1564
0
}
1565
1566
UnicodeString&
1567
TimeZone::getIanaID(const UnicodeString& id, UnicodeString& ianaID, UErrorCode& status)
1568
0
{
1569
0
    ianaID.remove();
1570
0
    if (U_FAILURE(status)) {
1571
0
        return ianaID;
1572
0
    }
1573
0
    if (id.compare(ConstChar16Ptr(UNKNOWN_ZONE_ID), UNKNOWN_ZONE_ID_LENGTH) == 0) {
1574
0
        status = U_ILLEGAL_ARGUMENT_ERROR;
1575
0
        ianaID.setToBogus();
1576
0
    } else {
1577
0
        ZoneMeta::getIanaID(id, ianaID, status);
1578
0
    }
1579
0
    return ianaID;
1580
0
}
1581
1582
UnicodeString&
1583
0
TimeZone::getWindowsID(const UnicodeString& id, UnicodeString& winid, UErrorCode& status) {
1584
0
    winid.remove();
1585
0
    if (U_FAILURE(status)) {
1586
0
        return winid;
1587
0
    }
1588
1589
    // canonicalize the input ID
1590
0
    UnicodeString canonicalID;
1591
0
    UBool isSystemID = false;
1592
1593
0
    getCanonicalID(id, canonicalID, isSystemID, status);
1594
0
    if (U_FAILURE(status) || !isSystemID) {
1595
        // mapping data is only applicable to tz database IDs
1596
0
        if (status == U_ILLEGAL_ARGUMENT_ERROR) {
1597
            // getWindowsID() sets an empty string where
1598
            // getCanonicalID() sets a U_ILLEGAL_ARGUMENT_ERROR.
1599
0
            status = U_ZERO_ERROR;
1600
0
        }
1601
0
        return winid;
1602
0
    }
1603
1604
0
    LocalUResourceBundlePointer mapTimezones(ures_openDirect(nullptr, "windowsZones", &status));
1605
0
    if (U_FAILURE(status)) {
1606
0
        return winid;
1607
0
    }
1608
0
    ures_getByKey(mapTimezones.getAlias(), "mapTimezones", mapTimezones.getAlias(), &status);
1609
1610
0
    if (U_FAILURE(status)) {
1611
0
        return winid;
1612
0
    }
1613
1614
0
    UResourceBundle *winzone = nullptr;
1615
0
    UBool found = false;
1616
0
    while (ures_hasNext(mapTimezones.getAlias()) && !found) {
1617
0
        winzone = ures_getNextResource(mapTimezones.getAlias(), winzone, &status);
1618
0
        if (U_FAILURE(status)) {
1619
0
            break;
1620
0
        }
1621
0
        if (ures_getType(winzone) != URES_TABLE) {
1622
0
            continue;
1623
0
        }
1624
0
        UResourceBundle *regionalData = nullptr;
1625
0
        while (ures_hasNext(winzone) && !found) {
1626
0
            regionalData = ures_getNextResource(winzone, regionalData, &status);
1627
0
            if (U_FAILURE(status)) {
1628
0
                break;
1629
0
            }
1630
0
            if (ures_getType(regionalData) != URES_STRING) {
1631
0
                continue;
1632
0
            }
1633
0
            int32_t len;
1634
0
            const char16_t *tzids = ures_getString(regionalData, &len, &status);
1635
0
            if (U_FAILURE(status)) {
1636
0
                break;
1637
0
            }
1638
1639
0
            const char16_t *start = tzids;
1640
0
            UBool hasNext = true;
1641
0
            while (hasNext) {
1642
0
                const char16_t* end = u_strchr(start, static_cast<char16_t>(0x20));
1643
0
                if (end == nullptr) {
1644
0
                    end = tzids + len;
1645
0
                    hasNext = false;
1646
0
                }
1647
0
                if (canonicalID.compare(start, static_cast<int32_t>(end - start)) == 0) {
1648
0
                    winid = UnicodeString(ures_getKey(winzone), -1 , US_INV);
1649
0
                    found = true;
1650
0
                    break;
1651
0
                }
1652
0
                start = end + 1;
1653
0
            }
1654
0
        }
1655
0
        ures_close(regionalData);
1656
0
    }
1657
0
    ures_close(winzone);
1658
1659
0
    return winid;
1660
0
}
1661
1662
#define MAX_WINDOWS_ID_SIZE 128
1663
1664
UnicodeString&
1665
0
TimeZone::getIDForWindowsID(const UnicodeString& winid, const char* region, UnicodeString& id, UErrorCode& status) {
1666
0
    id.remove();
1667
0
    if (U_FAILURE(status)) {
1668
0
        return id;
1669
0
    }
1670
1671
0
    UResourceBundle *zones = ures_openDirect(nullptr, "windowsZones", &status);
1672
0
    ures_getByKey(zones, "mapTimezones", zones, &status);
1673
0
    if (U_FAILURE(status)) {
1674
0
        ures_close(zones);
1675
0
        return id;
1676
0
    }
1677
1678
0
    UErrorCode tmperr = U_ZERO_ERROR;
1679
0
    char winidKey[MAX_WINDOWS_ID_SIZE];
1680
0
    int32_t winKeyLen = winid.extract(0, winid.length(), winidKey, sizeof(winidKey) - 1, US_INV);
1681
1682
0
    if (winKeyLen == 0 || winKeyLen >= static_cast<int32_t>(sizeof(winidKey))) {
1683
0
        ures_close(zones);
1684
0
        return id;
1685
0
    }
1686
0
    winidKey[winKeyLen] = 0;
1687
1688
0
    ures_getByKey(zones, winidKey, zones, &tmperr); // use tmperr, because windows mapping might not
1689
                                                    // be available by design
1690
0
    if (U_FAILURE(tmperr)) {
1691
0
        ures_close(zones);
1692
0
        return id;
1693
0
    }
1694
1695
0
    const char16_t *tzid = nullptr;
1696
0
    int32_t len = 0;
1697
0
    UBool gotID = false;
1698
0
    if (region) {
1699
0
        const char16_t *tzids = ures_getStringByKey(zones, region, &len, &tmperr); // use tmperr, because
1700
                                                                                // regional mapping is optional
1701
0
        if (U_SUCCESS(tmperr)) {
1702
            // first ID delimited by space is the default one
1703
0
            const char16_t* end = u_strchr(tzids, static_cast<char16_t>(0x20));
1704
0
            if (end == nullptr) {
1705
0
                id.setTo(tzids, -1);
1706
0
            } else {
1707
0
                id.setTo(tzids, static_cast<int32_t>(end - tzids));
1708
0
            }
1709
0
            gotID = true;
1710
0
        }
1711
0
    }
1712
1713
0
    if (!gotID) {
1714
0
        tzid = ures_getStringByKey(zones, "001", &len, &status);    // using status, because "001" must be
1715
                                                                // available at this point
1716
0
        if (U_SUCCESS(status)) {
1717
0
            id.setTo(tzid, len);
1718
0
        }
1719
0
    }
1720
1721
0
    ures_close(zones);
1722
0
    return id;
1723
0
}
1724
1725
1726
U_NAMESPACE_END
1727
1728
#endif /* #if !UCONFIG_NO_FORMATTING */
1729
1730
//eof