staticconstchar EMPTY[] = ""; // place holder for empty ZNames staticconstchar DUMMY_LOADER[] = ""; // place holder for dummy ZNamesLoader staticconst char16_t NO_NAME[] = { 0 }; // for empty no-fallback time zone names
// The order in which strings are stored may be different than the order in the public enum. enum UTimeZoneNameTypeIndex {
UTZNM_INDEX_UNKNOWN = -1,
UTZNM_INDEX_EXEMPLAR_LOCATION,
UTZNM_INDEX_LONG_GENERIC,
UTZNM_INDEX_LONG_STANDARD,
UTZNM_INDEX_LONG_DAYLIGHT,
UTZNM_INDEX_SHORT_GENERIC,
UTZNM_INDEX_SHORT_STANDARD,
UTZNM_INDEX_SHORT_DAYLIGHT,
UTZNM_INDEX_COUNT
}; staticconst char16_t* const EMPTY_NAMES[UTZNM_INDEX_COUNT] = {
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr
};
void CharacterNode::deleteValues(UObjectDeleter *valueDeleter) { if (fValues == nullptr) { // Do nothing.
} elseif (!fHasValuesVector) { if (valueDeleter) {
valueDeleter(fValues);
}
} else { deletestatic_cast<UVector*>(fValues);
}
}
void
CharacterNode::addValue(void *value, UObjectDeleter *valueDeleter, UErrorCode &status) { if (U_FAILURE(status)) { if (valueDeleter) {
valueDeleter(value);
} return;
} if (fValues == nullptr) {
fValues = value;
} else { // At least one value already. if (!fHasValuesVector) { // There is only one value so far, and not in a vector yet. // Create a vector and add the old value.
LocalPointer<UVector> values( new UVector(valueDeleter, nullptr, DEFAULT_CHARACTERNODE_CAPACITY, status), status); if (U_FAILURE(status)) { if (valueDeleter) {
valueDeleter(value);
} return;
} if (values->hasDeleter()) {
values->adoptElement(fValues, status);
} else {
values->addElement(fValues, status);
}
fValues = values.orphan();
fHasValuesVector = true;
} // Add the new value.
UVector* values = static_cast<UVector*>(fValues); if (values->hasDeleter()) {
values->adoptElement(value, status);
} else {
values->addElement(value, status);
}
}
}
// --------------------------------------------------- // TextTrieMapSearchResultHandler class implementation // ---------------------------------------------------
TextTrieMapSearchResultHandler::~TextTrieMapSearchResultHandler(){
}
TextTrieMap::~TextTrieMap() {
int32_t index; for (index = 0; index < fNodesCount; ++index) {
fNodes[index].deleteValues(fValueDeleter);
}
uprv_free(fNodes); if (fLazyContents != nullptr) { for (int32_t i=0; i<fLazyContents->size(); i+=2) { if (fValueDeleter) {
fValueDeleter(fLazyContents->elementAt(i+1));
}
} delete fLazyContents;
}
}
int32_t TextTrieMap::isEmpty() const { // Use a separate field for fIsEmpty because it will remain unchanged once the // Trie is built, while fNodes and fLazyContents change with the lazy init // of the nodes structure. Trying to test the changing fields has // thread safety complications. return fIsEmpty;
}
// We defer actually building the TextTrieMap node structure until the first time a // search is performed. put() simply saves the parameters in case we do // eventually need to build it. // void
TextTrieMap::put(const UnicodeString &key, void *value, ZNStringPool &sp, UErrorCode &status) { const char16_t *s = sp.get(key, status);
put(s, value, status);
}
// This method is designed for a persistent key, such as string key stored in // resource bundle. void
TextTrieMap::put(const char16_t *key, void *value, UErrorCode &status) {
fIsEmpty = false; if (fLazyContents == nullptr) {
LocalPointer<UVector> lpLazyContents(new UVector(status), status);
fLazyContents = lpLazyContents.orphan();
} if (U_SUCCESS(status)) {
U_ASSERT(fLazyContents != nullptr);
char16_t *s = const_cast<char16_t *>(key);
fLazyContents->addElement(s, status); if (U_SUCCESS(status)) {
fLazyContents->addElement(value, status); return;
}
} if (fValueDeleter) {
fValueDeleter(value);
}
}
UnicodeString foldedKey; const char16_t *keyBuffer;
int32_t keyLength; if (fIgnoreCase) { // Ok to use fastCopyFrom() because we discard the copy when we return.
foldedKey.fastCopyFrom(key).foldCase();
keyBuffer = foldedKey.getBuffer();
keyLength = foldedKey.length();
} else {
keyBuffer = key.getBuffer();
keyLength = key.length();
}
CharacterNode*
TextTrieMap::addChildNode(CharacterNode *parent, char16_t c, UErrorCode &status) { if (U_FAILURE(status)) { return nullptr;
} // Linear search of the sorted list of children.
uint16_t prevIndex = 0;
uint16_t nodeIndex = parent->fFirstChild; while (nodeIndex > 0) {
CharacterNode *current = fNodes + nodeIndex;
char16_t childCharacter = current->fCharacter; if (childCharacter == c) { return current;
} elseif (childCharacter > c) { break;
}
prevIndex = nodeIndex;
nodeIndex = current->fNextSibling;
}
// Ensure capacity. Grow fNodes[] if needed. if (fNodesCount == fNodesCapacity) {
int32_t parentIndex = static_cast<int32_t>(parent - fNodes); if (!growNodes()) {
status = U_MEMORY_ALLOCATION_ERROR; return nullptr;
}
parent = fNodes + parentIndex;
}
// Insert a new child node with c in sorted order.
CharacterNode *node = fNodes + fNodesCount;
node->clear();
node->fCharacter = c;
node->fNextSibling = nodeIndex; if (prevIndex == 0) {
parent->fFirstChild = static_cast<uint16_t>(fNodesCount);
} else {
fNodes[prevIndex].fNextSibling = static_cast<uint16_t>(fNodesCount);
}
++fNodesCount; return node;
}
CharacterNode*
TextTrieMap::getChildNode(CharacterNode *parent, char16_t c) const { // Linear search of the sorted list of children.
uint16_t nodeIndex = parent->fFirstChild; while (nodeIndex > 0) {
CharacterNode *current = fNodes + nodeIndex;
char16_t childCharacter = current->fCharacter; if (childCharacter == c) { return current;
} elseif (childCharacter > c) { break;
}
nodeIndex = current->fNextSibling;
} return nullptr;
}
// buildTrie() - The Trie node structure is needed. Create it from the data that was // saved at the time the ZoneStringFormatter was created. The Trie is only // needed for parsing operations, which are less common than formatting, // and the Trie is big, which is why its creation is deferred until first use. void TextTrieMap::buildTrie(UErrorCode &status) { if (fLazyContents != nullptr) { for (int32_t i=0; i<fLazyContents->size(); i+=2) { const char16_t* key = static_cast<char16_t*>(fLazyContents->elementAt(i)); void *val = fLazyContents->elementAt(i+1);
UnicodeString keyString(true, key, -1); // Aliasing UnicodeString constructor.
putImpl(keyString, val, status);
} delete fLazyContents;
fLazyContents = nullptr;
}
}
void
TextTrieMap::search(const UnicodeString &text, int32_t start,
TextTrieMapSearchResultHandler *handler, UErrorCode &status) const {
{ // TODO: if locking the mutex for each check proves to be a performance problem, // add a flag of type atomic_int32_t to class TextTrieMap, and use only // the ICU atomic safe functions for assigning and testing. // Don't test the pointer fLazyContents. // Don't do unless it's really required.
// Mutex for protecting the lazy creation of the Trie node structure on the first call to search(). static UMutex TextTrieMutex;
void
TextTrieMap::search(CharacterNode *node, const UnicodeString &text, int32_t start,
int32_t index, TextTrieMapSearchResultHandler *handler, UErrorCode &status) const { if (U_FAILURE(status)) { return;
} if (node->hasValues()) { if (!handler->handleMatch(index - start, node, status)) { return;
} if (U_FAILURE(status)) { return;
}
} if (fIgnoreCase) { // for folding we need to get a complete code point. // size of character may grow after fold operation; // then we need to get result as UTF16 code units.
UChar32 c32 = text.char32At(index);
index += U16_LENGTH(c32);
UnicodeString tmp(c32);
tmp.foldCase();
int32_t tmpidx = 0; while (tmpidx < tmp.length()) {
char16_t c = tmp.charAt(tmpidx++);
node = getChildNode(node, c); if (node == nullptr) { break;
}
}
} else { // here we just get the next UTF16 code unit
char16_t c = text.charAt(index++);
node = getChildNode(node, c);
} if (node != nullptr) {
search(node, text, start, index, handler, status);
}
}
// --------------------------------------------------- // ZNStringPool class implementation // --------------------------------------------------- staticconst int32_t POOL_CHUNK_SIZE = 2000; struct ZNStringPoolChunk: public UMemory {
ZNStringPoolChunk *fNext; // Ptr to next pool chunk
int32_t fLimit; // Index to start of unused area at end of fStrings
char16_t fStrings[POOL_CHUNK_SIZE]; // Strings array
ZNStringPoolChunk();
};
// // ZNStringPool::adopt() Put a string into the hash, but do not copy the string data // into the pool's storage. Used for strings from resource bundles, // which will persist for the life of the zone string formatter, and // therefore can be used directly without copying. const char16_t *ZNStringPool::adopt(const char16_t * s, UErrorCode &status) { const char16_t *pooledString; if (U_FAILURE(status)) { return &EmptyString;
} if (s != nullptr) {
pooledString = static_cast<char16_t *>(uhash_get(fHash, s)); if (pooledString == nullptr) {
char16_t *ncs = const_cast<char16_t *>(s);
uhash_put(fHash, ncs, ncs, &status);
}
} return s;
}
/* * freeze(). Close the hash table that maps to the pooled strings. * After freezing, the pool can not be searched or added to, * but all existing references to pooled strings remain valid. * * The main purpose is to recover the storage used for the hash.
*/ void ZNStringPool::freeze() {
uhash_close(fHash);
fHash = nullptr;
}
/** * This class stores name data for a meta zone or time zone.
*/ class ZNames : public UMemory { private: friendclass TimeZoneNamesImpl;
static UTimeZoneNameTypeIndex getTZNameTypeIndex(UTimeZoneNameType type) { switch(type) { case UTZNM_EXEMPLAR_LOCATION: return UTZNM_INDEX_EXEMPLAR_LOCATION; case UTZNM_LONG_GENERIC: return UTZNM_INDEX_LONG_GENERIC; case UTZNM_LONG_STANDARD: return UTZNM_INDEX_LONG_STANDARD; case UTZNM_LONG_DAYLIGHT: return UTZNM_INDEX_LONG_DAYLIGHT; case UTZNM_SHORT_GENERIC: return UTZNM_INDEX_SHORT_GENERIC; case UTZNM_SHORT_STANDARD: return UTZNM_INDEX_SHORT_STANDARD; case UTZNM_SHORT_DAYLIGHT: return UTZNM_INDEX_SHORT_DAYLIGHT; default: return UTZNM_INDEX_UNKNOWN;
}
} static UTimeZoneNameType getTZNameType(UTimeZoneNameTypeIndex index) { switch(index) { case UTZNM_INDEX_EXEMPLAR_LOCATION: return UTZNM_EXEMPLAR_LOCATION; case UTZNM_INDEX_LONG_GENERIC: return UTZNM_LONG_GENERIC; case UTZNM_INDEX_LONG_STANDARD: return UTZNM_LONG_STANDARD; case UTZNM_INDEX_LONG_DAYLIGHT: return UTZNM_LONG_DAYLIGHT; case UTZNM_INDEX_SHORT_GENERIC: return UTZNM_SHORT_GENERIC; case UTZNM_INDEX_SHORT_STANDARD: return UTZNM_SHORT_STANDARD; case UTZNM_INDEX_SHORT_DAYLIGHT: return UTZNM_SHORT_DAYLIGHT; default: return UTZNM_UNKNOWN;
}
}
// Whether we own the location string, if computed rather than loaded from a bundle. // A meta zone names instance never has an exemplar location string.
UBool fOwnsLocationName;
// Use the persistent ID as the resource key, so we can // avoid duplications. // TODO: Is there a more efficient way, like intern() in Java? void* key = (void*) ZoneMeta::findMetaZoneID(mzID); void* value; if (uprv_memcmp(names, EMPTY_NAMES, sizeof(EMPTY_NAMES)) == 0) {
value = (void*) EMPTY;
} else {
value = (void*) (new ZNames(names, nullptr)); if (value == nullptr) {
status = U_MEMORY_ALLOCATION_ERROR; return nullptr;
}
}
uhash_put(cache, key, value, &status); return value;
}
// If necessary, compute the location name from the time zone name.
char16_t* locationName = nullptr; if (names[UTZNM_INDEX_EXEMPLAR_LOCATION] == nullptr) {
UnicodeString locationNameUniStr;
TimeZoneNamesImpl::getDefaultExemplarLocationName(tzID, locationNameUniStr);
// Copy the computed location name to the heap if (locationNameUniStr.length() > 0) { const char16_t* buff = locationNameUniStr.getTerminatedBuffer();
int32_t len = sizeof(char16_t) * (locationNameUniStr.length() + 1);
locationName = static_cast<char16_t*>(uprv_malloc(len)); if (locationName == nullptr) {
status = U_MEMORY_ALLOCATION_ERROR; return nullptr;
}
uprv_memcpy(locationName, buff, len);
}
}
// Use the persistent ID as the resource key, so we can // avoid duplications. // TODO: Is there a more efficient way, like intern() in Java? void* key = (void*) ZoneMeta::findTimeZoneID(tzID); void* value = (void*) (new ZNames(names, locationName)); if (value == nullptr) {
status = U_MEMORY_ALLOCATION_ERROR; return nullptr;
}
uhash_put(cache, key, value, &status); return value;
}
const char16_t* getName(UTimeZoneNameType type) const {
UTimeZoneNameTypeIndex index = getTZNameTypeIndex(type); return index >= 0 ? fNames[index] : nullptr;
}
// Ignore errors, but propagate possible warnings. if (U_SUCCESS(localStatus)) {
errorCode = localStatus;
}
}
void setNameIfEmpty(constchar* key, const ResourceValue* value, UErrorCode& errorCode) {
UTimeZoneNameTypeIndex type = nameTypeFromKey(key); if (type == UTZNM_INDEX_UNKNOWN) { return; } if (names[type] == nullptr) {
int32_t length; // 'NO_NAME' indicates internally that this field should remain empty. It will be // replaced by 'nullptr' in getNames()
names[type] = (value == nullptr) ? NO_NAME : value->getString(length, errorCode);
}
}
/** * Returns an array of names. It is the caller's responsibility to copy the data into a * permanent location, as the returned array is owned by the loader instance and may be * cleared or leave scope. * * This is different than Java, where the array will no longer be modified and null * may be returned.
*/ const char16_t** getNames() { // Remove 'NO_NAME' references in the array and replace with 'nullptr' for (int32_t i = 0; i < UTZNM_INDEX_COUNT; ++i) { if (names[i] == NO_NAME) {
names[i] = nullptr;
}
} return names;
}
};
ZNames::ZNamesLoader::~ZNamesLoader() {}
// --------------------------------------------------- // The meta zone ID enumeration class // --------------------------------------------------- class MetaZoneIDsEnumeration : public StringEnumeration { public:
MetaZoneIDsEnumeration();
MetaZoneIDsEnumeration(const UVector& mzIDs);
MetaZoneIDsEnumeration(LocalPointer<UVector> mzIDs); virtual ~MetaZoneIDsEnumeration(); static UClassID U_EXPORT2 getStaticClassID(); virtual UClassID getDynamicClassID() const override; virtualconst UnicodeString* snext(UErrorCode& status) override; virtualvoid reset(UErrorCode& status) override; virtual int32_t count(UErrorCode& status) const override; private:
int32_t fLen;
int32_t fPos; const UVector* fMetaZoneIDs;
LocalPointer<UVector> fLocalVector;
};
UBool
ZNameSearchHandler::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++) {
ZNameInfo *nameinfo = (ZNameInfo *)node->getValue(i); if (nameinfo == nullptr) { continue;
} if ((nameinfo->type & fTypes) != 0) { // matches a requested type if (fResults == nullptr) {
fResults = new TimeZoneNames::MatchInfoCollection(); if (fResults == nullptr) {
status = U_MEMORY_ALLOCATION_ERROR;
}
} if (U_SUCCESS(status)) {
U_ASSERT(fResults != nullptr); if (nameinfo->tzID) {
fResults->addZone(nameinfo->type, matchLength, UnicodeString(nameinfo->tzID, -1), status);
} else {
U_ASSERT(nameinfo->mzID);
fResults->addMetaZone(nameinfo->type, matchLength, UnicodeString(nameinfo->mzID, -1), status);
} if (U_SUCCESS(status) && matchLength > fMaxMatchLen) {
fMaxMatchLen = matchLength;
}
}
}
}
} returntrue;
}
TimeZoneNames::MatchInfoCollection*
ZNameSearchHandler::getMatches(int32_t& maxMatchLen) { // give the ownership to the caller
TimeZoneNames::MatchInfoCollection* results = fResults;
maxMatchLen = fMaxMatchLen;
// --------------------------------------------------- // TimeZoneNamesImpl // // TimeZoneNames implementation class. This is the main // part of this module. // ---------------------------------------------------
// Load zoneStrings bundle
UErrorCode tmpsts = U_ZERO_ERROR; // OK with fallback warning..
fZoneStrings = ures_open(U_ICUDATA_ZONE, locale.getName(), &tmpsts);
fZoneStrings = ures_getByKeyWithFallback(fZoneStrings, gZoneStrings, fZoneStrings, &tmpsts); if (U_FAILURE(tmpsts)) {
status = tmpsts;
cleanup(); return;
}
// Initialize hashtables holding time zone/meta zone names
fMZNamesMap = uhash_open(uhash_hashUChars, uhash_compareUChars, nullptr, &status);
fTZNamesMap = uhash_open(uhash_hashUChars, uhash_compareUChars, nullptr, &status); if (U_FAILURE(status)) {
cleanup(); return;
}
uhash_setValueDeleter(fMZNamesMap, deleteZNames);
uhash_setValueDeleter(fTZNamesMap, deleteZNames); // no key deleters for name maps
// preload zone strings for the default zone
TimeZone *tz = TimeZone::createDefault(); const char16_t *tzID = ZoneMeta::getCanonicalCLDRID(*tz); if (tzID != nullptr) {
loadStrings(UnicodeString(tzID), status);
} delete tz;
}
/* * This method updates the cache and must be called with a lock, * except initializer.
*/ void
TimeZoneNamesImpl::loadStrings(const UnicodeString& tzCanonicalID, UErrorCode& status) {
loadTimeZoneNames(tzCanonicalID, status);
LocalPointer<StringEnumeration> mzIDs(getAvailableMetaZoneIDs(tzCanonicalID, status)); if (U_FAILURE(status)) { return; }
U_ASSERT(!mzIDs.isNull());
/* * This method updates the cache and must be called with a lock
*/
ZNames*
TimeZoneNamesImpl::loadMetaZoneNames(const UnicodeString& mzID, UErrorCode& status) { if (U_FAILURE(status)) { return nullptr; } if (mzID.length() > ZID_KEY_MAX - MZ_PREFIX_LEN) {
status = U_INTERNAL_PROGRAM_ERROR; return nullptr;
}
/* * This method updates the cache and must be called with a lock
*/
ZNames*
TimeZoneNamesImpl::loadTimeZoneNames(const UnicodeString& tzID, UErrorCode& status) { if (U_FAILURE(status)) { return nullptr; } if (tzID.length() > ZID_KEY_MAX) {
status = U_INTERNAL_PROGRAM_ERROR; return nullptr;
}
// Synchronize so that data is not loaded multiple times. // TODO: Consider more fine-grained synchronization.
{
Mutex lock(&gDataMutex);
// First try of lookup.
matches = doFind(handler, text, start, status); if (U_FAILURE(status)) { return nullptr; } if (matches != nullptr) { return matches;
}
// All names are not yet loaded into the trie. // We may have loaded names for formatting several time zones, // and might be parsing one of those. // Populate the parsing trie from all of the already-loaded names.
nonConstThis->addAllNamesIntoTrie(status);
// Second try of lookup.
matches = doFind(handler, text, start, status); if (U_FAILURE(status)) { return nullptr; } if (matches != nullptr) { return matches;
}
// There are still some names we haven't loaded into the trie yet. // Load everything now.
nonConstThis->internalLoadAllDisplayNames(status);
nonConstThis->addAllNamesIntoTrie(status);
nonConstThis->fNamesTrieFullyLoaded = true; if (U_FAILURE(status)) { return nullptr; }
// Third try: we must return this one. return doFind(handler, text, start, status);
}
}
uhash_put(keyToLoader, newKey, loader, &status); if (U_FAILURE(status)) { return; }
}
if (loader != DUMMY_LOADER) { // Let the ZNamesLoader consume the names table. static_cast<ZNames::ZNamesLoader*>(loader)->put(key, value, noFallback, status);
}
}
virtualvoid put(constchar *key, ResourceValue &value, UBool noFallback,
UErrorCode &status) override {
ResourceTable timeZonesTable = value.getTable(status); if (U_FAILURE(status)) { return; } for (int32_t i = 0; timeZonesTable.getKeyAndValue(i, key, value); ++i) {
U_ASSERT(!value.isNoInheritanceMarker()); if (value.getType() == URES_TABLE) {
consumeNamesTable(key, value, noFallback, status);
} else { // Ignore fields that aren't tables (e.g., fallbackFormat and regionFormatStandard). // All time zone fields are tables.
} if (U_FAILURE(status)) { return; }
}
}
};
// Virtual destructors must be defined out of line.
TimeZoneNamesImpl::ZoneStringsLoader::~ZoneStringsLoader() {
uhash_close(keyToLoader);
}
void TimeZoneNamesImpl::loadAllDisplayNames(UErrorCode& status) { if (U_FAILURE(status)) return;
// Load the time zone strings
{
Mutex lock(&gDataMutex);
tznames = (void*) nonConstThis->loadTimeZoneNames(tzID, status); if (U_FAILURE(status)) { return; }
}
U_ASSERT(tznames != nullptr);
// Load the values into the dest array for (int i = 0; i < numTypes; i++) {
UTimeZoneNameType type = types[i]; const char16_t* name = static_cast<ZNames*>(tznames)->getName(type); if (name == nullptr) { if (mznames == nullptr) { // Load the meta zone name
UnicodeString mzID;
getMetaZoneID(tzID, date, mzID); if (mzID.isEmpty()) {
mznames = (void*) EMPTY;
} else { // Load the meta zone strings // Mutex is scoped to the "else" statement
Mutex lock(&gDataMutex);
mznames = (void*) nonConstThis->loadMetaZoneNames(mzID, status); if (U_FAILURE(status)) { return; } // Note: when the metazone doesn't exist, in Java, loadMetaZoneNames returns // a dummy object instead of nullptr. if (mznames == nullptr) {
mznames = (void*) EMPTY;
}
}
}
U_ASSERT(mznames != nullptr); if (mznames != EMPTY) {
name = static_cast<ZNames*>(mznames)->getName(type);
}
} if (name != nullptr) {
dest[i].setTo(true, name, -1);
} else {
dest[i].setToBogus();
}
}
}
// Caller must synchronize. void TimeZoneNamesImpl::internalLoadAllDisplayNames(UErrorCode& status) { if (!fNamesFullyLoaded) {
fNamesFullyLoaded = true;
ZoneStringsLoader loader(*this, status);
loader.load(status); if (U_FAILURE(status)) { return; }
const UnicodeString *id;
// load strings for all zones
StringEnumeration *tzIDs = TimeZone::createTimeZoneIDEnumeration(
UCAL_ZONE_TYPE_CANONICAL, nullptr, nullptr, status); if (U_SUCCESS(status)) { while ((id = tzIDs->snext(status)) != nullptr) { if (U_FAILURE(status)) { break;
}
UnicodeString copy(*id); void* value = uhash_get(fTZNamesMap, copy.getTerminatedBuffer()); if (value == nullptr) { // loadStrings also loads related metazone strings
loadStrings(*id, status);
}
}
} delete tzIDs;
}
}
// --------------------------------------------------- // TZDBTimeZoneNames and its supporting classes // // TZDBTimeZoneNames is an implementation class of // TimeZoneNames holding the IANA tz database abbreviations. // ---------------------------------------------------
class TZDBNames : public UMemory { public: virtual ~TZDBNames();
if (node->hasValues()) {
int32_t valuesCount = node->countValues(); for (int32_t i = 0; i < valuesCount; i++) {
TZDBNameInfo *ninfo = (TZDBNameInfo *)node->getValue(i); if (ninfo == nullptr) { continue;
} if ((ninfo->type & fTypes) != 0) { // Some tz database abbreviations are ambiguous. For example, // CST means either Central Standard Time or China Standard Time. // Unlike CLDR time zone display names, this implementation // does not use unique names. And TimeZoneFormat does not expect // multiple results returned for the same time zone type. // For this reason, this implementation resolve one among same // zone type with a same name at this level. if (ninfo->parseRegions == nullptr) { // parseRegions == null means this is the default metazone // mapping for the abbreviation. if (defaultRegionMatch == nullptr) {
match = defaultRegionMatch = ninfo;
}
} else {
UBool matchRegion = false; // non-default metazone mapping for an abbreviation // comes with applicable regions. For example, the default // metazone mapping for "CST" is America_Central,
--> --------------------
--> maximum size reached
--> --------------------
Messung V0.5
¤ Dauer der Verarbeitung: 0.66 Sekunden
(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.