static icu::UInitOnce gStaticZonesInitOnce {}; static UBool gStaticZonesInitialized = false; // Whether the static zones are initialized and ready to use.
/** *GivenanID,opentheappropriateresourceforthegiventimezone. *Dereferencealiasesifnecessary. *@paramidzoneid *@paramresresource,whichmustbereadyforuse(initializedbutnotopen) *@paramecinput-outputerrorcode *@returntop-levelresourcebundle
*/ static UResourceBundle* openOlsonResource(const UnicodeString& id,
UResourceBundle& res,
UErrorCode& ec)
{ #ifdef U_DEBUG_TZ char buf[128];
id.extract(0, sizeof(buf)-1, buf, sizeof(buf), ""); #endif
UResourceBundle *top = ures_openDirect(nullptr, kZONEINFO, &ec);
U_DEBUG_TZ_MSG(("pre: res sz=%d\n", ures_getSize(&res))); /* &res = */ getZoneByName(top, id, &res, ec); // Dereference if this is an alias. Docs say result should be 1 // but it is 0 in 2.8 (?).
U_DEBUG_TZ_MSG(("Loading zone '%s' (%s, size %d) - %s\n", buf, ures_getKey((UResourceBundle*)&res), ures_getSize(&res), u_errorName(ec))); if (ures_getType(&res) == URES_INT) {
int32_t deref = ures_getInt(&res, &ec) + 0;
U_DEBUG_TZ_MSG(("getInt: %s - type is %d\n", u_errorName(ec), ures_getType(&res)));
UResourceBundle *ares = ures_getByKey(top, kZONES, nullptr, &ec); // dereference Zones section
ures_getByIndex(ares, deref, &res, &ec);
ures_close(ares);
U_DEBUG_TZ_MSG(("alias to #%d (%s) - %s\n", deref, "??", u_errorName(ec)));
} else {
U_DEBUG_TZ_MSG(("not an alias - size %d\n", ures_getSize(&res)));
}
U_DEBUG_TZ_MSG(("%s - final status is %s\n", buf, u_errorName(ec))); return top;
}
// -------------------------------------
namespace {
void U_CALLCONV initStaticTimeZones() { // Initialize _GMT independently of other static data; it should // be valid even if we can't load the time zone UDataMemory.
ucln_i18n_registerCleanup(UCLN_I18N_TIMEZONE, timeZone_cleanup);
// new can't fail below, as we use placement new into statically allocated space. new(gRawGMT) SimpleTimeZone(0, UnicodeString(true, GMT_ID, GMT_ID_LENGTH)); new(gRawUNKNOWN) SimpleTimeZone(0, UnicodeString(true, UNKNOWN_ZONE_ID, UNKNOWN_ZONE_ID_LENGTH));
TimeZone* U_EXPORT2
TimeZone::createTimeZone(const UnicodeString& ID)
{ /* We first try to lookup the zone ID in our system list. If this *fails,wetrytoparseitasacustomstringGMT[+-]hh:mm.If *allelsefails,wereturnGMT,whichisprobablynotwhatthe *userwants,butatleastisafunctioningTimeZoneobject. * *Wecannotreturnnullptr,becausethatwouldbreakcompatibility *withtheJDK.
*/
TimeZone* result = createSystemTimeZone(ID);
if (result == nullptr) {
U_DEBUG_TZ_MSG(("failed to load system time zone with id - falling to custom"));
result = createCustomTimeZone(ID);
} if (result == nullptr) {
U_DEBUG_TZ_MSG(("failed to load time zone with id - falling to Etc/Unknown(GMT)")); const TimeZone& unknown = getUnknown(); // Unknown zone uses statically allocated memory, so creation of it can never fail due to OOM.
result = unknown.clone();
} return result;
}
// -------------------------------------
TimeZone* U_EXPORT2
TimeZone::detectHostTimeZone()
{ // We access system timezone data through uprv_tzset(), uprv_tzname(), and others, // which have platform specific implementations in putil.cpp
int32_t rawOffset = 0; constchar *hostID;
UBool hostDetectionSucceeded = true;
// First, try to create a system timezone, based // on the string ID in tzname[0].
uprv_tzset(); // Initialize tz... system data
uprv_tzname_clear_cache();
// Get the timezone ID from the host. This function should do // any required host-specific remapping; e.g., on Windows this // function maps the Windows Time Zone name to an ICU timezone ID.
hostID = uprv_tzname(0);
// Invert sign because UNIX semantics are backwards
rawOffset = uprv_timezone() * -U_MILLIS_PER_SECOND;
TimeZone* hostZone = nullptr;
UnicodeString hostStrID(hostID, -1, US_INV);
if (hostStrID.length() == 0) { // The host time zone detection (or remapping) above has failed and // we have no name at all. Fallback to using the Unknown zone.
hostStrID = UnicodeString(true, UNKNOWN_ZONE_ID, UNKNOWN_ZONE_ID_LENGTH);
hostDetectionSucceeded = false;
}
hostZone = createSystemTimeZone(hostStrID);
#if U_PLATFORM_USES_ONLY_WIN32_API // hostID points to a heap-allocated location on Windows.
uprv_free(const_cast<char *>(hostID)); #endif
int32_t hostIDLen = hostStrID.length(); if (hostZone != nullptr && rawOffset != hostZone->getRawOffset()
&& (3 <= hostIDLen && hostIDLen <= 4))
{ // Uh oh. This probably wasn't a good id. // It was probably an ambiguous abbreviation delete hostZone;
hostZone = nullptr;
}
// Construct a fixed standard zone with the host's ID // and raw offset. if (hostZone == nullptr && hostDetectionSucceeded) {
hostZone = new SimpleTimeZone(rawOffset, hostStrID);
}
// If we _still_ don't have a time zone, use the Unknown zone. // // Note: This is extremely unlikely situation. If // new SimpleTimeZone(...) above fails, the following // code may also fail. if (hostZone == nullptr) { // Unknown zone uses static allocated memory, so it must always exist. // However, clone() allocates memory and can fail.
hostZone = TimeZone::getUnknown().clone();
}
Mutex lock(&gDefaultZoneMutex); // If setDefault() has already been called we can skip getting the // default zone information from the system. if (DEFAULT_ZONE != nullptr) { return;
}
// NOTE: this code is safely single threaded, being only // run via umtx_initOnce(). // // Some of the locale/timezone OS functions may not be thread safe, // // The operating system might actually use ICU to implement timezones. // So we may have ICU calling ICU here, like on AIX. // There shouldn't be a problem with this; initOnce does not hold a mutex // while the init function is being run.
// The code detecting the host time zone was separated from this // and implemented as TimeZone::detectHostTimeZone()
rawOffset = getRawOffset(); if (!local) {
date += rawOffset; // now in local standard millis
}
// When local == true, date might not be in local standard // millis. getOffset taking 7 parameters used here assume // the given time in day is local standard time. // At STD->DST transition, there is a range of time which // does not exist. When 'date' is in this time range // (and local == true), this method interprets the specified // local time as DST. At DST->STD transition, there is a // range of time which occurs twice. In this case, this // method interprets the specified local time as STD. // To support the behavior above, we need to call getOffset // (with 7 args) twice when local == true and DST is // detected in the initial call. for (int32_t pass=0; ; ++pass) {
int32_t year, millis;
int8_t month, dom, dow;
Grego::timeToFields(date, year, month, dom, dow, millis, ec); if (U_FAILURE(ec)) return;
// Recompute if local==true, dstOffset!=0. if (pass!=0 || !local || dstOffset == 0) { break;
} // adjust to local standard millis
date -= dstOffset;
}
}
// -------------------------------------
// New available IDs API as of ICU 2.4. Uses StringEnumeration API.
class TZEnumeration : public StringEnumeration { private:
// Map into to zones. Our results are zone[map[i]] for // i=0..len-1, where zone[i] is the i-th Olson zone. If map==nullptr // then our results are zone[i] for i=0..len-1. Len will be zero // if the zone data could not be loaded.
int32_t* map;
int32_t* localMap;
int32_t len;
int32_t pos;
// Walk through the base map
UResourceBundle *res = ures_openDirect(nullptr, kZONEINFO, &ec);
res = ures_getByKey(res, kNAMES, res, &ec); // dereference Zones section for (int32_t i = 0; i < baseLen; i++) {
int32_t zidx = baseMap[i];
UnicodeString id = ures_getUnicodeStringByIndex(res, zidx, &ec); if (U_FAILURE(ec)) { break;
} if (region != nullptr) { // Filter by region char tzregion[4]; // max 3 letters + null term
TimeZone::getRegion(id, tzregion, sizeof(tzregion), ec); if (U_FAILURE(ec)) { break;
} if (uprv_stricmp(tzregion, region) != 0) { // region does not match continue;
}
} if (rawOffset != nullptr) { // Filter by raw offset // Note: This is VERY inefficient
TimeZone *z = createSystemTimeZone(id, ec); if (U_FAILURE(ec)) { break;
}
int32_t tzoffset = z->getRawOffset(); delete z;
// resolve zone index by name
UResourceBundle *names = ures_getByKey(rb, kNAMES, nullptr, &ec);
int32_t idx = findInStringArray(names, id, ec);
result = ures_getStringByIndex(names, idx, nullptr, &ec);
// open the zone bundle by index
ures_getByKey(rb, kZONES, rb, &ec);
ures_getByIndex(rb, idx, rb, &ec);
if (U_SUCCESS(ec)) { if (ures_getType(rb) == URES_INT) { // this is a link - dereference the link
int32_t deref = ures_getInt(rb, &ec); const char16_t* tmp = ures_getStringByIndex(names, deref, nullptr, &ec); if (U_SUCCESS(ec)) {
result = tmp;
}
}
}
const char16_t *uregion = nullptr; // "Etc/Unknown" is not a system zone ID, // but in the zone data if (id.compare(UNKNOWN_ZONE_ID, UNKNOWN_ZONE_ID_LENGTH) != 0) {
uregion = getRegion(id);
} if (uregion == nullptr) {
status = U_ILLEGAL_ARGUMENT_ERROR; return0;
}
resultLen = u_strlen(uregion); // A region code is represented by invariant characters
u_UCharsToChars(uregion, region, uprv_min(resultLen, capacity));
if (capacity < resultLen) {
status = U_BUFFER_OVERFLOW_ERROR; return resultLen;
}
if (id[GMT_ID_LENGTH] == MINUS /*'-'*/) {
sign = -1;
} elseif (id[GMT_ID_LENGTH] != PLUS /*'+'*/) { returnfalse;
}
int32_t start = GMT_ID_LENGTH + 1;
int32_t pos = start;
hour = ICU_Utility::parseNumber(id, pos, 10); if (pos == id.length()) { // Handle the following cases // HHmmss // Hmmss // HHmm // Hmm // HH // H
// Get all digits // Should be 1 to 6 digits.
int32_t length = pos - start; switch (length) { case1: // H case2: // HH // already set to hour break; case3: // Hmm case4: // HHmm
min = hour % 100;
hour /= 100; break; case5: // Hmmss case6: // HHmmss
sec = hour % 100;
min = (hour/100) % 100;
hour /= 10000; break; default: // invalid range returnfalse;
}
} else { // Handle the following cases // HH:mm:ss // H:mm:ss // HH:mm // H:mm if (pos - start < 1 || pos - start > 2 || id[pos] != COLON) { returnfalse;
}
pos++; // skip : after H or HH if (id.length() == pos) { returnfalse;
}
start = pos;
min = ICU_Utility::parseNumber(id, pos, 10); if (pos - start != 2) { returnfalse;
} if (id.length() > pos) { if (id[pos] != COLON) { returnfalse;
}
pos++; // skip : after mm
start = pos;
sec = ICU_Utility::parseNumber(id, pos, 10); if (pos - start != 2 || id.length() > pos) { returnfalse;
}
}
} if (hour > kMAX_CUSTOM_HOUR ||
min > kMAX_CUSTOM_MIN ||
sec > kMAX_CUSTOM_SEC) { returnfalse;
} return true;
}
UnicodeString&
TimeZone::formatCustomID(int32_t hour, int32_t min, int32_t sec,
UBool negative, UnicodeString& id) { // Create time zone ID - GMT[+|-]hhmm[ss]
id.setTo(GMT_ID, GMT_ID_LENGTH); if (hour | min | sec) { if (negative) {
id += static_cast<char16_t>(MINUS);
} else {
id += static_cast<char16_t>(PLUS);
}
if (hour < 10) {
id += static_cast<char16_t>(ZERO_DIGIT);
} else {
id += static_cast<char16_t>(ZERO_DIGIT + hour / 10);
}
id += static_cast<char16_t>(ZERO_DIGIT + hour % 10);
id += static_cast<char16_t>(COLON); if (min < 10) {
id += static_cast<char16_t>(ZERO_DIGIT);
} else {
id += static_cast<char16_t>(ZERO_DIGIT + min / 10);
}
id += static_cast<char16_t>(ZERO_DIGIT + min % 10);
if (sec) {
id += static_cast<char16_t>(COLON); if (sec < 10) {
id += static_cast<char16_t>(ZERO_DIGIT);
} else {
id += static_cast<char16_t>(ZERO_DIGIT + sec / 10);
}
id += static_cast<char16_t>(ZERO_DIGIT + sec % 10);
}
} return id;
}
if (U_SUCCESS(status)) { if (len >= static_cast<int32_t>(sizeof(TZDATA_VERSION))) { // Ensure that there is always space for a trailing nul in TZDATA_VERSION
len = sizeof(TZDATA_VERSION) - 1;
}
u_UCharsToChars(tzver, TZDATA_VERSION, len);
}
}
UnicodeString&
TimeZone::getCanonicalID(const UnicodeString& id, UnicodeString& canonicalID, UBool& isSystemID,
UErrorCode& status)
{
canonicalID.remove();
isSystemID = false; if (U_FAILURE(status)) { return canonicalID;
} if (id.compare(UNKNOWN_ZONE_ID, UNKNOWN_ZONE_ID_LENGTH) == 0) { // special case - Etc/Unknown is a canonical ID, but not system ID
canonicalID.fastCopyFrom(id);
isSystemID = false;
} else {
ZoneMeta::getCanonicalCLDRID(id, canonicalID, status); if (U_SUCCESS(status)) {
isSystemID = true;
} else { // Not a system ID
status = U_ZERO_ERROR;
getCustomID(id, canonicalID, status);
}
} return canonicalID;
}
// canonicalize the input ID
UnicodeString canonicalID;
UBool isSystemID = false;
getCanonicalID(id, canonicalID, isSystemID, status); if (U_FAILURE(status) || !isSystemID) { // mapping data is only applicable to tz database IDs if (status == U_ILLEGAL_ARGUMENT_ERROR) { // getWindowsID() sets an empty string where // getCanonicalID() sets a U_ILLEGAL_ARGUMENT_ERROR.
status = U_ZERO_ERROR;
} return winid;
}
ures_getByKey(zones, winidKey, zones, &tmperr); // use tmperr, because windows mapping might not // be available by design if (U_FAILURE(tmperr)) {
ures_close(zones); return id;
}
const char16_t *tzid = nullptr;
int32_t len = 0;
UBool gotID = false; if (region) { const char16_t *tzids = ures_getStringByKey(zones, region, &len, &tmperr); // use tmperr, because // regional mapping is optional if (U_SUCCESS(tmperr)) { // first ID delimited by space is the default one const char16_t* end = u_strchr(tzids, static_cast<char16_t>(0x20)); if (end == nullptr) {
id.setTo(tzids, -1);
} else {
id.setTo(tzids, static_cast<int32_t>(end - tzids));
}
gotID = true;
}
}
if (!gotID) {
tzid = ures_getStringByKey(zones, "001", &len, &status); // using status, because "001" must be // available at this point if (U_SUCCESS(status)) {
id.setTo(tzid, len);
}
}
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.