Coverage Report

Created: 2025-06-24 06:43

/src/icu/source/i18n/zonemeta.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) 2007-2014, International Business Machines Corporation and
6
* others. All Rights Reserved.
7
*******************************************************************************
8
*/
9
10
#include "unicode/utypes.h"
11
12
#if !UCONFIG_NO_FORMATTING
13
14
#include "zonemeta.h"
15
16
#include "unicode/timezone.h"
17
#include "unicode/ustring.h"
18
#include "unicode/putil.h"
19
#include "unicode/simpletz.h"
20
#include "unicode/strenum.h"
21
#include "umutex.h"
22
#include "uvector.h"
23
#include "cmemory.h"
24
#include "gregoimp.h"
25
#include "cstring.h"
26
#include "ucln_in.h"
27
#include "uassert.h"
28
#include "uresimp.h"
29
#include "uhash.h"
30
#include "olsontz.h"
31
#include "uinvchar.h"
32
33
static icu::UMutex gZoneMetaLock;
34
35
// CLDR Canonical ID mapping table
36
static UHashtable *gCanonicalIDCache = NULL;
37
static icu::UInitOnce gCanonicalIDCacheInitOnce = U_INITONCE_INITIALIZER;
38
39
// Metazone mapping table
40
static UHashtable *gOlsonToMeta = NULL;
41
static icu::UInitOnce gOlsonToMetaInitOnce = U_INITONCE_INITIALIZER;
42
43
// Available metazone IDs vector and table
44
static icu::UVector *gMetaZoneIDs = NULL;
45
static UHashtable *gMetaZoneIDTable = NULL;
46
static icu::UInitOnce gMetaZoneIDsInitOnce = U_INITONCE_INITIALIZER;
47
48
// Country info vectors
49
static icu::UVector *gSingleZoneCountries = NULL;
50
static icu::UVector *gMultiZonesCountries = NULL;
51
static icu::UInitOnce gCountryInfoVectorsInitOnce = U_INITONCE_INITIALIZER;
52
53
U_CDECL_BEGIN
54
55
/**
56
 * Cleanup callback func
57
 */
58
static UBool U_CALLCONV zoneMeta_cleanup(void)
59
0
{
60
0
    if (gCanonicalIDCache != NULL) {
61
0
        uhash_close(gCanonicalIDCache);
62
0
        gCanonicalIDCache = NULL;
63
0
    }
64
0
    gCanonicalIDCacheInitOnce.reset();
65
66
0
    if (gOlsonToMeta != NULL) {
67
0
        uhash_close(gOlsonToMeta);
68
0
        gOlsonToMeta = NULL;
69
0
    }
70
0
    gOlsonToMetaInitOnce.reset();
71
72
0
    if (gMetaZoneIDTable != NULL) {
73
0
        uhash_close(gMetaZoneIDTable);
74
0
        gMetaZoneIDTable = NULL;
75
0
    }
76
    // delete after closing gMetaZoneIDTable, because it holds
77
    // value objects held by the hashtable
78
0
    delete gMetaZoneIDs;
79
0
    gMetaZoneIDs = NULL;
80
0
    gMetaZoneIDsInitOnce.reset();
81
82
0
    delete gSingleZoneCountries;
83
0
    gSingleZoneCountries = NULL;
84
0
    delete gMultiZonesCountries;
85
0
    gMultiZonesCountries = NULL;
86
0
    gCountryInfoVectorsInitOnce.reset();
87
88
0
    return TRUE;
89
0
}
90
91
/**
92
 * Deleter for UChar* string
93
 */
94
static void U_CALLCONV
95
0
deleteUCharString(void *obj) {
96
0
    UChar *entry = (UChar*)obj;
97
0
    uprv_free(entry);
98
0
}
99
100
/**
101
 * Deleter for UVector
102
 */
103
static void U_CALLCONV
104
0
deleteUVector(void *obj) {
105
0
   delete (icu::UVector*) obj;
106
0
}
107
108
/**
109
 * Deleter for OlsonToMetaMappingEntry
110
 */
111
static void U_CALLCONV
112
0
deleteOlsonToMetaMappingEntry(void *obj) {
113
0
    icu::OlsonToMetaMappingEntry *entry = (icu::OlsonToMetaMappingEntry*)obj;
114
0
    uprv_free(entry);
115
0
}
116
117
U_CDECL_END
118
119
U_NAMESPACE_BEGIN
120
121
0
#define ZID_KEY_MAX 128
122
123
static const char gMetaZones[]          = "metaZones";
124
static const char gMetazoneInfo[]       = "metazoneInfo";
125
static const char gMapTimezonesTag[]    = "mapTimezones";
126
127
static const char gKeyTypeData[]        = "keyTypeData";
128
static const char gTypeAliasTag[]       = "typeAlias";
129
static const char gTypeMapTag[]         = "typeMap";
130
static const char gTimezoneTag[]        = "timezone";
131
132
static const char gPrimaryZonesTag[]    = "primaryZones";
133
134
static const char gWorldTag[]           = "001";
135
136
static const UChar gWorld[] = {0x30, 0x30, 0x31, 0x00}; // "001"
137
138
static const UChar gDefaultFrom[] = {0x31, 0x39, 0x37, 0x30, 0x2D, 0x30, 0x31, 0x2D, 0x30, 0x31,
139
                                     0x20, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x00}; // "1970-01-01 00:00"
