if (gMetaZoneIDTable != nullptr) {
uhash_close(gMetaZoneIDTable);
gMetaZoneIDTable = nullptr;
} // delete after closing gMetaZoneIDTable, because it holds // value objects held by the hashtable delete gMetaZoneIDs;
gMetaZoneIDs = nullptr;
gMetaZoneIDsInitOnce.reset();
/* * Convert a date string used by metazone mappings to UDate. * The format used by CLDR metazone mapping is "yyyy-MM-dd HH:mm".
*/ static UDate
parseDate (const char16_t *text, UErrorCode &status) { if (U_FAILURE(status)) { return 0;
}
int32_t len = u_strlen(text); if (len != 16 && len != 10) { // It must be yyyy-MM-dd HH:mm (length 16) or yyyy-MM-dd (length 10)
status = U_INVALID_FORMAT_ERROR; return 0;
}
int32_t year = 0, month = 0, day = 0, hour = 0, min = 0, n;
int32_t idx;
// "yyyy" (0 - 3) for (idx = 0; idx <= 3 && U_SUCCESS(status); idx++) {
n = ASCII_DIGIT((int32_t)text[idx]); if (n >= 0) {
year = 10*year + n;
} else {
status = U_INVALID_FORMAT_ERROR;
}
} // "MM" (5 - 6) for (idx = 5; idx <= 6 && U_SUCCESS(status); idx++) {
n = ASCII_DIGIT((int32_t)text[idx]); if (n >= 0) {
month = 10*month + n;
} else {
status = U_INVALID_FORMAT_ERROR;
}
} // "dd" (8 - 9) for (idx = 8; idx <= 9 && U_SUCCESS(status); idx++) {
n = ASCII_DIGIT((int32_t)text[idx]); if (n >= 0) {
day = 10*day + n;
} else {
status = U_INVALID_FORMAT_ERROR;
}
} if (len == 16) { // "HH" (11 - 12) for (idx = 11; idx <= 12 && U_SUCCESS(status); idx++) {
n = ASCII_DIGIT((int32_t)text[idx]); if (n >= 0) {
hour = 10*hour + n;
} else {
status = U_INVALID_FORMAT_ERROR;
}
} // "mm" (14 - 15) for (idx = 14; idx <= 15 && U_SUCCESS(status); idx++) {
n = ASCII_DIGIT((int32_t)text[idx]); if (n >= 0) {
min = 10*min + n;
} else {
status = U_INVALID_FORMAT_ERROR;
}
}
}
if (U_SUCCESS(status)) {
UDate date = Grego::fieldsToDay(year, month - 1, day) * U_MILLIS_PER_DAY
+ hour * U_MILLIS_PER_HOUR + min * U_MILLIS_PER_MINUTE; return date;
} return 0;
}
staticvoid U_CALLCONV initCanonicalIDCache(UErrorCode &status) {
gCanonicalIDCache = uhash_open(uhash_hashUChars, uhash_compareUChars, nullptr, &status); if (gCanonicalIDCache == nullptr) {
status = U_MEMORY_ALLOCATION_ERROR;
} if (U_FAILURE(status)) {
gCanonicalIDCache = nullptr;
} // No key/value deleters - keys/values are from a resource bundle
ucln_i18n_registerCleanup(UCLN_I18N_ZONEMETA, zoneMeta_cleanup);
}
if (tzid.isBogus() || tzid.length() > ZID_KEY_MAX) {
status = U_ILLEGAL_ARGUMENT_ERROR; return nullptr;
}
// Checking the cached results
umtx_initOnce(gCanonicalIDCacheInitOnce, &initCanonicalIDCache, status); if (U_FAILURE(status)) { return nullptr;
}
const char16_t *canonicalID = nullptr;
UErrorCode tmpStatus = U_ZERO_ERROR;
char16_t utzid[ZID_KEY_MAX + 1];
tzid.extract(utzid, ZID_KEY_MAX + 1, tmpStatus);
U_ASSERT(tmpStatus == U_ZERO_ERROR); // we checked the length of tzid already
if (!uprv_isInvariantUString(utzid, -1)) { // All of known tz IDs are only containing ASCII invariant characters.
status = U_ILLEGAL_ARGUMENT_ERROR; return nullptr;
}
// Check if it was already cached
umtx_lock(&gZoneMetaLock);
{
canonicalID = static_cast<const char16_t*>(uhash_get(gCanonicalIDCache, utzid));
}
umtx_unlock(&gZoneMetaLock);
if (canonicalID != nullptr) { return canonicalID;
}
// If not, resolve CLDR canonical ID with resource data
UBool isInputCanonical = false; char id[ZID_KEY_MAX + 1];
tzid.extract(0, 0x7fffffff, id, UPRV_LENGTHOF(id), US_INV);
// replace '/' with ':' char *p = id; while (*p++) { if (*p == '/') {
*p = ':';
}
}
UResourceBundle *top = ures_openDirect(nullptr, gKeyTypeData, &tmpStatus);
UResourceBundle *rb = ures_getByKey(top, gTypeMapTag, nullptr, &tmpStatus);
ures_getByKey(rb, gTimezoneTag, rb, &tmpStatus);
ures_getByKey(rb, id, rb, &tmpStatus); if (U_SUCCESS(tmpStatus)) { // type entry (canonical) found // the input is the canonical ID. resolve to const char16_t*
canonicalID = TimeZone::findID(tzid);
isInputCanonical = true;
}
if (canonicalID == nullptr) { // If a map element not found, then look for an alias
tmpStatus = U_ZERO_ERROR;
ures_getByKey(top, gTypeAliasTag, rb, &tmpStatus);
ures_getByKey(rb, gTimezoneTag, rb, &tmpStatus); const char16_t *canonical = ures_getStringByKey(rb,id,nullptr,&tmpStatus); if (U_SUCCESS(tmpStatus)) { // canonical map found
canonicalID = canonical;
}
if (canonicalID == nullptr) { // Dereference the input ID using the tz data const char16_t *derefer = TimeZone::dereferOlsonLink(tzid); if (derefer == nullptr) {
status = U_ILLEGAL_ARGUMENT_ERROR;
} else {
int32_t len = u_strlen(derefer);
u_UCharsToChars(derefer,id,len);
id[len] = static_cast<char>(0); // Make sure it is null terminated.
// replace '/' with ':' char *q = id; while (*q++) { if (*q == '/') {
*q = ':';
}
}
// If a dereference turned something up then look for an alias. // rb still points to the alias table, so we don't have to go looking // for it.
tmpStatus = U_ZERO_ERROR;
canonical = ures_getStringByKey(rb,id,nullptr,&tmpStatus); if (U_SUCCESS(tmpStatus)) { // canonical map for the dereferenced ID found
canonicalID = canonical;
} else {
canonicalID = derefer;
isInputCanonical = true;
}
}
}
}
ures_close(rb);
ures_close(top);
if (U_SUCCESS(status)) {
U_ASSERT(canonicalID != nullptr); // canocanilD must be non-nullptr here
// Put the resolved canonical ID to the cache
umtx_lock(&gZoneMetaLock);
{ const char16_t* idInCache = static_cast<const char16_t*>(uhash_get(gCanonicalIDCache, utzid)); if (idInCache == nullptr) { const char16_t* key = ZoneMeta::findTimeZoneID(tzid);
U_ASSERT(key != nullptr); if (key != nullptr) {
idInCache = static_cast<const char16_t*>(uhash_put(gCanonicalIDCache, const_cast<char16_t*>(key), const_cast<char16_t*>(canonicalID), &status));
U_ASSERT(idInCache == nullptr);
}
} if (U_SUCCESS(status) && isInputCanonical) { // Also put canonical ID itself into the cache if not exist const char16_t* canonicalInCache = static_cast<const char16_t*>(uhash_get(gCanonicalIDCache, canonicalID)); if (canonicalInCache == nullptr) {
canonicalInCache = static_cast<const char16_t*>(uhash_put(gCanonicalIDCache, const_cast<char16_t*>(canonicalID), const_cast<char16_t*>(canonicalID), &status));
U_ASSERT(canonicalInCache == nullptr);
}
}
}
umtx_unlock(&gZoneMetaLock);
}
staticvoid U_CALLCONV countryInfoVectorsInit(UErrorCode &status) { // Create empty vectors // No deleters for these UVectors, it's a reference to a resource bundle string.
gSingleZoneCountries = new UVector(nullptr, uhash_compareUChars, status); if (gSingleZoneCountries == nullptr) {
status = U_MEMORY_ALLOCATION_ERROR;
}
gMultiZonesCountries = new UVector(nullptr, uhash_compareUChars, status); if (gMultiZonesCountries == nullptr) {
status = U_MEMORY_ALLOCATION_ERROR;
}
// Checking the cached results
UErrorCode status = U_ZERO_ERROR;
umtx_initOnce(gCountryInfoVectorsInitOnce, &countryInfoVectorsInit, status); if (U_FAILURE(status)) { return country;
}
// Check if it was already cached
UBool cached = false;
UBool singleZone = false;
umtx_lock(&gZoneMetaLock);
{
singleZone = cached = gSingleZoneCountries->contains((void*)region); if (!cached) {
cached = gMultiZonesCountries->contains((void*)region);
}
}
umtx_unlock(&gZoneMetaLock);
if (!cached) { // We need to go through all zones associated with the region. // This is relatively heavy operation.
U_ASSERT(u_strlen(region) == 2);
u_UCharsToChars(region, regionBuf, 2);
StringEnumeration *ids = TimeZone::createTimeZoneIDEnumeration(UCAL_ZONE_TYPE_CANONICAL_LOCATION, regionBuf, nullptr, status);
int32_t idsLen = ids->count(status); if (U_SUCCESS(status) && idsLen == 1) { // only the single zone is available for the region
singleZone = true;
} delete ids;
// Cache the result
umtx_lock(&gZoneMetaLock);
{
UErrorCode ec = U_ZERO_ERROR; if (singleZone) { if (!gSingleZoneCountries->contains((void*)region)) {
gSingleZoneCountries->addElement((void*)region, ec);
}
} else { if (!gMultiZonesCountries->contains((void*)region)) {
gMultiZonesCountries->addElement((void*)region, ec);
}
}
}
umtx_unlock(&gZoneMetaLock);
}
if (singleZone) {
*isPrimary = true;
} else { // Note: We may cache the primary zone map in future.
// Even a country has multiple zones, one of them might be // dominant and treated as a primary zone
int32_t idLen = 0; if (regionBuf[0] == 0) {
u_UCharsToChars(region, regionBuf, 2);
}
UResourceBundle *rb = ures_openDirect(nullptr, gMetaZones, &status);
ures_getByKey(rb, gPrimaryZonesTag, rb, &status); const char16_t *primaryZone = ures_getStringByKey(rb, regionBuf, &idLen, &status); if (U_SUCCESS(status)) { if (tzid.compare(primaryZone, idLen) == 0) {
*isPrimary = true;
} else { // The given ID might not be a canonical ID
UnicodeString canonicalID;
TimeZone::getCanonicalID(tzid, canonicalID, status); if (U_SUCCESS(status) && canonicalID.compare(primaryZone, idLen) == 0) {
*isPrimary = true;
}
}
}
ures_close(rb);
}
}
return country;
}
UnicodeString& U_EXPORT2
ZoneMeta::getMetazoneID(const UnicodeString &tzid, UDate date, UnicodeString &result) {
UBool isSet = false; const UVector *mappings = getMetazoneMappings(tzid); if (mappings != nullptr) { for (int32_t i = 0; i < mappings->size(); i++) {
OlsonToMetaMappingEntry* mzm = static_cast<OlsonToMetaMappingEntry*>(mappings->elementAt(i)); if (mzm->from <= date && mzm->to > date) {
result.setTo(mzm->mzid, -1);
isSet = true; break;
}
}
} if (!isSet) {
result.setToBogus();
} return result;
}
const UVector* U_EXPORT2
ZoneMeta::getMetazoneMappings(const UnicodeString &tzid) {
UErrorCode status = U_ZERO_ERROR;
char16_t tzidUChars[ZID_KEY_MAX + 1];
tzid.extract(tzidUChars, ZID_KEY_MAX + 1, status); if (U_FAILURE(status) || status == U_STRING_NOT_TERMINATED_WARNING) { return nullptr;
}
umtx_initOnce(gOlsonToMetaInitOnce, &olsonToMetaInit, status); if (U_FAILURE(status)) { return nullptr;
}
// get the mapping from cache const UVector *result = nullptr;
umtx_lock(&gZoneMetaLock);
{
result = static_cast<UVector*>(uhash_get(gOlsonToMeta, tzidUChars));
}
umtx_unlock(&gZoneMetaLock);
if (result != nullptr) { return result;
}
// miss the cache - create new one
UVector *tmpResult = createMetazoneMappings(tzid); if (tmpResult == nullptr) { // not available return nullptr;
}
// put the new one into the cache
umtx_lock(&gZoneMetaLock);
{ // make sure it's already created
result = static_cast<UVector*>(uhash_get(gOlsonToMeta, tzidUChars)); if (result == nullptr) { // add the one just created
int32_t tzidLen = tzid.length() + 1;
char16_t* key = static_cast<char16_t*>(uprv_malloc(tzidLen * sizeof(char16_t))); if (key == nullptr) { // memory allocation error.. just return nullptr
result = nullptr; delete tmpResult;
} else {
tzid.extract(key, tzidLen, status);
uhash_put(gOlsonToMeta, key, tmpResult, &status); if (U_FAILURE(status)) { // delete the mapping
result = nullptr; delete tmpResult;
} else {
result = tmpResult;
}
}
} else { // another thread already put the one delete tmpResult;
}
}
umtx_unlock(&gZoneMetaLock);
if(U_FAILURE(status)){
status = U_ZERO_ERROR; continue;
} // We do not want to use SimpleDateformat to parse boundary dates, // because this code could be triggered by the initialization code // used by SimpleDateFormat.
UDate from = parseDate(mz_from, status);
UDate to = parseDate(mz_to, status); if (U_FAILURE(status)) {
status = U_ZERO_ERROR; continue;
}
if (mzMappings.isNull()) {
mzMappings.adoptInsteadAndCheckErrorCode( new UVector(deleteOlsonToMetaMappingEntry, nullptr, status), status); if (U_FAILURE(status)) { break;
}
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.