// Order of GMT offset pattern parsing, *_HMS must be evaluated first // because *_HM is most likely a substring of *_HMS staticconst int32_t PARSE_GMT_OFFSET_TYPES[] = {
UTZFMT_PAT_POSITIVE_HMS,
UTZFMT_PAT_NEGATIVE_HMS,
UTZFMT_PAT_POSITIVE_HM,
UTZFMT_PAT_NEGATIVE_HM,
UTZFMT_PAT_POSITIVE_H,
UTZFMT_PAT_NEGATIVE_H,
-1
};
// ------------------------------------------------------------------ // GMTOffsetField // // This class represents a localized GMT offset pattern // item and used by TimeZoneFormat // ------------------------------------------------------------------ class GMTOffsetField : public UMemory { public: enum FieldType {
TEXT = 0,
HOUR = 1,
MINUTE = 2,
SECOND = 4
};
fTimeZoneNames = other.fTimeZoneNames->clone(); if (other.fTimeZoneGenericNames) { // TODO: this test has dubious thread safety.
fTimeZoneGenericNames = other.fTimeZoneGenericNames->clone();
}
for (int32_t i = 0; i < UTZFMT_PAT_COUNT && isEqual; i++) {
isEqual = fGMTOffsetPatterns[i] == tzfmt->fGMTOffsetPatterns[i];
} for (int32_t i = 0; i < 10 && isEqual; i++) {
isEqual = fGMTOffsetDigits[i] == tzfmt->fGMTOffsetDigits[i];
} // TODO // Check fTimeZoneGenericNames. For now, // if fTimeZoneNames is same, fTimeZoneGenericNames should // be also equivalent. return isEqual;
}
case UTZFMT_STYLE_EXEMPLAR_LOCATION:
formatExemplarLocation(tz, name);
noOffsetFormatFallback = true; break;
default: // will be handled below break;
}
if (name.isEmpty() && !noOffsetFormatFallback) {
UErrorCode status = U_ZERO_ERROR;
int32_t rawOffset, dstOffset;
tz.getOffset(date, false, rawOffset, dstOffset, status);
int32_t offset = rawOffset + dstOffset; if (U_SUCCESS(status)) { switch (style) { case UTZFMT_STYLE_GENERIC_LOCATION: case UTZFMT_STYLE_GENERIC_LONG: case UTZFMT_STYLE_SPECIFIC_LONG: case UTZFMT_STYLE_LOCALIZED_GMT:
formatOffsetLocalizedGMT(offset, name, status); break;
case UTZFMT_STYLE_GENERIC_SHORT: case UTZFMT_STYLE_SPECIFIC_SHORT: case UTZFMT_STYLE_LOCALIZED_GMT_SHORT:
formatOffsetShortLocalizedGMT(offset, name, status); break;
case UTZFMT_STYLE_ISO_BASIC_SHORT:
formatOffsetISO8601Basic(offset, true, true, true, name, status); break;
case UTZFMT_STYLE_ISO_BASIC_LOCAL_SHORT:
formatOffsetISO8601Basic(offset, false, true, true, name, status); break;
case UTZFMT_STYLE_ISO_BASIC_FIXED:
formatOffsetISO8601Basic(offset, true, false, true, name, status); break;
case UTZFMT_STYLE_ISO_BASIC_LOCAL_FIXED:
formatOffsetISO8601Basic(offset, false, false, true, name, status); break;
case UTZFMT_STYLE_ISO_EXTENDED_FIXED:
formatOffsetISO8601Extended(offset, true, false, true, name, status); break;
case UTZFMT_STYLE_ISO_EXTENDED_LOCAL_FIXED:
formatOffsetISO8601Extended(offset, false, false, true, name, status); break;
case UTZFMT_STYLE_ISO_BASIC_FULL:
formatOffsetISO8601Basic(offset, true, false, false, name, status); break;
case UTZFMT_STYLE_ISO_BASIC_LOCAL_FULL:
formatOffsetISO8601Basic(offset, false, false, false, name, status); break;
case UTZFMT_STYLE_ISO_EXTENDED_FULL:
formatOffsetISO8601Extended(offset, true, false, false, name, status); break;
case UTZFMT_STYLE_ISO_EXTENDED_LOCAL_FULL:
formatOffsetISO8601Extended(offset, false, false, false, name, status); break;
int32_t parsedOffset = UNKNOWN_OFFSET; // stores successfully parsed offset for later use
int32_t parsedPos = -1; // stores successfully parsed offset position for later use
// Try localized GMT format first if necessary if (fallbackLocalizedGMT || fallbackShortLocalizedGMT) {
UBool hasDigitOffset = false;
offset = parseOffsetLocalizedGMT(text, tmpPos, fallbackShortLocalizedGMT, &hasDigitOffset); if (tmpPos.getErrorIndex() == -1) { // Even when the input text was successfully parsed as a localized GMT format text, // we may still need to evaluate the specified style if - // 1) GMT zero format was used, and // 2) The input text was not completely processed if (tmpPos.getIndex() == maxPos || hasDigitOffset) {
pos.setIndex(tmpPos.getIndex()); return createTimeZoneForOffset(offset);
}
parsedOffset = offset;
parsedPos = tmpPos.getIndex();
} // Note: For now, no distinction between long/short localized GMT format in the parser. // This might be changed in future. // evaluated |= (fallbackLocalizedGMT ? STYLE_PARSE_FLAGS[UTZFMT_STYLE_LOCALIZED_GMT] : STYLE_PARSE_FLAGS[UTZFMT_STYLE_LOCALIZED_GMT_SHORT]);
evaluated |= STYLE_PARSE_FLAGS[UTZFMT_STYLE_LOCALIZED_GMT] | STYLE_PARSE_FLAGS[UTZFMT_STYLE_LOCALIZED_GMT_SHORT];
}
UErrorCode status = U_ZERO_ERROR;
char16_t tzIDBuf[32];
UnicodeString tzID(tzIDBuf, 0, UPRV_LENGTHOF(tzIDBuf));
// Note: For now, no distinction between long/short localized GMT format in the parser. // This might be changed in future.
evaluated |= STYLE_PARSE_FLAGS[UTZFMT_STYLE_LOCALIZED_GMT_SHORT];
break;
} case UTZFMT_STYLE_LOCALIZED_GMT_SHORT:
{
tmpPos.setIndex(startIdx);
tmpPos.setErrorIndex(-1);
// Note: For now, no distinction between long/short localized GMT format in the parser. // This might be changed in future.
evaluated |= STYLE_PARSE_FLAGS[UTZFMT_STYLE_LOCALIZED_GMT];
break;
} case UTZFMT_STYLE_ISO_BASIC_SHORT: case UTZFMT_STYLE_ISO_BASIC_FIXED: case UTZFMT_STYLE_ISO_BASIC_FULL: case UTZFMT_STYLE_ISO_EXTENDED_FIXED: case UTZFMT_STYLE_ISO_EXTENDED_FULL:
{
tmpPos.setIndex(startIdx);
tmpPos.setErrorIndex(-1);
case UTZFMT_STYLE_ISO_BASIC_LOCAL_SHORT: case UTZFMT_STYLE_ISO_BASIC_LOCAL_FIXED: case UTZFMT_STYLE_ISO_BASIC_LOCAL_FULL: case UTZFMT_STYLE_ISO_EXTENDED_LOCAL_FIXED: case UTZFMT_STYLE_ISO_EXTENDED_LOCAL_FULL:
{
tmpPos.setIndex(startIdx);
tmpPos.setErrorIndex(-1);
// Exclude the case of UTC Indicator "Z" here
UBool hasDigitOffset = false;
offset = parseOffsetISO8601(text, tmpPos, false, &hasDigitOffset); if (tmpPos.getErrorIndex() == -1 && hasDigitOffset) {
pos.setIndex(tmpPos.getIndex()); return createTimeZoneForOffset(offset);
}
break;
}
case UTZFMT_STYLE_SPECIFIC_LONG: case UTZFMT_STYLE_SPECIFIC_SHORT:
{ // Specific styles
int32_t nameTypes = 0; if (style == UTZFMT_STYLE_SPECIFIC_LONG) {
nameTypes = (UTZNM_LONG_STANDARD | UTZNM_LONG_DAYLIGHT);
} else {
U_ASSERT(style == UTZFMT_STYLE_SPECIFIC_SHORT);
nameTypes = (UTZNM_SHORT_STANDARD | UTZNM_SHORT_DAYLIGHT);
}
LocalPointer<TimeZoneNames::MatchInfoCollection> specificMatches(fTimeZoneNames->find(text, startIdx, nameTypes, status)); if (U_FAILURE(status)) {
pos.setErrorIndex(startIdx); return nullptr;
} if (!specificMatches.isNull()) {
int32_t matchIdx = -1;
int32_t matchPos = -1; for (int32_t i = 0; i < specificMatches->size(); i++) {
matchPos = startIdx + specificMatches->getMatchLengthAt(i); if (matchPos > parsedPos) {
matchIdx = i;
parsedPos = matchPos;
}
} if (matchIdx >= 0) { if (timeType) {
*timeType = getTimeType(specificMatches->getNameTypeAt(matchIdx));
}
pos.setIndex(matchPos);
getTimeZoneID(specificMatches.getAlias(), matchIdx, tzID);
U_ASSERT(!tzID.isEmpty()); return TimeZone::createTimeZone(tzID);
}
}
if (parsedPos > startIdx) { // When the specified style is one of SPECIFIC_XXX or GENERIC_XXX, we tried to parse the input // as localized GMT format earlier. If parsedOffset is positive, it means it was successfully // parsed as localized GMT format, but offset digits were not detected (more specifically, GMT // zero format). Then, it tried to find a match within the set of display names, but could not // find a match. At this point, we can safely assume the input text contains the localized // GMT format.
U_ASSERT(parsedOffset != UNKNOWN_OFFSET);
pos.setIndex(parsedPos); return createTimeZoneForOffset(parsedOffset);
}
// Failed to parse the input text as the time zone format in the specified style. // Check the longest match among other styles below.
char16_t parsedIDBuf[32];
UnicodeString parsedID(parsedIDBuf, 0, UPRV_LENGTHOF(parsedIDBuf));
UTimeZoneFormatTimeType parsedTimeType = UTZFMT_TIME_TYPE_UNKNOWN;
UBool hasDigitOffset = false;
offset = parseOffsetISO8601(text, tmpPos, false, &hasDigitOffset); if (tmpPos.getErrorIndex() == -1) { if (tmpPos.getIndex() == maxPos || hasDigitOffset) {
pos.setIndex(tmpPos.getIndex()); return createTimeZoneForOffset(offset);
} // Note: When ISO 8601 format contains offset digits, it should not // collide with other formats. However, ISO 8601 UTC format "Z" (single letter) // may collide with other names. In this case, we need to evaluate other names. if (parsedPos < tmpPos.getIndex()) {
parsedOffset = offset;
parsedID.setToBogus();
parsedTimeType = UTZFMT_TIME_TYPE_UNKNOWN;
parsedPos = tmpPos.getIndex();
U_ASSERT(parsedPos == startIdx + 1); // only when "Z" is used
}
}
}
// Localized GMT format if (parsedPos < maxPos &&
(evaluated & STYLE_PARSE_FLAGS[UTZFMT_STYLE_LOCALIZED_GMT]) == 0) {
tmpPos.setIndex(startIdx);
tmpPos.setErrorIndex(-1);
UBool hasDigitOffset = false;
offset = parseOffsetLocalizedGMT(text, tmpPos, false, &hasDigitOffset); if (tmpPos.getErrorIndex() == -1) { if (tmpPos.getIndex() == maxPos || hasDigitOffset) {
pos.setIndex(tmpPos.getIndex()); return createTimeZoneForOffset(offset);
} // Evaluate other names - see the comment earlier in this method. if (parsedPos < tmpPos.getIndex()) {
parsedOffset = offset;
parsedID.setToBogus();
parsedTimeType = UTZFMT_TIME_TYPE_UNKNOWN;
parsedPos = tmpPos.getIndex();
}
}
}
UBool hasDigitOffset = false;
offset = parseOffsetLocalizedGMT(text, tmpPos, true, &hasDigitOffset); if (tmpPos.getErrorIndex() == -1) { if (tmpPos.getIndex() == maxPos || hasDigitOffset) {
pos.setIndex(tmpPos.getIndex()); return createTimeZoneForOffset(offset);
} // Evaluate other names - see the comment earlier in this method. if (parsedPos < tmpPos.getIndex()) {
parsedOffset = offset;
parsedID.setToBogus();
parsedTimeType = UTZFMT_TIME_TYPE_UNKNOWN;
parsedPos = tmpPos.getIndex();
}
}
}
// When ParseOption.ALL_STYLES is available, we also try to look all possible display names and IDs. // For example, when style is GENERIC_LONG, "EST" (SPECIFIC_SHORT) is never // used for America/New_York. With parseAllStyles true, this code parses "EST" // as America/New_York.
// Note: Adding all possible names into the trie used by the implementation is quite heavy operation, // which we want to avoid normally (note that we cache the trie, so this is applicable to the // first time only as long as the cache does not expire).
if (parseOptions & UTZFMT_PARSE_OPTION_ALL_STYLES) { // Try all specific names and exemplar location names if (parsedPos < maxPos) {
LocalPointer<TimeZoneNames::MatchInfoCollection> specificMatches(fTimeZoneNames->find(text, startIdx, ALL_SIMPLE_NAME_TYPES, status)); if (U_FAILURE(status)) {
pos.setErrorIndex(startIdx); return nullptr;
}
int32_t specificMatchIdx = -1;
int32_t matchPos = -1; if (!specificMatches.isNull()) { for (int32_t i = 0; i < specificMatches->size(); i++) { if (startIdx + specificMatches->getMatchLengthAt(i) > matchPos) {
specificMatchIdx = i;
matchPos = startIdx + specificMatches->getMatchLengthAt(i);
}
}
} if (parsedPos < matchPos) {
U_ASSERT(specificMatchIdx >= 0);
parsedPos = matchPos;
getTimeZoneID(specificMatches.getAlias(), specificMatchIdx, parsedID);
parsedTimeType = getTimeType(specificMatches->getNameTypeAt(specificMatchIdx));
parsedOffset = UNKNOWN_OFFSET;
}
} if (parseTZDBAbbrev && parsedPos < maxPos && (evaluated & STYLE_PARSE_FLAGS[UTZFMT_STYLE_SPECIFIC_SHORT]) == 0) { const TZDBTimeZoneNames *tzdbTimeZoneNames = getTZDBTimeZoneNames(status); if (U_SUCCESS(status)) {
LocalPointer<TimeZoneNames::MatchInfoCollection> tzdbNameMatches(
tzdbTimeZoneNames->find(text, startIdx, ALL_SIMPLE_NAME_TYPES, status)); if (U_FAILURE(status)) {
pos.setErrorIndex(startIdx); return nullptr;
}
int32_t tzdbNameMatchIdx = -1;
int32_t matchPos = -1; if (!tzdbNameMatches.isNull()) { for (int32_t i = 0; i < tzdbNameMatches->size(); i++) { if (startIdx + tzdbNameMatches->getMatchLengthAt(i) > matchPos) {
tzdbNameMatchIdx = i;
matchPos = startIdx + tzdbNameMatches->getMatchLengthAt(i);
}
}
} if (parsedPos < matchPos) {
U_ASSERT(tzdbNameMatchIdx >= 0);
parsedPos = matchPos;
getTimeZoneID(tzdbNameMatches.getAlias(), tzdbNameMatchIdx, parsedID);
parsedTimeType = getTimeType(tzdbNameMatches->getNameTypeAt(tzdbNameMatchIdx));
parsedOffset = UNKNOWN_OFFSET;
}
}
} // Try generic names if (parsedPos < maxPos) {
int32_t genMatchLen = -1;
UTimeZoneFormatTimeType tt = UTZFMT_TIME_TYPE_UNKNOWN;
// Try time zone ID if (parsedPos < maxPos && (evaluated & STYLE_PARSE_FLAGS[UTZFMT_STYLE_ZONE_ID]) == 0) {
tmpPos.setIndex(startIdx);
tmpPos.setErrorIndex(-1);
parseZoneID(text, tmpPos, tzID); if (tmpPos.getErrorIndex() == -1 && parsedPos < tmpPos.getIndex()) {
parsedPos = tmpPos.getIndex();
parsedID.setTo(tzID);
parsedTimeType = UTZFMT_TIME_TYPE_UNKNOWN;
parsedOffset = UNKNOWN_OFFSET;
}
} // Try short time zone ID if (parsedPos < maxPos && (evaluated & STYLE_PARSE_FLAGS[UTZFMT_STYLE_ZONE_ID]) == 0) {
tmpPos.setIndex(startIdx);
tmpPos.setErrorIndex(-1);
int32_t lastIdx = maxFields; while (lastIdx > minFields) { if (fields[lastIdx] != 0) { break;
}
lastIdx--;
}
char16_t sign = PLUS; if (offset < 0) { // if all output fields are 0s, do not use negative sign for (int32_t idx = 0; idx <= lastIdx; idx++) { if (fields[idx] != 0) {
sign = MINUS; break;
}
}
}
result.setTo(sign);
// Building the GMT format string
result.setTo(fGMTPatternPrefix);
for (int32_t i = 0; i < offsetPatternItems->size(); i++) { const GMTOffsetField* item = static_cast<GMTOffsetField*>(offsetPatternItems->elementAt(i));
GMTOffsetField::FieldType type = item->getType();
switch (type) { case GMTOffsetField::TEXT:
result.append(item->getPatternText(), -1); break;
case GMTOffsetField::HOUR:
appendOffsetDigits(result, offsetH, (isShort ? 1 : 2)); break;
case GMTOffsetField::MINUTE:
appendOffsetDigits(result, offsetM, 2); break;
case GMTOffsetField::SECOND:
appendOffsetDigits(result, offsetS, 2); break;
}
}
int32_t sign = 1; if (firstChar == PLUS) {
sign = 1;
} elseif (firstChar == MINUS) {
sign = -1;
} else { // Not an ISO 8601 offset string
pos.setErrorIndex(start); return 0;
}
ParsePosition posOffset(start + 1);
int32_t offset = parseAsciiOffsetFields(text, posOffset, ISO8601_SEP, FIELDS_H, FIELDS_HMS); if (posOffset.getErrorIndex() == -1 && !extendedOnly && (posOffset.getIndex() - start <= 3)) { // If the text is successfully parsed as extended format with the options above, it can be also parsed // as basic format. For example, "0230" can be parsed as offset 2:00 (only first digits are valid for // extended format), but it can be parsed as offset 2:30 with basic format. We use longer result.
ParsePosition posBasic(start + 1);
int32_t tmpOffset = parseAbuttingAsciiOffsetFields(text, posBasic, FIELDS_H, FIELDS_HMS, false); if (posBasic.getErrorIndex() == -1 && posBasic.getIndex() > posOffset.getIndex()) {
offset = tmpOffset;
posOffset.setIndex(posBasic.getIndex());
}
}
if (posOffset.getErrorIndex() != -1) {
pos.setErrorIndex(start); return 0;
}
// For now, parseOffsetLocalizedGMTPattern handles both long and short // formats, no matter isShort is true or false. This might be changed in future // when strict parsing is necessary, or different set of patterns are used for // short/long formats. #if 0 if (parsedLength == 0) {
offset = parseOffsetLocalizedGMTPattern(text, start, !isShort, parsedLength);
} #endif
if (parsedLength > 0) { if (hasDigitOffset) {
*hasDigitOffset = true;
}
pos.setIndex(start + parsedLength); return offset;
}
// Try the default patterns
offset = parseOffsetDefaultLocalizedGMT(text, start, parsedLength); if (parsedLength > 0) { if (hasDigitOffset) {
*hasDigitOffset = true;
}
pos.setIndex(start + parsedLength); return offset;
}
// Check if this is a GMT zero format if (text.caseCompare(start, fGMTZeroFormat.length(), fGMTZeroFormat, 0) == 0) {
pos.setIndex(start + fGMTZeroFormat.length()); return 0;
}
// Check if this is a default GMT zero format for (int32_t i = 0; ALT_GMT_STRINGS[i][0] != 0; i++) { const char16_t* defGMTZero = ALT_GMT_STRINGS[i];
int32_t defGMTZeroLen = u_strlen(defGMTZero); if (text.caseCompare(start, defGMTZeroLen, defGMTZero, 0) == 0) {
pos.setIndex(start + defGMTZeroLen); return 0;
}
}
if (outLen > 0 && fAbuttingOffsetHoursAndMinutes) { // When hours field is sabutting minutes field, // the parse result above may not be appropriate. // For example, "01020" is parsed as 01:02: above, // but it should be parsed as 00:10:20.
int32_t tmpLen = 0;
int32_t tmpSign = 1;
int32_t tmpH = 0;
int32_t tmpM = 0;
int32_t tmpS = 0;
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.