+// © 2016 and later: Unicode, Inc. and others.
+// License & terms of use: http://www.unicode.org/copyright.html
/*
*******************************************************************************
-* Copyright (C) 2011-2013, International Business Machines Corporation and
+* Copyright (C) 2011-2015, International Business Machines Corporation and
* others. All Rights Reserved.
*******************************************************************************
*/
#include "unicode/calendar.h"
#include "unicode/tzfmt.h"
#include "unicode/numsys.h"
+#include "unicode/strenum.h"
#include "unicode/uchar.h"
#include "unicode/udat.h"
+#include "unicode/ustring.h"
+#include "unicode/utf16.h"
#include "tzgnames.h"
#include "cmemory.h"
#include "cstring.h"
#include "uvector.h"
#include "zonemeta.h"
#include "tznames_impl.h" // TextTrieMap
+#include "patternprops.h"
U_NAMESPACE_BEGIN
UOBJECT_DEFINE_RTTI_IMPLEMENTATION(TimeZoneFormat)
TimeZoneFormat::TimeZoneFormat(const Locale& locale, UErrorCode& status)
-: fLocale(locale), fTimeZoneNames(NULL), fTimeZoneGenericNames(NULL), fDefParseOptionFlags(0) {
+: fLocale(locale), fTimeZoneNames(NULL), fTimeZoneGenericNames(NULL),
+ fDefParseOptionFlags(0), fTZDBTimeZoneNames(NULL) {
for (int32_t i = 0; i < UTZFMT_PAT_COUNT; i++) {
fGMTOffsetPatternItems[i] = NULL;
}
const char* region = fLocale.getCountry();
- int32_t regionLen = uprv_strlen(region);
+ int32_t regionLen = static_cast<int32_t>(uprv_strlen(region));
if (regionLen == 0) {
char loc[ULOC_FULLNAME_CAPACITY];
uloc_addLikelySubtags(fLocale.getName(), loc, sizeof(loc), &status);
if (gmtPattern == NULL) {
gmtPattern = DEFAULT_GMT_PATTERN;
}
- initGMTPattern(UnicodeString(gmtPattern, -1), status);
+ initGMTPattern(UnicodeString(TRUE, gmtPattern, -1), status);
UBool useDefaultOffsetPatterns = TRUE;
if (hourFormats) {
}
TimeZoneFormat::TimeZoneFormat(const TimeZoneFormat& other)
-: Format(other), fTimeZoneNames(NULL), fTimeZoneGenericNames(NULL) {
+: Format(other), fTimeZoneNames(NULL), fTimeZoneGenericNames(NULL),
+ fTZDBTimeZoneNames(NULL) {
for (int32_t i = 0; i < UTZFMT_PAT_COUNT; i++) {
fGMTOffsetPatternItems[i] = NULL;
TimeZoneFormat::~TimeZoneFormat() {
delete fTimeZoneNames;
delete fTimeZoneGenericNames;
+ delete fTZDBTimeZoneNames;
for (int32_t i = 0; i < UTZFMT_PAT_COUNT; i++) {
delete fGMTOffsetPatternItems[i];
}
delete fTimeZoneNames;
delete fTimeZoneGenericNames;
fTimeZoneGenericNames = NULL;
+ delete fTZDBTimeZoneNames;
+ fTZDBTimeZoneNames = NULL;
fLocale = other.fLocale;
uprv_memcpy(fTargetRegion, other.fTargetRegion, sizeof(fTargetRegion));
for (int32_t i = 0; i < UTZFMT_PAT_COUNT; i++) {
fGMTOffsetPatterns[i] = other.fGMTOffsetPatterns[i];
delete fGMTOffsetPatternItems[i];
+ fGMTOffsetPatternItems[i] = NULL;
}
initGMTOffsetPatterns(status);
U_ASSERT(U_SUCCESS(status));
if (tz != NULL) {
int32_t rawOffset, dstOffset;
tz->getOffset(date, FALSE, rawOffset, dstOffset, status);
- UnicodeString result;
+ UChar buf[ZONE_NAME_U16_MAX];
+ UnicodeString result(buf, 0, UPRV_LENGTHOF(buf));
formatOffsetLocalizedGMT(rawOffset + dstOffset, result, status);
if (U_SUCCESS(status)) {
appendTo.append(result);
}
UErrorCode status = U_ZERO_ERROR;
- UnicodeString tzID;
+ UChar tzIDBuf[32];
+ UnicodeString tzID(tzIDBuf, 0, UPRV_LENGTHOF(tzIDBuf));
+
+ UBool parseTZDBAbbrev = ((parseOptions & UTZFMT_PARSE_OPTION_TZ_DATABASE_ABBREVIATIONS) != 0);
// Try the specified style
switch (style) {
return TimeZone::createTimeZone(tzID);
}
}
+
+ if (parseTZDBAbbrev && style == UTZFMT_STYLE_SPECIFIC_SHORT) {
+ U_ASSERT((nameTypes & UTZNM_SHORT_STANDARD) != 0);
+ U_ASSERT((nameTypes & UTZNM_SHORT_DAYLIGHT) != 0);
+
+ const TZDBTimeZoneNames *tzdbTimeZoneNames = getTZDBTimeZoneNames(status);
+ if (U_SUCCESS(status)) {
+ LocalPointer<TimeZoneNames::MatchInfoCollection> tzdbNameMatches(
+ tzdbTimeZoneNames->find(text, startIdx, nameTypes, status));
+ if (U_FAILURE(status)) {
+ pos.setErrorIndex(startIdx);
+ return NULL;
+ }
+ if (!tzdbNameMatches.isNull()) {
+ int32_t matchIdx = -1;
+ int32_t matchPos = -1;
+ for (int32_t i = 0; i < tzdbNameMatches->size(); i++) {
+ matchPos = startIdx + tzdbNameMatches->getMatchLengthAt(i);
+ if (matchPos > parsedPos) {
+ matchIdx = i;
+ parsedPos = matchPos;
+ }
+ }
+ if (matchIdx >= 0) {
+ if (timeType) {
+ *timeType = getTimeType(tzdbNameMatches->getNameTypeAt(matchIdx));
+ }
+ pos.setIndex(matchPos);
+ getTimeZoneID(tzdbNameMatches.getAlias(), matchIdx, tzID);
+ U_ASSERT(!tzID.isEmpty());
+ return TimeZone::createTimeZone(tzID);
+ }
+ }
+ }
+ }
break;
}
case UTZFMT_STYLE_GENERIC_LONG:
// Failed to parse the input text as the time zone format in the specified style.
// Check the longest match among other styles below.
- UnicodeString parsedID;
+ UChar parsedIDBuf[32];
+ UnicodeString parsedID(parsedIDBuf, 0, UPRV_LENGTHOF(parsedIDBuf));
UTimeZoneFormatTimeType parsedTimeType = UTZFMT_TIME_TYPE_UNKNOWN;
U_ASSERT(parsedPos < 0);
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 NULL;
+ }
+ 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;
return NULL;
}
- if (parsedPos < startIdx + genMatchLen) {
+ if (genMatchLen > 0 && parsedPos < startIdx + genMatchLen) {
parsedPos = startIdx + genMatchLen;
parsedID.setTo(tzID);
parsedTimeType = tt;
name.setToBogus();
return name;
}
- return gnames->getGenericLocationName(UnicodeString(canonicalID), name);
+ return gnames->getGenericLocationName(UnicodeString(TRUE, canonicalID, -1), name);
}
return gnames->getDisplayName(tz, (UTimeZoneGenericNameType)genType, date, name);
}
}
if (isDaylight) {
- fTimeZoneNames->getDisplayName(UnicodeString(canonicalID), dstType, date, name);
+ fTimeZoneNames->getDisplayName(UnicodeString(TRUE, canonicalID, -1), dstType, date, name);
} else {
- fTimeZoneNames->getDisplayName(UnicodeString(canonicalID), stdType, date, name);
+ fTimeZoneNames->getDisplayName(UnicodeString(TRUE, canonicalID, -1), stdType, date, name);
}
if (timeType && !name.isEmpty()) {
return fTimeZoneGenericNames;
}
+const TZDBTimeZoneNames*
+TimeZoneFormat::getTZDBTimeZoneNames(UErrorCode& status) const {
+ if (U_FAILURE(status)) {
+ return NULL;
+ }
+
+ umtx_lock(&gLock);
+ if (fTZDBTimeZoneNames == NULL) {
+ TZDBTimeZoneNames *tzdbNames = new TZDBTimeZoneNames(fLocale);
+ if (tzdbNames == NULL) {
+ status = U_MEMORY_ALLOCATION_ERROR;
+ } else {
+ TimeZoneFormat *nonConstThis = const_cast<TimeZoneFormat *>(this);
+ nonConstThis->fTZDBTimeZoneNames = tzdbNames;
+ }
+ }
+ umtx_unlock(&gLock);
+
+ return fTZDBTimeZoneNames;
+}
+
UnicodeString&
TimeZoneFormat::formatExemplarLocation(const TimeZone& tz, UnicodeString& name) const {
- UnicodeString location;
+ UChar locationBuf[ZONE_NAME_U16_MAX];
+ UnicodeString location(locationBuf, 0, UPRV_LENGTHOF(locationBuf));
const UChar* canonicalID = ZoneMeta::getCanonicalCLDRID(tz);
if (canonicalID) {
- fTimeZoneNames->getExemplarLocationName(UnicodeString(canonicalID), location);
+ fTimeZoneNames->getExemplarLocationName(UnicodeString(TRUE, canonicalID, -1), location);
}
if (location.length() > 0) {
name.setTo(location);
} else {
// Use "unknown" location
- fTimeZoneNames->getExemplarLocationName(UnicodeString(UNKNOWN_ZONE_ID), location);
+ fTimeZoneNames->getExemplarLocationName(UnicodeString(TRUE, UNKNOWN_ZONE_ID, -1), location);
if (location.length() > 0) {
name.setTo(location);
} else {
// but it should be parsed as 00:10:20.
int32_t tmpLen = 0;
int32_t tmpSign = 1;
- int32_t tmpH, tmpM, tmpS;
+ int32_t tmpH = 0;
+ int32_t tmpM = 0;
+ int32_t tmpS = 0;
for (int32_t patidx = 0; PARSE_GMT_OFFSET_TYPES[patidx] >= 0; patidx++) {
int32_t gmtPatType = PARSE_GMT_OFFSET_TYPES[patidx];
if (fieldType == GMTOffsetField::TEXT) {
const UChar* patStr = field->getPatternText();
len = u_strlen(patStr);
+ if (i == 0) {
+ // When TimeZoneFormat parse() is called from SimpleDateFormat,
+ // leading space characters might be truncated. If the first pattern text
+ // starts with such character (e.g. Bidi control), then we need to
+ // skip the leading space charcters.
+ if (idx < text.length() && !PatternProps::isWhiteSpace(text.char32At(idx))) {
+ while (len > 0) {
+ UChar32 ch;
+ int32_t chLen;
+ U16_GET(patStr, 0, 0, len, ch)
+ if (PatternProps::isWhiteSpace(ch)) {
+ chLen = U16_LENGTH(ch);
+ len -= chLen;
+ patStr += chLen;
+ }
+ else {
+ break;
+ }
+ }
+ }
+ }
if (text.caseCompare(idx, len, patStr, 0) != 0) {
failed = TRUE;
break;
int32_t checkBits = 0;
UBool isPrevQuote = FALSE;
UBool inQuote = FALSE;
- UnicodeString text;
+ UChar textBuf[32];
+ UnicodeString text(textBuf, 0, UPRV_LENGTHOF(textBuf));
GMTOffsetField::FieldType itemType = GMTOffsetField::TEXT;
int32_t itemLength = 1;
isPrevQuote = TRUE;
if (itemType != GMTOffsetField::TEXT) {
if (GMTOffsetField::isValid(itemType, itemLength)) {
- GMTOffsetField* fld = GMTOffsetField::createTimeField(itemType, (uint8_t)itemLength, status);
+ GMTOffsetField* fld = GMTOffsetField::createTimeField(itemType, static_cast<uint8_t>(itemLength), status);
result->addElement(fld, status);
if (U_FAILURE(status)) {
break;
}
} else {
if (GMTOffsetField::isValid(itemType, itemLength)) {
- GMTOffsetField* fld = GMTOffsetField::createTimeField(itemType, itemLength, status);
+ GMTOffsetField* fld = GMTOffsetField::createTimeField(itemType, static_cast<uint8_t>(itemLength), status);
result->addElement(fld, status);
if (U_FAILURE(status)) {
break;
// a string literal
if (itemType != GMTOffsetField::TEXT) {
if (GMTOffsetField::isValid(itemType, itemLength)) {
- GMTOffsetField* fld = GMTOffsetField::createTimeField(itemType, itemLength, status);
+ GMTOffsetField* fld = GMTOffsetField::createTimeField(itemType, static_cast<uint8_t>(itemLength), status);
result->addElement(fld, status);
if (U_FAILURE(status)) {
break;
}
} else {
if (GMTOffsetField::isValid(itemType, itemLength)) {
- GMTOffsetField* fld = GMTOffsetField::createTimeField(itemType, itemLength, status);
+ GMTOffsetField* fld = GMTOffsetField::createTimeField(itemType, static_cast<uint8_t>(itemLength), status);
result->addElement(fld, status);
} else {
status = U_ILLEGAL_ARGUMENT_ERROR;
TimeZoneFormat::createTimeZoneForOffset(int32_t offset) const {
if (offset == 0) {
// when offset is 0, we should use "Etc/GMT"
- return TimeZone::createTimeZone(UnicodeString(TZID_GMT));
+ return TimeZone::createTimeZone(UnicodeString(TRUE, TZID_GMT, -1));
}
return ZoneMeta::createCustomTimeZone(offset);
}
return UTZFMT_TIME_TYPE_DAYLIGHT;
default:
- U_ASSERT(FALSE);
+ return UTZFMT_TIME_TYPE_UNKNOWN;
}
- return UTZFMT_TIME_TYPE_UNKNOWN;
}
UnicodeString&
TimeZoneFormat::getTimeZoneID(const TimeZoneNames::MatchInfoCollection* matches, int32_t idx, UnicodeString& tzID) const {
if (!matches->getTimeZoneIDAt(idx, tzID)) {
- UnicodeString mzID;
+ UChar mzIDBuf[32];
+ UnicodeString mzID(mzIDBuf, 0, UPRV_LENGTHOF(mzIDBuf));
if (matches->getMetaZoneIDAt(idx, mzID)) {
fTimeZoneNames->getReferenceZoneID(mzID, fTargetRegion, tzID);
}
}
StringEnumeration *tzenum = TimeZone::createEnumeration();
const UnicodeString *id;
- while ((id = tzenum->snext(status))) {
+ while ((id = tzenum->snext(status)) != NULL) {
const UChar* uid = ZoneMeta::findTimeZoneID(*id);
if (uid) {
gZoneIdTrie->put(uid, const_cast<UChar *>(uid), status);
status = U_MEMORY_ALLOCATION_ERROR;
} else {
const UnicodeString *id;
- while ((id = tzenum->snext(status))) {
+ while ((id = tzenum->snext(status)) != NULL) {
const UChar* uID = ZoneMeta::findTimeZoneID(*id);
const UChar* shortID = ZoneMeta::getShortID(*id);
if (shortID && uID) {