/** * GNameInfo stores zone name information in the local trie
*/ typedefstruct GNameInfo {
UTimeZoneGenericNameType type; const char16_t* tzID;
} ZNameInfo;
/** * GMatchInfo stores zone name match information used by find method
*/ typedefstruct GMatchInfo { const GNameInfo* gnameInfo;
int32_t matchLength;
UTimeZoneFormatTimeType timeType;
} ZMatchInfo;
U_CDECL_END
// --------------------------------------------------- // The class stores time zone generic name match information // --------------------------------------------------- class TimeZoneGenericNameMatchInfo : public UMemory { public:
TimeZoneGenericNameMatchInfo(UVector* matches);
~TimeZoneGenericNameMatchInfo();
UBool
GNameSearchHandler::handleMatch(int32_t matchLength, const CharacterNode *node, UErrorCode &status) { if (U_FAILURE(status)) { returnfalse;
} if (node->hasValues()) {
int32_t valuesCount = node->countValues(); for (int32_t i = 0; i < valuesCount; i++) {
GNameInfo *nameinfo = (ZNameInfo *)node->getValue(i); if (nameinfo == nullptr) { break;
} if ((nameinfo->type & fTypes) != 0) { // matches a requested type if (fResults == nullptr) {
LocalPointer<UVector> lpResults(new UVector(uprv_free, nullptr, status), status); if (U_FAILURE(status)) { returnfalse;
}
fResults = lpResults.orphan();
}
GMatchInfo* gmatch = static_cast<GMatchInfo*>(uprv_malloc(sizeof(GMatchInfo))); if (gmatch == nullptr) {
status = U_MEMORY_ALLOCATION_ERROR; returnfalse;
} // add the match to the vector
gmatch->gnameInfo = nameinfo;
gmatch->matchLength = matchLength;
gmatch->timeType = UTZFMT_TIME_TYPE_UNKNOWN;
fResults->adoptElement(gmatch, status); if (U_FAILURE(status)) { returnfalse;
} if (matchLength > fMaxMatchLen) {
fMaxMatchLen = matchLength;
}
}
}
} returntrue;
}
UVector*
GNameSearchHandler::getMatches(int32_t& maxMatchLen) { // give the ownership to the caller
UVector *results = fResults;
maxMatchLen = fMaxMatchLen;
// --------------------------------------------------- // TZGNCore - core implementation of TimeZoneGenericNames // // TimeZoneGenericNames is parallel to TimeZoneNames, // but handles run-time generated time zone names. // This is the main part of this module. // ---------------------------------------------------
TZGNCore::TZGNCore(const Locale& locale, UErrorCode& status)
: fLocale(locale),
fTimeZoneNames(nullptr),
fLocationNamesMap(nullptr),
fPartialLocationNamesMap(nullptr),
fLocaleDisplayNames(nullptr),
fStringPool(status),
fGNamesTrie(true, deleteGNameInfo),
fGNamesTrieFullyLoaded(false),
fTargetRegion() {
initialize(locale, status);
}
/* * This method updates the cache and must be called with a lock
*/ const char16_t*
TZGNCore::getGenericLocationName(const UnicodeString& tzCanonicalID) {
U_ASSERT(!tzCanonicalID.isEmpty()); if (tzCanonicalID.length() > ZID_KEY_MAX) { return nullptr;
}
if (!usCountryCode.isEmpty()) { if (isPrimary) { // If this is the primary zone in the country, use the country name. char countryCode[ULOC_COUNTRY_CAPACITY];
U_ASSERT(usCountryCode.length() < ULOC_COUNTRY_CAPACITY);
int32_t ccLen = usCountryCode.extract(0, usCountryCode.length(), countryCode, sizeof(countryCode), US_INV);
countryCode[ccLen] = 0;
UnicodeString country;
fLocaleDisplayNames->regionDisplayName(countryCode, country);
fRegionFormat.format(country, name, status);
} else { // If this is not the primary zone in the country, // use the exemplar city name.
// getExemplarLocationName should return non-empty string // if the time zone is associated with a region
// Try to get a name from time zone first
UTimeZoneNameType nameType = (type == UTZGNM_LONG) ? UTZNM_LONG_GENERIC : UTZNM_SHORT_GENERIC;
fTimeZoneNames->getTimeZoneDisplayName(tzID, nameType, name);
if (!name.isEmpty()) { return name;
}
// Try meta zone
char16_t mzIDBuf[32];
UnicodeString mzID(mzIDBuf, 0, UPRV_LENGTHOF(mzIDBuf));
fTimeZoneNames->getMetaZoneID(tzID, date, mzID); if (!mzID.isEmpty()) {
UErrorCode status = U_ZERO_ERROR;
UBool useStandard = false;
int32_t raw, sav;
char16_t tmpNameBuf[ZONE_NAME_U16_MAX];
TimeZone *tmptz = tz.clone(); // Check if the zone actually uses daylight saving time around the time
BasicTimeZone *btz = nullptr; if (dynamic_cast<OlsonTimeZone *>(tmptz) != nullptr
|| dynamic_cast<SimpleTimeZone *>(tmptz) != nullptr
|| dynamic_cast<RuleBasedTimeZone *>(tmptz) != nullptr
|| dynamic_cast<VTimeZone *>(tmptz) != nullptr) {
btz = (BasicTimeZone*)tmptz;
}
if (btz != nullptr) {
TimeZoneTransition before;
UBool beforTrs = btz->getPreviousTransition(date, true, before); if (beforTrs
&& (date - before.getTime() < kDstCheckRange)
&& before.getFrom()->getDSTSavings() != 0) {
useStandard = false;
} else {
TimeZoneTransition after;
UBool afterTrs = btz->getNextTransition(date, false, after); if (afterTrs
&& (after.getTime() - date < kDstCheckRange)
&& after.getTo()->getDSTSavings() != 0) {
useStandard = false;
}
}
} else { // If not BasicTimeZone... only if the instance is not an ICU's implementation. // We may get a wrong answer in edge case, but it should practically work OK.
tmptz->getOffset(date - kDstCheckRange, false, raw, sav, status); if (sav != 0) {
useStandard = false;
} else {
tmptz->getOffset(date + kDstCheckRange, false, raw, sav, status); if (sav != 0){
useStandard = false;
}
} if (U_FAILURE(status)) { delete tmptz; return name;
}
} delete tmptz;
} if (useStandard) {
UTimeZoneNameType stdNameType = (nameType == UTZNM_LONG_GENERIC)
? UTZNM_LONG_STANDARD : UTZNM_SHORT_STANDARD;
UnicodeString stdName(tmpNameBuf, 0, UPRV_LENGTHOF(tmpNameBuf));
fTimeZoneNames->getDisplayName(tzID, stdNameType, date, stdName); if (!stdName.isEmpty()) {
name.setTo(stdName);
// TODO: revisit this issue later // In CLDR, a same display name is used for both generic and standard // for some meta zones in some locales. This looks like a data bugs. // For now, we check if the standard name is different from its generic // name below.
char16_t genNameBuf[ZONE_NAME_U16_MAX];
UnicodeString mzGenericName(genNameBuf, 0, UPRV_LENGTHOF(genNameBuf));
fTimeZoneNames->getMetaZoneDisplayName(mzID, nameType, mzGenericName); if (stdName.caseCompare(mzGenericName, 0) == 0) {
name.setToBogus();
}
}
} if (name.isEmpty()) { // Get a name from meta zone
UnicodeString mzName(tmpNameBuf, 0, UPRV_LENGTHOF(tmpNameBuf));
fTimeZoneNames->getMetaZoneDisplayName(mzID, nameType, mzName); if (!mzName.isEmpty()) { // Check if we need to use a partial location format. // This check is done by comparing offset with the meta zone's // golden zone at the given date.
char16_t idBuf[32];
UnicodeString goldenID(idBuf, 0, UPRV_LENGTHOF(idBuf));
fTimeZoneNames->getReferenceZoneID(mzID, fTargetRegion.data(), goldenID); if (!goldenID.isEmpty() && goldenID != tzID) {
TimeZone *goldenZone = TimeZone::createTimeZone(goldenID);
int32_t raw1, sav1;
// Check offset in the golden zone with wall time. // With getOffset(date, false, offsets1), // you may get incorrect results because of time overlap at DST->STD // transition.
goldenZone->getOffset(date + raw + sav, true, raw1, sav1, status); delete goldenZone; if (U_SUCCESS(status)) { if (raw != raw1 || sav != sav1) { // Now we need to use a partial location format
getPartialLocationName(tzID, mzID, (nameType == UTZNM_LONG_GENERIC), mzName, name);
} else {
name.setTo(mzName);
}
}
} else {
name.setTo(mzName);
}
}
}
} return name;
}
/* * This method updates the cache and must be called with a lock
*/ const char16_t*
TZGNCore::getPartialLocationName(const UnicodeString& tzCanonicalID, const UnicodeString& mzID, UBool isLong, const UnicodeString& mzDisplayName) {
U_ASSERT(!tzCanonicalID.isEmpty());
U_ASSERT(!mzID.isEmpty());
U_ASSERT(!mzDisplayName.isEmpty());
UnicodeString regionalGolden;
fTimeZoneNames->getReferenceZoneID(mzID, countryCode, regionalGolden); if (tzCanonicalID == regionalGolden) { // Use country name
fLocaleDisplayNames->regionDisplayName(countryCode, location);
} else { // Otherwise, use exemplar city name
fTimeZoneNames->getExemplarLocationName(tzCanonicalID, location);
}
} else {
fTimeZoneNames->getExemplarLocationName(tzCanonicalID, location); if (location.isEmpty()) { // This could happen when the time zone is not associated with a country, // and its ID is not hierarchical, for example, CST6CDT. // We use the canonical ID itself as the location for this case.
location.setTo(tzCanonicalID);
}
}
UErrorCode status = U_ZERO_ERROR;
UnicodeString name;
fFallbackFormat.format(location, mzDisplayName, name, status); if (U_FAILURE(status)) { return nullptr;
}
uplname = fStringPool.get(name, status); if (U_SUCCESS(status)) { // Add the name to cache
PartialLocationKey* cacheKey = static_cast<PartialLocationKey*>(uprv_malloc(sizeof(PartialLocationKey))); if (cacheKey != nullptr) {
cacheKey->tzID = key.tzID;
cacheKey->mzID = key.mzID;
cacheKey->isLong = key.isLong;
uhash_put(fPartialLocationNamesMap, (void *)cacheKey, (void *)uplname, &status); if (U_FAILURE(status)) {
uprv_free(cacheKey);
} else { // put the name to the local trie as well
GNameInfo* nameinfo = static_cast<ZNameInfo*>(uprv_malloc(sizeof(GNameInfo))); if (nameinfo != nullptr) {
nameinfo->type = isLong ? UTZGNM_LONG : UTZGNM_SHORT;
nameinfo->tzID = key.tzID;
fGNamesTrie.put(uplname, nameinfo, status);
}
}
}
} return uplname;
}
/* * This method updates the cache and must be called with a lock, * except initializer.
*/ void
TZGNCore::loadStrings(const UnicodeString& tzCanonicalID) { // load the generic location name
getGenericLocationName(tzCanonicalID);
// partial location names
UErrorCode status = U_ZERO_ERROR;
StringEnumeration *mzIDs = fTimeZoneNames->getAvailableMetaZoneIDs(tzCanonicalID, status); while ((mzID = mzIDs->snext(status)) != nullptr) { if (U_FAILURE(status)) { break;
} // if this time zone is not the golden zone of the meta zone, // partial location name (such as "PT (Los Angeles)") might be // available.
fTimeZoneNames->getReferenceZoneID(*mzID, fTargetRegion.data(), goldenID); if (tzCanonicalID != goldenID) { for (int32_t i = 0; genNonLocTypes[i] != UTZNM_UNKNOWN; i++) {
fTimeZoneNames->getMetaZoneDisplayName(*mzID, genNonLocTypes[i], mzGenName); if (!mzGenName.isEmpty()) { // getPartialLocationName formats a name and put it into the trie
getPartialLocationName(tzCanonicalID, *mzID,
(genNonLocTypes[i] == UTZNM_LONG_GENERIC), mzGenName);
}
}
}
} delete mzIDs;
}
// Find matches in the TimeZoneNames first
TimeZoneNames::MatchInfoCollection *tznamesMatches = findTimeZoneNames(text, start, types, status); if (U_FAILURE(status)) { return 0;
}
int32_t bestMatchLen = 0;
UTimeZoneFormatTimeType bestMatchTimeType = UTZFMT_TIME_TYPE_UNKNOWN;
UnicodeString bestMatchTzID; // UBool isLongStandard = false; // workaround - see the comments below
UBool isStandard = false; // TODO: Temporary hack (on hack) for short standard name/location name conflict (found in zh_Hant), should be removed after CLDR 21m1 integration
if (tznamesMatches != nullptr) {
UnicodeString mzID; for (int32_t i = 0; i < tznamesMatches->size(); i++) {
int32_t len = tznamesMatches->getMatchLengthAt(i); if (len > bestMatchLen) {
bestMatchLen = len; if (!tznamesMatches->getTimeZoneIDAt(i, bestMatchTzID)) { // name for a meta zone if (tznamesMatches->getMetaZoneIDAt(i, mzID)) {
fTimeZoneNames->getReferenceZoneID(mzID, fTargetRegion.data(), bestMatchTzID);
}
}
UTimeZoneNameType nameType = tznamesMatches->getNameTypeAt(i); if (U_FAILURE(status)) { break;
} switch (nameType) { case UTZNM_LONG_STANDARD: // isLongStandard = true; case UTZNM_SHORT_STANDARD: // this one is never used for generic, but just in case
isStandard = true; // TODO: Remove this later, see the comments above.
bestMatchTimeType = UTZFMT_TIME_TYPE_STANDARD; break; case UTZNM_LONG_DAYLIGHT: case UTZNM_SHORT_DAYLIGHT: // this one is never used for generic, but just in case
bestMatchTimeType = UTZFMT_TIME_TYPE_DAYLIGHT; break; default:
bestMatchTimeType = UTZFMT_TIME_TYPE_UNKNOWN;
}
}
} delete tznamesMatches; if (U_FAILURE(status)) { return 0;
}
if (bestMatchLen == (text.length() - start)) { // Full match
// TODO Some time zone uses a same name for the long standard name // and the location name. When the match is a long standard name, // then we need to check if the name is same with the location name. // This is probably a data error or a design bug. /* if (!isLongStandard) { tzID.setTo(bestMatchTzID); timeType = bestMatchTimeType; return bestMatchLen; }
*/ // TODO The deprecation of commonlyUsed flag introduced the name // conflict not only for long standard names, but short standard names too. // These short names (found in zh_Hant) should be gone once we clean // up CLDR time zone display name data. Once the short name conflict // problem (with location name) is resolved, we should change the condition // below back to the original one above. -Yoshito (2011-09-14) if (!isStandard) {
tzID.setTo(bestMatchTzID);
timeType = bestMatchTimeType; return bestMatchLen;
}
}
}
// Find matches in the local trie
TimeZoneGenericNameMatchInfo *localMatches = findLocal(text, start, types, status); if (U_FAILURE(status)) { return 0;
} if (localMatches != nullptr) { for (int32_t i = 0; i < localMatches->size(); i++) {
int32_t len = localMatches->getMatchLength(i);
// TODO See the above TODO. We use len >= bestMatchLen // because of the long standard/location name collision // problem. If it is also a location name, carrying // timeType = UTZFMT_TIME_TYPE_STANDARD will cause a // problem in SimpleDateFormat if (len >= bestMatchLen) {
bestMatchLen = localMatches->getMatchLength(i);
bestMatchTimeType = UTZFMT_TIME_TYPE_UNKNOWN; // because generic
localMatches->getTimeZoneID(i, bestMatchTzID);
}
} delete localMatches;
}
int32_t maxLen = 0;
UVector *results = handler.getMatches(maxLen); if (results != nullptr && ((maxLen == (text.length() - start)) || fGNamesTrieFullyLoaded)) { // perfect match
gmatchInfo = new TimeZoneGenericNameMatchInfo(results); if (gmatchInfo == nullptr) {
status = U_MEMORY_ALLOCATION_ERROR; delete results; return nullptr;
} return gmatchInfo;
}
delete results;
// All names are not yet loaded into the local trie. // Load all available names into the trie. This could be very heavy.
umtx_lock(&gLock);
{ if (!fGNamesTrieFullyLoaded) {
StringEnumeration *tzIDs = TimeZone::createTimeZoneIDEnumeration(UCAL_ZONE_TYPE_CANONICAL, nullptr, nullptr, status); if (U_SUCCESS(status)) { const UnicodeString *tzID; while ((tzID = tzIDs->snext(status)) != nullptr) { if (U_FAILURE(status)) { break;
}
nonConstThis->loadStrings(*tzID);
}
} delete tzIDs;
if (U_SUCCESS(status)) {
nonConstThis->fGNamesTrieFullyLoaded = true;
}
}
}
umtx_unlock(&gLock);
if (U_FAILURE(status)) { return nullptr;
}
umtx_lock(&gLock);
{ // now try it again
fGNamesTrie.search(text, start, (TextTrieMapSearchResultHandler *)&handler, status);
}
umtx_unlock(&gLock);
results = handler.getMatches(maxLen); if (results != nullptr && maxLen > 0) {
gmatchInfo = new TimeZoneGenericNameMatchInfo(results); if (gmatchInfo == nullptr) {
status = U_MEMORY_ALLOCATION_ERROR; delete results; return nullptr;
}
}
return gmatchInfo;
}
TimeZoneNames::MatchInfoCollection*
TZGNCore::findTimeZoneNames(const UnicodeString& text, int32_t start, uint32_t types, UErrorCode& status) const { // Check if the target name typs is really in the TimeZoneNames
uint32_t nameTypes = 0; if (types & UTZGNM_LONG) {
nameTypes |= (UTZNM_LONG_GENERIC | UTZNM_LONG_STANDARD);
} if (types & UTZGNM_SHORT) {
nameTypes |= (UTZNM_SHORT_GENERIC | UTZNM_SHORT_STANDARD);
}
if (types) { // Find matches in the TimeZoneNames return fTimeZoneNames->find(text, start, nameTypes, status);
}
// Access count - incremented every time up to SWEEP_INTERVAL, // then reset to 0 static int32_t gAccessCount = 0;
// Interval for calling the cache sweep function - every 100 times #define SWEEP_INTERVAL 100
// Cache expiration in millisecond. When a cached entry is no // longer referenced and exceeding this threshold since last // access time, then the cache entry will be deleted by the sweep // function. For now, 3 minutes. #define CACHE_EXPIRATION 180000.0
/** * Function used for removing unreferrenced cache entries exceeding * the expiration time. This function must be called with in the mutex * block.
*/ staticvoid sweepCache() {
int32_t pos = UHASH_FIRST; const UHashElement* elem; double now = static_cast<double>(uprv_getUTCtime());
// Check the cache, if not available, create new one and cache constchar *key = locale.getName();
cacheEntry = static_cast<TZGNCoreRef*>(uhash_get(gTZGNCoreCache, key)); if (cacheEntry == nullptr) {
TZGNCore *tzgnCore = nullptr; char *newKey = nullptr;
tzgnCore = new TZGNCore(locale, status); if (tzgnCore == nullptr) {
status = U_MEMORY_ALLOCATION_ERROR;
} if (U_SUCCESS(status)) {
newKey = static_cast<char*>(uprv_malloc(uprv_strlen(key) + 1)); if (newKey == nullptr) {
status = U_MEMORY_ALLOCATION_ERROR;
} else {
uprv_strcpy(newKey, key);
}
} if (U_SUCCESS(status)) {
cacheEntry = static_cast<TZGNCoreRef*>(uprv_malloc(sizeof(TZGNCoreRef))); if (cacheEntry == nullptr) {
status = U_MEMORY_ALLOCATION_ERROR;
} else {
cacheEntry->obj = tzgnCore;
cacheEntry->refCount = 1;
cacheEntry->lastAccess = static_cast<double>(uprv_getUTCtime());
uhash_put(gTZGNCoreCache, newKey, cacheEntry, &status);
}
} if (U_FAILURE(status)) { delete tzgnCore; if (newKey != nullptr) {
uprv_free(newKey);
} if (cacheEntry != nullptr) {
uprv_free(cacheEntry);
}
cacheEntry = nullptr;
}
} else { // Update the reference count
cacheEntry->refCount++;
cacheEntry->lastAccess = static_cast<double>(uprv_getUTCtime());
}
gAccessCount++; if (gAccessCount >= SWEEP_INTERVAL) { // sweep
sweepCache();
gAccessCount = 0;
}
} // End of mutex locked block
if (cacheEntry == nullptr) { delete instance; return nullptr;
}
instance->fRef = cacheEntry; return instance;
}
bool
TimeZoneGenericNames::operator==(const TimeZoneGenericNames& other) const { // Just compare if the other object also use the same // ref entry return fRef == other.fRef;
}
TimeZoneGenericNames*
TimeZoneGenericNames::clone() const {
TimeZoneGenericNames* other = new TimeZoneGenericNames(); if (other) {
umtx_lock(&gTZGNLock);
{ // Just increments the reference count
fRef->refCount++;
other->fRef = fRef;
}
umtx_unlock(&gTZGNLock);
} return other;
}
¤ 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.0.21Bemerkung:
(vorverarbeitet)
¤
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.