11cb0ef41Sopenharmony_ci// © 2016 and later: Unicode, Inc. and others.
21cb0ef41Sopenharmony_ci// License & terms of use: http://www.unicode.org/copyright.html
31cb0ef41Sopenharmony_ci/*
41cb0ef41Sopenharmony_ci*******************************************************************************
51cb0ef41Sopenharmony_ci* Copyright (C) 2007-2014, International Business Machines Corporation and
61cb0ef41Sopenharmony_ci* others. All Rights Reserved.
71cb0ef41Sopenharmony_ci*******************************************************************************
81cb0ef41Sopenharmony_ci*/
91cb0ef41Sopenharmony_ci
101cb0ef41Sopenharmony_ci#include "unicode/utypes.h"
111cb0ef41Sopenharmony_ci
121cb0ef41Sopenharmony_ci#if !UCONFIG_NO_FORMATTING
131cb0ef41Sopenharmony_ci
141cb0ef41Sopenharmony_ci#include "zonemeta.h"
151cb0ef41Sopenharmony_ci
161cb0ef41Sopenharmony_ci#include "unicode/timezone.h"
171cb0ef41Sopenharmony_ci#include "unicode/ustring.h"
181cb0ef41Sopenharmony_ci#include "unicode/putil.h"
191cb0ef41Sopenharmony_ci#include "unicode/simpletz.h"
201cb0ef41Sopenharmony_ci#include "unicode/strenum.h"
211cb0ef41Sopenharmony_ci#include "umutex.h"
221cb0ef41Sopenharmony_ci#include "uvector.h"
231cb0ef41Sopenharmony_ci#include "cmemory.h"
241cb0ef41Sopenharmony_ci#include "gregoimp.h"
251cb0ef41Sopenharmony_ci#include "cstring.h"
261cb0ef41Sopenharmony_ci#include "ucln_in.h"
271cb0ef41Sopenharmony_ci#include "uassert.h"
281cb0ef41Sopenharmony_ci#include "uresimp.h"
291cb0ef41Sopenharmony_ci#include "uhash.h"
301cb0ef41Sopenharmony_ci#include "olsontz.h"
311cb0ef41Sopenharmony_ci#include "uinvchar.h"
321cb0ef41Sopenharmony_ci
331cb0ef41Sopenharmony_cistatic icu::UMutex gZoneMetaLock;
341cb0ef41Sopenharmony_ci
351cb0ef41Sopenharmony_ci// CLDR Canonical ID mapping table
361cb0ef41Sopenharmony_cistatic UHashtable *gCanonicalIDCache = nullptr;
371cb0ef41Sopenharmony_cistatic icu::UInitOnce gCanonicalIDCacheInitOnce {};
381cb0ef41Sopenharmony_ci
391cb0ef41Sopenharmony_ci// Metazone mapping table
401cb0ef41Sopenharmony_cistatic UHashtable *gOlsonToMeta = nullptr;
411cb0ef41Sopenharmony_cistatic icu::UInitOnce gOlsonToMetaInitOnce {};
421cb0ef41Sopenharmony_ci
431cb0ef41Sopenharmony_ci// Available metazone IDs vector and table
441cb0ef41Sopenharmony_cistatic icu::UVector *gMetaZoneIDs = nullptr;
451cb0ef41Sopenharmony_cistatic UHashtable *gMetaZoneIDTable = nullptr;
461cb0ef41Sopenharmony_cistatic icu::UInitOnce gMetaZoneIDsInitOnce {};
471cb0ef41Sopenharmony_ci
481cb0ef41Sopenharmony_ci// Country info vectors
491cb0ef41Sopenharmony_cistatic icu::UVector *gSingleZoneCountries = nullptr;
501cb0ef41Sopenharmony_cistatic icu::UVector *gMultiZonesCountries = nullptr;
511cb0ef41Sopenharmony_cistatic icu::UInitOnce gCountryInfoVectorsInitOnce {};
521cb0ef41Sopenharmony_ci
531cb0ef41Sopenharmony_ciU_CDECL_BEGIN
541cb0ef41Sopenharmony_ci
551cb0ef41Sopenharmony_ci/**
561cb0ef41Sopenharmony_ci * Cleanup callback func
571cb0ef41Sopenharmony_ci */
581cb0ef41Sopenharmony_cistatic UBool U_CALLCONV zoneMeta_cleanup()
591cb0ef41Sopenharmony_ci{
601cb0ef41Sopenharmony_ci    if (gCanonicalIDCache != nullptr) {
611cb0ef41Sopenharmony_ci        uhash_close(gCanonicalIDCache);
621cb0ef41Sopenharmony_ci        gCanonicalIDCache = nullptr;
631cb0ef41Sopenharmony_ci    }
641cb0ef41Sopenharmony_ci    gCanonicalIDCacheInitOnce.reset();
651cb0ef41Sopenharmony_ci
661cb0ef41Sopenharmony_ci    if (gOlsonToMeta != nullptr) {
671cb0ef41Sopenharmony_ci        uhash_close(gOlsonToMeta);
681cb0ef41Sopenharmony_ci        gOlsonToMeta = nullptr;
691cb0ef41Sopenharmony_ci    }
701cb0ef41Sopenharmony_ci    gOlsonToMetaInitOnce.reset();
711cb0ef41Sopenharmony_ci
721cb0ef41Sopenharmony_ci    if (gMetaZoneIDTable != nullptr) {
731cb0ef41Sopenharmony_ci        uhash_close(gMetaZoneIDTable);
741cb0ef41Sopenharmony_ci        gMetaZoneIDTable = nullptr;
751cb0ef41Sopenharmony_ci    }
761cb0ef41Sopenharmony_ci    // delete after closing gMetaZoneIDTable, because it holds
771cb0ef41Sopenharmony_ci    // value objects held by the hashtable
781cb0ef41Sopenharmony_ci    delete gMetaZoneIDs;
791cb0ef41Sopenharmony_ci    gMetaZoneIDs = nullptr;
801cb0ef41Sopenharmony_ci    gMetaZoneIDsInitOnce.reset();
811cb0ef41Sopenharmony_ci
821cb0ef41Sopenharmony_ci    delete gSingleZoneCountries;
831cb0ef41Sopenharmony_ci    gSingleZoneCountries = nullptr;
841cb0ef41Sopenharmony_ci    delete gMultiZonesCountries;
851cb0ef41Sopenharmony_ci    gMultiZonesCountries = nullptr;
861cb0ef41Sopenharmony_ci    gCountryInfoVectorsInitOnce.reset();
871cb0ef41Sopenharmony_ci
881cb0ef41Sopenharmony_ci    return true;
891cb0ef41Sopenharmony_ci}
901cb0ef41Sopenharmony_ci
911cb0ef41Sopenharmony_ci/**
921cb0ef41Sopenharmony_ci * Deleter for char16_t* string
931cb0ef41Sopenharmony_ci */
941cb0ef41Sopenharmony_cistatic void U_CALLCONV
951cb0ef41Sopenharmony_cideleteUCharString(void *obj) {
961cb0ef41Sopenharmony_ci    char16_t *entry = (char16_t*)obj;
971cb0ef41Sopenharmony_ci    uprv_free(entry);
981cb0ef41Sopenharmony_ci}
991cb0ef41Sopenharmony_ci
1001cb0ef41Sopenharmony_ci/**
1011cb0ef41Sopenharmony_ci * Deleter for OlsonToMetaMappingEntry
1021cb0ef41Sopenharmony_ci */
1031cb0ef41Sopenharmony_cistatic void U_CALLCONV
1041cb0ef41Sopenharmony_cideleteOlsonToMetaMappingEntry(void *obj) {
1051cb0ef41Sopenharmony_ci    icu::OlsonToMetaMappingEntry *entry = (icu::OlsonToMetaMappingEntry*)obj;
1061cb0ef41Sopenharmony_ci    delete entry;
1071cb0ef41Sopenharmony_ci}
1081cb0ef41Sopenharmony_ci
1091cb0ef41Sopenharmony_ciU_CDECL_END
1101cb0ef41Sopenharmony_ci
1111cb0ef41Sopenharmony_ciU_NAMESPACE_BEGIN
1121cb0ef41Sopenharmony_ci
1131cb0ef41Sopenharmony_ci#define ZID_KEY_MAX 128
1141cb0ef41Sopenharmony_ci
1151cb0ef41Sopenharmony_cistatic const char gMetaZones[]          = "metaZones";
1161cb0ef41Sopenharmony_cistatic const char gMetazoneInfo[]       = "metazoneInfo";
1171cb0ef41Sopenharmony_cistatic const char gMapTimezonesTag[]    = "mapTimezones";
1181cb0ef41Sopenharmony_ci
1191cb0ef41Sopenharmony_cistatic const char gKeyTypeData[]        = "keyTypeData";
1201cb0ef41Sopenharmony_cistatic const char gTypeAliasTag[]       = "typeAlias";
1211cb0ef41Sopenharmony_cistatic const char gTypeMapTag[]         = "typeMap";
1221cb0ef41Sopenharmony_cistatic const char gTimezoneTag[]        = "timezone";
1231cb0ef41Sopenharmony_cistatic const char gIanaMapTag[]         = "ianaMap";
1241cb0ef41Sopenharmony_ci
1251cb0ef41Sopenharmony_cistatic const char gPrimaryZonesTag[]    = "primaryZones";
1261cb0ef41Sopenharmony_ci
1271cb0ef41Sopenharmony_cistatic const char gWorldTag[]           = "001";
1281cb0ef41Sopenharmony_ci
1291cb0ef41Sopenharmony_cistatic const char16_t gWorld[] = {0x30, 0x30, 0x31, 0x00}; // "001"
1301cb0ef41Sopenharmony_ci
1311cb0ef41Sopenharmony_cistatic const char16_t gDefaultFrom[] = {0x31, 0x39, 0x37, 0x30, 0x2D, 0x30, 0x31, 0x2D, 0x30, 0x31,
1321cb0ef41Sopenharmony_ci                                     0x20, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x00}; // "1970-01-01 00:00"
1331cb0ef41Sopenharmony_cistatic const char16_t gDefaultTo[]   = {0x39, 0x39, 0x39, 0x39, 0x2D, 0x31, 0x32, 0x2D, 0x33, 0x31,
1341cb0ef41Sopenharmony_ci                                     0x20, 0x32, 0x33, 0x3A, 0x35, 0x39, 0x00}; // "9999-12-31 23:59"
1351cb0ef41Sopenharmony_ci
1361cb0ef41Sopenharmony_cistatic const char16_t gCustomTzPrefix[]    = {0x47, 0x4D, 0x54, 0};    // "GMT"
1371cb0ef41Sopenharmony_ci
1381cb0ef41Sopenharmony_ci#define ASCII_DIGIT(c) (((c)>=0x30 && (c)<=0x39) ? (c)-0x30 : -1)
1391cb0ef41Sopenharmony_ci
1401cb0ef41Sopenharmony_ci/*
1411cb0ef41Sopenharmony_ci * Convert a date string used by metazone mappings to UDate.
1421cb0ef41Sopenharmony_ci * The format used by CLDR metazone mapping is "yyyy-MM-dd HH:mm".
1431cb0ef41Sopenharmony_ci */
1441cb0ef41Sopenharmony_cistatic UDate
1451cb0ef41Sopenharmony_ciparseDate (const char16_t *text, UErrorCode &status) {
1461cb0ef41Sopenharmony_ci    if (U_FAILURE(status)) {
1471cb0ef41Sopenharmony_ci        return 0;
1481cb0ef41Sopenharmony_ci    }
1491cb0ef41Sopenharmony_ci    int32_t len = u_strlen(text);
1501cb0ef41Sopenharmony_ci    if (len != 16 && len != 10) {
1511cb0ef41Sopenharmony_ci        // It must be yyyy-MM-dd HH:mm (length 16) or yyyy-MM-dd (length 10)
1521cb0ef41Sopenharmony_ci        status = U_INVALID_FORMAT_ERROR;
1531cb0ef41Sopenharmony_ci        return 0;
1541cb0ef41Sopenharmony_ci    }
1551cb0ef41Sopenharmony_ci
1561cb0ef41Sopenharmony_ci    int32_t year = 0, month = 0, day = 0, hour = 0, min = 0, n;
1571cb0ef41Sopenharmony_ci    int32_t idx;
1581cb0ef41Sopenharmony_ci
1591cb0ef41Sopenharmony_ci    // "yyyy" (0 - 3)
1601cb0ef41Sopenharmony_ci    for (idx = 0; idx <= 3 && U_SUCCESS(status); idx++) {
1611cb0ef41Sopenharmony_ci        n = ASCII_DIGIT((int32_t)text[idx]);
1621cb0ef41Sopenharmony_ci        if (n >= 0) {
1631cb0ef41Sopenharmony_ci            year = 10*year + n;
1641cb0ef41Sopenharmony_ci        } else {
1651cb0ef41Sopenharmony_ci            status = U_INVALID_FORMAT_ERROR;
1661cb0ef41Sopenharmony_ci        }
1671cb0ef41Sopenharmony_ci    }
1681cb0ef41Sopenharmony_ci    // "MM" (5 - 6)
1691cb0ef41Sopenharmony_ci    for (idx = 5; idx <= 6 && U_SUCCESS(status); idx++) {
1701cb0ef41Sopenharmony_ci        n = ASCII_DIGIT((int32_t)text[idx]);
1711cb0ef41Sopenharmony_ci        if (n >= 0) {
1721cb0ef41Sopenharmony_ci            month = 10*month + n;
1731cb0ef41Sopenharmony_ci        } else {
1741cb0ef41Sopenharmony_ci            status = U_INVALID_FORMAT_ERROR;
1751cb0ef41Sopenharmony_ci        }
1761cb0ef41Sopenharmony_ci    }
1771cb0ef41Sopenharmony_ci    // "dd" (8 - 9)
1781cb0ef41Sopenharmony_ci    for (idx = 8; idx <= 9 && U_SUCCESS(status); idx++) {
1791cb0ef41Sopenharmony_ci        n = ASCII_DIGIT((int32_t)text[idx]);
1801cb0ef41Sopenharmony_ci        if (n >= 0) {
1811cb0ef41Sopenharmony_ci            day = 10*day + n;
1821cb0ef41Sopenharmony_ci        } else {
1831cb0ef41Sopenharmony_ci            status = U_INVALID_FORMAT_ERROR;
1841cb0ef41Sopenharmony_ci        }
1851cb0ef41Sopenharmony_ci    }
1861cb0ef41Sopenharmony_ci    if (len == 16) {
1871cb0ef41Sopenharmony_ci        // "HH" (11 - 12)
1881cb0ef41Sopenharmony_ci        for (idx = 11; idx <= 12 && U_SUCCESS(status); idx++) {
1891cb0ef41Sopenharmony_ci            n = ASCII_DIGIT((int32_t)text[idx]);
1901cb0ef41Sopenharmony_ci            if (n >= 0) {
1911cb0ef41Sopenharmony_ci                hour = 10*hour + n;
1921cb0ef41Sopenharmony_ci            } else {
1931cb0ef41Sopenharmony_ci                status = U_INVALID_FORMAT_ERROR;
1941cb0ef41Sopenharmony_ci            }
1951cb0ef41Sopenharmony_ci        }
1961cb0ef41Sopenharmony_ci        // "mm" (14 - 15)
1971cb0ef41Sopenharmony_ci        for (idx = 14; idx <= 15 && U_SUCCESS(status); idx++) {
1981cb0ef41Sopenharmony_ci            n = ASCII_DIGIT((int32_t)text[idx]);
1991cb0ef41Sopenharmony_ci            if (n >= 0) {
2001cb0ef41Sopenharmony_ci                min = 10*min + n;
2011cb0ef41Sopenharmony_ci            } else {
2021cb0ef41Sopenharmony_ci                status = U_INVALID_FORMAT_ERROR;
2031cb0ef41Sopenharmony_ci            }
2041cb0ef41Sopenharmony_ci        }
2051cb0ef41Sopenharmony_ci    }
2061cb0ef41Sopenharmony_ci
2071cb0ef41Sopenharmony_ci    if (U_SUCCESS(status)) {
2081cb0ef41Sopenharmony_ci        UDate date = Grego::fieldsToDay(year, month - 1, day) * U_MILLIS_PER_DAY
2091cb0ef41Sopenharmony_ci            + hour * U_MILLIS_PER_HOUR + min * U_MILLIS_PER_MINUTE;
2101cb0ef41Sopenharmony_ci        return date;
2111cb0ef41Sopenharmony_ci    }
2121cb0ef41Sopenharmony_ci    return 0;
2131cb0ef41Sopenharmony_ci}
2141cb0ef41Sopenharmony_ci
2151cb0ef41Sopenharmony_cistatic void U_CALLCONV initCanonicalIDCache(UErrorCode &status) {
2161cb0ef41Sopenharmony_ci    gCanonicalIDCache = uhash_open(uhash_hashUChars, uhash_compareUChars, nullptr, &status);
2171cb0ef41Sopenharmony_ci    if (gCanonicalIDCache == nullptr) {
2181cb0ef41Sopenharmony_ci        status = U_MEMORY_ALLOCATION_ERROR;
2191cb0ef41Sopenharmony_ci    }
2201cb0ef41Sopenharmony_ci    if (U_FAILURE(status)) {
2211cb0ef41Sopenharmony_ci        gCanonicalIDCache = nullptr;
2221cb0ef41Sopenharmony_ci    }
2231cb0ef41Sopenharmony_ci    // No key/value deleters - keys/values are from a resource bundle
2241cb0ef41Sopenharmony_ci    ucln_i18n_registerCleanup(UCLN_I18N_ZONEMETA, zoneMeta_cleanup);
2251cb0ef41Sopenharmony_ci}
2261cb0ef41Sopenharmony_ci
2271cb0ef41Sopenharmony_ci
2281cb0ef41Sopenharmony_ciconst char16_t* U_EXPORT2
2291cb0ef41Sopenharmony_ciZoneMeta::getCanonicalCLDRID(const UnicodeString &tzid, UErrorCode& status) {
2301cb0ef41Sopenharmony_ci    if (U_FAILURE(status)) {
2311cb0ef41Sopenharmony_ci        return nullptr;
2321cb0ef41Sopenharmony_ci    }
2331cb0ef41Sopenharmony_ci
2341cb0ef41Sopenharmony_ci    if (tzid.isBogus() || tzid.length() > ZID_KEY_MAX) {
2351cb0ef41Sopenharmony_ci        status = U_ILLEGAL_ARGUMENT_ERROR;
2361cb0ef41Sopenharmony_ci        return nullptr;
2371cb0ef41Sopenharmony_ci    }
2381cb0ef41Sopenharmony_ci
2391cb0ef41Sopenharmony_ci    // Checking the cached results
2401cb0ef41Sopenharmony_ci    umtx_initOnce(gCanonicalIDCacheInitOnce, &initCanonicalIDCache, status);
2411cb0ef41Sopenharmony_ci    if (U_FAILURE(status)) {
2421cb0ef41Sopenharmony_ci        return nullptr;
2431cb0ef41Sopenharmony_ci    }
2441cb0ef41Sopenharmony_ci
2451cb0ef41Sopenharmony_ci    const char16_t *canonicalID = nullptr;
2461cb0ef41Sopenharmony_ci
2471cb0ef41Sopenharmony_ci    UErrorCode tmpStatus = U_ZERO_ERROR;
2481cb0ef41Sopenharmony_ci    char16_t utzid[ZID_KEY_MAX + 1];
2491cb0ef41Sopenharmony_ci    tzid.extract(utzid, ZID_KEY_MAX + 1, tmpStatus);
2501cb0ef41Sopenharmony_ci    U_ASSERT(tmpStatus == U_ZERO_ERROR);    // we checked the length of tzid already
2511cb0ef41Sopenharmony_ci
2521cb0ef41Sopenharmony_ci    if (!uprv_isInvariantUString(utzid, -1)) {
2531cb0ef41Sopenharmony_ci        // All of known tz IDs are only containing ASCII invariant characters.
2541cb0ef41Sopenharmony_ci        status = U_ILLEGAL_ARGUMENT_ERROR;
2551cb0ef41Sopenharmony_ci        return nullptr;
2561cb0ef41Sopenharmony_ci    }
2571cb0ef41Sopenharmony_ci
2581cb0ef41Sopenharmony_ci    // Check if it was already cached
2591cb0ef41Sopenharmony_ci    umtx_lock(&gZoneMetaLock);
2601cb0ef41Sopenharmony_ci    {
2611cb0ef41Sopenharmony_ci        canonicalID = (const char16_t *)uhash_get(gCanonicalIDCache, utzid);
2621cb0ef41Sopenharmony_ci    }
2631cb0ef41Sopenharmony_ci    umtx_unlock(&gZoneMetaLock);
2641cb0ef41Sopenharmony_ci
2651cb0ef41Sopenharmony_ci    if (canonicalID != nullptr) {
2661cb0ef41Sopenharmony_ci        return canonicalID;
2671cb0ef41Sopenharmony_ci    }
2681cb0ef41Sopenharmony_ci
2691cb0ef41Sopenharmony_ci    // If not, resolve CLDR canonical ID with resource data
2701cb0ef41Sopenharmony_ci    UBool isInputCanonical = false;
2711cb0ef41Sopenharmony_ci    char id[ZID_KEY_MAX + 1];
2721cb0ef41Sopenharmony_ci    tzid.extract(0, 0x7fffffff, id, UPRV_LENGTHOF(id), US_INV);
2731cb0ef41Sopenharmony_ci
2741cb0ef41Sopenharmony_ci    // replace '/' with ':'
2751cb0ef41Sopenharmony_ci    char *p = id;
2761cb0ef41Sopenharmony_ci    while (*p++) {
2771cb0ef41Sopenharmony_ci        if (*p == '/') {
2781cb0ef41Sopenharmony_ci            *p = ':';
2791cb0ef41Sopenharmony_ci        }
2801cb0ef41Sopenharmony_ci    }
2811cb0ef41Sopenharmony_ci
2821cb0ef41Sopenharmony_ci    UResourceBundle *top = ures_openDirect(nullptr, gKeyTypeData, &tmpStatus);
2831cb0ef41Sopenharmony_ci    UResourceBundle *rb = ures_getByKey(top, gTypeMapTag, nullptr, &tmpStatus);
2841cb0ef41Sopenharmony_ci    ures_getByKey(rb, gTimezoneTag, rb, &tmpStatus);
2851cb0ef41Sopenharmony_ci    ures_getByKey(rb, id, rb, &tmpStatus);
2861cb0ef41Sopenharmony_ci    if (U_SUCCESS(tmpStatus)) {
2871cb0ef41Sopenharmony_ci        // type entry (canonical) found
2881cb0ef41Sopenharmony_ci        // the input is the canonical ID. resolve to const char16_t*
2891cb0ef41Sopenharmony_ci        canonicalID = TimeZone::findID(tzid);
2901cb0ef41Sopenharmony_ci        isInputCanonical = true;
2911cb0ef41Sopenharmony_ci    }
2921cb0ef41Sopenharmony_ci
2931cb0ef41Sopenharmony_ci    if (canonicalID == nullptr) {
2941cb0ef41Sopenharmony_ci        // If a map element not found, then look for an alias
2951cb0ef41Sopenharmony_ci        tmpStatus = U_ZERO_ERROR;
2961cb0ef41Sopenharmony_ci        ures_getByKey(top, gTypeAliasTag, rb, &tmpStatus);
2971cb0ef41Sopenharmony_ci        ures_getByKey(rb, gTimezoneTag, rb, &tmpStatus);
2981cb0ef41Sopenharmony_ci        const char16_t *canonical = ures_getStringByKey(rb,id,nullptr,&tmpStatus);
2991cb0ef41Sopenharmony_ci        if (U_SUCCESS(tmpStatus)) {
3001cb0ef41Sopenharmony_ci            // canonical map found
3011cb0ef41Sopenharmony_ci            canonicalID = canonical;
3021cb0ef41Sopenharmony_ci        }
3031cb0ef41Sopenharmony_ci
3041cb0ef41Sopenharmony_ci        if (canonicalID == nullptr) {
3051cb0ef41Sopenharmony_ci            // Dereference the input ID using the tz data
3061cb0ef41Sopenharmony_ci            const char16_t *derefer = TimeZone::dereferOlsonLink(tzid);
3071cb0ef41Sopenharmony_ci            if (derefer == nullptr) {
3081cb0ef41Sopenharmony_ci                status = U_ILLEGAL_ARGUMENT_ERROR;
3091cb0ef41Sopenharmony_ci            } else {
3101cb0ef41Sopenharmony_ci                int32_t len = u_strlen(derefer);
3111cb0ef41Sopenharmony_ci                u_UCharsToChars(derefer,id,len);
3121cb0ef41Sopenharmony_ci                id[len] = (char) 0; // Make sure it is null terminated.
3131cb0ef41Sopenharmony_ci
3141cb0ef41Sopenharmony_ci                // replace '/' with ':'
3151cb0ef41Sopenharmony_ci                char *q = id;
3161cb0ef41Sopenharmony_ci                while (*q++) {
3171cb0ef41Sopenharmony_ci                    if (*q == '/') {
3181cb0ef41Sopenharmony_ci                        *q = ':';
3191cb0ef41Sopenharmony_ci                    }
3201cb0ef41Sopenharmony_ci                }
3211cb0ef41Sopenharmony_ci
3221cb0ef41Sopenharmony_ci                // If a dereference turned something up then look for an alias.
3231cb0ef41Sopenharmony_ci                // rb still points to the alias table, so we don't have to go looking
3241cb0ef41Sopenharmony_ci                // for it.
3251cb0ef41Sopenharmony_ci                tmpStatus = U_ZERO_ERROR;
3261cb0ef41Sopenharmony_ci                canonical = ures_getStringByKey(rb,id,nullptr,&tmpStatus);
3271cb0ef41Sopenharmony_ci                if (U_SUCCESS(tmpStatus)) {
3281cb0ef41Sopenharmony_ci                    // canonical map for the dereferenced ID found
3291cb0ef41Sopenharmony_ci                    canonicalID = canonical;
3301cb0ef41Sopenharmony_ci                } else {
3311cb0ef41Sopenharmony_ci                    canonicalID = derefer;
3321cb0ef41Sopenharmony_ci                    isInputCanonical = true;
3331cb0ef41Sopenharmony_ci                }
3341cb0ef41Sopenharmony_ci            }
3351cb0ef41Sopenharmony_ci        }
3361cb0ef41Sopenharmony_ci    }
3371cb0ef41Sopenharmony_ci    ures_close(rb);
3381cb0ef41Sopenharmony_ci    ures_close(top);
3391cb0ef41Sopenharmony_ci
3401cb0ef41Sopenharmony_ci    if (U_SUCCESS(status)) {
3411cb0ef41Sopenharmony_ci        U_ASSERT(canonicalID != nullptr);  // canocanilD must be non-nullptr here
3421cb0ef41Sopenharmony_ci
3431cb0ef41Sopenharmony_ci        // Put the resolved canonical ID to the cache
3441cb0ef41Sopenharmony_ci        umtx_lock(&gZoneMetaLock);
3451cb0ef41Sopenharmony_ci        {
3461cb0ef41Sopenharmony_ci            const char16_t* idInCache = (const char16_t *)uhash_get(gCanonicalIDCache, utzid);
3471cb0ef41Sopenharmony_ci            if (idInCache == nullptr) {
3481cb0ef41Sopenharmony_ci                const char16_t* key = ZoneMeta::findTimeZoneID(tzid);
3491cb0ef41Sopenharmony_ci                U_ASSERT(key != nullptr);
3501cb0ef41Sopenharmony_ci                if (key != nullptr) {
3511cb0ef41Sopenharmony_ci                    idInCache = (const char16_t *)uhash_put(gCanonicalIDCache, (void *)key, (void *)canonicalID, &status);
3521cb0ef41Sopenharmony_ci                    U_ASSERT(idInCache == nullptr);
3531cb0ef41Sopenharmony_ci                }
3541cb0ef41Sopenharmony_ci            }
3551cb0ef41Sopenharmony_ci            if (U_SUCCESS(status) && isInputCanonical) {
3561cb0ef41Sopenharmony_ci                // Also put canonical ID itself into the cache if not exist
3571cb0ef41Sopenharmony_ci                const char16_t *canonicalInCache = (const char16_t*)uhash_get(gCanonicalIDCache, canonicalID);
3581cb0ef41Sopenharmony_ci                if (canonicalInCache == nullptr) {
3591cb0ef41Sopenharmony_ci                    canonicalInCache = (const char16_t *)uhash_put(gCanonicalIDCache, (void *)canonicalID, (void *)canonicalID, &status);
3601cb0ef41Sopenharmony_ci                    U_ASSERT(canonicalInCache == nullptr);
3611cb0ef41Sopenharmony_ci                }
3621cb0ef41Sopenharmony_ci            }
3631cb0ef41Sopenharmony_ci        }
3641cb0ef41Sopenharmony_ci        umtx_unlock(&gZoneMetaLock);
3651cb0ef41Sopenharmony_ci    }
3661cb0ef41Sopenharmony_ci
3671cb0ef41Sopenharmony_ci    return canonicalID;
3681cb0ef41Sopenharmony_ci}
3691cb0ef41Sopenharmony_ci
3701cb0ef41Sopenharmony_ciUnicodeString& U_EXPORT2
3711cb0ef41Sopenharmony_ciZoneMeta::getCanonicalCLDRID(const UnicodeString &tzid, UnicodeString &systemID, UErrorCode& status) {
3721cb0ef41Sopenharmony_ci    const char16_t *canonicalID = getCanonicalCLDRID(tzid, status);
3731cb0ef41Sopenharmony_ci    if (U_FAILURE(status) || canonicalID == nullptr) {
3741cb0ef41Sopenharmony_ci        systemID.setToBogus();
3751cb0ef41Sopenharmony_ci        return systemID;
3761cb0ef41Sopenharmony_ci    }
3771cb0ef41Sopenharmony_ci    systemID.setTo(true, canonicalID, -1);
3781cb0ef41Sopenharmony_ci    return systemID;
3791cb0ef41Sopenharmony_ci}
3801cb0ef41Sopenharmony_ci
3811cb0ef41Sopenharmony_ciconst char16_t* U_EXPORT2
3821cb0ef41Sopenharmony_ciZoneMeta::getCanonicalCLDRID(const TimeZone& tz) {
3831cb0ef41Sopenharmony_ci    if (dynamic_cast<const OlsonTimeZone *>(&tz) != nullptr) {
3841cb0ef41Sopenharmony_ci        // short cut for OlsonTimeZone
3851cb0ef41Sopenharmony_ci        const OlsonTimeZone *otz = (const OlsonTimeZone*)&tz;
3861cb0ef41Sopenharmony_ci        return otz->getCanonicalID();
3871cb0ef41Sopenharmony_ci    }
3881cb0ef41Sopenharmony_ci    UErrorCode status = U_ZERO_ERROR;
3891cb0ef41Sopenharmony_ci    UnicodeString tzID;
3901cb0ef41Sopenharmony_ci    return getCanonicalCLDRID(tz.getID(tzID), status);
3911cb0ef41Sopenharmony_ci}
3921cb0ef41Sopenharmony_ci
3931cb0ef41Sopenharmony_ciUnicodeString& U_EXPORT2
3941cb0ef41Sopenharmony_ciZoneMeta::getIanaID(const UnicodeString& tzid, UnicodeString& ianaID, UErrorCode& status) {
3951cb0ef41Sopenharmony_ci    // First, get CLDR canonical ID
3961cb0ef41Sopenharmony_ci    const char16_t *canonicalID = getCanonicalCLDRID(tzid, status);
3971cb0ef41Sopenharmony_ci    if (U_FAILURE(status) || canonicalID == nullptr) {
3981cb0ef41Sopenharmony_ci        ianaID.setToBogus();
3991cb0ef41Sopenharmony_ci        return ianaID;
4001cb0ef41Sopenharmony_ci    }
4011cb0ef41Sopenharmony_ci    // Find IANA mapping if any.
4021cb0ef41Sopenharmony_ci    UErrorCode tmpStatus = U_ZERO_ERROR;
4031cb0ef41Sopenharmony_ci    UnicodeString tmpKey(canonicalID);
4041cb0ef41Sopenharmony_ci    tmpKey.findAndReplace(UnicodeString("/"), UnicodeString(":"));
4051cb0ef41Sopenharmony_ci    char keyBuf[ZID_KEY_MAX + 1];
4061cb0ef41Sopenharmony_ci    /* int32_t keyLen = */ tmpKey.extract(0, tmpKey.length(), keyBuf, sizeof(keyBuf), US_INV);
4071cb0ef41Sopenharmony_ci
4081cb0ef41Sopenharmony_ci    StackUResourceBundle r;
4091cb0ef41Sopenharmony_ci    ures_openDirectFillIn(r.getAlias(), nullptr, gKeyTypeData, &tmpStatus);
4101cb0ef41Sopenharmony_ci    ures_getByKey(r.getAlias(), gIanaMapTag, r.getAlias(), &tmpStatus);
4111cb0ef41Sopenharmony_ci    ures_getByKey(r.getAlias(), gTimezoneTag, r.getAlias(), &tmpStatus);
4121cb0ef41Sopenharmony_ci    int32_t tmpLen = 0;
4131cb0ef41Sopenharmony_ci    const char16_t* tmpIana = ures_getStringByKey(r.getAlias(), keyBuf, &tmpLen, &tmpStatus);
4141cb0ef41Sopenharmony_ci    if (U_SUCCESS(tmpStatus)) {
4151cb0ef41Sopenharmony_ci        ianaID.setTo(true, tmpIana, -1);
4161cb0ef41Sopenharmony_ci    } else {
4171cb0ef41Sopenharmony_ci        ianaID.setTo(true, canonicalID, -1);
4181cb0ef41Sopenharmony_ci    }
4191cb0ef41Sopenharmony_ci    return ianaID;
4201cb0ef41Sopenharmony_ci}
4211cb0ef41Sopenharmony_ci
4221cb0ef41Sopenharmony_cistatic void U_CALLCONV countryInfoVectorsInit(UErrorCode &status) {
4231cb0ef41Sopenharmony_ci    // Create empty vectors
4241cb0ef41Sopenharmony_ci    // No deleters for these UVectors, it's a reference to a resource bundle string.
4251cb0ef41Sopenharmony_ci    gSingleZoneCountries = new UVector(nullptr, uhash_compareUChars, status);
4261cb0ef41Sopenharmony_ci    if (gSingleZoneCountries == nullptr) {
4271cb0ef41Sopenharmony_ci        status = U_MEMORY_ALLOCATION_ERROR;
4281cb0ef41Sopenharmony_ci    }
4291cb0ef41Sopenharmony_ci    gMultiZonesCountries = new UVector(nullptr, uhash_compareUChars, status);
4301cb0ef41Sopenharmony_ci    if (gMultiZonesCountries == nullptr) {
4311cb0ef41Sopenharmony_ci        status = U_MEMORY_ALLOCATION_ERROR;
4321cb0ef41Sopenharmony_ci    }
4331cb0ef41Sopenharmony_ci
4341cb0ef41Sopenharmony_ci    if (U_FAILURE(status)) {
4351cb0ef41Sopenharmony_ci        delete gSingleZoneCountries;
4361cb0ef41Sopenharmony_ci        delete gMultiZonesCountries;
4371cb0ef41Sopenharmony_ci        gSingleZoneCountries = nullptr;
4381cb0ef41Sopenharmony_ci        gMultiZonesCountries  = nullptr;
4391cb0ef41Sopenharmony_ci    }
4401cb0ef41Sopenharmony_ci    ucln_i18n_registerCleanup(UCLN_I18N_ZONEMETA, zoneMeta_cleanup);
4411cb0ef41Sopenharmony_ci}
4421cb0ef41Sopenharmony_ci
4431cb0ef41Sopenharmony_ci
4441cb0ef41Sopenharmony_ciUnicodeString& U_EXPORT2
4451cb0ef41Sopenharmony_ciZoneMeta::getCanonicalCountry(const UnicodeString &tzid, UnicodeString &country, UBool *isPrimary /* = nullptr */) {
4461cb0ef41Sopenharmony_ci    if (isPrimary != nullptr) {
4471cb0ef41Sopenharmony_ci        *isPrimary = false;
4481cb0ef41Sopenharmony_ci    }
4491cb0ef41Sopenharmony_ci
4501cb0ef41Sopenharmony_ci    const char16_t *region = TimeZone::getRegion(tzid);
4511cb0ef41Sopenharmony_ci    if (region != nullptr && u_strcmp(gWorld, region) != 0) {
4521cb0ef41Sopenharmony_ci        country.setTo(region, -1);
4531cb0ef41Sopenharmony_ci    } else {
4541cb0ef41Sopenharmony_ci        country.setToBogus();
4551cb0ef41Sopenharmony_ci        return country;
4561cb0ef41Sopenharmony_ci    }
4571cb0ef41Sopenharmony_ci
4581cb0ef41Sopenharmony_ci    if (isPrimary != nullptr) {
4591cb0ef41Sopenharmony_ci        char regionBuf[] = {0, 0, 0};
4601cb0ef41Sopenharmony_ci
4611cb0ef41Sopenharmony_ci        // Checking the cached results
4621cb0ef41Sopenharmony_ci        UErrorCode status = U_ZERO_ERROR;
4631cb0ef41Sopenharmony_ci        umtx_initOnce(gCountryInfoVectorsInitOnce, &countryInfoVectorsInit, status);
4641cb0ef41Sopenharmony_ci        if (U_FAILURE(status)) {
4651cb0ef41Sopenharmony_ci            return country;
4661cb0ef41Sopenharmony_ci        }
4671cb0ef41Sopenharmony_ci
4681cb0ef41Sopenharmony_ci        // Check if it was already cached
4691cb0ef41Sopenharmony_ci        UBool cached = false;
4701cb0ef41Sopenharmony_ci        UBool singleZone = false;
4711cb0ef41Sopenharmony_ci        umtx_lock(&gZoneMetaLock);
4721cb0ef41Sopenharmony_ci        {
4731cb0ef41Sopenharmony_ci            singleZone = cached = gSingleZoneCountries->contains((void*)region);
4741cb0ef41Sopenharmony_ci            if (!cached) {
4751cb0ef41Sopenharmony_ci                cached = gMultiZonesCountries->contains((void*)region);
4761cb0ef41Sopenharmony_ci            }
4771cb0ef41Sopenharmony_ci        }
4781cb0ef41Sopenharmony_ci        umtx_unlock(&gZoneMetaLock);
4791cb0ef41Sopenharmony_ci
4801cb0ef41Sopenharmony_ci        if (!cached) {
4811cb0ef41Sopenharmony_ci            // We need to go through all zones associated with the region.
4821cb0ef41Sopenharmony_ci            // This is relatively heavy operation.
4831cb0ef41Sopenharmony_ci
4841cb0ef41Sopenharmony_ci            U_ASSERT(u_strlen(region) == 2);
4851cb0ef41Sopenharmony_ci
4861cb0ef41Sopenharmony_ci            u_UCharsToChars(region, regionBuf, 2);
4871cb0ef41Sopenharmony_ci
4881cb0ef41Sopenharmony_ci            StringEnumeration *ids = TimeZone::createTimeZoneIDEnumeration(UCAL_ZONE_TYPE_CANONICAL_LOCATION, regionBuf, nullptr, status);
4891cb0ef41Sopenharmony_ci            int32_t idsLen = ids->count(status);
4901cb0ef41Sopenharmony_ci            if (U_SUCCESS(status) && idsLen == 1) {
4911cb0ef41Sopenharmony_ci                // only the single zone is available for the region
4921cb0ef41Sopenharmony_ci                singleZone = true;
4931cb0ef41Sopenharmony_ci            }
4941cb0ef41Sopenharmony_ci            delete ids;
4951cb0ef41Sopenharmony_ci
4961cb0ef41Sopenharmony_ci            // Cache the result
4971cb0ef41Sopenharmony_ci            umtx_lock(&gZoneMetaLock);
4981cb0ef41Sopenharmony_ci            {
4991cb0ef41Sopenharmony_ci                UErrorCode ec = U_ZERO_ERROR;
5001cb0ef41Sopenharmony_ci                if (singleZone) {
5011cb0ef41Sopenharmony_ci                    if (!gSingleZoneCountries->contains((void*)region)) {
5021cb0ef41Sopenharmony_ci                        gSingleZoneCountries->addElement((void*)region, ec);
5031cb0ef41Sopenharmony_ci                    }
5041cb0ef41Sopenharmony_ci                } else {
5051cb0ef41Sopenharmony_ci                    if (!gMultiZonesCountries->contains((void*)region)) {
5061cb0ef41Sopenharmony_ci                        gMultiZonesCountries->addElement((void*)region, ec);
5071cb0ef41Sopenharmony_ci                    }
5081cb0ef41Sopenharmony_ci                }
5091cb0ef41Sopenharmony_ci            }
5101cb0ef41Sopenharmony_ci            umtx_unlock(&gZoneMetaLock);
5111cb0ef41Sopenharmony_ci        }
5121cb0ef41Sopenharmony_ci
5131cb0ef41Sopenharmony_ci        if (singleZone) {
5141cb0ef41Sopenharmony_ci            *isPrimary = true;
5151cb0ef41Sopenharmony_ci        } else {
5161cb0ef41Sopenharmony_ci            // Note: We may cache the primary zone map in future.
5171cb0ef41Sopenharmony_ci
5181cb0ef41Sopenharmony_ci            // Even a country has multiple zones, one of them might be
5191cb0ef41Sopenharmony_ci            // dominant and treated as a primary zone
5201cb0ef41Sopenharmony_ci            int32_t idLen = 0;
5211cb0ef41Sopenharmony_ci            if (regionBuf[0] == 0) {
5221cb0ef41Sopenharmony_ci                u_UCharsToChars(region, regionBuf, 2);
5231cb0ef41Sopenharmony_ci            }
5241cb0ef41Sopenharmony_ci
5251cb0ef41Sopenharmony_ci            UResourceBundle *rb = ures_openDirect(nullptr, gMetaZones, &status);
5261cb0ef41Sopenharmony_ci            ures_getByKey(rb, gPrimaryZonesTag, rb, &status);
5271cb0ef41Sopenharmony_ci            const char16_t *primaryZone = ures_getStringByKey(rb, regionBuf, &idLen, &status);
5281cb0ef41Sopenharmony_ci            if (U_SUCCESS(status)) {
5291cb0ef41Sopenharmony_ci                if (tzid.compare(primaryZone, idLen) == 0) {
5301cb0ef41Sopenharmony_ci                    *isPrimary = true;
5311cb0ef41Sopenharmony_ci                } else {
5321cb0ef41Sopenharmony_ci                    // The given ID might not be a canonical ID
5331cb0ef41Sopenharmony_ci                    UnicodeString canonicalID;
5341cb0ef41Sopenharmony_ci                    TimeZone::getCanonicalID(tzid, canonicalID, status);
5351cb0ef41Sopenharmony_ci                    if (U_SUCCESS(status) && canonicalID.compare(primaryZone, idLen) == 0) {
5361cb0ef41Sopenharmony_ci                        *isPrimary = true;
5371cb0ef41Sopenharmony_ci                    }
5381cb0ef41Sopenharmony_ci                }
5391cb0ef41Sopenharmony_ci            }
5401cb0ef41Sopenharmony_ci            ures_close(rb);
5411cb0ef41Sopenharmony_ci        }
5421cb0ef41Sopenharmony_ci    }
5431cb0ef41Sopenharmony_ci
5441cb0ef41Sopenharmony_ci    return country;
5451cb0ef41Sopenharmony_ci}
5461cb0ef41Sopenharmony_ci
5471cb0ef41Sopenharmony_ciUnicodeString& U_EXPORT2
5481cb0ef41Sopenharmony_ciZoneMeta::getMetazoneID(const UnicodeString &tzid, UDate date, UnicodeString &result) {
5491cb0ef41Sopenharmony_ci    UBool isSet = false;
5501cb0ef41Sopenharmony_ci    const UVector *mappings = getMetazoneMappings(tzid);
5511cb0ef41Sopenharmony_ci    if (mappings != nullptr) {
5521cb0ef41Sopenharmony_ci        for (int32_t i = 0; i < mappings->size(); i++) {
5531cb0ef41Sopenharmony_ci            OlsonToMetaMappingEntry *mzm = (OlsonToMetaMappingEntry*)mappings->elementAt(i);
5541cb0ef41Sopenharmony_ci            if (mzm->from <= date && mzm->to > date) {
5551cb0ef41Sopenharmony_ci                result.setTo(mzm->mzid, -1);
5561cb0ef41Sopenharmony_ci                isSet = true;
5571cb0ef41Sopenharmony_ci                break;
5581cb0ef41Sopenharmony_ci            }
5591cb0ef41Sopenharmony_ci        }
5601cb0ef41Sopenharmony_ci    }
5611cb0ef41Sopenharmony_ci    if (!isSet) {
5621cb0ef41Sopenharmony_ci        result.setToBogus();
5631cb0ef41Sopenharmony_ci    }
5641cb0ef41Sopenharmony_ci    return result;
5651cb0ef41Sopenharmony_ci}
5661cb0ef41Sopenharmony_ci
5671cb0ef41Sopenharmony_cistatic void U_CALLCONV olsonToMetaInit(UErrorCode &status) {
5681cb0ef41Sopenharmony_ci    U_ASSERT(gOlsonToMeta == nullptr);
5691cb0ef41Sopenharmony_ci    ucln_i18n_registerCleanup(UCLN_I18N_ZONEMETA, zoneMeta_cleanup);
5701cb0ef41Sopenharmony_ci    gOlsonToMeta = uhash_open(uhash_hashUChars, uhash_compareUChars, nullptr, &status);
5711cb0ef41Sopenharmony_ci    if (U_FAILURE(status)) {
5721cb0ef41Sopenharmony_ci        gOlsonToMeta = nullptr;
5731cb0ef41Sopenharmony_ci    } else {
5741cb0ef41Sopenharmony_ci        uhash_setKeyDeleter(gOlsonToMeta, deleteUCharString);
5751cb0ef41Sopenharmony_ci        uhash_setValueDeleter(gOlsonToMeta, uprv_deleteUObject);
5761cb0ef41Sopenharmony_ci    }
5771cb0ef41Sopenharmony_ci}
5781cb0ef41Sopenharmony_ci
5791cb0ef41Sopenharmony_ci
5801cb0ef41Sopenharmony_ciconst UVector* U_EXPORT2
5811cb0ef41Sopenharmony_ciZoneMeta::getMetazoneMappings(const UnicodeString &tzid) {
5821cb0ef41Sopenharmony_ci    UErrorCode status = U_ZERO_ERROR;
5831cb0ef41Sopenharmony_ci    char16_t tzidUChars[ZID_KEY_MAX + 1];
5841cb0ef41Sopenharmony_ci    tzid.extract(tzidUChars, ZID_KEY_MAX + 1, status);
5851cb0ef41Sopenharmony_ci    if (U_FAILURE(status) || status == U_STRING_NOT_TERMINATED_WARNING) {
5861cb0ef41Sopenharmony_ci        return nullptr;
5871cb0ef41Sopenharmony_ci    }
5881cb0ef41Sopenharmony_ci
5891cb0ef41Sopenharmony_ci    umtx_initOnce(gOlsonToMetaInitOnce, &olsonToMetaInit, status);
5901cb0ef41Sopenharmony_ci    if (U_FAILURE(status)) {
5911cb0ef41Sopenharmony_ci        return nullptr;
5921cb0ef41Sopenharmony_ci    }
5931cb0ef41Sopenharmony_ci
5941cb0ef41Sopenharmony_ci    // get the mapping from cache
5951cb0ef41Sopenharmony_ci    const UVector *result = nullptr;
5961cb0ef41Sopenharmony_ci
5971cb0ef41Sopenharmony_ci    umtx_lock(&gZoneMetaLock);
5981cb0ef41Sopenharmony_ci    {
5991cb0ef41Sopenharmony_ci        result = (UVector*) uhash_get(gOlsonToMeta, tzidUChars);
6001cb0ef41Sopenharmony_ci    }
6011cb0ef41Sopenharmony_ci    umtx_unlock(&gZoneMetaLock);
6021cb0ef41Sopenharmony_ci
6031cb0ef41Sopenharmony_ci    if (result != nullptr) {
6041cb0ef41Sopenharmony_ci        return result;
6051cb0ef41Sopenharmony_ci    }
6061cb0ef41Sopenharmony_ci
6071cb0ef41Sopenharmony_ci    // miss the cache - create new one
6081cb0ef41Sopenharmony_ci    UVector *tmpResult = createMetazoneMappings(tzid);
6091cb0ef41Sopenharmony_ci    if (tmpResult == nullptr) {
6101cb0ef41Sopenharmony_ci        // not available
6111cb0ef41Sopenharmony_ci        return nullptr;
6121cb0ef41Sopenharmony_ci    }
6131cb0ef41Sopenharmony_ci
6141cb0ef41Sopenharmony_ci    // put the new one into the cache
6151cb0ef41Sopenharmony_ci    umtx_lock(&gZoneMetaLock);
6161cb0ef41Sopenharmony_ci    {
6171cb0ef41Sopenharmony_ci        // make sure it's already created
6181cb0ef41Sopenharmony_ci        result = (UVector*) uhash_get(gOlsonToMeta, tzidUChars);
6191cb0ef41Sopenharmony_ci        if (result == nullptr) {
6201cb0ef41Sopenharmony_ci            // add the one just created
6211cb0ef41Sopenharmony_ci            int32_t tzidLen = tzid.length() + 1;
6221cb0ef41Sopenharmony_ci            char16_t *key = (char16_t*)uprv_malloc(tzidLen * sizeof(char16_t));
6231cb0ef41Sopenharmony_ci            if (key == nullptr) {
6241cb0ef41Sopenharmony_ci                // memory allocation error..  just return nullptr
6251cb0ef41Sopenharmony_ci                result = nullptr;
6261cb0ef41Sopenharmony_ci                delete tmpResult;
6271cb0ef41Sopenharmony_ci            } else {
6281cb0ef41Sopenharmony_ci                tzid.extract(key, tzidLen, status);
6291cb0ef41Sopenharmony_ci                uhash_put(gOlsonToMeta, key, tmpResult, &status);
6301cb0ef41Sopenharmony_ci                if (U_FAILURE(status)) {
6311cb0ef41Sopenharmony_ci                    // delete the mapping
6321cb0ef41Sopenharmony_ci                    result = nullptr;
6331cb0ef41Sopenharmony_ci                    delete tmpResult;
6341cb0ef41Sopenharmony_ci                } else {
6351cb0ef41Sopenharmony_ci                    result = tmpResult;
6361cb0ef41Sopenharmony_ci                }
6371cb0ef41Sopenharmony_ci            }
6381cb0ef41Sopenharmony_ci        } else {
6391cb0ef41Sopenharmony_ci            // another thread already put the one
6401cb0ef41Sopenharmony_ci            delete tmpResult;
6411cb0ef41Sopenharmony_ci        }
6421cb0ef41Sopenharmony_ci    }
6431cb0ef41Sopenharmony_ci    umtx_unlock(&gZoneMetaLock);
6441cb0ef41Sopenharmony_ci
6451cb0ef41Sopenharmony_ci    return result;
6461cb0ef41Sopenharmony_ci}
6471cb0ef41Sopenharmony_ci
6481cb0ef41Sopenharmony_ciUVector*
6491cb0ef41Sopenharmony_ciZoneMeta::createMetazoneMappings(const UnicodeString &tzid) {
6501cb0ef41Sopenharmony_ci    LocalPointer <UVector> mzMappings;
6511cb0ef41Sopenharmony_ci    UErrorCode status = U_ZERO_ERROR;
6521cb0ef41Sopenharmony_ci
6531cb0ef41Sopenharmony_ci    UnicodeString canonicalID;
6541cb0ef41Sopenharmony_ci    UResourceBundle *rb = ures_openDirect(nullptr, gMetaZones, &status);
6551cb0ef41Sopenharmony_ci    ures_getByKey(rb, gMetazoneInfo, rb, &status);
6561cb0ef41Sopenharmony_ci    getCanonicalCLDRID(tzid, canonicalID, status);
6571cb0ef41Sopenharmony_ci
6581cb0ef41Sopenharmony_ci    if (U_SUCCESS(status)) {
6591cb0ef41Sopenharmony_ci        char tzKey[ZID_KEY_MAX + 1];
6601cb0ef41Sopenharmony_ci        int32_t tzKeyLen = canonicalID.extract(0, canonicalID.length(), tzKey, sizeof(tzKey), US_INV);
6611cb0ef41Sopenharmony_ci        tzKey[tzKeyLen] = 0;
6621cb0ef41Sopenharmony_ci
6631cb0ef41Sopenharmony_ci        // tzid keys are using ':' as separators
6641cb0ef41Sopenharmony_ci        char *p = tzKey;
6651cb0ef41Sopenharmony_ci        while (*p) {
6661cb0ef41Sopenharmony_ci            if (*p == '/') {
6671cb0ef41Sopenharmony_ci                *p = ':';
6681cb0ef41Sopenharmony_ci            }
6691cb0ef41Sopenharmony_ci            p++;
6701cb0ef41Sopenharmony_ci        }
6711cb0ef41Sopenharmony_ci
6721cb0ef41Sopenharmony_ci        ures_getByKey(rb, tzKey, rb, &status);
6731cb0ef41Sopenharmony_ci
6741cb0ef41Sopenharmony_ci        if (U_SUCCESS(status)) {
6751cb0ef41Sopenharmony_ci            UResourceBundle *mz = nullptr;
6761cb0ef41Sopenharmony_ci            while (ures_hasNext(rb)) {
6771cb0ef41Sopenharmony_ci                mz = ures_getNextResource(rb, mz, &status);
6781cb0ef41Sopenharmony_ci
6791cb0ef41Sopenharmony_ci                const char16_t *mz_name = ures_getStringByIndex(mz, 0, nullptr, &status);
6801cb0ef41Sopenharmony_ci                const char16_t *mz_from = gDefaultFrom;
6811cb0ef41Sopenharmony_ci                const char16_t *mz_to = gDefaultTo;
6821cb0ef41Sopenharmony_ci
6831cb0ef41Sopenharmony_ci                if (ures_getSize(mz) == 3) {
6841cb0ef41Sopenharmony_ci                    mz_from = ures_getStringByIndex(mz, 1, nullptr, &status);
6851cb0ef41Sopenharmony_ci                    mz_to   = ures_getStringByIndex(mz, 2, nullptr, &status);
6861cb0ef41Sopenharmony_ci                }
6871cb0ef41Sopenharmony_ci
6881cb0ef41Sopenharmony_ci                if(U_FAILURE(status)){
6891cb0ef41Sopenharmony_ci                    status = U_ZERO_ERROR;
6901cb0ef41Sopenharmony_ci                    continue;
6911cb0ef41Sopenharmony_ci                }
6921cb0ef41Sopenharmony_ci                // We do not want to use SimpleDateformat to parse boundary dates,
6931cb0ef41Sopenharmony_ci                // because this code could be triggered by the initialization code
6941cb0ef41Sopenharmony_ci                // used by SimpleDateFormat.
6951cb0ef41Sopenharmony_ci                UDate from = parseDate(mz_from, status);
6961cb0ef41Sopenharmony_ci                UDate to = parseDate(mz_to, status);
6971cb0ef41Sopenharmony_ci                if (U_FAILURE(status)) {
6981cb0ef41Sopenharmony_ci                    status = U_ZERO_ERROR;
6991cb0ef41Sopenharmony_ci                    continue;
7001cb0ef41Sopenharmony_ci                }
7011cb0ef41Sopenharmony_ci
7021cb0ef41Sopenharmony_ci                LocalPointer<OlsonToMetaMappingEntry> entry(new OlsonToMetaMappingEntry, status);
7031cb0ef41Sopenharmony_ci                if (U_FAILURE(status)) {
7041cb0ef41Sopenharmony_ci                    break;
7051cb0ef41Sopenharmony_ci                }
7061cb0ef41Sopenharmony_ci                entry->mzid = mz_name;
7071cb0ef41Sopenharmony_ci                entry->from = from;
7081cb0ef41Sopenharmony_ci                entry->to = to;
7091cb0ef41Sopenharmony_ci
7101cb0ef41Sopenharmony_ci                if (mzMappings.isNull()) {
7111cb0ef41Sopenharmony_ci                    mzMappings.adoptInsteadAndCheckErrorCode(
7121cb0ef41Sopenharmony_ci                        new UVector(deleteOlsonToMetaMappingEntry, nullptr, status), status);
7131cb0ef41Sopenharmony_ci                    if (U_FAILURE(status)) {
7141cb0ef41Sopenharmony_ci                        break;
7151cb0ef41Sopenharmony_ci                    }
7161cb0ef41Sopenharmony_ci                }
7171cb0ef41Sopenharmony_ci
7181cb0ef41Sopenharmony_ci                mzMappings->adoptElement(entry.orphan(), status);
7191cb0ef41Sopenharmony_ci                if (U_FAILURE(status)) {
7201cb0ef41Sopenharmony_ci                    break;
7211cb0ef41Sopenharmony_ci                }
7221cb0ef41Sopenharmony_ci            }
7231cb0ef41Sopenharmony_ci            ures_close(mz);
7241cb0ef41Sopenharmony_ci        }
7251cb0ef41Sopenharmony_ci    }
7261cb0ef41Sopenharmony_ci    ures_close(rb);
7271cb0ef41Sopenharmony_ci    return U_SUCCESS(status) ? mzMappings.orphan() : nullptr;
7281cb0ef41Sopenharmony_ci}
7291cb0ef41Sopenharmony_ci
7301cb0ef41Sopenharmony_ciUnicodeString& U_EXPORT2
7311cb0ef41Sopenharmony_ciZoneMeta::getZoneIdByMetazone(const UnicodeString &mzid, const UnicodeString &region, UnicodeString &result) {
7321cb0ef41Sopenharmony_ci    UErrorCode status = U_ZERO_ERROR;
7331cb0ef41Sopenharmony_ci    const char16_t *tzid = nullptr;
7341cb0ef41Sopenharmony_ci    int32_t tzidLen = 0;
7351cb0ef41Sopenharmony_ci    char keyBuf[ZID_KEY_MAX + 1];
7361cb0ef41Sopenharmony_ci    int32_t keyLen = 0;
7371cb0ef41Sopenharmony_ci
7381cb0ef41Sopenharmony_ci    if (mzid.isBogus() || mzid.length() > ZID_KEY_MAX) {
7391cb0ef41Sopenharmony_ci        result.setToBogus();
7401cb0ef41Sopenharmony_ci        return result;
7411cb0ef41Sopenharmony_ci    }
7421cb0ef41Sopenharmony_ci
7431cb0ef41Sopenharmony_ci    keyLen = mzid.extract(0, mzid.length(), keyBuf, ZID_KEY_MAX + 1, US_INV);
7441cb0ef41Sopenharmony_ci    keyBuf[keyLen] = 0;
7451cb0ef41Sopenharmony_ci
7461cb0ef41Sopenharmony_ci    UResourceBundle *rb = ures_openDirect(nullptr, gMetaZones, &status);
7471cb0ef41Sopenharmony_ci    ures_getByKey(rb, gMapTimezonesTag, rb, &status);
7481cb0ef41Sopenharmony_ci    ures_getByKey(rb, keyBuf, rb, &status);
7491cb0ef41Sopenharmony_ci
7501cb0ef41Sopenharmony_ci    if (U_SUCCESS(status)) {
7511cb0ef41Sopenharmony_ci        // check region mapping
7521cb0ef41Sopenharmony_ci        if (region.length() == 2 || region.length() == 3) {
7531cb0ef41Sopenharmony_ci            keyLen = region.extract(0, region.length(), keyBuf, ZID_KEY_MAX + 1, US_INV);
7541cb0ef41Sopenharmony_ci            keyBuf[keyLen] = 0;
7551cb0ef41Sopenharmony_ci            tzid = ures_getStringByKey(rb, keyBuf, &tzidLen, &status);
7561cb0ef41Sopenharmony_ci            if (status == U_MISSING_RESOURCE_ERROR) {
7571cb0ef41Sopenharmony_ci                status = U_ZERO_ERROR;
7581cb0ef41Sopenharmony_ci            }
7591cb0ef41Sopenharmony_ci        }
7601cb0ef41Sopenharmony_ci        if (U_SUCCESS(status) && tzid == nullptr) {
7611cb0ef41Sopenharmony_ci            // try "001"
7621cb0ef41Sopenharmony_ci            tzid = ures_getStringByKey(rb, gWorldTag, &tzidLen, &status);
7631cb0ef41Sopenharmony_ci        }
7641cb0ef41Sopenharmony_ci    }
7651cb0ef41Sopenharmony_ci    ures_close(rb);
7661cb0ef41Sopenharmony_ci
7671cb0ef41Sopenharmony_ci    if (tzid == nullptr) {
7681cb0ef41Sopenharmony_ci        result.setToBogus();
7691cb0ef41Sopenharmony_ci    } else {
7701cb0ef41Sopenharmony_ci        result.setTo(tzid, tzidLen);
7711cb0ef41Sopenharmony_ci    }
7721cb0ef41Sopenharmony_ci
7731cb0ef41Sopenharmony_ci    return result;
7741cb0ef41Sopenharmony_ci}
7751cb0ef41Sopenharmony_ci
7761cb0ef41Sopenharmony_cistatic void U_CALLCONV initAvailableMetaZoneIDs () {
7771cb0ef41Sopenharmony_ci    U_ASSERT(gMetaZoneIDs == nullptr);
7781cb0ef41Sopenharmony_ci    U_ASSERT(gMetaZoneIDTable == nullptr);
7791cb0ef41Sopenharmony_ci    ucln_i18n_registerCleanup(UCLN_I18N_ZONEMETA, zoneMeta_cleanup);
7801cb0ef41Sopenharmony_ci
7811cb0ef41Sopenharmony_ci    UErrorCode status = U_ZERO_ERROR;
7821cb0ef41Sopenharmony_ci    gMetaZoneIDTable = uhash_open(uhash_hashUnicodeString, uhash_compareUnicodeString, nullptr, &status);
7831cb0ef41Sopenharmony_ci    if (U_FAILURE(status) || gMetaZoneIDTable == nullptr) {
7841cb0ef41Sopenharmony_ci        gMetaZoneIDTable = nullptr;
7851cb0ef41Sopenharmony_ci        return;
7861cb0ef41Sopenharmony_ci    }
7871cb0ef41Sopenharmony_ci    uhash_setKeyDeleter(gMetaZoneIDTable, uprv_deleteUObject);
7881cb0ef41Sopenharmony_ci    // No valueDeleter, because the vector maintain the value objects
7891cb0ef41Sopenharmony_ci    gMetaZoneIDs = new UVector(nullptr, uhash_compareUChars, status);
7901cb0ef41Sopenharmony_ci    if (U_FAILURE(status) || gMetaZoneIDs == nullptr) {
7911cb0ef41Sopenharmony_ci        delete gMetaZoneIDs;
7921cb0ef41Sopenharmony_ci        gMetaZoneIDs = nullptr;
7931cb0ef41Sopenharmony_ci        uhash_close(gMetaZoneIDTable);
7941cb0ef41Sopenharmony_ci        gMetaZoneIDTable = nullptr;
7951cb0ef41Sopenharmony_ci        return;
7961cb0ef41Sopenharmony_ci    }
7971cb0ef41Sopenharmony_ci    gMetaZoneIDs->setDeleter(uprv_free);
7981cb0ef41Sopenharmony_ci
7991cb0ef41Sopenharmony_ci    UResourceBundle *rb = ures_openDirect(nullptr, gMetaZones, &status);
8001cb0ef41Sopenharmony_ci    UResourceBundle *bundle = ures_getByKey(rb, gMapTimezonesTag, nullptr, &status);
8011cb0ef41Sopenharmony_ci    StackUResourceBundle res;
8021cb0ef41Sopenharmony_ci    while (U_SUCCESS(status) && ures_hasNext(bundle)) {
8031cb0ef41Sopenharmony_ci        ures_getNextResource(bundle, res.getAlias(), &status);
8041cb0ef41Sopenharmony_ci        if (U_FAILURE(status)) {
8051cb0ef41Sopenharmony_ci            break;
8061cb0ef41Sopenharmony_ci        }
8071cb0ef41Sopenharmony_ci        const char *mzID = ures_getKey(res.getAlias());
8081cb0ef41Sopenharmony_ci        int32_t len = static_cast<int32_t>(uprv_strlen(mzID));
8091cb0ef41Sopenharmony_ci        LocalMemory<char16_t> uMzID((char16_t*)uprv_malloc(sizeof(char16_t) * (len + 1)));
8101cb0ef41Sopenharmony_ci        if (uMzID.isNull()) {
8111cb0ef41Sopenharmony_ci            status = U_MEMORY_ALLOCATION_ERROR;
8121cb0ef41Sopenharmony_ci            break;
8131cb0ef41Sopenharmony_ci        }
8141cb0ef41Sopenharmony_ci        u_charsToUChars(mzID, uMzID.getAlias(), len);
8151cb0ef41Sopenharmony_ci        uMzID[len] = 0;
8161cb0ef41Sopenharmony_ci        LocalPointer<UnicodeString> usMzID(new UnicodeString(uMzID.getAlias()), status);
8171cb0ef41Sopenharmony_ci        if (U_FAILURE(status)) {
8181cb0ef41Sopenharmony_ci            break;
8191cb0ef41Sopenharmony_ci        }
8201cb0ef41Sopenharmony_ci        if (uhash_get(gMetaZoneIDTable, usMzID.getAlias()) == nullptr) {
8211cb0ef41Sopenharmony_ci            // Note: gMetaZoneIDTable adopts its keys, but not its values.
8221cb0ef41Sopenharmony_ci            //       gMetaZoneIDs adopts its values.
8231cb0ef41Sopenharmony_ci            uhash_put(gMetaZoneIDTable, usMzID.orphan(), uMzID.getAlias(), &status);
8241cb0ef41Sopenharmony_ci            gMetaZoneIDs->adoptElement(uMzID.orphan(), status);
8251cb0ef41Sopenharmony_ci        }
8261cb0ef41Sopenharmony_ci    }
8271cb0ef41Sopenharmony_ci    ures_close(bundle);
8281cb0ef41Sopenharmony_ci    ures_close(rb);
8291cb0ef41Sopenharmony_ci
8301cb0ef41Sopenharmony_ci    if (U_FAILURE(status)) {
8311cb0ef41Sopenharmony_ci        uhash_close(gMetaZoneIDTable);
8321cb0ef41Sopenharmony_ci        delete gMetaZoneIDs;
8331cb0ef41Sopenharmony_ci        gMetaZoneIDTable = nullptr;
8341cb0ef41Sopenharmony_ci        gMetaZoneIDs = nullptr;
8351cb0ef41Sopenharmony_ci    }
8361cb0ef41Sopenharmony_ci}
8371cb0ef41Sopenharmony_ci
8381cb0ef41Sopenharmony_ciconst UVector*
8391cb0ef41Sopenharmony_ciZoneMeta::getAvailableMetazoneIDs() {
8401cb0ef41Sopenharmony_ci    umtx_initOnce(gMetaZoneIDsInitOnce, &initAvailableMetaZoneIDs);
8411cb0ef41Sopenharmony_ci    return gMetaZoneIDs;
8421cb0ef41Sopenharmony_ci}
8431cb0ef41Sopenharmony_ci
8441cb0ef41Sopenharmony_ciconst char16_t*
8451cb0ef41Sopenharmony_ciZoneMeta::findMetaZoneID(const UnicodeString& mzid) {
8461cb0ef41Sopenharmony_ci    umtx_initOnce(gMetaZoneIDsInitOnce, &initAvailableMetaZoneIDs);
8471cb0ef41Sopenharmony_ci    if (gMetaZoneIDTable == nullptr) {
8481cb0ef41Sopenharmony_ci        return nullptr;
8491cb0ef41Sopenharmony_ci    }
8501cb0ef41Sopenharmony_ci    return (const char16_t*)uhash_get(gMetaZoneIDTable, &mzid);
8511cb0ef41Sopenharmony_ci}
8521cb0ef41Sopenharmony_ci
8531cb0ef41Sopenharmony_ciconst char16_t*
8541cb0ef41Sopenharmony_ciZoneMeta::findTimeZoneID(const UnicodeString& tzid) {
8551cb0ef41Sopenharmony_ci    return TimeZone::findID(tzid);
8561cb0ef41Sopenharmony_ci}
8571cb0ef41Sopenharmony_ci
8581cb0ef41Sopenharmony_ci
8591cb0ef41Sopenharmony_ciTimeZone*
8601cb0ef41Sopenharmony_ciZoneMeta::createCustomTimeZone(int32_t offset) {
8611cb0ef41Sopenharmony_ci    UBool negative = false;
8621cb0ef41Sopenharmony_ci    int32_t tmp = offset;
8631cb0ef41Sopenharmony_ci    if (offset < 0) {
8641cb0ef41Sopenharmony_ci        negative = true;
8651cb0ef41Sopenharmony_ci        tmp = -offset;
8661cb0ef41Sopenharmony_ci    }
8671cb0ef41Sopenharmony_ci    uint8_t hour, min, sec;
8681cb0ef41Sopenharmony_ci
8691cb0ef41Sopenharmony_ci    tmp /= 1000;
8701cb0ef41Sopenharmony_ci    sec = static_cast<uint8_t>(tmp % 60);
8711cb0ef41Sopenharmony_ci    tmp /= 60;
8721cb0ef41Sopenharmony_ci    min = static_cast<uint8_t>(tmp % 60);
8731cb0ef41Sopenharmony_ci    hour = static_cast<uint8_t>(tmp / 60);
8741cb0ef41Sopenharmony_ci
8751cb0ef41Sopenharmony_ci    UnicodeString zid;
8761cb0ef41Sopenharmony_ci    formatCustomID(hour, min, sec, negative, zid);
8771cb0ef41Sopenharmony_ci    return new SimpleTimeZone(offset, zid);
8781cb0ef41Sopenharmony_ci}
8791cb0ef41Sopenharmony_ci
8801cb0ef41Sopenharmony_ciUnicodeString&
8811cb0ef41Sopenharmony_ciZoneMeta::formatCustomID(uint8_t hour, uint8_t min, uint8_t sec, UBool negative, UnicodeString& id) {
8821cb0ef41Sopenharmony_ci    // Create normalized time zone ID - GMT[+|-]HH:mm[:ss]
8831cb0ef41Sopenharmony_ci    id.setTo(gCustomTzPrefix, -1);
8841cb0ef41Sopenharmony_ci    if (hour != 0 || min != 0) {
8851cb0ef41Sopenharmony_ci        if (negative) {
8861cb0ef41Sopenharmony_ci          id.append((char16_t)0x2D);    // '-'
8871cb0ef41Sopenharmony_ci        } else {
8881cb0ef41Sopenharmony_ci          id.append((char16_t)0x2B);    // '+'
8891cb0ef41Sopenharmony_ci        }
8901cb0ef41Sopenharmony_ci        // Always use US-ASCII digits
8911cb0ef41Sopenharmony_ci        id.append((char16_t)(0x30 + (hour%100)/10));
8921cb0ef41Sopenharmony_ci        id.append((char16_t)(0x30 + (hour%10)));
8931cb0ef41Sopenharmony_ci        id.append((char16_t)0x3A);    // ':'
8941cb0ef41Sopenharmony_ci        id.append((char16_t)(0x30 + (min%100)/10));
8951cb0ef41Sopenharmony_ci        id.append((char16_t)(0x30 + (min%10)));
8961cb0ef41Sopenharmony_ci        if (sec != 0) {
8971cb0ef41Sopenharmony_ci          id.append((char16_t)0x3A);    // ':'
8981cb0ef41Sopenharmony_ci          id.append((char16_t)(0x30 + (sec%100)/10));
8991cb0ef41Sopenharmony_ci          id.append((char16_t)(0x30 + (sec%10)));
9001cb0ef41Sopenharmony_ci        }
9011cb0ef41Sopenharmony_ci    }
9021cb0ef41Sopenharmony_ci    return id;
9031cb0ef41Sopenharmony_ci}
9041cb0ef41Sopenharmony_ci
9051cb0ef41Sopenharmony_ciconst char16_t*
9061cb0ef41Sopenharmony_ciZoneMeta::getShortID(const TimeZone& tz) {
9071cb0ef41Sopenharmony_ci    const char16_t* canonicalID = nullptr;
9081cb0ef41Sopenharmony_ci    if (dynamic_cast<const OlsonTimeZone *>(&tz) != nullptr) {
9091cb0ef41Sopenharmony_ci        // short cut for OlsonTimeZone
9101cb0ef41Sopenharmony_ci        const OlsonTimeZone *otz = (const OlsonTimeZone*)&tz;
9111cb0ef41Sopenharmony_ci        canonicalID = otz->getCanonicalID();
9121cb0ef41Sopenharmony_ci    }
9131cb0ef41Sopenharmony_ci    if (canonicalID == nullptr) {
9141cb0ef41Sopenharmony_ci        return nullptr;
9151cb0ef41Sopenharmony_ci    }
9161cb0ef41Sopenharmony_ci    return getShortIDFromCanonical(canonicalID);
9171cb0ef41Sopenharmony_ci}
9181cb0ef41Sopenharmony_ci
9191cb0ef41Sopenharmony_ciconst char16_t*
9201cb0ef41Sopenharmony_ciZoneMeta::getShortID(const UnicodeString& id) {
9211cb0ef41Sopenharmony_ci    UErrorCode status = U_ZERO_ERROR;
9221cb0ef41Sopenharmony_ci    const char16_t* canonicalID = ZoneMeta::getCanonicalCLDRID(id, status);
9231cb0ef41Sopenharmony_ci    if (U_FAILURE(status) || canonicalID == nullptr) {
9241cb0ef41Sopenharmony_ci        return nullptr;
9251cb0ef41Sopenharmony_ci    }
9261cb0ef41Sopenharmony_ci    return ZoneMeta::getShortIDFromCanonical(canonicalID);
9271cb0ef41Sopenharmony_ci}
9281cb0ef41Sopenharmony_ci
9291cb0ef41Sopenharmony_ciconst char16_t*
9301cb0ef41Sopenharmony_ciZoneMeta::getShortIDFromCanonical(const char16_t* canonicalID) {
9311cb0ef41Sopenharmony_ci    const char16_t* shortID = nullptr;
9321cb0ef41Sopenharmony_ci    int32_t len = u_strlen(canonicalID);
9331cb0ef41Sopenharmony_ci    char tzidKey[ZID_KEY_MAX + 1];
9341cb0ef41Sopenharmony_ci
9351cb0ef41Sopenharmony_ci    u_UCharsToChars(canonicalID, tzidKey, len);
9361cb0ef41Sopenharmony_ci    tzidKey[len] = (char) 0; // Make sure it is null terminated.
9371cb0ef41Sopenharmony_ci
9381cb0ef41Sopenharmony_ci    // replace '/' with ':'
9391cb0ef41Sopenharmony_ci    char *p = tzidKey;
9401cb0ef41Sopenharmony_ci    while (*p++) {
9411cb0ef41Sopenharmony_ci        if (*p == '/') {
9421cb0ef41Sopenharmony_ci            *p = ':';
9431cb0ef41Sopenharmony_ci        }
9441cb0ef41Sopenharmony_ci    }
9451cb0ef41Sopenharmony_ci
9461cb0ef41Sopenharmony_ci    UErrorCode status = U_ZERO_ERROR;
9471cb0ef41Sopenharmony_ci    UResourceBundle *rb = ures_openDirect(nullptr, gKeyTypeData, &status);
9481cb0ef41Sopenharmony_ci    ures_getByKey(rb, gTypeMapTag, rb, &status);
9491cb0ef41Sopenharmony_ci    ures_getByKey(rb, gTimezoneTag, rb, &status);
9501cb0ef41Sopenharmony_ci    shortID = ures_getStringByKey(rb, tzidKey, nullptr, &status);
9511cb0ef41Sopenharmony_ci    ures_close(rb);
9521cb0ef41Sopenharmony_ci
9531cb0ef41Sopenharmony_ci    return shortID;
9541cb0ef41Sopenharmony_ci}
9551cb0ef41Sopenharmony_ci
9561cb0ef41Sopenharmony_ciU_NAMESPACE_END
9571cb0ef41Sopenharmony_ci
9581cb0ef41Sopenharmony_ci#endif /* #if !UCONFIG_NO_FORMATTING */
959