140
static const UChar gDefaultTo[]   = {0x39, 0x39, 0x39, 0x39, 0x2D, 0x31, 0x32, 0x2D, 0x33, 0x31,
141
                                     0x20, 0x32, 0x33, 0x3A, 0x35, 0x39, 0x00}; // "9999-12-31 23:59"
142
143
static const UChar gCustomTzPrefix[]    = {0x47, 0x4D, 0x54, 0};    // "GMT"
144
145
0
#define ASCII_DIGIT(c) (((c)>=0x30 && (c)<=0x39) ? (c)-0x30 : -1)
146
147
/*
148
 * Convert a date string used by metazone mappings to UDate.
149
 * The format used by CLDR metazone mapping is "yyyy-MM-dd HH:mm".
150
 */
151
static UDate
152
0
parseDate (const UChar *text, UErrorCode &status) {
153
0
    if (U_FAILURE(status)) {
154
0
        return 0;
155
0
    }
156
0
    int32_t len = u_strlen(text);
157
0
    if (len != 16 && len != 10) {
158
        // It must be yyyy-MM-dd HH:mm (length 16) or yyyy-MM-dd (length 10)
159
0
        status = U_INVALID_FORMAT_ERROR;
160
0
        return 0;
161
0
    }
162
163
0
    int32_t year = 0, month = 0, day = 0, hour = 0, min = 0, n;
164
0
    int32_t idx;
165
166
    // "yyyy" (0 - 3)
167
0
    for (idx = 0; idx <= 3 && U_SUCCESS(status); idx++) {
168
0
        n = ASCII_DIGIT((int32_t)text[idx]);
169
0
        if (n >= 0) {
170
0
            year = 10*year + n;
171
0
        } else {
172
0
            status = U_INVALID_FORMAT_ERROR;
173
0
        }
174
0
    }
175
    // "MM" (5 - 6)
176
0
    for (idx = 5; idx <= 6 && U_SUCCESS(status); idx++) {
177
0
        n = ASCII_DIGIT((int32_t)text[idx]);
178
0
        if (n >= 0) {
179
0
            month = 10*month + n;
180
0
        } else {
181
0
            status = U_INVALID_FORMAT_ERROR;
182
0
        }
183
0
    }
184
    // "dd" (8 - 9)
185
0
    for (idx = 8; idx <= 9 && U_SUCCESS(status); idx++) {
186
0
        n = ASCII_DIGIT((int32_t)text[idx]);
187
0
        if (n >= 0) {
188
0
            day = 10*day + n;
189
0
        } else {
190
0
            status = U_INVALID_FORMAT_ERROR;
191
0
        }
192
0
    }
193
0
    if (len == 16) {
194
        // "HH" (11 - 12)
195
0
        for (idx = 11; idx <= 12 && U_SUCCESS(status); idx++) {
196
0
            n = ASCII_DIGIT((int32_t)text[idx]);
197
0
            if (n >= 0) {
198
0
                hour = 10*hour + n;
199
0
            } else {
200
0
                status = U_INVALID_FORMAT_ERROR;
201
0
            }
202
0
        }
203
        // "mm" (14 - 15)
204
0
        for (idx = 14; idx <= 15 && U_SUCCESS(status); idx++) {
205
0
            n = ASCII_DIGIT((int32_t)text[idx]);
206
0
            if (n >= 0) {
207
0
                min = 10*min + n;
208
0
            } else {
209
0
                status = U_INVALID_FORMAT_ERROR;
210
0
            }
211
0
        }
212
0
    }
213
214
0
    if (U_SUCCESS(status)) {
215
0
        UDate date = Grego::fieldsToDay(year, month - 1, day) * U_MILLIS_PER_DAY
216
0
            + hour * U_MILLIS_PER_HOUR + min * U_MILLIS_PER_MINUTE;
217
0
        return date;
218
0
    }
219
0
    return 0;
220
0
}
221
222
0
static void U_CALLCONV initCanonicalIDCache(UErrorCode &status) {
223
0
    gCanonicalIDCache = uhash_open(uhash_hashUChars, uhash_compareUChars, NULL, &status);
224
0
    if (gCanonicalIDCache == NULL) {
225
0
        status = U_MEMORY_ALLOCATION_ERROR;
226
0
    }
227
0
    if (U_FAILURE(status)) {
228
0
        gCanonicalIDCache = NULL;
229
0
    }
230
    // No key/value deleters - keys/values are from a resource bundle
231
0
    ucln_i18n_registerCleanup(UCLN_I18N_ZONEMETA, zoneMeta_cleanup);
232
0
}
233
234
235
const UChar* U_EXPORT2
236
0
ZoneMeta::getCanonicalCLDRID(const UnicodeString &tzid, UErrorCode& status) {
237
0
    if (U_FAILURE(status)) {
238
0
        return NULL;
239
0
    }
240
241
0
    if (tzid.isBogus() || tzid.length() > ZID_KEY_MAX) {
242
0
        status = U_ILLEGAL_ARGUMENT_ERROR;
243
0
        return NULL;
244
0
    }
245
246
    // Checking the cached results
247
0
    umtx_initOnce(gCanonicalIDCacheInitOnce, &initCanonicalIDCache, status);
248
0
    if (U_FAILURE(status)) {
249
0
        return NULL;
250
0
    }
251
252
0
    const UChar *canonicalID = NULL;
253
254
0
    UErrorCode tmpStatus = U_ZERO_ERROR;
255
0
    UChar utzid[ZID_KEY_MAX + 1];
256
0
    tzid.extract(utzid, ZID_KEY_MAX + 1, tmpStatus);
257
0
    U_ASSERT(tmpStatus == U_ZERO_ERROR);    // we checked the length of tzid already
258
259
0
    if (!uprv_isInvariantUString(utzid, -1)) {
260
        // All of known tz IDs are only containing ASCII invariant characters.
261
0
        status = U_ILLEGAL_ARGUMENT_ERROR;
262
0
        return NULL;
263
0
    }
264
265
    // Check if it was already cached
266
0
    umtx_lock(&gZoneMetaLock);
267
0
    {
268
0
        canonicalID = (const UChar *)uhash_get(gCanonicalIDCache, utzid);
269
0
    }
270
0
    umtx_unlock(&gZoneMetaLock);
271
272
0
    if (canonicalID != NULL) {
273
0
        return canonicalID;
274
0
    }
275
276
    // If not, resolve CLDR canonical ID with resource data
277
0
    UBool isInputCanonical = FALSE;
278
0
    char id[ZID_KEY_MAX + 1];
279
0
    tzid.extract(0, 0x7fffffff, id, UPRV_LENGTHOF(id), US_INV);
280
281
    // replace '/' with ':'
282
0
    char *p = id;
283
0
    while (*p++) {
284
0
        if (*p == '/') {
285
0
            *p = ':';
286
0
        }
287
0
    }
288
289
0
    UResourceBundle *top = ures_openDirect(NULL, gKeyTypeData, &tmpStatus);
290
0
    UResourceBundle *rb = ures_getByKey(top, gTypeMapTag, NULL, &tmpStatus);
291
0
    ures_getByKey(rb, gTimezoneTag, rb, &tmpStatus);
292
0
    ures_getByKey(rb, id, rb, &tmpStatus);
293
0
    if (U_SUCCESS(tmpStatus)) {
294
        // type entry (canonical) found
295
        // the input is the canonical ID. resolve to const UChar*
296
0
        canonicalID = TimeZone::findID(tzid);
297
0
        isInputCanonical = TRUE;
298
0
    }
299
300
0
    if (canonicalID == NULL) {
301
        // If a map element not found, then look for an alias
302
0
        tmpStatus = U_ZERO_ERROR;
303
0
        ures_getByKey(top, gTypeAliasTag, rb, &tmpStatus);
304
0
        ures_getByKey(rb, gTimezoneTag, rb, &tmpStatus);
305
0
        const UChar *canonical = ures_getStringByKey(rb,id,NULL,&tmpStatus);
306
0
        if (U_SUCCESS(tmpStatus)) {
307
            // canonical map found
308
0
            canonicalID = canonical;
309
0
        }
310
311
0
        if (canonicalID == NULL) {
312
            // Dereference the input ID using the tz data
313
0
            const UChar *derefer = TimeZone::dereferOlsonLink(tzid);
314
0
            if (derefer == NULL) {
315
0
                status = U_ILLEGAL_ARGUMENT_ERROR;
316
0
            } else {
317
0
                int32_t len = u_strlen(derefer);
318
0
                u_UCharsToChars(derefer,id,len);
319
0
                id[len] = (char) 0; // Make sure it is null terminated.
320
321
                // replace '/' with ':'
322
0
                char *q = id;
323
0
                while (*q++) {
324
0
                    if (*q == '/') {
325
0
                        *q = ':';
326
0
                    }
327
0
                }
328
329
                // If a dereference turned something up then look for an alias.
330
                // rb still points to the alias table, so we don't have to go looking
331
                // for it.
332
0
                tmpStatus = U_ZERO_ERROR;
333
0
                canonical = ures_getStringByKey(rb,id,NULL,&tmpStatus);
334
0
                if (U_SUCCESS(tmpStatus)) {
335
                    // canonical map for the dereferenced ID found
336
0
                    canonicalID = canonical;
337
0
                } else {
338
0
                    canonicalID = derefer;
339
0
                    isInputCanonical = TRUE;
340
0
                }
341
0
            }
342
0
        }
343
0
    }
344
0
    ures_close(rb);
345
0
    ures_close(top);
346
347
0
    if (U_SUCCESS(status)) {
348
0
        U_ASSERT(canonicalID != NULL);  // canocanilD must be non-NULL here
349
350
        // Put the resolved canonical ID to the cache
351
0
        umtx_lock(&gZoneMetaLock);
352
0
        {
353
0
            const UChar* idInCache = (const UChar *)uhash_get(gCanonicalIDCache, utzid);
354
0
            if (idInCache == NULL) {
355
0
                const UChar* key = ZoneMeta::findTimeZoneID(tzid);
356
0
                U_ASSERT(key != NULL);
357
0
                if (key != NULL) {
358
0
                    idInCache = (const UChar *)uhash_put(gCanonicalIDCache, (void *)key, (void *)canonicalID, &status);
359
0
                    U_ASSERT(idInCache == NULL);
360
0
                }
361
0
            }
362
0
            if (U_SUCCESS(status) && isInputCanonical) {
363
                // Also put canonical ID itself into the cache if not exist
364
0
                const UChar *canonicalInCache = (const UChar*)uhash_get(gCanonicalIDCache, canonicalID);
365
0
                if (canonicalInCache == NULL) {
366
0
                    canonicalInCache = (const UChar *)uhash_put(gCanonicalIDCache, (void *)canonicalID, (void *)canonicalID, &status);
367
0
                    U_ASSERT(canonicalInCache == NULL);
368
0
                }
369
0
            }
370
0
        }
371
0
        umtx_unlock(&gZoneMetaLock);
372
0
    }
373
374
0
    return canonicalID;
375
0
}
376
377
UnicodeString& U_EXPORT2
378
0
ZoneMeta::getCanonicalCLDRID(const UnicodeString &tzid, UnicodeString &systemID, UErrorCode& status) {
379
0
    const UChar *canonicalID = getCanonicalCLDRID(tzid, status);
380
0
    if (U_FAILURE(status) || canonicalID == NULL) {
381
0
        systemID.setToBogus();
382
0
        return systemID;
383
0
    }
384
0
    systemID.setTo(TRUE, canonicalID, -1);
385
0
    return systemID;
386
0
}
387
388
const UChar* U_EXPORT2
389
0
ZoneMeta::getCanonicalCLDRID(const TimeZone& tz) {
390
0
    if (dynamic_cast<const OlsonTimeZone *>(&tz) != NULL) {
391
        // short cut for OlsonTimeZone
392
0
        const OlsonTimeZone *otz = (const OlsonTimeZone*)&tz;
393
0
        return otz->getCanonicalID();
394
0
    }
395
0
    UErrorCode status = U_ZERO_ERROR;
396
0
    UnicodeString tzID;
397
0
    return getCanonicalCLDRID(tz.getID(tzID), status);
398
0
}
399
400
0
static void U_CALLCONV countryInfoVectorsInit(UErrorCode &status) {
401
    // Create empty vectors
402
    // No deleters for these UVectors, it's a reference to a resource bundle string.
403
0
    gSingleZoneCountries = new UVector(NULL, uhash_compareUChars, status);
404
0
    if (gSingleZoneCountries == NULL) {
405
0
        status = U_MEMORY_ALLOCATION_ERROR;
406
0
    }
407
0
    gMultiZonesCountries = new UVector(NULL, uhash_compareUChars, status);
408
0
    if (gMultiZonesCountries == NULL) {
409
0
        status = U_MEMORY_ALLOCATION_ERROR;
410
0
    }
411
412
0
    if (U_FAILURE(status)) {
413
0
        delete gSingleZoneCountries;
414
0
        delete gMultiZonesCountries;
415
0
        gSingleZoneCountries = NULL;
416
0
        gMultiZonesCountries  = NULL;
417
0
    }
418
0
    ucln_i18n_registerCleanup(UCLN_I18N_ZONEMETA, zoneMeta_cleanup);
419
0
}
420
421
422
UnicodeString& U_EXPORT2
423
0
ZoneMeta::getCanonicalCountry(const UnicodeString &tzid, UnicodeString &country, UBool *isPrimary /* = NULL */) {
424
0
    if (isPrimary != NULL) {
425
0
        *isPrimary = FALSE;
426
0
    }
427
428
0
    const UChar *region = TimeZone::getRegion(tzid);
429
0
    if (region != NULL && u_strcmp(gWorld, region) != 0) {
430
0
        country.setTo(region, -1);
431
0
    } else {
432
0
        country.setToBogus();
433
0
        return country;
434
0
    }
435
436
0
    if (isPrimary != NULL) {
437
0
        char regionBuf[] = {0, 0, 0};
438
439
        // Checking the cached results
440
0
        UErrorCode status = U_ZERO_ERROR;
441
0
        umtx_initOnce(gCountryInfoVectorsInitOnce, &countryInfoVectorsInit, status);
442
0
        if (U_FAILURE(status)) {
443
0
            return country;
444
0
        }
445
446
        // Check if it was already cached
447
0
        UBool cached = FALSE;
448
0
        UBool singleZone = FALSE;
449
0
        umtx_lock(&gZoneMetaLock);
450
0
        {
451
0
            singleZone = cached = gSingleZoneCountries->contains((void*)region);
452
0
            if (!cached) {
453
0
                cached = gMultiZonesCountries->contains((void*)region);
454
0
            }
455
0
        }
456
0
        umtx_unlock(&gZoneMetaLock);
457
458
0
        if (!cached) {
459
            // We need to go through all zones associated with the region.
460
            // This is relatively heavy operation.
461
462
0
            U_ASSERT(u_strlen(region) == 2);
463
464
0
            u_UCharsToChars(region, regionBuf, 2);
465
466
0
            StringEnumeration *ids = TimeZone::createTimeZoneIDEnumeration(UCAL_ZONE_TYPE_CANONICAL_LOCATION, regionBuf, NULL, status);
467
0
            int32_t idsLen = ids->count(status);
468
0
            if (U_SUCCESS(status) && idsLen == 1) {
469
                // only the single zone is available for the region
470
0
                singleZone = TRUE;
471
0
            }
472
0
            delete ids;
473
474
            // Cache the result
475
0
            umtx_lock(&gZoneMetaLock);
476
0
            {
477
0
                UErrorCode ec = U_ZERO_ERROR;
478
0
                if (singleZone) {
479
0
                    if (!gSingleZoneCountries->contains((void*)region)) {
480
0
                        gSingleZoneCountries->addElementX((void*)region, ec);
481
0
                    }
482
0
                } else {
483
0
                    if (!gMultiZonesCountries->contains((void*)region)) {
484
0
                        gMultiZonesCountries->addElementX((void*)region, ec);
485
0
                    }
486
0
                }
487
0
            }
488
0
            umtx_unlock(&gZoneMetaLock);
489
0
        }
490
491
0
        if (singleZone) {
492
0
            *isPrimary = TRUE;
493
0
        } else {
494
            // Note: We may cache the primary zone map in future.
495
496
            // Even a country has multiple zones, one of them might be
497
            // dominant and treated as a primary zone
498
0
            int32_t idLen = 0;
499
0
            if (regionBuf[0] == 0) {
500
0
                u_UCharsToChars(region, regionBuf, 2);
501
0
            }
502
503
0
            UResourceBundle *rb = ures_openDirect(NULL, gMetaZones, &status);
504
0
            ures_getByKey(rb, gPrimaryZonesTag, rb, &status);
505
0
            const UChar *primaryZone = ures_getStringByKey(rb, regionBuf, &idLen, &status);
506
0
            if (U_SUCCESS(status)) {
507
0
                if (tzid.compare(primaryZone, idLen) == 0) {
508
0
                    *isPrimary = TRUE;
509
0
                } else {
510
                    // The given ID might not be a canonical ID
511
0
                    UnicodeString canonicalID;
512
0
                    TimeZone::getCanonicalID(tzid, canonicalID, status);
513
0
                    if (U_SUCCESS(status) && canonicalID.compare(primaryZone, idLen) == 0) {
514
0
                        *isPrimary = TRUE;
515
0
                    }
516
0
                }
517
0
            }
518
0
            ures_close(rb);
519
0
        }
520
0
    }
521
522
0
    return country;
523
0
}
524
525
UnicodeString& U_EXPORT2
526
0
ZoneMeta::getMetazoneID(const UnicodeString &tzid, UDate date, UnicodeString &result) {
527
0
    UBool isSet = FALSE;
528
0
    const UVector *mappings = getMetazoneMappings(tzid);
529
0
    if (mappings != NULL) {
530
0
        for (int32_t i = 0; i < mappings->size(); i++) {
531
0
            OlsonToMetaMappingEntry *mzm = (OlsonToMetaMappingEntry*)mappings->elementAt(i);
532
0
            if (mzm->from <= date && mzm->to > date) {
533
0
                result.setTo(mzm->mzid, -1);
534
0
                isSet = TRUE;
535
0
                break;
536
0
            }
537
0
        }
538
0
    }
539
0
    if (!isSet) {
540
0
        result.setToBogus();
541
0
    }
542
0
    return result;
543
0
}
544
545
0
static void U_CALLCONV olsonToMetaInit(UErrorCode &status) {
546
0
    U_ASSERT(gOlsonToMeta == NULL);
547
0
    ucln_i18n_registerCleanup(UCLN_I18N_ZONEMETA, zoneMeta_cleanup);
548
0
    gOlsonToMeta = uhash_open(uhash_hashUChars, uhash_compareUChars, NULL, &status);
549
0
    if (U_FAILURE(status)) {
550
0
        gOlsonToMeta = NULL;
551
0
    } else {
552
0
        uhash_setKeyDeleter(gOlsonToMeta, deleteUCharString);
553
0
        uhash_setValueDeleter(gOlsonToMeta, deleteUVector);
554
0
    }
555
0
}
556
557
558
const UVector* U_EXPORT2
559
0
ZoneMeta::getMetazoneMappings(const UnicodeString &tzid) {
560
0
    UErrorCode status = U_ZERO_ERROR;
561
0
    UChar tzidUChars[ZID_KEY_MAX + 1];
562
0
    tzid.extract(tzidUChars, ZID_KEY_MAX + 1, status);
563
0
    if (U_FAILURE(status) || status == U_STRING_NOT_TERMINATED_WARNING) {
564
0
        return NULL;
565
0
    }
566
567
0
    umtx_initOnce(gOlsonToMetaInitOnce, &olsonToMetaInit, status);
568
0
    if (U_FAILURE(status)) {
569
0
        return NULL;
570
0
    }
571
572
    // get the mapping from cache
573
0
    const UVector *result = NULL;
574
575
0
    umtx_lock(&gZoneMetaLock);
576
0
    {
577
0
        result = (UVector*) uhash_get(gOlsonToMeta, tzidUChars);
578
0
    }
579
0
    umtx_unlock(&gZoneMetaLock);
580
581
0
    if (result != NULL) {
582
0
        return result;
583
0
    }
584
585
    // miss the cache - create new one
586
0
    UVector *tmpResult = createMetazoneMappings(tzid);
587
0
    if (tmpResult == NULL) {
588
        // not available
589
0
        return NULL;
590
0
    }
591
592
    // put the new one into the cache
593
0
    umtx_lock(&gZoneMetaLock);
594
0
    {
595
        // make sure it's already created
596
0
        result = (UVector*) uhash_get(gOlsonToMeta, tzidUChars);
597
0
        if (result == NULL) {
598
            // add the one just created
599
0
            int32_t tzidLen = tzid.length() + 1;
600
0
            UChar *key = (UChar*)uprv_malloc(tzidLen * sizeof(UChar));
601
0
            if (key == NULL) {
602
                // memory allocation error..  just return NULL
603
0
                result = NULL;
604
0
                delete tmpResult;
605
0
            } else {
606
0
                tzid.extract(key, tzidLen, status);
607
0
                uhash_put(gOlsonToMeta, key, tmpResult, &status);
608
0
                if (U_FAILURE(status)) {
609
                    // delete the mapping
610
0
                    result = NULL;
611
0
                    delete tmpResult;
612
0
                } else {
613
0
                    result = tmpResult;
614
0
                }
615
0
            }
616
0
        } else {
617
            // another thread already put the one
618
0
            delete tmpResult;
619
0
        }
620
0
    }
621
0
    umtx_unlock(&gZoneMetaLock);
622
623
0
    return result;
624
0
}
625
626
UVector*
627
0
ZoneMeta::createMetazoneMappings(const UnicodeString &tzid) {
628
0
    UVector *mzMappings = NULL;
629
0
    UErrorCode status = U_ZERO_ERROR;
630
631
0
    UnicodeString canonicalID;
632
0
    UResourceBundle *rb = ures_openDirect(NULL, gMetaZones, &status);
633
0
    ures_getByKey(rb, gMetazoneInfo, rb, &status);
634
0
    getCanonicalCLDRID(tzid, canonicalID, status);
635
636
0
    if (U_SUCCESS(status)) {
637
0
        char tzKey[ZID_KEY_MAX + 1];
638
0
        int32_t tzKeyLen = canonicalID.extract(0, canonicalID.length(), tzKey, sizeof(tzKey), US_INV);
639
0
        tzKey[tzKeyLen] = 0;
640
641
        // tzid keys are using ':' as separators
642
0
        char *p = tzKey;
643
0
        while (*p) {
644
0
            if (*p == '/') {
645
0
                *p = ':';
646
0
            }
647
0
            p++;
648
0
        }
649
650
0
        ures_getByKey(rb, tzKey, rb, &status);
651
652
0
        if (U_SUCCESS(status)) {
653
0
            UResourceBundle *mz = NULL;
654
0
            while (ures_hasNext(rb)) {
655
0
                mz = ures_getNextResource(rb, mz, &status);
656
657
0
                const UChar *mz_name = ures_getStringByIndex(mz, 0, NULL, &status);
658
0
                const UChar *mz_from = gDefaultFrom;
659
0
                const UChar *mz_to = gDefaultTo;
660
661
0
                if (ures_getSize(mz) == 3) {
662
0
                    mz_from = ures_getStringByIndex(mz, 1, NULL, &status);
663
0
                    mz_to   = ures_getStringByIndex(mz, 2, NULL, &status);
664
0
                }
665
666
0
                if(U_FAILURE(status)){
667
0
                    status = U_ZERO_ERROR;
668
0
                    continue;
669
0
                }
670
                // We do not want to use SimpleDateformat to parse boundary dates,
671
                // because this code could be triggered by the initialization code
672
                // used by SimpleDateFormat.
673
0
                UDate from = parseDate(mz_from, status);
674
0
                UDate to = parseDate(mz_to, status);
675
0
                if (U_FAILURE(status)) {
676
0
                    status = U_ZERO_ERROR;
677
0
                    continue;
678
0
                }
679
680
0
                OlsonToMetaMappingEntry *entry = (OlsonToMetaMappingEntry*)uprv_malloc(sizeof(OlsonToMetaMappingEntry));
681
0
                if (entry == NULL) {
682
0
                    status = U_MEMORY_ALLOCATION_ERROR;
683
0
                    break;
684
0
                }
685
0
                entry->mzid = mz_name;
686
0
                entry->from = from;
687
0
                entry->to = to;
688
689
0
                if (mzMappings == NULL) {
690
0
                    mzMappings = new UVector(deleteOlsonToMetaMappingEntry, NULL, status);
691
0
                    if (U_FAILURE(status)) {
692
0
                        delete mzMappings;
693
0
                        mzMappings = NULL;
694
0
                        uprv_free(entry);
695
0
                        break;
696
0
                    }
697
0
                }
698
699
0
                mzMappings->addElementX(entry, status);
700
0
                if (U_FAILURE(status)) {
701
0
                    break;
702
0
                }
703
0
            }
704
0
            ures_close(mz);
705
0
            if (U_FAILURE(status)) {
706
0
                if (mzMappings != NULL) {
707
0
                    delete mzMappings;
708
0
                    mzMappings = NULL;
709
0
                }
710
0
            }
711
0
        }
712
0
    }
713
0
    ures_close(rb);
714
0
    return mzMappings;
715
0
}
716
717
UnicodeString& U_EXPORT2
718
0
ZoneMeta::getZoneIdByMetazone(const UnicodeString &mzid, const UnicodeString &region, UnicodeString &result) {
719
0
    UErrorCode status = U_ZERO_ERROR;
720
0
    const UChar *tzid = NULL;
721
0
    int32_t tzidLen = 0;
722
0
    char keyBuf[ZID_KEY_MAX + 1];
723
0
    int32_t keyLen = 0;
724
725
0
    if (mzid.isBogus() || mzid.length() > ZID_KEY_MAX) {
726
0
        result.setToBogus();
727
0
        return result;
728
0
    }
729
730
0
    keyLen = mzid.extract(0, mzid.length(), keyBuf, ZID_KEY_MAX + 1, US_INV);
731
0
    keyBuf[keyLen] = 0;
732
733
0
    UResourceBundle *rb = ures_openDirect(NULL, gMetaZones, &status);
734
0
    ures_getByKey(rb, gMapTimezonesTag, rb, &status);
735
0
    ures_getByKey(rb, keyBuf, rb, &status);
736
737
0
    if (U_SUCCESS(status)) {
738
        // check region mapping
739
0
        if (region.length() == 2 || region.length() == 3) {
740
0
            keyLen = region.extract(0, region.length(), keyBuf, ZID_KEY_MAX + 1, US_INV);
741
0
            keyBuf[keyLen] = 0;
742
0
            tzid = ures_getStringByKey(rb, keyBuf, &tzidLen, &status);
743
0
            if (status == U_MISSING_RESOURCE_ERROR) {
744
0
                status = U_ZERO_ERROR;
745
0
            }
746
0
        }
747
0
        if (U_SUCCESS(status) && tzid == NULL) {
748
            // try "001"
749
0
            tzid = ures_getStringByKey(rb, gWorldTag, &tzidLen, &status);
750
0
        }
751
0
    }
752
0
    ures_close(rb);
753
754
0
    if (tzid == NULL) {
755
0
        result.setToBogus();
756
0
    } else {
757
0
        result.setTo(tzid, tzidLen);
758
0
    }
759
760
0
    return result;
761
0
}
762
763
0
static void U_CALLCONV initAvailableMetaZoneIDs () {
764
0
    U_ASSERT(gMetaZoneIDs == NULL);
765
0
    U_ASSERT(gMetaZoneIDTable == NULL);
766
0
    ucln_i18n_registerCleanup(UCLN_I18N_ZONEMETA, zoneMeta_cleanup);
767
768
0
    UErrorCode status = U_ZERO_ERROR;
769
0
    gMetaZoneIDTable = uhash_open(uhash_hashUnicodeString, uhash_compareUnicodeString, NULL, &status);
770
0
    if (U_FAILURE(status) || gMetaZoneIDTable == NULL) {
771
0
        gMetaZoneIDTable = NULL;
772
0
        return;
773
0
    }
774
0
    uhash_setKeyDeleter(gMetaZoneIDTable, uprv_deleteUObject);
775
    // No valueDeleter, because the vector maintain the value objects
776
0
    gMetaZoneIDs = new UVector(NULL, uhash_compareUChars, status);
777
0
    if (U_FAILURE(status) || gMetaZoneIDs == NULL) {
778
0
        gMetaZoneIDs = NULL;
779
0
        uhash_close(gMetaZoneIDTable);
780
0
        gMetaZoneIDTable = NULL;
781
0
        return;
782
0
    }
783
0
    gMetaZoneIDs->setDeleter(uprv_free);
784
785
0
    UResourceBundle *rb = ures_openDirect(NULL, gMetaZones, &status);
786
0
    UResourceBundle *bundle = ures_getByKey(rb, gMapTimezonesTag, NULL, &status);
787
0
    StackUResourceBundle res;
788
0
    while (U_SUCCESS(status) && ures_hasNext(bundle)) {
789
0
        ures_getNextResource(bundle, res.getAlias(), &status);
790
0
        if (U_FAILURE(status)) {
791
0
            break;
792
0
        }
793
0
        const char *mzID = ures_getKey(res.getAlias());
794
0
        int32_t len = static_cast<int32_t>(uprv_strlen(mzID));
795
0
        UChar *uMzID = (UChar*)uprv_malloc(sizeof(UChar) * (len + 1));
796
0
        if (uMzID == NULL) {
797
0
            status = U_MEMORY_ALLOCATION_ERROR;
798
0
            break;
799
0
        }
800
0
        u_charsToUChars(mzID, uMzID, len);
801
0
        uMzID[len] = 0;
802
0
        UnicodeString *usMzID = new UnicodeString(uMzID);
803
0
        if (uhash_get(gMetaZoneIDTable, usMzID) == NULL) {
804
0
            gMetaZoneIDs->addElementX((void *)uMzID, status);
805
0
            uhash_put(gMetaZoneIDTable, (void *)usMzID, (void *)uMzID, &status);
806
0
        } else {
807
0
            uprv_free(uMzID);
808
0
            delete usMzID;
809
0
        }
810
0
    }
811
0
    ures_close(bundle);
812
0
    ures_close(rb);
813
814
0
    if (U_FAILURE(status)) {
815
0
        uhash_close(gMetaZoneIDTable);
816
0
        delete gMetaZoneIDs;
817
0
        gMetaZoneIDTable = NULL;
818
0
        gMetaZoneIDs = NULL;
819
0
    }
820
0
}
821
822
const UVector*
823
0
ZoneMeta::getAvailableMetazoneIDs() {
824
0
    umtx_initOnce(gMetaZoneIDsInitOnce, &initAvailableMetaZoneIDs);
825
0
    return gMetaZoneIDs;
826
0
}
827
828
const UChar*
829
0
ZoneMeta::findMetaZoneID(const UnicodeString& mzid) {
830
0
    umtx_initOnce(gMetaZoneIDsInitOnce, &initAvailableMetaZoneIDs);
831
0
    if (gMetaZoneIDTable == NULL) {
832
0
        return NULL;
833
0
    }
834
0
    return (const UChar*)uhash_get(gMetaZoneIDTable, &mzid);
835
0
}
836
837
const UChar*
838
0
ZoneMeta::findTimeZoneID(const UnicodeString& tzid) {
839
0
    return TimeZone::findID(tzid);
840
0
}
841
842
843
TimeZone*
844
0
ZoneMeta::createCustomTimeZone(int32_t offset) {
845
0
    UBool negative = FALSE;
846
0
    int32_t tmp = offset;
847
0
    if (offset < 0) {
848
0
        negative = TRUE;
849
0
        tmp = -offset;
850
0
    }
851
0
    uint8_t hour, min, sec;
852
853
0
    tmp /= 1000;
854
0
    sec = static_cast<uint8_t>(tmp % 60);
855
0
    tmp /= 60;
856
0
    min = static_cast<uint8_t>(tmp % 60);
857
0
    hour = static_cast<uint8_t>(tmp / 60);
858
859
0
    UnicodeString zid;
860
0
    formatCustomID(hour, min, sec, negative, zid);
861
0
    return new SimpleTimeZone(offset, zid);
862
0
}
863
864
UnicodeString&
865
0
ZoneMeta::formatCustomID(uint8_t hour, uint8_t min, uint8_t sec, UBool negative, UnicodeString& id) {
866
    // Create normalized time zone ID - GMT[+|-]HH:mm[:ss]
867
0
    id.setTo(gCustomTzPrefix, -1);
868
0
    if (hour != 0 || min != 0) {
869
0
        if (negative) {
870
0
          id.append((UChar)0x2D);    // '-'
871
0
        } else {
872
0
          id.append((UChar)0x2B);    // '+'
873
0
        }
874
        // Always use US-ASCII digits
875
0
        id.append((UChar)(0x30 + (hour%100)/10));
876
0
        id.append((UChar)(0x30 + (hour%10)));
877
0
        id.append((UChar)0x3A);    // ':'
878
0
        id.append((UChar)(0x30 + (min%100)/10));
879
0
        id.append((UChar)(0x30 + (min%10)));
880
0
        if (sec != 0) {
881
0
          id.append((UChar)0x3A);    // ':'
882
0
          id.append((UChar)(0x30 + (sec%100)/10));
883
0
          id.append((UChar)(0x30 + (sec%10)));
884
0
        }
885
0
    }
886
0
    return id;
887
0
}
888
889
const UChar*
890
0
ZoneMeta::getShortID(const TimeZone& tz) {
891
0
    const UChar* canonicalID = NULL;
892
0
    if (dynamic_cast<const OlsonTimeZone *>(&tz) != NULL) {
893
        // short cut for OlsonTimeZone
894
0
        const OlsonTimeZone *otz = (const OlsonTimeZone*)&tz;
895
0
        canonicalID = otz->getCanonicalID();
896
0
    }
897
0
    if (canonicalID == NULL) {
898
0
        return NULL;
899
0
    }
900
0
    return getShortIDFromCanonical(canonicalID);
901
0
}
902
903
const UChar*
904
0
ZoneMeta::getShortID(const UnicodeString& id) {
905
0
    UErrorCode status = U_ZERO_ERROR;
906
0
    const UChar* canonicalID = ZoneMeta::getCanonicalCLDRID(id, status);
907
0
    if (U_FAILURE(status) || canonicalID == NULL) {
908
0
        return NULL;
909
0
    }
910
0
    return ZoneMeta::getShortIDFromCanonical(canonicalID);
911
0
}
912
913
const UChar*
914
0
ZoneMeta::getShortIDFromCanonical(const UChar* canonicalID) {
915
0
    const UChar* shortID = NULL;
916
0
    int32_t len = u_strlen(canonicalID);
917
0
    char tzidKey[ZID_KEY_MAX + 1];
918
919
0
    u_UCharsToChars(canonicalID, tzidKey, len);
920
0
    tzidKey[len] = (char) 0; // Make sure it is null terminated.
921
922
    // replace '/' with ':'
923
0
    char *p = tzidKey;
924
0
    while (*p++) {
925
0
        if (*p == '/') {
926
0
            *p = ':';
927
0
        }
928
0
    }
929
930
0
    UErrorCode status = U_ZERO_ERROR;
931
0
    UResourceBundle *rb = ures_openDirect(NULL, gKeyTypeData, &status);
932
0
    ures_getByKey(rb, gTypeMapTag, rb, &status);
933
0
    ures_getByKey(rb, gTimezoneTag, rb, &status);
934
0
    shortID = ures_getStringByKey(rb, tzidKey, NULL, &status);
935
0
    ures_close(rb);
936
937
0
    return shortID;
938
0
}
939
940
U_NAMESPACE_END
941
942
#endif /* #if !UCONFIG_NO_FORMATTING */