aboutsummaryrefslogtreecommitdiff
path: root/i18n
diff options
context:
space:
mode:
authorJean-Baptiste Queru <jbq@google.com>2009-07-17 17:56:25 -0700
committerJean-Baptiste Queru <jbq@google.com>2009-07-17 17:56:25 -0700
commit30b33a2316a7fe6a2c0f690e319b3a0a1b8f4044 (patch)
treeebe09fbb02206e99163aa7a26cc1e03d3040b724 /i18n
parent77d00534d7d1b0988659bde4ca7356e7e04758b9 (diff)
parentb13da9df870a61b11249bf741347908dbea0edd8 (diff)
downloadicu4c-30b33a2316a7fe6a2c0f690e319b3a0a1b8f4044.tar.gz
import cl @68900 - merge 3.8.1
Diffstat (limited to 'i18n')
-rw-r--r--i18n/Android.mk4
-rw-r--r--i18n/Makefile.in2
-rw-r--r--i18n/basictz.cpp9
-rw-r--r--i18n/calendar.cpp32
-rw-r--r--i18n/datefmt.cpp26
-rw-r--r--i18n/dtfmtsym.cpp1253
-rw-r--r--i18n/i18n.vcproj133
-rw-r--r--i18n/nfsubs.cpp6
-rw-r--r--i18n/olsontz.cpp173
-rw-r--r--i18n/olsontz.h10
-rw-r--r--i18n/rbtz.cpp134
-rw-r--r--i18n/simpletz.cpp53
-rw-r--r--i18n/smpdtfmt.cpp1096
-rw-r--r--i18n/timezone.cpp216
-rw-r--r--i18n/ucln_in.h2
-rw-r--r--i18n/ucol.cpp2
-rw-r--r--i18n/ucol_bld.cpp23
-rw-r--r--i18n/ucol_elm.cpp464
-rw-r--r--i18n/ucol_elm.h36
-rw-r--r--i18n/ucol_tok.cpp2
-rw-r--r--i18n/ucol_tok.h3
-rw-r--r--i18n/unicode/basictz.h29
-rw-r--r--i18n/unicode/dtfmtsym.h205
-rw-r--r--i18n/unicode/rbtz.h17
-rw-r--r--i18n/unicode/simpletz.h7
-rw-r--r--i18n/unicode/smpdtfmt.h54
-rw-r--r--i18n/unicode/timezone.h9
-rw-r--r--i18n/usearch.cpp97
-rw-r--r--i18n/usrchimp.h3
-rw-r--r--i18n/zonemeta.cpp873
-rw-r--r--i18n/zonemeta.h84
-rw-r--r--i18n/zstrfmt.cpp1604
-rw-r--r--i18n/zstrfmt.h442
33 files changed, 5167 insertions, 1936 deletions
diff --git a/i18n/Android.mk b/i18n/Android.mk
index 2e5fa05c..d48adec7 100644
--- a/i18n/Android.mk
+++ b/i18n/Android.mk
@@ -44,7 +44,7 @@ LOCAL_SRC_FILES += \
umsg.cpp unesctrn.cpp uni2name.cpp \
unum.cpp uregexc.cpp uregex.cpp \
usearch.cpp utrans.cpp windtfmt.cpp \
- winnmfmt.cpp
+ winnmfmt.cpp zonemeta.cpp zstrfmt.cpp
LOCAL_C_INCLUDES = \
$(LOCAL_PATH) \
@@ -62,4 +62,4 @@ LOCAL_LDLIBS += -lpthread -lm
LOCAL_MODULE := libicui18n
-include $(BUILD_SHARED_LIBRARY) \ No newline at end of file
+include $(BUILD_SHARED_LIBRARY)
diff --git a/i18n/Makefile.in b/i18n/Makefile.in
index e2b9791b..a74fbbe0 100644
--- a/i18n/Makefile.in
+++ b/i18n/Makefile.in
@@ -78,7 +78,7 @@ name2uni.o uni2name.o nortrans.o quant.o transreg.o \
regexcmp.o rematch.o repattrn.o regexst.o udatpg.o uregex.o uregexc.o \
ulocdata.o measfmt.o currfmt.o curramt.o currunit.o measure.o utmscale.o \
csdetect.o csmatch.o csr2022.o csrecog.o csrmbcs.o csrsbcs.o csrucode.o csrutf8.o inputext.o \
-windtfmt.o winnmfmt.o basictz.o dtrule.o rbtz.o tzrule.o tztrans.o vtzone.o
+windtfmt.o winnmfmt.o basictz.o dtrule.o rbtz.o tzrule.o tztrans.o vtzone.o zonemeta.o zstrfmt.o
## Header files to install
HEADERS = $(srcdir)/unicode/*.h
diff --git a/i18n/basictz.cpp b/i18n/basictz.cpp
index 12ac310d..546adb69 100644
--- a/i18n/basictz.cpp
+++ b/i18n/basictz.cpp
@@ -512,6 +512,15 @@ error:
transitionRules = NULL;
}
+void
+BasicTimeZone::getOffsetFromLocal(UDate date, int32_t nonExistingTimeOpt, int32_t duplicatedTimeOpt,
+ int32_t& rawOffset, int32_t& dstOffset, UErrorCode& status) /*const*/ {
+ if (U_FAILURE(status)) {
+ return;
+ }
+ status = U_UNSUPPORTED_ERROR;
+}
+
U_NAMESPACE_END
#endif /* #if !UCONFIG_NO_FORMATTING */
diff --git a/i18n/calendar.cpp b/i18n/calendar.cpp
index 6a932f68..03bedde0 100644
--- a/i18n/calendar.cpp
+++ b/i18n/calendar.cpp
@@ -1093,7 +1093,7 @@ void Calendar::computeFields(UErrorCode &ec)
double localMillis = internalGetTime();
int32_t rawOffset, dstOffset;
getTimeZone().getOffset(localMillis, FALSE, rawOffset, dstOffset, ec);
- localMillis += rawOffset;
+ localMillis += (rawOffset + dstOffset);
// Mark fields as set. Do this before calling handleComputeFields().
uint32_t mask = //fInternalSetMask;
@@ -1133,33 +1133,8 @@ void Calendar::computeFields(UErrorCode &ec)
//__FILE__, __LINE__, fFields[UCAL_JULIAN_DAY], localMillis);
#endif
- // In some cases we will have to call this method again below to
- // adjust for DST pushing us into the next Julian day.
computeGregorianAndDOWFields(fFields[UCAL_JULIAN_DAY], ec);
- int32_t millisInDay = (int32_t) (localMillis - (days * kOneDay));
- if (millisInDay < 0) millisInDay += (int32_t)kOneDay;
-
- // Adjust our millisInDay for DST. dstOffset will be zero if DST
- // is not in effect at this time of year, or if our zone does not
- // use DST.
- millisInDay += dstOffset;
-
- // If DST has pushed us into the next day, we must call
- // computeGregorianAndDOWFields() again. This happens in DST between
- // 12:00 am and 1:00 am every day. The first call to
- // computeGregorianAndDOWFields() will give the wrong day, since the
- // Standard time is in the previous day.
- if (millisInDay >= (int32_t)kOneDay) {
- millisInDay -= (int32_t)kOneDay; // ASSUME dstOffset < 24:00
-
- // We don't worry about overflow of JULIAN_DAY because the
- // allowable range of JULIAN_DAY has slop at the ends (that is,
- // the max is less that 0x7FFFFFFF and the min is greater than
- // -0x80000000).
- computeGregorianAndDOWFields(++fFields[UCAL_JULIAN_DAY], ec);
- }
-
// Call framework method to have subclass compute its fields.
// These must include, at a minimum, MONTH, DAY_OF_MONTH,
// EXTENDED_YEAR, YEAR, DAY_OF_YEAR. This method will call internalSet(),
@@ -1173,6 +1148,7 @@ void Calendar::computeFields(UErrorCode &ec)
// Compute time-related fields. These are indepent of the date and
// of the subclass algorithm. They depend only on the local zone
// wall milliseconds in day.
+ int32_t millisInDay = (int32_t) (localMillis - (days * kOneDay));
fFields[UCAL_MILLISECONDS_IN_DAY] = millisInDay;
fFields[UCAL_MILLISECOND] = millisInDay % 1000;
millisInDay /= 1000;
@@ -2346,11 +2322,11 @@ void Calendar::computeTime(UErrorCode& status) {
// 1. The transition into DST. Here, a designated time of 2:00 am - 2:59 am
// can be in standard or in DST depending. However, 2:00 am is an invalid
// representation (the representation jumps from 1:59:59 am Std to 3:00:00 am DST).
- // We assume standard time.
+ // We assume standard time, that is, 2:30 am is interpreted as 3:30 am DST.
// 2. The transition out of DST. Here, a designated time of 1:00 am - 1:59 am
// can be in standard or DST. Both are valid representations (the rep
// jumps from 1:59:59 DST to 1:00:00 Std).
- // Again, we assume standard time.
+ // Again, we assume standard time, that is, 1:30 am is interpreted as 1:30 am Std.
// We use the TimeZone object, unless the user has explicitly set the ZONE_OFFSET
// or DST_OFFSET fields; then we use those fields.
if (fStamp[UCAL_ZONE_OFFSET] >= ((int32_t)kMinimumUserStamp) ||
diff --git a/i18n/datefmt.cpp b/i18n/datefmt.cpp
index daeb2e3d..f49ca94c 100644
--- a/i18n/datefmt.cpp
+++ b/i18n/datefmt.cpp
@@ -170,24 +170,32 @@ UDate
DateFormat::parse(const UnicodeString& text,
ParsePosition& pos) const
{
+ UDate d = 0; // Error return UDate is 0 (the epoch)
if (fCalendar != NULL) {
int32_t start = pos.getIndex();
+
+ // Parse may update TimeZone used by the calendar.
+ TimeZone *tzsav = (TimeZone*)fCalendar->getTimeZone().clone();
+
fCalendar->clear();
parse(text, *fCalendar, pos);
if (pos.getIndex() != start) {
UErrorCode ec = U_ZERO_ERROR;
- UDate d = fCalendar->getTime(ec);
- if (U_SUCCESS(ec)) {
- return d; // Successful function exit
+ d = fCalendar->getTime(ec);
+ if (U_FAILURE(ec)) {
+ // We arrive here if fCalendar is non-lenient and there
+ // is an out-of-range field. We don't know which field
+ // was illegal so we set the error index to the start.
+ pos.setIndex(start);
+ pos.setErrorIndex(start);
+ d = 0;
}
- // We arrive here if fCalendar is non-lenient and there
- // is an out-of-range field. We don't know which field
- // was illegal so we set the error index to the start.
- pos.setIndex(start);
- pos.setErrorIndex(start);
}
+
+ // Restore TimeZone
+ fCalendar->adoptTimeZone(tzsav);
}
- return 0; // Error return UDate is 0 (the epoch)
+ return d;
}
//----------------------------------------------------------------------
diff --git a/i18n/dtfmtsym.cpp b/i18n/dtfmtsym.cpp
index f350d3a8..86144937 100644
--- a/i18n/dtfmtsym.cpp
+++ b/i18n/dtfmtsym.cpp
@@ -34,7 +34,8 @@
#include "locbased.h"
#include "gregoimp.h"
#include "hash.h"
-#include "uresimp.h"
+#include "uresimp.h"
+#include "zstrfmt.h"
// *****************************************************************************
// class DateFormatSymbols
@@ -130,6 +131,17 @@ static const UChar gLastResortZoneStrings[7][4] =
{0x0047, 0x004D, 0x0054, 0x0000} /* "GMT" */
};
+static const UChar gLastResortGmtFormat[] =
+ {0x0047, 0x004D, 0x0054, 0x007B, 0x0030, 0x007D, 0x0000}; /* GMT{0} */
+
+static const UChar gLastResortGmtHourFormats[4][10] =
+{
+ {0x002D, 0x0048, 0x0048, 0x003A, 0x006D, 0x006D, 0x003A, 0x0073, 0x0073, 0x0000}, /* -HH:mm:ss */
+ {0x002D, 0x0048, 0x0048, 0x003A, 0x006D, 0x006D, 0x0000, 0x0000, 0x0000, 0x0000}, /* -HH:mm */
+ {0x002B, 0x0048, 0x0048, 0x003A, 0x006D, 0x006D, 0x003A, 0x0073, 0x0073, 0x0000}, /* +HH:mm:ss */
+ {0x002B, 0x0048, 0x0048, 0x003A, 0x006D, 0x006D, 0x0000, 0x0000, 0x0000, 0x0000} /* +HH:mm */
+};
+
/* Sizes for the last resort string arrays */
typedef enum LastResortSize {
kMonthNum = 13,
@@ -148,7 +160,10 @@ typedef enum LastResortSize {
kEraLen = 3,
kZoneNum = 5,
- kZoneLen = 4
+ kZoneLen = 4,
+
+ kGmtHourNum = 4,
+ kGmtHourLen = 10
} LastResortSize;
U_NAMESPACE_BEGIN
@@ -171,39 +186,15 @@ static const char gNamesNarrowTag[]="narrow";
static const char gNamesStandaloneTag[]="stand-alone";
static const char gAmPmMarkersTag[]="AmPmMarkers";
static const char gQuartersTag[]="quarters";
-static const char gMaptimezonesTag[]="mapTimezones";
-static const char gMetazonesTag[]="metazones";
-static const char gTerritoryTag[]="territory";
-static const char gCountriesTag[]="Countries";
-static const char gZoneFormattingTag[]="zoneFormatting";
-static const char gMultizoneTag[]="multizone";
-static const char gRegionFormatTag[]="zoneStrings/regionFormat";
-static const char gFallbackFormatTag[]="zoneStrings/fallbackFormat";
-/**
- * These are the tags we expect to see in time zone data resource bundle files
- * associated with a locale.
- */
static const char gZoneStringsTag[]="zoneStrings";
+static const char gGmtFormatTag[]="gmtFormat";
+static const char gHourFormatTag[]="hourFormat";
+
static const char gLocalPatternCharsTag[]="localPatternChars";
static UMTX LOCK;
-/*
- * Keep this variable in synch with max length of display strings
- */
-#define ZID_KEY_MAX 128
-#define UTZ_MAX_DISPLAY_STRINGS_LENGTH 7
-#define UTZ_SHORT_GENERIC "sg"
-#define UTZ_SHORT_STANDARD "ss"
-#define UTZ_SHORT_DAYLIGHT "sd"
-#define UTZ_LONG_GENERIC "lg"
-#define UTZ_LONG_STANDARD "ls"
-#define UTZ_LONG_DAYLIGHT "ld"
-#define UTZ_EXEMPLAR_CITY "ec"
-#define UTZ_USES_METAZONE "um"
-#define UTZ_COMMONLY_USED "cu"
-
/**
* Jitterbug 2974: MSVC has a bug whereby new X[0] behaves badly.
* Work around this.
@@ -212,12 +203,6 @@ static inline UnicodeString* newUnicodeStringArray(size_t count) {
return new UnicodeString[count ? count : 1];
}
-U_CDECL_BEGIN
-static void deleteUnicodeStringArray(void* obj) {
- delete[] (UnicodeString*)obj;
-}
-U_CDECL_END
-
//------------------------------------------------------
DateFormatSymbols::DateFormatSymbols(const Locale& locale,
@@ -327,26 +312,25 @@ DateFormatSymbols::copyData(const DateFormatSymbols& other) {
assignArray(fShortQuarters, fShortQuartersCount, other.fShortQuarters, other.fShortQuartersCount);
assignArray(fStandaloneQuarters, fStandaloneQuartersCount, other.fStandaloneQuarters, other.fStandaloneQuartersCount);
assignArray(fStandaloneShortQuarters, fStandaloneShortQuartersCount, other.fStandaloneShortQuarters, other.fStandaloneShortQuartersCount);
- // the zoneStrings data is initialized on demand
- //fZoneStringsRowCount = other.fZoneStringsRowCount;
- //fZoneStringsColCount = other.fZoneStringsColCount;
- //createZoneStrings((const UnicodeString**)other.fZoneStrings);
- // initialize on demand
- fZoneStringsHash = NULL;
- fZoneIDEnumeration = NULL;
- fZoneStrings = NULL;
- fZoneStringsColCount = 0;
- fZoneStringsRowCount = 0;
- fResourceBundle = NULL;
- fCountry = other.fCountry;
- if(other.fZoneStringsHash!=NULL){
- fZoneStringsHash = createZoneStringsHash(other.fZoneStringsHash);
- fZoneIDEnumeration = other.fZoneIDEnumeration->clone();
- }else{
- UErrorCode status =U_ZERO_ERROR;
- fResourceBundle = ures_clone(other.fResourceBundle, &status);
- // TODO: what should be done in case of error?
+ fGmtFormat = other.fGmtFormat;
+ assignArray(fGmtHourFormats, fGmtHourFormatsCount, other.fGmtHourFormats, other.fGmtHourFormatsCount);
+
+ if (other.fZoneStrings != NULL) {
+ fZoneStringsColCount = other.fZoneStringsColCount;
+ fZoneStringsRowCount = other.fZoneStringsRowCount;
+ createZoneStrings((const UnicodeString**)other.fZoneStrings);
+
+ } else {
+ fZoneStrings = NULL;
+ fZoneStringsColCount = 0;
+ fZoneStringsRowCount = 0;
}
+ fZSFLocale = other.fZSFLocale;
+ // Other zone strings data is created on demand
+ fZoneStringFormat = NULL;
+ fLocaleZoneStrings = NULL;
+ fZSFCachePtr = NULL;
+ fZSFLocal = NULL;
// fastCopyFrom() - see assignArray comments
fLocalPatternChars.fastCopyFrom(other.fLocalPatternChars);
@@ -389,6 +373,7 @@ void DateFormatSymbols::dispose()
if (fShortQuarters) delete[] fShortQuarters;
if (fStandaloneQuarters) delete[] fStandaloneQuarters;
if (fStandaloneShortQuarters) delete[] fStandaloneShortQuarters;
+ if (fGmtHourFormats) delete[] fGmtHourFormats;
disposeZoneStrings();
}
@@ -396,23 +381,32 @@ void DateFormatSymbols::dispose()
void DateFormatSymbols::disposeZoneStrings()
{
if (fZoneStrings) {
- for (int32_t row=0; row<fZoneStringsRowCount; ++row)
+ for (int32_t row = 0; row < fZoneStringsRowCount; ++row) {
delete[] fZoneStrings[row];
+ }
uprv_free(fZoneStrings);
- }
- if(fZoneStringsHash){
- delete fZoneStringsHash;
- fZoneStringsHash = NULL;
}
- if(fZoneIDEnumeration){
- delete fZoneIDEnumeration;
- fZoneIDEnumeration = NULL;
+ if (fLocaleZoneStrings) {
+ for (int32_t row = 0; row < fZoneStringsRowCount; ++row) {
+ delete[] fLocaleZoneStrings[row];
+ }
+ uprv_free(fLocaleZoneStrings);
}
- if (fResourceBundle){
- ures_close(fResourceBundle);
- fResourceBundle = NULL;
+ if (fZSFLocal) {
+ delete fZSFLocal;
}
+ if (fZSFCachePtr) {
+ delete fZSFCachePtr;
+ }
+
+ fZoneStrings = NULL;
+ fLocaleZoneStrings = NULL;
+ fZoneStringsRowCount = 0;
+ fZoneStringsColCount = 0;
+ fZoneStringFormat = NULL;
+ fZSFLocal = NULL;
+ fZSFCachePtr = NULL;
}
UBool
@@ -454,7 +448,9 @@ DateFormatSymbols::operator==(const DateFormatSymbols& other) const
fQuartersCount == other.fQuartersCount &&
fShortQuartersCount == other.fShortQuartersCount &&
fStandaloneQuartersCount == other.fStandaloneQuartersCount &&
- fStandaloneShortQuartersCount == other.fStandaloneShortQuartersCount)
+ fStandaloneShortQuartersCount == other.fStandaloneShortQuartersCount &&
+ fGmtHourFormatsCount == other.fGmtHourFormatsCount &&
+ fGmtFormat == other.fGmtFormat)
{
// Now compare the arrays themselves
if (arrayCompare(fEras, other.fEras, fErasCount) &&
@@ -475,25 +471,25 @@ DateFormatSymbols::operator==(const DateFormatSymbols& other) const
arrayCompare(fQuarters, other.fQuarters, fQuartersCount) &&
arrayCompare(fShortQuarters, other.fShortQuarters, fShortQuartersCount) &&
arrayCompare(fStandaloneQuarters, other.fStandaloneQuarters, fStandaloneQuartersCount) &&
- arrayCompare(fStandaloneShortQuarters, other.fStandaloneShortQuarters, fStandaloneShortQuartersCount))
+ arrayCompare(fStandaloneShortQuarters, other.fStandaloneShortQuarters, fStandaloneShortQuartersCount) &&
+ arrayCompare(fGmtHourFormats, other.fGmtHourFormats, fGmtHourFormatsCount))
{
-
- if(fZoneStringsHash == NULL || other.fZoneStringsHash == NULL){
- // fZoneStringsHash is not initialized compare the resource bundles
- if(ures_equal(fResourceBundle, other.fResourceBundle)== FALSE){
- return FALSE;
+ // Compare the contents of fZoneStrings
+ if (fZoneStrings == NULL && other.fZoneStrings == NULL) {
+ if (fZSFLocale == other.fZSFLocale) {
+ return TRUE;
}
- }else{
- if(fZoneStringsHash->equals(*other.fZoneStringsHash) == FALSE){
- return FALSE;
+ } else if (fZoneStrings != NULL && other.fZoneStrings != NULL) {
+ if (fZoneStringsRowCount == other.fZoneStringsRowCount
+ && fZoneStringsColCount == other.fZoneStringsColCount) {
+ UBool cmpres = TRUE;
+ for (int32_t i = 0; (i < fZoneStringsRowCount) && cmpres; i++) {
+ cmpres = arrayCompare(fZoneStrings[i], other.fZoneStrings[i], fZoneStringsColCount);
+ }
+ return cmpres;
}
- // we always make sure that we update the enumeration when the hash is
- // updated. So we can be sure that once we compare the hashes the
- // enumerations are also equal
}
- // since fZoneStrings data member is deprecated .. and may not be initialized
- // so don't compare them
- return TRUE;
+ return FALSE;
}
}
return FALSE;
@@ -997,25 +993,73 @@ DateFormatSymbols::setAmPmStrings(const UnicodeString* amPmsArray, int32_t count
}
//------------------------------------------------------
+const ZoneStringFormat*
+DateFormatSymbols::getZoneStringFormat(void) const {
+ umtx_lock(&LOCK);
+ if (fZoneStringFormat == NULL) {
+ ((DateFormatSymbols*)this)->initZoneStringFormat();
+ }
+ umtx_unlock(&LOCK);
+ return fZoneStringFormat;
+}
+
+void
+DateFormatSymbols::initZoneStringFormat(void) {
+ if (fZoneStringFormat == NULL) {
+ UErrorCode status = U_ZERO_ERROR;
+ if (fZoneStrings) {
+ // Create an istance of ZoneStringFormat by the custom zone strings array
+ fZSFLocal = new ZoneStringFormat(fZoneStrings, fZoneStringsRowCount,
+ fZoneStringsColCount, status);
+ if (U_FAILURE(status)) {
+ delete fZSFLocal;
+ } else {
+ fZoneStringFormat = (const ZoneStringFormat*)fZSFLocal;
+ }
+ } else {
+ fZSFCachePtr = ZoneStringFormat::getZoneStringFormat(fZSFLocale, status);
+ if (U_FAILURE(status)) {
+ delete fZSFCachePtr;
+ } else {
+ fZoneStringFormat = fZSFCachePtr->get();
+ }
+ }
+ }
+}
const UnicodeString**
DateFormatSymbols::getZoneStrings(int32_t& rowCount, int32_t& columnCount) const
{
+ const UnicodeString **result = NULL;
+
umtx_lock(&LOCK);
- UErrorCode status = U_ZERO_ERROR;
- if(fZoneStrings==NULL){
- // cast away const to get around the problem for lazy initialization
- ((DateFormatSymbols*)this)->initZoneStringsArray(status);
+ if (fZoneStrings == NULL) {
+ if (fLocaleZoneStrings == NULL) {
+ ((DateFormatSymbols*)this)->initZoneStringsArray();
+ }
+ result = (const UnicodeString**)fLocaleZoneStrings;
+ } else {
+ result = (const UnicodeString**)fZoneStrings;
}
rowCount = fZoneStringsRowCount;
columnCount = fZoneStringsColCount;
umtx_unlock(&LOCK);
- if(U_FAILURE(status)){
- rowCount = 0;
- columnCount = 0;
- return NULL;
+
+ return result;
+}
+
+void
+DateFormatSymbols::initZoneStringsArray(void) {
+ if (fZoneStrings == NULL && fLocaleZoneStrings == NULL) {
+ if (fZoneStringFormat == NULL) {
+ initZoneStringFormat();
+ }
+ if (fZoneStringFormat) {
+ UErrorCode status = U_ZERO_ERROR;
+ fLocaleZoneStrings = fZoneStringFormat->createZoneStringsArray(uprv_getUTCtime() /* use current time */,
+ fZoneStringsRowCount, fZoneStringsColCount, status);
+ }
}
- return (const UnicodeString**)fZoneStrings; // Compiler requires cast
}
void
@@ -1030,7 +1074,6 @@ DateFormatSymbols::setZoneStrings(const UnicodeString* const *strings, int32_t r
fZoneStringsRowCount = rowCount;
fZoneStringsColCount = columnCount;
createZoneStrings((const UnicodeString**)strings);
- initZoneStrings((const UnicodeString**)strings, rowCount,columnCount, status);
}
//------------------------------------------------------
@@ -1144,14 +1187,22 @@ DateFormatSymbols::initializeData(const Locale& locale, const char *type, UError
fStandaloneQuartersCount = 0;
fStandaloneShortQuarters = NULL;
fStandaloneShortQuartersCount = 0;
+ fGmtHourFormats = NULL;
+ fGmtHourFormatsCount = 0;
fZoneStringsRowCount = 0;
fZoneStringsColCount = 0;
fZoneStrings = NULL;
- fZoneStringsHash = NULL;
- fZoneIDEnumeration = NULL;
- fResourceBundle = NULL;
- fCountry = NULL;
-
+ fLocaleZoneStrings = NULL;
+
+ fZoneStringFormat = NULL;
+ fZSFLocal = NULL;
+ fZSFCachePtr = NULL;
+
+ // We need to preserve the requested locale for
+ // lazy ZoneStringFormat instantiation. ZoneStringFormat
+ // is region sensitive, thus, bundle locale bundle's locale
+ // is not sufficient.
+ fZSFLocale = locale;
if (U_FAILURE(status)) return;
@@ -1161,8 +1212,12 @@ DateFormatSymbols::initializeData(const Locale& locale, const char *type, UError
* these.
*/
CalendarData calData(locale, type, status);
- fResourceBundle = ures_open(NULL, locale.getName(), &status);
- fCountry = locale.getCountry();
+
+ /**
+ * Use the localeBundle for getting zone GMT formatting patterns
+ */
+ UResourceBundle *localeBundle = ures_open(NULL, locale.getName(), &status);
+ UResourceBundle *zoneStringsArray = ures_getByKeyWithFallback(localeBundle, gZoneStringsTag, NULL, &status);
// load the first data item
UResourceBundle *erasMain = calData.getByKey(gErasTag, status);
@@ -1212,6 +1267,8 @@ DateFormatSymbols::initializeData(const Locale& locale, const char *type, UError
initField(&fShortQuarters, fShortQuartersCount, (const UChar *)gLastResortQuarters, kQuarterNum, kQuarterLen, status);
initField(&fStandaloneQuarters, fStandaloneQuartersCount, (const UChar *)gLastResortQuarters, kQuarterNum, kQuarterLen, status);
initField(&fStandaloneShortQuarters, fStandaloneShortQuartersCount, (const UChar *)gLastResortQuarters, kQuarterNum, kQuarterLen, status);
+ initField(&fGmtHourFormats, fGmtHourFormatsCount, (const UChar *)gLastResortGmtHourFormats, kGmtHourNum, kGmtHourLen, status);
+ fGmtFormat.setTo(TRUE, gLastResortGmtFormat, -1);
fLocalPatternChars.setTo(TRUE, gPatternChars, PATTERN_CHARS_LEN);
}
goto cleanup;
@@ -1274,6 +1331,41 @@ DateFormatSymbols::initializeData(const Locale& locale, const char *type, UError
status = U_ZERO_ERROR;
initField(&fStandaloneShortQuarters, fStandaloneShortQuartersCount, calData.getByKey2(gQuartersTag, gNamesAbbrTag, status), status);
}
+
+ // GMT format patterns
+ resStr = ures_getStringByKeyWithFallback(zoneStringsArray, gGmtFormatTag, &len, &status);
+ if (len > 0) {
+ fGmtFormat.setTo(TRUE, resStr, len);
+ }
+
+ resStr = ures_getStringByKeyWithFallback(zoneStringsArray, gHourFormatTag, &len, &status);
+ if (len > 0) {
+ UChar *sep = u_strchr(resStr, (UChar)0x003B /* ';' */);
+ if (sep != NULL) {
+ fGmtHourFormats = newUnicodeStringArray(GMT_HOUR_COUNT);
+ if (fGmtHourFormats == NULL) {
+ status = U_MEMORY_ALLOCATION_ERROR;
+ } else {
+ fGmtHourFormatsCount = GMT_HOUR_COUNT;
+ fGmtHourFormats[GMT_NEGATIVE_HM].setTo(TRUE, sep + 1, -1);
+ fGmtHourFormats[GMT_POSITIVE_HM].setTo(FALSE, resStr, sep - resStr);
+
+ // CLDR 1.5 does not have GMT offset pattern including second field.
+ // For now, append "ss" to the end.
+ if (fGmtHourFormats[GMT_NEGATIVE_HM].indexOf((UChar)0x003A /* ':' */) != -1) {
+ fGmtHourFormats[GMT_NEGATIVE_HMS] = fGmtHourFormats[GMT_NEGATIVE_HM] + ":ss";
+ } else {
+ fGmtHourFormats[GMT_NEGATIVE_HMS] = fGmtHourFormats[GMT_NEGATIVE_HM] + "ss";
+ }
+ if (fGmtHourFormats[GMT_POSITIVE_HM].indexOf((UChar)0x003A /* ':' */) != -1) {
+ fGmtHourFormats[GMT_POSITIVE_HMS] = fGmtHourFormats[GMT_POSITIVE_HM] + ":ss";
+ } else {
+ fGmtHourFormats[GMT_POSITIVE_HMS] = fGmtHourFormats[GMT_POSITIVE_HM] + "ss";
+ }
+ }
+ }
+ }
+
// ICU 3.8 or later version no longer uses localized date-time pattern characters by default (ticket#5597)
/*
// fastCopyFrom()/setTo() - see assignArray comments
@@ -1411,55 +1503,8 @@ DateFormatSymbols::initializeData(const Locale& locale, const char *type, UError
cleanup:
ures_close(eras);
ures_close(eraNames);
-}
-
-/**
- * Package private: used by SimpleDateFormat
- * Gets the index for the given time zone ID to obtain the timezone
- * strings for formatting. The time zone ID is just for programmatic
- * lookup. NOT LOCALIZED!!!
- * @param ID the given time zone ID.
- * @return the index of the given time zone ID. Returns -1 if
- * the given time zone ID can't be located in the DateFormatSymbols object.
- * @see java.util.SimpleTimeZone
- */
-int32_t DateFormatSymbols::getZoneIndex(const UnicodeString& ID) const
-{
- int32_t result = _getZoneIndex(ID);
- if (result >= 0) {
- return result;
- }
-
- // Do a search through the equivalency group for the given ID
- int32_t n = TimeZone::countEquivalentIDs(ID);
- if (n > 1) {
- int32_t i;
- for (i=0; i<n; ++i) {
- UnicodeString equivID = TimeZone::getEquivalentID(ID, i);
- if (equivID != ID) {
- int32_t equivResult = _getZoneIndex(equivID);
- if (equivResult >= 0) {
- return equivResult;
- }
- }
- }
- }
-
- return -1;
-}
-
-/**
- * Lookup the given ID. Do NOT do an equivalency search.
- */
-int32_t DateFormatSymbols::_getZoneIndex(const UnicodeString& ID) const
-{
- for(int32_t index = 0; index < fZoneStringsRowCount; index++) {
- if (0 == ID.caseCompare(fZoneStrings[index][0], 0)) {
- return index;
- }
- }
-
- return -1;
+ ures_close(zoneStringsArray);
+ ures_close(localeBundle);
}
Locale
@@ -1468,920 +1513,6 @@ DateFormatSymbols::getLocale(ULocDataLocaleType type, UErrorCode& status) const
return locBased.getLocale(type, status);
}
-class TimeZoneKeysEnumeration : public StringEnumeration {
-private:
- UnicodeString* strings;
- int32_t length;
- int32_t current;
- int32_t capacity;
- TimeZoneKeysEnumeration(UnicodeString* oldStrs, int32_t count){
- strings = newUnicodeStringArray(count);
- if(strings==NULL){
- return;
- }
- capacity = count;
- current = 0;
- for(length = 0; length<capacity; length++){
- strings[length].setTo(oldStrs[length]);
- }
- }
-public:
- static UClassID U_EXPORT2 getStaticClassID(void);
- virtual UClassID getDynamicClassID(void) const;
-
- TimeZoneKeysEnumeration(int32_t count, UErrorCode status){
- strings = newUnicodeStringArray(count);
- if(strings == NULL){
- status = U_MEMORY_ALLOCATION_ERROR;
- }
- length = 0;
- current = 0;
- capacity = count;
- }
-
- void put(const UnicodeString& str, UErrorCode& status){
- if(length < capacity){
- strings[length++].setTo(str);
- }else{
- status = U_INDEX_OUTOFBOUNDS_ERROR;
- }
- }
- virtual ~TimeZoneKeysEnumeration() {
- delete[] strings;
- }
-
- virtual StringEnumeration * clone() const
- {
- return new TimeZoneKeysEnumeration(strings, length);
- }
-
- virtual int32_t count(UErrorCode &/*status*/) const {
- return length;
- }
- virtual const UChar* unext(int32_t *resultLength, UErrorCode& /*status*/){
- if(current < length){
- const UChar* ret = strings[current].getBuffer();
- *resultLength = strings[current].length();
- current++;
- return ret;
- }
- return NULL;
- }
-
- virtual const UnicodeString* snext(UErrorCode& status) {
- if(U_FAILURE(status)){
- return NULL;
- }
- if(current < length){
- return &strings[current++];
- }
- return NULL;
- }
- /* this method is for thread safe iteration */
- const UnicodeString* snext(int32_t& pos, UErrorCode& status)const {
- if(U_FAILURE(status)){
- return NULL;
- }
- if(pos < length){
- return &strings[pos++];
- }
- return NULL;
- }
-
- virtual void reset(UErrorCode& /*status*/) {
- current = 0;
-
- }
-private:
- UBool equals(const StringEnumeration& other) const{
- if (other.getDynamicClassID() != TimeZoneKeysEnumeration::getStaticClassID()) {
- return FALSE;
- }
- TimeZoneKeysEnumeration& enum2 = (TimeZoneKeysEnumeration&)(other);
- UErrorCode status = U_ZERO_ERROR;
-
- int32_t count1 = count(status);
- int32_t count2 = other.count(status);
- if(count1 != count2){
- return FALSE;
- }
- int32_t pos1 = 0;
- int32_t pos2 = 0;
- const UnicodeString* str1 = NULL;
- const UnicodeString* str2 = NULL;
-
- while((str1 = snext(pos1, status))!=NULL){
- str2 = enum2.snext(pos2, status);
- if(U_FAILURE(status)){
- return FALSE;
- }
- if(*str1 != *str2){
- // bail out at the first failure
- return FALSE;
- }
-
- }
- // if we reached here that means that the enumerations are equal
- return TRUE;
- }
-public:
- virtual UBool operator==(const StringEnumeration& that)const{
- return ((this == &that) ||
- (getDynamicClassID() == that.getDynamicClassID() &&
- StringEnumeration::operator==(that) &&
- equals(that)));
- }
-};
-
-UOBJECT_DEFINE_RTTI_IMPLEMENTATION(TimeZoneKeysEnumeration)
-
-void
-DateFormatSymbols::initZoneStringsArray(UErrorCode& status){
- if(fZoneStringsHash == NULL){
- initZoneStrings(status);
- }
- if(U_FAILURE(status)){
- return;
- }
- fZoneStringsRowCount = fZoneIDEnumeration->count(status);
- fZoneStringsColCount = 8;
- fZoneStrings = (UnicodeString **)uprv_malloc(fZoneStringsRowCount * sizeof(UnicodeString *));
- /* if we can't get a chunk of heap then the system is going down. Pin the blame on system*/
- if (fZoneStrings == NULL) {
- status = U_MEMORY_ALLOCATION_ERROR;
- return;
- }
- const UnicodeString *zid = NULL;
- TimeZoneKeysEnumeration *keys = (TimeZoneKeysEnumeration*) fZoneIDEnumeration;
- int32_t pos = 0;
- int32_t i = 0;
- while((zid=keys->snext(pos,status))!=NULL){
- *(fZoneStrings+i) = newUnicodeStringArray(fZoneStringsColCount);
- /* test for NULL */
- if ((*(fZoneStrings+i)) == 0) {
- status = U_MEMORY_ALLOCATION_ERROR;
- return;
- }
- UnicodeString* strings = (UnicodeString*)fZoneStringsHash->get(*zid);
- fZoneStrings[i][0].setTo(*zid);
- fZoneStrings[i][1].setTo(strings[TIMEZONE_LONG_STANDARD]);
- fZoneStrings[i][2].setTo(strings[TIMEZONE_SHORT_STANDARD]);
- fZoneStrings[i][3].setTo(strings[TIMEZONE_LONG_DAYLIGHT]);
- fZoneStrings[i][4].setTo(strings[TIMEZONE_SHORT_DAYLIGHT]);
- fZoneStrings[i][5].setTo(strings[TIMEZONE_EXEMPLAR_CITY]);
- fZoneStrings[i][6].setTo(strings[TIMEZONE_LONG_GENERIC]);
- fZoneStrings[i][7].setTo(strings[TIMEZONE_SHORT_GENERIC]);
- i++;
- }
-}
-
-U_CDECL_BEGIN
-static UBool U_CALLCONV
-compareTZHashValues(const UHashTok val1, const UHashTok val2){
-
- const UnicodeString* array1 = (UnicodeString*) val1.pointer;
- const UnicodeString* array2 = (UnicodeString*) val2.pointer;
- if(array1==array2){
- return TRUE;
- }
- if(array1==NULL || array2==NULL){
- return FALSE;
- }
- for(int32_t j=0; j< UTZ_MAX_DISPLAY_STRINGS_LENGTH; j++){
- if(array1[j] != array2[j]){
- return FALSE;
- }
- }
- return TRUE;
-}
-U_CDECL_END
-
-void
-DateFormatSymbols::initZoneStrings(UErrorCode &status){
- if(U_FAILURE(status)){
- return;
- }
-
- if(fZoneStringsHash != NULL){
- return;
- }
- int32_t i;
-
- fZoneStringsHash = new Hashtable(uhash_compareUnicodeString, compareTZHashValues, status);
- if(fZoneStringsHash==NULL){
- status = U_MEMORY_ALLOCATION_ERROR;
- return;
- }
- fZoneStringsHash->setValueDeleter(deleteUnicodeStringArray);
-
- if(fResourceBundle != NULL){
- UnicodeString solidus = UNICODE_STRING_SIMPLE("/");
- UnicodeString colon = UNICODE_STRING_SIMPLE(":");
- UResourceBundle *zoneArray, *zoneItem;
- for(const UResourceBundle* rb = fResourceBundle; rb!=NULL; rb=ures_getParentBundle(rb)){
- zoneArray = ures_getByKey(rb, gZoneStringsTag, NULL, &status);
- if(U_FAILURE(status)){
- break;
- }
- while(ures_hasNext(zoneArray)){
- UErrorCode tempStatus = U_ZERO_ERROR;
- zoneItem = ures_getNextResource(zoneArray, NULL, &status);
- UnicodeString key(ures_getKey(zoneItem), -1, US_INV);
- if (key.indexOf(colon) == -1) {
- ures_close(zoneItem);
- continue;
- }
- UnicodeString* strArray = newUnicodeStringArray(UTZ_MAX_DISPLAY_STRINGS_LENGTH);
- key.findAndReplace(colon, solidus);
- int32_t len = 0;
- //fetch the strings with fine grained fallback
- const UChar* str = ures_getStringByKeyWithFallback(zoneItem,UTZ_SHORT_STANDARD, &len, &tempStatus);
- if(U_SUCCESS(tempStatus)){
- strArray[TIMEZONE_SHORT_STANDARD].setTo(TRUE, str, len);
- }else{
- tempStatus = U_ZERO_ERROR;
- }
- str = ures_getStringByKeyWithFallback(zoneItem,UTZ_SHORT_GENERIC, &len, &tempStatus);
- if(U_SUCCESS(tempStatus)){
- strArray[TIMEZONE_SHORT_GENERIC].setTo(TRUE, str, len);
- }else{
- tempStatus = U_ZERO_ERROR;
- }
- str = ures_getStringByKeyWithFallback(zoneItem,UTZ_SHORT_DAYLIGHT, &len, &tempStatus);
- if(U_SUCCESS(tempStatus)){
- strArray[TIMEZONE_SHORT_DAYLIGHT].setTo(TRUE, str, len);
- }else{
- tempStatus = U_ZERO_ERROR;
- }
- str = ures_getStringByKeyWithFallback(zoneItem,UTZ_LONG_STANDARD, &len, &tempStatus);
- if(U_SUCCESS(tempStatus)){
- strArray[TIMEZONE_LONG_STANDARD].setTo(TRUE, str, len);
- }else{
- tempStatus = U_ZERO_ERROR;
- }
- str = ures_getStringByKeyWithFallback(zoneItem,UTZ_LONG_GENERIC, &len, &tempStatus);
- if(U_SUCCESS(tempStatus)){
- strArray[TIMEZONE_LONG_GENERIC].setTo(TRUE, str, len);
- }else{
- tempStatus = U_ZERO_ERROR;
- }
- str = ures_getStringByKeyWithFallback(zoneItem,UTZ_LONG_DAYLIGHT, &len, &tempStatus);
- if(U_SUCCESS(tempStatus)){
- strArray[TIMEZONE_LONG_DAYLIGHT].setTo(TRUE, str, len);
- }else{
- tempStatus = U_ZERO_ERROR;
- }
- str = ures_getStringByKeyWithFallback(zoneItem,UTZ_EXEMPLAR_CITY, &len, &tempStatus);
- if(U_SUCCESS(tempStatus)){
- strArray[TIMEZONE_EXEMPLAR_CITY].setTo(TRUE, str, len);
- }else{
- tempStatus = U_ZERO_ERROR;
- }
- // store the strings in hash
- fZoneStringsHash->put(key, strArray, status);
- ures_close(zoneItem);
- }
-
- ures_close(zoneArray);
- }
-
- // Need to make sure that all zoneStrings in root are covered as well, otherwise metazone lookups won't
- // work properly
- UResourceBundle* root_res = ures_open(NULL, "", &status);
- zoneArray = ures_getByKey(root_res, gZoneStringsTag, NULL, &status);
- if (U_SUCCESS(status)) {
- while(ures_hasNext(zoneArray)){
- UErrorCode tempStatus = U_ZERO_ERROR;
- zoneItem = ures_getNextResource(zoneArray, NULL, &status);
- UnicodeString key(ures_getKey(zoneItem), -1, US_INV);
- if ( key.indexOf(colon) == -1 ) {
- ures_close(zoneItem);
- continue;
- }
- key.findAndReplace(colon, solidus);
-
- // Don't step on anything that is already there
- UnicodeString* existingArray = (UnicodeString*)fZoneStringsHash->get(key);
- if(existingArray != NULL){
- ures_close(zoneItem);
- continue;
- }
- UnicodeString* strArray = newUnicodeStringArray(UTZ_MAX_DISPLAY_STRINGS_LENGTH);
- int32_t len = 0;
-
- const UChar *str = ures_getStringByKeyWithFallback(zoneItem,UTZ_EXEMPLAR_CITY, &len, &tempStatus);
- if(U_SUCCESS(tempStatus)){
- strArray[TIMEZONE_EXEMPLAR_CITY].setTo(TRUE, str, len);
- }else{
- tempStatus = U_ZERO_ERROR;
- }
- // store the strings in hash
- fZoneStringsHash->put(key, strArray, status);
- ures_close(zoneItem);
- }
- ures_close(zoneArray);
- ures_close(root_res);
- }
-
- int32_t length = fZoneStringsHash->count();
- TimeZoneKeysEnumeration* keysEnum = new TimeZoneKeysEnumeration(length, status);
- fZoneIDEnumeration = keysEnum;
- if(fZoneIDEnumeration==NULL){
- delete fZoneStringsHash;
- fZoneStringsHash = NULL;
- status = U_MEMORY_ALLOCATION_ERROR;
- return;
- }
- int32_t pos=-1;
- const UnicodeString* key;
- const UHashElement* elem = NULL;
- while((elem = fZoneStringsHash->nextElement(pos))!= NULL){
- const UHashTok keyTok = elem->key;
- key = (const UnicodeString*)keyTok.pointer;
- keysEnum->put(*key, status);
- }
- }else{
- //last resort strings
- UnicodeString* array = newUnicodeStringArray(UTZ_MAX_DISPLAY_STRINGS_LENGTH);
- if(array==NULL){
- delete fZoneStringsHash;
- status = U_MEMORY_ALLOCATION_ERROR;
- return;
- }
- int32_t length = ARRAY_LENGTH(gLastResortZoneStrings);
- UnicodeString key(gLastResortZoneStrings[0]);
- TimeZoneKeysEnumeration* keysEnum = new TimeZoneKeysEnumeration(length, status);
- fZoneIDEnumeration = keysEnum;
- if(fZoneIDEnumeration==NULL){
- delete fZoneStringsHash;
- delete[] array;
- fZoneStringsHash = NULL;
- status = U_MEMORY_ALLOCATION_ERROR;
- return;
- }
- keysEnum->put(key, status);
- int32_t j=1;
- for(i=0; i< length; ){
- array[i++].setTo(gLastResortZoneStrings[j++]);
- }
- fZoneStringsHash->put(key, array, status);
- }
-}
-void
-DateFormatSymbols::initZoneStrings(const UnicodeString** strings, int32_t rowCount, int32_t columnCount, UErrorCode& status){
- if(strings==NULL || rowCount<0 || columnCount<0){
- status = U_ILLEGAL_ARGUMENT_ERROR;
- return;
- }
- TimeZoneKeysEnumeration* keysEnum = new TimeZoneKeysEnumeration(rowCount, status);
- fZoneIDEnumeration = keysEnum;
- if(U_FAILURE(status)){
- return;
- }
- if(fZoneIDEnumeration==NULL){
- status = U_MEMORY_ALLOCATION_ERROR;
- return;
- }
- fZoneStringsHash = new Hashtable(uhash_compareUnicodeString, compareTZHashValues, status);
- if(U_FAILURE(status)){
- return;
- }
- if(fZoneStringsHash==NULL){
- status = U_MEMORY_ALLOCATION_ERROR;
- return;
- }
- fZoneStringsHash->setValueDeleter(deleteUnicodeStringArray);
- for (int32_t row=0; row<rowCount; ++row){
- // the first string in the array is the key.
- UnicodeString key = strings[row][0];
- keysEnum->put(key, status);
- UnicodeString* array = newUnicodeStringArray(UTZ_MAX_DISPLAY_STRINGS_LENGTH);
- if(array==NULL){
- status = U_MEMORY_ALLOCATION_ERROR;
- return;
- }
- for (int32_t col=1; col<columnCount; ++col) {
- // fastCopyFrom() - see assignArray comments
- switch (col){
- case 1:
- array[TIMEZONE_LONG_STANDARD].setTo(strings[row][col]);
- break;
- case 2:
- array[TIMEZONE_SHORT_STANDARD].setTo(strings[row][col]);
- break;
- case 3:
- array[TIMEZONE_LONG_DAYLIGHT].setTo(strings[row][col]);
- break;
- case 4:
- array[TIMEZONE_LONG_DAYLIGHT].setTo(strings[row][col]);
- break;
- case 5:
- array[TIMEZONE_EXEMPLAR_CITY].setTo(strings[row][col]);
- break;
- case 6:
- array[TIMEZONE_LONG_GENERIC].setTo(strings[row][col]);
- break;
- case 7:
- array[TIMEZONE_SHORT_GENERIC].setTo(strings[row][col]);
- break;
- default:
- status = U_ILLEGAL_ARGUMENT_ERROR;
- }
- // populate the hash table
- fZoneStringsHash->put(strings[row][0], array, status);
- }
- }
-
-}
-
-UnicodeString&
-DateFormatSymbols::getZoneString(const UnicodeString &zid, const TimeZoneTranslationType type,
- UnicodeString &result, UErrorCode &status){
-
- if(fZoneStringsHash == NULL){
- //lazy initialization
- initZoneStrings(status);
- }
- if(U_FAILURE(status)){
- return result;
- }
-
- UnicodeString* stringsArray = (UnicodeString*)fZoneStringsHash->get(zid);
- if(stringsArray != NULL){
- result.setTo(stringsArray[type],0);
- }
- return result;
-}
-
-UnicodeString
-DateFormatSymbols::getMetazoneString(const UnicodeString &zid, const TimeZoneTranslationType type, Calendar &cal,
- UnicodeString &result, UErrorCode &status)
-{
- UErrorCode tempStatus = U_ZERO_ERROR;
- int32_t len;
- UnicodeString mzid(UNICODE_STRING_SIMPLE("meta/"));
-
- // Get the appropriate metazone mapping from the resource bundles
-
- char usesMetazoneKey[ZID_KEY_MAX];
- char zidkey[ZID_KEY_MAX];
-
- uprv_strcpy(usesMetazoneKey,gZoneStringsTag);
- uprv_strcat(usesMetazoneKey,"/");
-
- len = zid.length();
- len = (len >= (ZID_KEY_MAX-1) ? ZID_KEY_MAX-1 : len);
- u_UCharsToChars(zid.getBuffer(), zidkey, len);
- zidkey[len] = 0; // NULL terminate
-
- // Replace / with : for zid
- len = (int32_t)uprv_strlen(zidkey);
- for (int i = 0; i < len; i++) {
- if (zidkey[i] == '/') {
- zidkey[i] = ':';
- }
- }
-
- uprv_strcat(usesMetazoneKey,zidkey);
- uprv_strcat(usesMetazoneKey,"/");
- uprv_strcat(usesMetazoneKey,UTZ_USES_METAZONE);
-
- UResourceBundle *um = ures_getByKeyWithFallback(fResourceBundle, usesMetazoneKey, NULL, &tempStatus);
- if (U_FAILURE(tempStatus)) {
- return result;
- }
-
- UnicodeString* stringsArray = (UnicodeString*)fZoneStringsHash->get(zid);
-
- if(stringsArray != NULL){
- SimpleDateFormat df(UNICODE_STRING_SIMPLE("yyyy-MM-dd HH:mm"), Locale(""),tempStatus);
- TimeZone *tz = TimeZone::createTimeZone(zid);
- df.setTimeZone(*tz);
- delete tz;
- UnicodeString theTime;
- df.format(cal.getTime(tempStatus),theTime);
-
- while (ures_hasNext(um)) {
- UResourceBundle *mz = ures_getNextResource(um,NULL,&status);
- const UChar *mz_name = ures_getStringByIndex(mz,0,&len,&status);
- const UChar *mz_from = ures_getStringByIndex(mz,1,&len,&status);
- const UChar *mz_to = ures_getStringByIndex(mz,2,&len,&status);
- ures_close(mz);
- if(U_FAILURE(status)){
- break;
- }
-
- if (mz_name[0] != 0 &&
- UnicodeString(TRUE, mz_from, -1) <= theTime &&
- UnicodeString(TRUE, mz_to, -1) > theTime )
- {
- mzid += mz_name;
- getZoneString(mzid,type,result,status);
- break;
- }
- }
- }
- ures_close(um);
- if ( mzid.length() > 5 ) {
- return mzid;
- }
- return result;
-}
-
-UnicodeString&
-DateFormatSymbols::getFallbackString(const UnicodeString &zid, UnicodeString &result, UErrorCode &status)
-{
- UnicodeString exemplarCity;
- char zidkey[ZID_KEY_MAX];
- char zoneTerritoryChars[ULOC_COUNTRY_CAPACITY];
- UnicodeString displayCountry;
- UnicodeString solidus = UNICODE_STRING_SIMPLE("/");
- UnicodeString und = UNICODE_STRING_SIMPLE("_");
- UnicodeString spc = UNICODE_STRING_SIMPLE(" ");
- const UChar* aZone = NULL;
- UBool IsMultiZone = FALSE;
-
-
- int32_t len = zid.length();
- len = (len >= (ZID_KEY_MAX-1) ? ZID_KEY_MAX-1 : len);
- u_UCharsToChars(zid.getBuffer(), zidkey, len);
- zidkey[len] = 0; // NULL terminate
-
- // Replace / with : for zid
- len = (int32_t)uprv_strlen(zidkey);
- for (int i = 0; i < len; i++) {
- if (zidkey[i] == '/') {
- zidkey[i] = ':';
- }
- }
-
- result.remove();
-
- UResourceBundle* supplementalDataBundle = ures_openDirect(NULL, kSUPPLEMENTAL, &status);
- if (U_FAILURE(status) || fResourceBundle == NULL ) {
- return result;
- }
-
- UResourceBundle* zoneFormatting = ures_getByKey(supplementalDataBundle, gZoneFormattingTag, NULL, &status);
- UResourceBundle* thisZone = ures_getByKey(zoneFormatting, zidkey, NULL, &status);
- if (U_FAILURE(status)) {
- ures_close(zoneFormatting);
- ures_close(supplementalDataBundle);
- return result;
- }
-
- UResourceBundle* multiZone = ures_getByKey(zoneFormatting, gMultizoneTag, NULL, &status);
- const UChar *zoneTerritory = ures_getStringByKey(thisZone,gTerritoryTag,&len,&status);
- u_UCharsToChars(zoneTerritory, zoneTerritoryChars, u_strlen(zoneTerritory));
- zoneTerritoryChars[u_strlen(zoneTerritory)] = 0; // NULL terminate
-
- UResourceBundle* countries = ures_getByKey(fResourceBundle, gCountriesTag, NULL, &status);
- if ( u_strlen(zoneTerritory) > 0 && countries != NULL ) {
- displayCountry = ures_getStringByKeyWithFallback(countries,zoneTerritoryChars,&len,&status);
- }
-
- if ( U_FAILURE(status) ) {
- status = U_ZERO_ERROR;
- displayCountry = UnicodeString(zoneTerritory);
- }
-
- while ( ures_hasNext(multiZone) ) {
- aZone = ures_getNextString(multiZone,&len,NULL,&status);
- if ( u_strcmp(aZone,zoneTerritory) == 0 ) {
- IsMultiZone = TRUE;
- continue;
- }
- }
-
- if ( IsMultiZone ) {
- getZoneString(zid, TIMEZONE_EXEMPLAR_CITY, exemplarCity, status);
- if ( exemplarCity.length()==0 ) {
- exemplarCity.setTo(UnicodeString(zid,zid.lastIndexOf(solidus)+1));
- exemplarCity.findAndReplace(und,spc);
- }
- Formattable cityCountryArray[2];
- UnicodeString pattern = UnicodeString(ures_getStringByKeyWithFallback(fResourceBundle,gFallbackFormatTag,&len,&status));
- if ( U_FAILURE(status) ) {
- pattern = UNICODE_STRING_SIMPLE("{1} ({0})");
- status = U_ZERO_ERROR;
- }
- cityCountryArray[0].adoptString(new UnicodeString(exemplarCity));
- cityCountryArray[1].adoptString(new UnicodeString(displayCountry));
- MessageFormat::format(pattern,cityCountryArray, 2, result, status);
- } else {
- Formattable countryArray[1];
- UnicodeString pattern = UnicodeString(ures_getStringByKeyWithFallback(fResourceBundle,gRegionFormatTag,&len,&status));
- if ( U_FAILURE(status) ) {
- pattern = UNICODE_STRING_SIMPLE("{0}");
- status = U_ZERO_ERROR;
- }
- countryArray[0].adoptString(new UnicodeString(displayCountry));
- MessageFormat::format(pattern,countryArray, 1, result, status);
- }
-
- ures_close(thisZone);
- ures_close(zoneFormatting);
- ures_close(supplementalDataBundle);
- ures_close(countries);
- ures_close(multiZone);
-
- return result;
-}
-
-UBool
-DateFormatSymbols::isCommonlyUsed(const UnicodeString &zid){
- UErrorCode status=U_ZERO_ERROR;
- UResourceBundle *zoneArray, *zoneItem, *cuRes;
- UnicodeString solidus = UNICODE_STRING_SIMPLE("/");
- UnicodeString colon = UNICODE_STRING_SIMPLE(":");
- UnicodeString key(zid);
- char keychars[ZID_KEY_MAX+1];
-
- key.findAndReplace(solidus,colon);
-
- for(const UResourceBundle* rb = fResourceBundle; rb!=NULL; rb=ures_getParentBundle(rb)){
- zoneArray = ures_getByKey(rb, gZoneStringsTag, NULL, &status);
- if(U_FAILURE(status)){
- status = U_ZERO_ERROR;
- continue;
- }
- int32_t len = key.length();
- u_UCharsToChars(key.getBuffer(), keychars, len);
- keychars[len] = 0; // NULL terminate
- zoneItem = ures_getByKey(zoneArray,keychars,NULL, &status);
- if(U_FAILURE(status)){
- ures_close(zoneArray);
- status = U_ZERO_ERROR;
- continue;
- }
-
- cuRes = ures_getByKey(zoneItem,UTZ_COMMONLY_USED,NULL,&status);
- if(U_FAILURE(status)){
- ures_close(zoneItem);
- ures_close(zoneArray);
- status = U_ZERO_ERROR;
- continue;
- }
- int32_t cuValue = ures_getInt(cuRes,&status);
-
- ures_close(cuRes);
- ures_close(zoneItem);
- ures_close(zoneArray);
-
- if(U_FAILURE(status)){
- status = U_ZERO_ERROR;
- continue;
- }
-
- if ( cuValue == 1 ) {
- return TRUE;
- }
- }
- return FALSE;
-}
-
-StringEnumeration*
-DateFormatSymbols::createZoneStringIDs(UErrorCode &status){
- if(U_FAILURE(status)){
- return NULL;
- }
- if(fZoneStringsHash == NULL){
- //lazy initialization
- initZoneStrings(status);
- }
- return fZoneIDEnumeration->clone();
-}
-
-/**
- * Sets timezone strings.
- * @draft ICU 3.6
- */
-void
-DateFormatSymbols::setZoneString(const UnicodeString &zid, const TimeZoneTranslationType type,
- const UnicodeString &value, UErrorCode &status){
- if(fZoneStringsHash == NULL){
- //lazy initialization
- initZoneStrings(status);
- }
- if(U_FAILURE(status)){
- return;
- }
- UnicodeString* stringsArray = (UnicodeString*)fZoneStringsHash->get(zid);
- if(stringsArray != NULL){
- stringsArray[type].setTo(value);
- }else{
- stringsArray = newUnicodeStringArray(UTZ_MAX_DISPLAY_STRINGS_LENGTH);
- if(stringsArray==NULL){
- status = U_MEMORY_ALLOCATION_ERROR;
- return;
- }
- stringsArray[type].setTo(value);
- fZoneStringsHash->put(zid, stringsArray, status);
- TimeZoneKeysEnumeration* keys = (TimeZoneKeysEnumeration*) fZoneIDEnumeration;
- keys->put(zid, status);
- }
-}
-
-Hashtable*
-DateFormatSymbols::createZoneStringsHash(const Hashtable* otherHash){
- UErrorCode status = U_ZERO_ERROR;
- Hashtable* hash = new Hashtable(uhash_compareUnicodeString, compareTZHashValues, status);
- if(hash==NULL){
- return NULL;
- }
- if(U_FAILURE(status)){
- return NULL;
- }
- hash->setValueDeleter(deleteUnicodeStringArray);
- int32_t pos = -1;
- const UHashElement* elem = NULL;
- // walk through the hash table and create a deep clone
- while((elem = otherHash->nextElement(pos))!= NULL){
- const UHashTok otherKeyTok = elem->key;
- const UHashTok otherValueTok = elem->value;
- UnicodeString* otherKey = (UnicodeString*)otherKeyTok.pointer;
- UnicodeString* otherArray = (UnicodeString*)otherValueTok.pointer;
- UnicodeString* array = newUnicodeStringArray(UTZ_MAX_DISPLAY_STRINGS_LENGTH);
- if(array==NULL){
- return NULL;
- }
- UnicodeString key(*otherKey);
- for(int32_t i=0; i<UTZ_MAX_DISPLAY_STRINGS_LENGTH; i++){
- array[i].setTo(otherArray[i]);
- }
- hash->put(key, array, status);
- if(U_FAILURE(status)){
- delete[] array;
- return NULL;
- }
- }
- return hash;
-}
-
-
-UnicodeString&
-DateFormatSymbols::getZoneID(const UnicodeString& zid, UnicodeString& result, UErrorCode& status){
- if(fZoneStringsHash == NULL){
- initZoneStrings(status);
- }
- if(U_FAILURE(status)){
- return result;
- }
- UnicodeString* strings = (UnicodeString*)fZoneStringsHash->get(zid);
- if (strings != NULL) {
- return result.setTo(zid,0);
- }
-
- // Do a search through the equivalency group for the given ID
- int32_t n = TimeZone::countEquivalentIDs(zid);
- if (n > 1) {
- int32_t i;
- for (i=0; i<n; ++i) {
- UnicodeString equivID = TimeZone::getEquivalentID(zid, i);
- if (equivID != zid) {
- strings = (UnicodeString*)fZoneStringsHash->get(equivID);
- if (strings != NULL) {
- return result.setTo(equivID,0);
- }
- }
- }
- }else{
- result.setTo(zid);
- }
- return result;
-}
-
-void
-DateFormatSymbols::getZoneType(const UnicodeString& zid, const UnicodeString& text, int32_t start,
- TimeZoneTranslationType& type, UnicodeString& value, UErrorCode& status){
- if(fZoneStringsHash == NULL){
- initZoneStrings(status);
- }
- if(U_FAILURE(status)){
- return;
- }
- type = TIMEZONE_COUNT;
- UnicodeString* strings = (UnicodeString*)fZoneStringsHash->get(zid);
- if(strings != NULL){
- for(int32_t j=0; j<UTZ_MAX_DISPLAY_STRINGS_LENGTH; j++){
- if(strings[j].length() >0 && text.caseCompare(start, strings[j].length(), strings[j], 0)==0){
- type = (TimeZoneTranslationType)j;
- value.setTo(strings[j]);
- return;
- }
- }
- }
-}
-void
-DateFormatSymbols::findZoneIDTypeValue( UnicodeString& zid, const UnicodeString& text, int32_t start,
- TimeZoneTranslationType& type, UnicodeString& value,
- UErrorCode& status){
- if(fZoneStringsHash == NULL){
- initZoneStrings(status);
- }
- if(U_FAILURE(status)){
- return;
- }
- const UnicodeString* myKey = NULL;
- int32_t pos = 0;
- TimeZoneKeysEnumeration *keys = (TimeZoneKeysEnumeration*)fZoneIDEnumeration;
- while( (myKey=keys->snext(pos, status))!= NULL){
- UnicodeString* strings = (UnicodeString*)fZoneStringsHash->get(*myKey);
- if(strings != NULL){
- for(int32_t j=0; j<UTZ_MAX_DISPLAY_STRINGS_LENGTH; j++){
- if(strings[j].length()>0 && text.caseCompare(start, strings[j].length(), strings[j], 0)==0){
- type = (TimeZoneTranslationType)j;
- value.setTo(strings[j]);
- if (myKey->startsWith(UNICODE_STRING_SIMPLE("meta"))) {
- zid.setTo(resolveParsedMetazone(*myKey));
- }
- else {
- zid.setTo(*myKey);
- }
- return;
- }
- }
- }
- }
-
- // Check for generic tz fallback strings if we have gone through all zone strings and haven't found
- // anything.
-
- UnicodeString fbString;
- StringEnumeration *tzKeys = TimeZone::createEnumeration();
-
- while( (myKey=tzKeys->snext(status))!= NULL){
- status = U_ZERO_ERROR;
- this->getFallbackString(*myKey,fbString,status);
- if ( U_FAILURE(status) ) {
- status = U_ZERO_ERROR;
- continue;
- }
-
- if(fbString.length()>0 && text.compare(start, fbString.length(), fbString)==0){
- type = (TimeZoneTranslationType) TIMEZONE_LONG_GENERIC;
- value.setTo(fbString);
- zid.setTo(*myKey);
- break;
- }
- }
- delete tzKeys;
-}
-
-UnicodeString
-DateFormatSymbols::resolveParsedMetazone( const UnicodeString& zid ) {
-
- UErrorCode status = U_ZERO_ERROR;
-
- UResourceBundle* supplementalDataBundle = ures_openDirect(NULL, kSUPPLEMENTAL, &status);
- UResourceBundle* mapTz = ures_getByKey(supplementalDataBundle, gMaptimezonesTag, NULL, &status);
- if(U_FAILURE(status)){
- ures_close(supplementalDataBundle);
- return UNICODE_STRING_SIMPLE("Etc/GMT");
- }
-
- UResourceBundle* metazoneMap = ures_getByKey(mapTz, gMetazonesTag, NULL, &status);
- char mzMapKey[ZID_KEY_MAX+4];
-
- int32_t len = zid.length();
- len = (len >= (ZID_KEY_MAX-1) ? ZID_KEY_MAX-1 : len);
- u_UCharsToChars(zid.getBuffer(), mzMapKey, len);
- mzMapKey[len] = 0; // NULL terminate
-
- for (int i = 0; i < len; i++) {
- if (mzMapKey[i] == '/') {
- mzMapKey[i] = ':';
- }
- }
-
- uprv_strcat(mzMapKey,"_");
- uprv_strcat(mzMapKey,fCountry);
-
- int32_t len2;
- const UChar* resStr = ures_getStringByKey(metazoneMap, mzMapKey, &len2, &status);
-
- // If we can't find a territory-specific metazone mapping, then use the generic one
- // which is the metazone name followed by _001
-
- if(U_FAILURE(status)){
- status = U_ZERO_ERROR;
- mzMapKey[len] = 0;
- uprv_strcat(mzMapKey,"_001");
- resStr = ures_getStringByKey(metazoneMap, mzMapKey, &len2, &status);
- }
-
- ures_close(metazoneMap);
- ures_close(mapTz);
- ures_close(supplementalDataBundle);
-
- if(U_SUCCESS(status)){
- return resStr;
- }
- else {
- return UNICODE_STRING_SIMPLE("Etc/GMT");
- }
-
-}
U_NAMESPACE_END
#endif /* #if !UCONFIG_NO_FORMATTING */
diff --git a/i18n/i18n.vcproj b/i18n/i18n.vcproj
index c6f8b06f..288a45cd 100644
--- a/i18n/i18n.vcproj
+++ b/i18n/i18n.vcproj
@@ -529,6 +529,24 @@
<File
RelativePath=".\unicode\basictz.h"
>
+ <FileConfiguration
+ Name="Release|Win32"
+ >
+ <Tool
+ Name="VCCustomBuildTool"
+ CommandLine="copy &quot;$(InputPath)&quot; ..\..\include\unicode"
+ Outputs="..\..\include\unicode\$(InputFileName)"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug|Win32"
+ >
+ <Tool
+ Name="VCCustomBuildTool"
+ CommandLine="copy &quot;$(InputPath)&quot; ..\..\include\unicode"
+ Outputs="..\..\include\unicode\$(InputFileName)"
+ />
+ </FileConfiguration>
</File>
<File
RelativePath=".\buddhcal.cpp"
@@ -799,6 +817,24 @@
<File
RelativePath=".\unicode\dtrule.h"
>
+ <FileConfiguration
+ Name="Release|Win32"
+ >
+ <Tool
+ Name="VCCustomBuildTool"
+ CommandLine="copy &quot;$(InputPath)&quot; ..\..\include\unicode"
+ Outputs="..\..\include\unicode\$(InputFileName)"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug|Win32"
+ >
+ <Tool
+ Name="VCCustomBuildTool"
+ CommandLine="copy &quot;$(InputPath)&quot; ..\..\include\unicode"
+ Outputs="..\..\include\unicode\$(InputFileName)"
+ />
+ </FileConfiguration>
</File>
<File
RelativePath=".\unicode\fieldpos.h"
@@ -1147,6 +1183,24 @@
<File
RelativePath=".\unicode\rbtz.h"
>
+ <FileConfiguration
+ Name="Release|Win32"
+ >
+ <Tool
+ Name="VCCustomBuildTool"
+ CommandLine="copy &quot;$(InputPath)&quot; ..\..\include\unicode&#x0D;&#x0A;"
+ Outputs="..\..\include\unicode\$(InputFileName)"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug|Win32"
+ >
+ <Tool
+ Name="VCCustomBuildTool"
+ CommandLine="copy &quot;$(InputPath)&quot; ..\..\include\unicode&#x0D;&#x0A;"
+ Outputs="..\..\include\unicode\$(InputFileName)"
+ />
+ </FileConfiguration>
</File>
<File
RelativePath=".\reldtfmt.cpp"
@@ -1249,6 +1303,24 @@
<File
RelativePath=".\unicode\tzrule.h"
>
+ <FileConfiguration
+ Name="Release|Win32"
+ >
+ <Tool
+ Name="VCCustomBuildTool"
+ CommandLine="copy &quot;$(InputPath)&quot; ..\..\include\unicode"
+ Outputs="..\..\include\unicode\$(InputFileName)"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug|Win32"
+ >
+ <Tool
+ Name="VCCustomBuildTool"
+ CommandLine="copy &quot;$(InputPath)&quot; ..\..\include\unicode"
+ Outputs="..\..\include\unicode\$(InputFileName)"
+ />
+ </FileConfiguration>
</File>
<File
RelativePath=".\tztrans.cpp"
@@ -1257,6 +1329,24 @@
<File
RelativePath=".\unicode\tztrans.h"
>
+ <FileConfiguration
+ Name="Release|Win32"
+ >
+ <Tool
+ Name="VCCustomBuildTool"
+ CommandLine="copy &quot;$(InputPath)&quot; ..\..\include\unicode"
+ Outputs="..\..\include\unicode\$(InputFileName)"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug|Win32"
+ >
+ <Tool
+ Name="VCCustomBuildTool"
+ CommandLine="copy &quot;$(InputPath)&quot; ..\..\include\unicode"
+ Outputs="..\..\include\unicode\$(InputFileName)"
+ />
+ </FileConfiguration>
</File>
<File
RelativePath=".\ucal.cpp"
@@ -1456,6 +1546,15 @@
RelativePath=".\unicode\utmscale.h"
>
<FileConfiguration
+ Name="Release|Win32"
+ >
+ <Tool
+ Name="VCCustomBuildTool"
+ CommandLine="copy &quot;$(InputPath)&quot; ..\..\include\unicode"
+ Outputs="..\..\include\unicode\$(InputFileName)"
+ />
+ </FileConfiguration>
+ <FileConfiguration
Name="Debug|Win32"
>
<Tool
@@ -1472,6 +1571,24 @@
<File
RelativePath=".\unicode\vtzone.h"
>
+ <FileConfiguration
+ Name="Release|Win32"
+ >
+ <Tool
+ Name="VCCustomBuildTool"
+ CommandLine="copy &quot;$(InputPath)&quot; ..\..\include\unicode"
+ Outputs="..\..\include\unicode\$(InputFileName)"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug|Win32"
+ >
+ <Tool
+ Name="VCCustomBuildTool"
+ CommandLine="copy &quot;$(InputPath)&quot; ..\..\include\unicode"
+ Outputs="..\..\include\unicode\$(InputFileName)"
+ />
+ </FileConfiguration>
</File>
<File
RelativePath=".\windtfmt.cpp"
@@ -1521,6 +1638,22 @@
RelativePath=".\winnmfmt.h"
>
</File>
+ <File
+ RelativePath=".\zonemeta.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\zonemeta.h"
+ >
+ </File>
+ <File
+ RelativePath=".\zstrfmt.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\zstrfmt.h"
+ >
+ </File>
</Filter>
<Filter
Name="misc"
diff --git a/i18n/nfsubs.cpp b/i18n/nfsubs.cpp
index 353a1b24..37de55a4 100644
--- a/i18n/nfsubs.cpp
+++ b/i18n/nfsubs.cpp
@@ -97,7 +97,11 @@ public:
}
virtual double transformNumber(double number) const {
- return uprv_floor(number / divisor);
+ if (getRuleSet()) {
+ return uprv_floor(number / divisor);
+ } else {
+ return number/divisor;
+ }
}
virtual double composeRuleValue(double newRuleValue, double /*oldRuleValue*/) const {
diff --git a/i18n/olsontz.cpp b/i18n/olsontz.cpp
index 8599d4d2..e98b9ef6 100644
--- a/i18n/olsontz.cpp
+++ b/i18n/olsontz.cpp
@@ -317,11 +317,11 @@ int32_t OlsonTimeZone::getOffset(uint8_t era, int32_t year, int32_t month,
millis, monthLength, ec);
}
- // Compute local epoch seconds from input fields
- double time = Grego::fieldsToDay(year, month, dom) * SECONDS_PER_DAY +
- uprv_floor(millis / (double) U_MILLIS_PER_SECOND);
-
- return zoneOffset(findTransition(time, TRUE)) * U_MILLIS_PER_SECOND;
+ // Compute local epoch millis from input fields
+ UDate date = (UDate)(Grego::fieldsToDay(year, month, dom) * U_MILLIS_PER_DAY + millis);
+ int32_t rawoff, dstoff;
+ getHistoricalOffset(date, TRUE, kDaylight, kStandard, rawoff, dstoff);
+ return rawoff + dstoff;
}
/**
@@ -332,40 +332,30 @@ void OlsonTimeZone::getOffset(UDate date, UBool local, int32_t& rawoff,
if (U_FAILURE(ec)) {
return;
}
-
// The check against finalMillis will suffice most of the time, except
// for the case in which finalMillis == DBL_MAX, date == DBL_MAX,
// and finalZone == 0. For this case we add "&& finalZone != 0".
if (date >= finalMillis && finalZone != 0) {
- int32_t year, month, dom, dow;
- double millis;
- double days = Math::floorDivide(date, (double)U_MILLIS_PER_DAY, millis);
-
- Grego::dayToFields(days, year, month, dom, dow);
-
- rawoff = finalZone->getRawOffset();
-
- if (!local) {
- // Adjust from GMT to local
- date += rawoff;
- double days2 = Math::floorDivide(date, (double)U_MILLIS_PER_DAY, millis);
- if (days2 != days) {
- Grego::dayToFields(days2, year, month, dom, dow);
- }
- }
+ finalZone->getOffset(date, local, rawoff, dstoff, ec);
+ } else {
+ getHistoricalOffset(date, local, kFormer, kLatter, rawoff, dstoff);
+ }
+}
- dstoff = finalZone->getOffset(
- GregorianCalendar::AD, year, month,
- dom, (uint8_t) dow, (int32_t) millis, ec) - rawoff;
+void
+OlsonTimeZone::getOffsetFromLocal(UDate date, int32_t nonExistingTimeOpt, int32_t duplicatedTimeOpt,
+ int32_t& rawoff, int32_t& dstoff, UErrorCode& ec) /*const*/ {
+ if (U_FAILURE(ec)) {
return;
}
-
- double secs = uprv_floor(date / U_MILLIS_PER_SECOND);
- int16_t i = findTransition(secs, local);
- rawoff = rawOffset(i) * U_MILLIS_PER_SECOND;
- dstoff = dstOffset(i) * U_MILLIS_PER_SECOND;
+ if (date >= finalMillis && finalZone != 0) {
+ finalZone->getOffsetFromLocal(date, nonExistingTimeOpt, duplicatedTimeOpt, rawoff, dstoff, ec);
+ } else {
+ getHistoricalOffset(date, TRUE, nonExistingTimeOpt, duplicatedTimeOpt, rawoff, dstoff);
+ }
}
+
/**
* TimeZone API.
*/
@@ -394,69 +384,84 @@ void printTime(double ms) {
double days = Math::floorDivide(((double)ms), (double)U_MILLIS_PER_DAY, millis);
Grego::dayToFields(days, year, month, dom, dow);
- U_DEBUG_TZ_MSG((" findTransition: time %.1f (%04d.%02d.%02d+%.1fh)\n", ms,
+ U_DEBUG_TZ_MSG((" getHistoricalOffset: time %.1f (%04d.%02d.%02d+%.1fh)\n", ms,
year, month+1, dom, (millis/kOneHour)));
}
#endif
-/**
- * Find the smallest i (in 0..transitionCount-1) such that time >=
- * transition(i), where transition(i) is either the GMT or the local
- * transition time, as specified by `local'.
- * @param time epoch seconds, either GMT or local wall
- * @param local if TRUE, `time' is in local wall units, otherwise it
- * is GMT
- * @return an index i, where 0 <= i < transitionCount, and
- * transition(i) <= time < transition(i+1), or i == 0 if
- * transitionCount == 0 or time < transition(0).
- */
-int16_t OlsonTimeZone::findTransition(double time, UBool local) const {
- int16_t i = 0;
- U_DEBUG_TZ_MSG(("findTransition(%.1f, %s)\n", time, local?"T":"F"));
+void
+OlsonTimeZone::getHistoricalOffset(UDate date, UBool local,
+ int32_t NonExistingTimeOpt, int32_t DuplicatedTimeOpt,
+ int32_t& rawoff, int32_t& dstoff) const {
+ U_DEBUG_TZ_MSG(("getHistoricalOffset(%.1f, %s, %d, %d, raw, dst)\n",
+ date, local?"T":"F", NonExistingTimeOpt, DuplicatedTimeOpt));
#if defined U_DEBUG_TZ
- printTime(time*1000.0);
+ printTime(date*1000.0);
#endif
-
if (transitionCount != 0) {
+ double sec = uprv_floor(date / U_MILLIS_PER_SECOND);
// Linear search from the end is the fastest approach, since
// most lookups will happen at/near the end.
+ int16_t i;
for (i = transitionCount - 1; i > 0; --i) {
int32_t transition = transitionTimes[i];
+
if (local) {
- int32_t zoneOffsetPrev = zoneOffset(typeData[i-1]);
- int32_t zoneOffsetCurr = zoneOffset(typeData[i]);
+ int32_t offsetBefore = zoneOffset(typeData[i-1]);
+ UBool dstBefore = dstOffset(typeData[i-1]) != 0;
+
+ int32_t offsetAfter = zoneOffset(typeData[i]);
+ UBool dstAfter = dstOffset(typeData[i]) != 0;
+
+ UBool dstToStd = dstBefore && !dstAfter;
+ UBool stdToDst = !dstBefore && dstAfter;
- // use the lowest offset ( == standard time ). as per tzregts.cpp which says:
-
- /**
- * @bug 4084933
- * The expected behavior of TimeZone around the boundaries is:
- * (Assume transition time of 2:00 AM)
- * day of onset 1:59 AM STD = display name 1:59 AM ST
- * 2:00 AM STD = display name 3:00 AM DT
- * day of end 0:59 AM STD = display name 1:59 AM DT
- * 1:00 AM STD = display name 1:00 AM ST
- */
- if(zoneOffsetPrev<zoneOffsetCurr) {
- transition += zoneOffsetPrev;
+ if (offsetAfter - offsetBefore >= 0) {
+ // Positive transition, which makes a non-existing local time range
+ if (((NonExistingTimeOpt & kStdDstMask) == kStandard && dstToStd)
+ || ((NonExistingTimeOpt & kStdDstMask) == kDaylight && stdToDst)) {
+ transition += offsetBefore;
+ } else if (((NonExistingTimeOpt & kStdDstMask) == kStandard && stdToDst)
+ || ((NonExistingTimeOpt & kStdDstMask) == kDaylight && dstToStd)) {
+ transition += offsetAfter;
+ } else if ((NonExistingTimeOpt & kFormerLatterMask) == kLatter) {
+ transition += offsetBefore;
+ } else {
+ // Interprets the time with rule before the transition,
+ // default for non-existing time range
+ transition += offsetAfter;
+ }
} else {
- transition += zoneOffsetCurr;
+ // Negative transition, which makes a duplicated local time range
+ if (((DuplicatedTimeOpt & kStdDstMask) == kStandard && dstToStd)
+ || ((DuplicatedTimeOpt & kStdDstMask) == kDaylight && stdToDst)) {
+ transition += offsetAfter;
+ } else if (((DuplicatedTimeOpt & kStdDstMask) == kStandard && stdToDst)
+ || ((DuplicatedTimeOpt & kStdDstMask) == kDaylight && dstToStd)) {
+ transition += offsetBefore;
+ } else if ((DuplicatedTimeOpt & kFormerLatterMask) == kFormer) {
+ transition += offsetBefore;
+ } else {
+ // Interprets the time with rule after the transition,
+ // default for duplicated local time range
+ transition += offsetAfter;
+ }
}
}
- if (time >= transition) {
- U_DEBUG_TZ_MSG(("Found@%d: time=%.1f, localtransition=%d (orig %d) dz %d\n", i, time, transition, transitionTimes[i],
+ if (sec >= transition) {
+ U_DEBUG_TZ_MSG(("Found@%d: time=%.1f, localtransition=%d (orig %d) dz %d\n", i, sec, transition, transitionTimes[i],
zoneOffset(typeData[i-1])));
#if defined U_DEBUG_TZ
- printTime(transition*1000.0);
- printTime(transitionTimes[i]*1000.0);
+ printTime(transition*1000.0);
+ printTime(transitionTimes[i]*1000.0);
#endif
break;
} else {
- U_DEBUG_TZ_MSG(("miss@%d: time=%.1f, localtransition=%d (orig %d) dz %d\n", i, time, transition, transitionTimes[i],
+ U_DEBUG_TZ_MSG(("miss@%d: time=%.1f, localtransition=%d (orig %d) dz %d\n", i, sec, transition, transitionTimes[i],
zoneOffset(typeData[i-1])));
#if defined U_DEBUG_TZ
- printTime(transition*1000.0);
- printTime(transitionTimes[i]*1000.0);
+ printTime(transition*1000.0);
+ printTime(transitionTimes[i]*1000.0);
#endif
}
}
@@ -465,17 +470,25 @@ int16_t OlsonTimeZone::findTransition(double time, UBool local) const {
// Check invariants for GMT times; if these pass for GMT times
// the local logic should be working too.
- U_ASSERT(local || time < transitionTimes[0] || time >= transitionTimes[i]);
- U_ASSERT(local || i == transitionCount-1 || time < transitionTimes[i+1]);
-
- U_DEBUG_TZ_MSG(("findTransition(%.1f, %s)= trans %d\n", time, local?"T":"F", i));
- i = typeData[i];
+ U_ASSERT(local || sec < transitionTimes[0] || sec >= transitionTimes[i]);
+ U_ASSERT(local || i == transitionCount-1 || sec < transitionTimes[i+1]);
+
+ U_DEBUG_TZ_MSG(("getHistoricalOffset(%.1f, %s, %d, %d, raw, dst) - trans %d\n",
+ date, local?"T":"F", NonExistingTimeOpt, DuplicatedTimeOpt, i));
+
+ // Since ICU tzdata 2007c, the first transition data is actually not a
+ // transition, but used for representing the initial offset. So the code
+ // below works even if i == 0.
+ int16_t index = typeData[i];
+ rawoff = rawOffset(index) * U_MILLIS_PER_SECOND;
+ dstoff = dstOffset(index) * U_MILLIS_PER_SECOND;
+ } else {
+ // No transitions, single pair of offsets only
+ rawoff = rawOffset(0) * U_MILLIS_PER_SECOND;
+ dstoff = dstOffset(0) * U_MILLIS_PER_SECOND;
}
-
- U_ASSERT(i>=0 && i<typeCount);
-
- U_DEBUG_TZ_MSG(("findTransition(%.1f, %s)=%d, offset %d\n", time, local?"T":"F", i, zoneOffset(i)));
- return i;
+ U_DEBUG_TZ_MSG(("getHistoricalOffset(%.1f, %s, %d, %d, raw, dst) - raw=%d, dst=%d\n",
+ date, local?"T":"F", NonExistingTimeOpt, DuplicatedTimeOpt, rawoff, dstoff));
}
/**
diff --git a/i18n/olsontz.h b/i18n/olsontz.h
index d5c4bce1..64f8c0d7 100644
--- a/i18n/olsontz.h
+++ b/i18n/olsontz.h
@@ -182,6 +182,12 @@ class OlsonTimeZone: public BasicTimeZone {
int32_t& dstOffset, UErrorCode& ec) const;
/**
+ * BasicTimeZone API.
+ */
+ virtual void getOffsetFromLocal(UDate date, int32_t nonExistingTimeOpt, int32_t duplicatedTimeOpt,
+ int32_t& rawoff, int32_t& dstoff, UErrorCode& ec) /*const*/;
+
+ /**
* TimeZone API. This method has no effect since objects of this
* class are quasi-immutable (the base class allows the ID to be
* changed).
@@ -279,7 +285,9 @@ private:
void constructEmpty();
- int16_t findTransition(double time, UBool local) const;
+ void getHistoricalOffset(UDate date, UBool local,
+ int32_t NonExistingTimeOpt, int32_t DuplicatedTimeOpt,
+ int32_t& rawoff, int32_t& dstoff) const;
int32_t zoneOffset(int16_t index) const;
int32_t rawOffset(int16_t index) const;
diff --git a/i18n/rbtz.cpp b/i18n/rbtz.cpp
index 38943763..f810d70b 100644
--- a/i18n/rbtz.cpp
+++ b/i18n/rbtz.cpp
@@ -46,14 +46,6 @@ static UBool compareRules(UVector* rules1, UVector* rules2) {
return TRUE;
}
-static UDate getTransitionTime(Transition* transition, UBool local) {
- UDate time = transition->time;
- if (local) {
- time += transition->from->getRawOffset() + transition->from->getDSTSavings();
- }
- return time;
-}
-
UOBJECT_DEFINE_RTTI_IMPLEMENTATION(RuleBasedTimeZone)
RuleBasedTimeZone::RuleBasedTimeZone(const UnicodeString& id, InitialTimeZoneRule* initialRule)
@@ -183,7 +175,7 @@ RuleBasedTimeZone::complete(UErrorCode& status) {
for (i = 0; i < historicCount; i++) {
done[i] = FALSE;
}
- while (true) {
+ while (TRUE) {
int32_t curStdOffset = curRule->getRawOffset();
int32_t curDstSavings = curRule->getDSTSavings();
UDate nextTransitionTime = MAX_MILLIS;
@@ -377,7 +369,7 @@ RuleBasedTimeZone::getOffset(uint8_t era, int32_t year, int32_t month, int32_t d
}
int32_t rawOffset, dstOffset;
UDate time = (UDate)Grego::fieldsToDay(year, month, day) * U_MILLIS_PER_DAY + millis;
- getOffset(time, true, rawOffset, dstOffset, status);
+ getOffsetInternal(time, TRUE, kDaylight, kStandard, rawOffset, dstOffset, status);
if (U_FAILURE(status)) {
return 0;
}
@@ -387,6 +379,24 @@ RuleBasedTimeZone::getOffset(uint8_t era, int32_t year, int32_t month, int32_t d
void
RuleBasedTimeZone::getOffset(UDate date, UBool local, int32_t& rawOffset,
int32_t& dstOffset, UErrorCode& status) const {
+ getOffsetInternal(date, local, kFormer, kLatter, rawOffset, dstOffset, status);
+}
+
+void
+RuleBasedTimeZone::getOffsetFromLocal(UDate date, int32_t nonExistingTimeOpt, int32_t duplicatedTimeOpt,
+ int32_t& rawOffset, int32_t& dstOffset, UErrorCode& status) /*const*/ {
+ getOffsetInternal(date, TRUE, nonExistingTimeOpt, duplicatedTimeOpt, rawOffset, dstOffset, status);
+}
+
+
+/*
+ * The internal getOffset implementation
+ */
+void
+RuleBasedTimeZone::getOffsetInternal(UDate date, UBool local,
+ int32_t NonExistingTimeOpt, int32_t DuplicatedTimeOpt,
+ int32_t& rawOffset, int32_t& dstOffset,
+ UErrorCode& status) const {
rawOffset = 0;
dstOffset = 0;
@@ -404,15 +414,17 @@ RuleBasedTimeZone::getOffset(UDate date, UBool local, int32_t& rawOffset,
if (fHistoricTransitions == NULL) {
rule = fInitialRule;
} else {
- UDate tstart = getTransitionTime((Transition*)fHistoricTransitions->elementAt(0), local);
+ UDate tstart = getTransitionTime((Transition*)fHistoricTransitions->elementAt(0),
+ local, NonExistingTimeOpt, DuplicatedTimeOpt);
if (date < tstart) {
rule = fInitialRule;
} else {
int32_t idx = fHistoricTransitions->size() - 1;
- UDate tend = getTransitionTime((Transition*)fHistoricTransitions->elementAt(idx), local);
+ UDate tend = getTransitionTime((Transition*)fHistoricTransitions->elementAt(idx),
+ local, NonExistingTimeOpt, DuplicatedTimeOpt);
if (date > tend) {
if (fFinalRules != NULL) {
- rule = findRuleInFinal(date, local);
+ rule = findRuleInFinal(date, local, NonExistingTimeOpt, DuplicatedTimeOpt);
} else {
// no final rule, use the last rule
rule = ((Transition*)fHistoricTransitions->elementAt(idx))->to;
@@ -420,7 +432,8 @@ RuleBasedTimeZone::getOffset(UDate date, UBool local, int32_t& rawOffset,
} else {
// Find a historical transition
while (idx >= 0) {
- if (date >= getTransitionTime((Transition*)fHistoricTransitions->elementAt(idx), local)) {
+ if (date >= getTransitionTime((Transition*)fHistoricTransitions->elementAt(idx),
+ local, NonExistingTimeOpt, DuplicatedTimeOpt)) {
break;
}
idx--;
@@ -651,7 +664,8 @@ RuleBasedTimeZone::copyRules(UVector* source) {
}
TimeZoneRule*
-RuleBasedTimeZone::findRuleInFinal(UDate date, UBool local) const {
+RuleBasedTimeZone::findRuleInFinal(UDate date, UBool local,
+ int32_t NonExistingTimeOpt, int32_t DuplicatedTimeOpt) const {
if (fFinalRules == NULL) {
return NULL;
}
@@ -664,12 +678,25 @@ RuleBasedTimeZone::findRuleInFinal(UDate date, UBool local) const {
UDate start0, start1;
UDate base;
+ int32_t localDelta;
- base = local ? date - fr1->getRawOffset() - fr1->getDSTSavings() : date;
- UBool avail0 = fr0->getPreviousStart(base, fr1->getRawOffset(), fr1->getDSTSavings(), true, start0);
+ base = date;
+ if (local) {
+ localDelta = getLocalDelta(fr1->getRawOffset(), fr1->getDSTSavings(),
+ fr0->getRawOffset(), fr0->getDSTSavings(),
+ NonExistingTimeOpt, DuplicatedTimeOpt);
+ base -= localDelta;
+ }
+ UBool avail0 = fr0->getPreviousStart(base, fr1->getRawOffset(), fr1->getDSTSavings(), TRUE, start0);
- base = local ? date - fr0->getRawOffset() - fr0->getDSTSavings() : date;
- UBool avail1 = fr1->getPreviousStart(base, fr0->getRawOffset(), fr0->getDSTSavings(), true, start1);
+ base = date;
+ if (local) {
+ localDelta = getLocalDelta(fr0->getRawOffset(), fr0->getDSTSavings(),
+ fr1->getRawOffset(), fr1->getDSTSavings(),
+ NonExistingTimeOpt, DuplicatedTimeOpt);
+ base -= localDelta;
+ }
+ UBool avail1 = fr1->getPreviousStart(base, fr0->getRawOffset(), fr0->getDSTSavings(), TRUE, start1);
if (avail0 && (!avail1 || start0 > start1)) {
return fr0;
@@ -689,14 +716,14 @@ RuleBasedTimeZone::findNext(UDate base, UBool inclusive, UDate& transitionTime,
UBool found = FALSE;
Transition result;
Transition *tzt = (Transition*)fHistoricTransitions->elementAt(0);
- UDate tt = getTransitionTime(tzt, FALSE);
+ UDate tt = tzt->time;
if (tt > base || (inclusive && tt == base)) {
result = *tzt;
found = TRUE;
} else {
int32_t idx = fHistoricTransitions->size() - 1;
tzt = (Transition*)fHistoricTransitions->elementAt(idx);
- tt = getTransitionTime(tzt, FALSE);
+ tt = tzt->time;
if (inclusive && tt == base) {
result = *tzt;
found = TRUE;
@@ -730,7 +757,7 @@ RuleBasedTimeZone::findNext(UDate base, UBool inclusive, UDate& transitionTime,
Transition *prev = tzt;
while (idx > 0) {
tzt = (Transition*)fHistoricTransitions->elementAt(idx);
- tt = getTransitionTime(tzt, FALSE);
+ tt = tzt->time;
if (tt < base || (!inclusive && tt == base)) {
break;
}
@@ -772,14 +799,14 @@ RuleBasedTimeZone::findPrev(UDate base, UBool inclusive, UDate& transitionTime,
UBool found = FALSE;
Transition result;
Transition *tzt = (Transition*)fHistoricTransitions->elementAt(0);
- UDate tt = getTransitionTime(tzt, FALSE);
+ UDate tt = tzt->time;
if (inclusive && tt == base) {
result = *tzt;
found = TRUE;
} else if (tt < base) {
int32_t idx = fHistoricTransitions->size() - 1;
tzt = (Transition*)fHistoricTransitions->elementAt(idx);
- tt = getTransitionTime(tzt, FALSE);
+ tt = tzt->time;
if (inclusive && tt == base) {
result = *tzt;
found = TRUE;
@@ -813,7 +840,7 @@ RuleBasedTimeZone::findPrev(UDate base, UBool inclusive, UDate& transitionTime,
idx--;
while (idx >= 0) {
tzt = (Transition*)fHistoricTransitions->elementAt(idx);
- tt = getTransitionTime(tzt, FALSE);
+ tt = tzt->time;
if (tt < base || (inclusive && tt == base)) {
break;
}
@@ -839,6 +866,63 @@ RuleBasedTimeZone::findPrev(UDate base, UBool inclusive, UDate& transitionTime,
return FALSE;
}
+UDate
+RuleBasedTimeZone::getTransitionTime(Transition* transition, UBool local,
+ int32_t NonExistingTimeOpt, int32_t DuplicatedTimeOpt) const {
+ UDate time = transition->time;
+ if (local) {
+ time += getLocalDelta(transition->from->getRawOffset(), transition->from->getDSTSavings(),
+ transition->to->getRawOffset(), transition->to->getDSTSavings(),
+ NonExistingTimeOpt, DuplicatedTimeOpt);
+ }
+ return time;
+}
+
+int32_t
+RuleBasedTimeZone::getLocalDelta(int32_t rawBefore, int32_t dstBefore, int32_t rawAfter, int32_t dstAfter,
+ int32_t NonExistingTimeOpt, int32_t DuplicatedTimeOpt) const {
+ int32_t delta = 0;
+
+ int32_t offsetBefore = rawBefore + dstBefore;
+ int32_t offsetAfter = rawAfter + dstAfter;
+
+ UBool dstToStd = (dstBefore != 0) && (dstAfter == 0);
+ UBool stdToDst = (dstBefore == 0) && (dstAfter != 0);
+
+ if (offsetAfter - offsetBefore >= 0) {
+ // Positive transition, which makes a non-existing local time range
+ if (((NonExistingTimeOpt & kStdDstMask) == kStandard && dstToStd)
+ || ((NonExistingTimeOpt & kStdDstMask) == kDaylight && stdToDst)) {
+ delta = offsetBefore;
+ } else if (((NonExistingTimeOpt & kStdDstMask) == kStandard && stdToDst)
+ || ((NonExistingTimeOpt & kStdDstMask) == kDaylight && dstToStd)) {
+ delta = offsetAfter;
+ } else if ((NonExistingTimeOpt & kFormerLatterMask) == kLatter) {
+ delta = offsetBefore;
+ } else {
+ // Interprets the time with rule before the transition,
+ // default for non-existing time range
+ delta = offsetAfter;
+ }
+ } else {
+ // Negative transition, which makes a duplicated local time range
+ if (((DuplicatedTimeOpt & kStdDstMask) == kStandard && dstToStd)
+ || ((DuplicatedTimeOpt & kStdDstMask) == kDaylight && stdToDst)) {
+ delta = offsetAfter;
+ } else if (((DuplicatedTimeOpt & kStdDstMask) == kStandard && stdToDst)
+ || ((DuplicatedTimeOpt & kStdDstMask) == kDaylight && dstToStd)) {
+ delta = offsetBefore;
+ } else if ((DuplicatedTimeOpt & kFormerLatterMask) == kFormer) {
+ delta = offsetBefore;
+ } else {
+ // Interprets the time with rule after the transition,
+ // default for duplicated local time range
+ delta = offsetAfter;
+ }
+ }
+ return delta;
+}
+
U_NAMESPACE_END
#endif /* #if !UCONFIG_NO_FORMATTING */
diff --git a/i18n/simpletz.cpp b/i18n/simpletz.cpp
index ca827f38..c9cdade3 100644
--- a/i18n/simpletz.cpp
+++ b/i18n/simpletz.cpp
@@ -502,6 +502,55 @@ SimpleTimeZone::getOffset(uint8_t era, int32_t year, int32_t month, int32_t day,
return result;
}
+void
+SimpleTimeZone::getOffsetFromLocal(UDate date, int32_t nonExistingTimeOpt, int32_t duplicatedTimeOpt,
+ int32_t& rawOffsetGMT, int32_t& savingsDST, UErrorCode& status) /*const*/ {
+ if (U_FAILURE(status)) {
+ return;
+ }
+
+ rawOffsetGMT = getRawOffset();
+ int32_t year, month, dom, dow;
+ double day = uprv_floor(date / U_MILLIS_PER_DAY);
+ int32_t millis = (int32_t) (date - day * U_MILLIS_PER_DAY);
+
+ Grego::dayToFields(day, year, month, dom, dow);
+
+ savingsDST = getOffset(GregorianCalendar::AD, year, month, dom,
+ (uint8_t) dow, millis,
+ Grego::monthLength(year, month),
+ status) - rawOffsetGMT;
+ if (U_FAILURE(status)) {
+ return;
+ }
+
+ UBool recalc = FALSE;
+
+ // Now we need some adjustment
+ if (savingsDST > 0) {
+ if ((nonExistingTimeOpt & kStdDstMask) == kStandard
+ || (nonExistingTimeOpt & kStdDstMask) != kDaylight && (nonExistingTimeOpt & kFormerLatterMask) != kLatter) {
+ date -= getDSTSavings();
+ recalc = TRUE;
+ }
+ } else {
+ if ((duplicatedTimeOpt & kStdDstMask) == kDaylight
+ || (duplicatedTimeOpt & kStdDstMask) != kStandard && (duplicatedTimeOpt & kFormerLatterMask) == kFormer) {
+ date -= getDSTSavings();
+ recalc = TRUE;
+ }
+ }
+ if (recalc) {
+ day = uprv_floor(date / U_MILLIS_PER_DAY);
+ millis = (int32_t) (date - day * U_MILLIS_PER_DAY);
+ Grego::dayToFields(day, year, month, dom, dow);
+ savingsDST = getOffset(GregorianCalendar::AD, year, month, dom,
+ (uint8_t) dow, millis,
+ Grego::monthLength(year, month),
+ status) - rawOffsetGMT;
+ }
+}
+
// -------------------------------------
/**
@@ -966,8 +1015,8 @@ SimpleTimeZone::getPreviousTransition(UDate base, UBool inclusive, TimeZoneTrans
return FALSE;
}
UDate stdDate, dstDate;
- UBool stdAvail = stdRule->getPreviousStart(base, dstRule->getRawOffset(), dstRule->getDSTSavings(), false, stdDate);
- UBool dstAvail = dstRule->getPreviousStart(base, stdRule->getRawOffset(), stdRule->getDSTSavings(), false, dstDate);
+ UBool stdAvail = stdRule->getPreviousStart(base, dstRule->getRawOffset(), dstRule->getDSTSavings(), inclusive, stdDate);
+ UBool dstAvail = dstRule->getPreviousStart(base, stdRule->getRawOffset(), stdRule->getDSTSavings(), inclusive, dstDate);
if (stdAvail && (!dstAvail || stdDate > dstDate)) {
result.setTime(stdDate);
result.setFrom((const TimeZoneRule&)*dstRule);
diff --git a/i18n/smpdtfmt.cpp b/i18n/smpdtfmt.cpp
index 17e9a520..488af9fd 100644
--- a/i18n/smpdtfmt.cpp
+++ b/i18n/smpdtfmt.cpp
@@ -41,10 +41,18 @@
#include "unicode/dcfmtsym.h"
#include "unicode/uchar.h"
#include "unicode/ustring.h"
+#include "unicode/basictz.h"
+#include "unicode/simpletz.h"
+#include "unicode/rbtz.h"
+#include "unicode/vtzone.h"
+#include "olsontz.h"
#include "../common/util.h"
#include "gregoimp.h"
#include "cstring.h"
#include "uassert.h"
+#include "zstrfmt.h"
+#include "cmemory.h"
+#include "umutex.h"
#include <float.h>
#if defined( U_DEBUG_CALSVC ) || defined (U_DEBUG_CAL)
@@ -66,6 +74,19 @@ U_NAMESPACE_BEGIN
static const UChar gGmt[] = {0x0047, 0x004D, 0x0054, 0x0000}; // "GMT"
static const UChar gGmtPlus[] = {0x0047, 0x004D, 0x0054, 0x002B, 0x0000}; // "GMT+"
static const UChar gGmtMinus[] = {0x0047, 0x004D, 0x0054, 0x002D, 0x0000}; // "GMT-"
+static const UChar gDefGmtPat[] = {0x0047, 0x004D, 0x0054, 0x007B, 0x0030, 0x007D, 0x0000}; /* GMT{0} */
+static const UChar gDefGmtNegHmsPat[] = {0x002D, 0x0048, 0x0048, 0x003A, 0x006D, 0x006D, 0x003A, 0x0073, 0x0073, 0x0000}; /* -HH:mm:ss */
+static const UChar gDefGmtNegHmPat[] = {0x002D, 0x0048, 0x0048, 0x003A, 0x006D, 0x006D, 0x0000}; /* -HH:mm */
+static const UChar gDefGmtPosHmsPat[] = {0x002B, 0x0048, 0x0048, 0x003A, 0x006D, 0x006D, 0x003A, 0x0073, 0x0073, 0x0000}; /* +HH:mm:ss */
+static const UChar gDefGmtPosHmPat[] = {0x002B, 0x0048, 0x0048, 0x003A, 0x006D, 0x006D, 0x0000}; /* +HH:mm */
+typedef enum GmtPatSize {
+ kGmtLen = 3,
+ kGmtPatLen = 6,
+ kNegHmsLen = 9,
+ kNegHmLen = 6,
+ kPosHmsLen = 9,
+ kPosHmLen = 6
+} GmtPatSize;
// This is a pattern-of-last-resort used when we can't load a usable pattern out
// of a resource.
@@ -83,21 +104,36 @@ static const UChar SUPPRESS_NEGATIVE_PREFIX[] = {0xAB00, 0};
* These are the tags we expect to see in normal resource bundle files associated
* with a locale.
*/
-static const char kSUPPLEMENTAL[]="supplementalData";
-static const char gZoneFormattingTag[]="zoneFormatting";
-static const char gAliases[]="aliases";
static const char gDateTimePatternsTag[]="DateTimePatterns";
-UOBJECT_DEFINE_RTTI_IMPLEMENTATION(SimpleDateFormat)
-
+static const UChar gEtcUTC[] = {0x45, 0x74, 0x63, 0x2F, 0x55, 0x54, 0x43, 0x00}; // "Etc/UTC"
static const UChar QUOTE = 0x27; // Single quote
+enum {
+ kGMTNegativeHMS = 0,
+ kGMTNegativeHM,
+ kGMTPositiveHMS,
+ kGMTPositiveHM,
+
+ kNumGMTFormatters
+};
+
+static UMTX LOCK;
+
+UOBJECT_DEFINE_RTTI_IMPLEMENTATION(SimpleDateFormat)
//----------------------------------------------------------------------
SimpleDateFormat::~SimpleDateFormat()
{
delete fSymbols;
- delete parsedTimeZone; // sanity check
+ if (fGMTFormatters) {
+ for (int32_t i = 0; i < kNumGMTFormatters; i++) {
+ if (fGMTFormatters[i]) {
+ delete fGMTFormatters[i];
+ }
+ }
+ uprv_free(fGMTFormatters);
+ }
}
//----------------------------------------------------------------------
@@ -105,7 +141,7 @@ SimpleDateFormat::~SimpleDateFormat()
SimpleDateFormat::SimpleDateFormat(UErrorCode& status)
: fLocale(Locale::getDefault()),
fSymbols(NULL),
- parsedTimeZone(NULL)
+ fGMTFormatters(NULL)
{
construct(kShort, (EStyle) (kShort + kDateOffset), fLocale, status);
initializeDefaultCentury();
@@ -118,7 +154,7 @@ SimpleDateFormat::SimpleDateFormat(const UnicodeString& pattern,
: fPattern(pattern),
fLocale(Locale::getDefault()),
fSymbols(NULL),
- parsedTimeZone(NULL)
+ fGMTFormatters(NULL)
{
initializeSymbols(fLocale, initializeCalendar(NULL,fLocale,status), status);
initialize(fLocale, status);
@@ -132,7 +168,7 @@ SimpleDateFormat::SimpleDateFormat(const UnicodeString& pattern,
UErrorCode& status)
: fPattern(pattern),
fLocale(locale),
- parsedTimeZone(NULL)
+ fGMTFormatters(NULL)
{
initializeSymbols(fLocale, initializeCalendar(NULL,fLocale,status), status);
initialize(fLocale, status);
@@ -147,7 +183,7 @@ SimpleDateFormat::SimpleDateFormat(const UnicodeString& pattern,
: fPattern(pattern),
fLocale(Locale::getDefault()),
fSymbols(symbolsToAdopt),
- parsedTimeZone(NULL)
+ fGMTFormatters(NULL)
{
initializeCalendar(NULL,fLocale,status);
initialize(fLocale, status);
@@ -162,7 +198,7 @@ SimpleDateFormat::SimpleDateFormat(const UnicodeString& pattern,
: fPattern(pattern),
fLocale(Locale::getDefault()),
fSymbols(new DateFormatSymbols(symbols)),
- parsedTimeZone(NULL)
+ fGMTFormatters(NULL)
{
initializeCalendar(NULL, fLocale, status);
initialize(fLocale, status);
@@ -178,7 +214,7 @@ SimpleDateFormat::SimpleDateFormat(EStyle timeStyle,
UErrorCode& status)
: fLocale(locale),
fSymbols(NULL),
- parsedTimeZone(NULL)
+ fGMTFormatters(NULL)
{
construct(timeStyle, dateStyle, fLocale, status);
if(U_SUCCESS(status)) {
@@ -198,7 +234,7 @@ SimpleDateFormat::SimpleDateFormat(const Locale& locale,
: fPattern(gDefaultPattern),
fLocale(locale),
fSymbols(NULL),
- parsedTimeZone(NULL)
+ fGMTFormatters(NULL)
{
if (U_FAILURE(status)) return;
initializeSymbols(fLocale, initializeCalendar(NULL, fLocale, status),status);
@@ -226,7 +262,7 @@ SimpleDateFormat::SimpleDateFormat(const Locale& locale,
SimpleDateFormat::SimpleDateFormat(const SimpleDateFormat& other)
: DateFormat(other),
fSymbols(NULL),
- parsedTimeZone(NULL)
+ fGMTFormatters(NULL)
{
*this = other;
}
@@ -243,8 +279,6 @@ SimpleDateFormat& SimpleDateFormat::operator=(const SimpleDateFormat& other)
delete fSymbols;
fSymbols = NULL;
- delete parsedTimeZone; parsedTimeZone = NULL;
-
if (other.fSymbols)
fSymbols = new DateFormatSymbols(*other.fSymbols);
@@ -585,20 +619,336 @@ _appendSymbol(UnicodeString& dst,
}
//---------------------------------------------------------------------
-inline void SimpleDateFormat::appendGMT(UnicodeString &appendTo, Calendar& cal, UErrorCode& status) const{
- int32_t value = cal.get(UCAL_ZONE_OFFSET, status) +
- cal.get(UCAL_DST_OFFSET, status);
+void
+SimpleDateFormat::appendGMT(UnicodeString &appendTo, Calendar& cal, UErrorCode& status) const{
+ int32_t offset = cal.get(UCAL_ZONE_OFFSET, status) + cal.get(UCAL_DST_OFFSET, status);
+ if (U_FAILURE(status)) {
+ return;
+ }
+ if (isDefaultGMTFormat()) {
+ formatGMTDefault(appendTo, offset);
+ } else {
+ ((SimpleDateFormat*)this)->initGMTFormatters(status);
+ if (U_SUCCESS(status)) {
+ int32_t type;
+ if (offset < 0) {
+ offset = -offset;
+ type = (offset % U_MILLIS_PER_MINUTE) == 0 ? kGMTNegativeHM : kGMTNegativeHMS;
+ } else {
+ type = (offset % U_MILLIS_PER_MINUTE) == 0 ? kGMTPositiveHM : kGMTPositiveHMS;
+ }
+ Formattable param(offset, Formattable::kIsDate);
+ FieldPosition fpos(0);
+ fGMTFormatters[type]->format(&param, 1, appendTo, fpos, status);
+ }
+ }
+}
+
+int32_t
+SimpleDateFormat::parseGMT(const UnicodeString &text, ParsePosition &pos) const {
+ if (!isDefaultGMTFormat()) {
+ int32_t start = pos.getIndex();
+
+ // Quick check
+ UBool prefixMatch = FALSE;
+ int32_t prefixLen = fSymbols->fGmtFormat.indexOf((UChar)0x007B /* '{' */);
+ if (prefixLen > 0 && text.compare(start, prefixLen, fSymbols->fGmtFormat, 0, prefixLen) == 0) {
+ prefixMatch = TRUE;
+ }
+ if (prefixMatch) {
+ // Prefix matched
+ UErrorCode status = U_ZERO_ERROR;
+ ((SimpleDateFormat*)this)->initGMTFormatters(status);
+ if (U_SUCCESS(status)) {
+ Formattable parsed;
+ int32_t parsedCount;
+
+ // Try negative Hms
+ fGMTFormatters[kGMTNegativeHMS]->parseObject(text, parsed, pos);
+ if (pos.getErrorIndex() == -1 && pos.getIndex() > start) {
+ parsed.getArray(parsedCount);
+ if (parsedCount == 1 && parsed[0].getType() == Formattable::kDate) {
+ return (int32_t)(-1 * parsed[0].getDate());
+ }
+ }
+
+ // Reset ParsePosition
+ pos.setIndex(start);
+ pos.setErrorIndex(-1);
+
+ // Try positive Hms
+ fGMTFormatters[kGMTPositiveHMS]->parseObject(text, parsed, pos);
+ if (pos.getErrorIndex() == -1 && pos.getIndex() > start) {
+ parsed.getArray(parsedCount);
+ if (parsedCount == 1 && parsed[0].getType() == Formattable::kDate) {
+ return (int32_t)parsed[0].getDate();
+ }
+ }
- if (value < 0) {
+ // Reset ParsePosition
+ pos.setIndex(start);
+ pos.setErrorIndex(-1);
+
+ // Try negative Hm
+ fGMTFormatters[kGMTNegativeHM]->parseObject(text, parsed, pos);
+ if (pos.getErrorIndex() == -1 && pos.getIndex() > start) {
+ parsed.getArray(parsedCount);
+ if (parsedCount == 1 && parsed[0].getType() == Formattable::kDate) {
+ return (int32_t)(-1 * parsed[0].getDate());
+ }
+ }
+
+ // Reset ParsePosition
+ pos.setIndex(start);
+ pos.setErrorIndex(-1);
+
+ // Try positive Hm
+ fGMTFormatters[kGMTPositiveHM]->parseObject(text, parsed, pos);
+ if (pos.getErrorIndex() == -1 && pos.getIndex() > start) {
+ parsed.getArray(parsedCount);
+ if (parsedCount == 1 && parsed[0].getType() == Formattable::kDate) {
+ return (int32_t)parsed[0].getDate();
+ }
+ }
+
+ // Reset ParsePosition
+ pos.setIndex(start);
+ pos.setErrorIndex(-1);
+ }
+ // fall through to the default GMT parsing method
+ }
+ }
+ return parseGMTDefault(text, pos);
+}
+
+void
+SimpleDateFormat::formatGMTDefault(UnicodeString &appendTo, int32_t offset) const {
+ if (offset < 0) {
appendTo += gGmtMinus;
- value = -value; // suppress the '-' sign for text display.
+ offset = -offset; // suppress the '-' sign for text display.
}else{
appendTo += gGmtPlus;
}
- zeroPaddingNumber(appendTo, (int32_t)(value/U_MILLIS_PER_HOUR), 2, 2);
+ offset /= U_MILLIS_PER_SECOND; // now in seconds
+ int32_t sec = offset % 60;
+ offset /= 60;
+ int32_t min = offset % 60;
+ int32_t hour = offset / 60;
+
+
+ zeroPaddingNumber(appendTo, hour, 2, 2);
appendTo += (UChar)0x003A /*':'*/;
- zeroPaddingNumber(appendTo, (int32_t)((value%U_MILLIS_PER_HOUR)/U_MILLIS_PER_MINUTE), 2, 2);
+ zeroPaddingNumber(appendTo, min, 2, 2);
+ if (sec != 0) {
+ appendTo += (UChar)0x003A /*':'*/;
+ zeroPaddingNumber(appendTo, sec, 2, 2);
+ }
+}
+
+int32_t
+SimpleDateFormat::parseGMTDefault(const UnicodeString &text, ParsePosition &pos) const {
+ int32_t start = pos.getIndex();
+
+ if (start + kGmtLen + 1 >= text.length()) {
+ pos.setErrorIndex(start);
+ return 0;
+ }
+
+ int32_t cur = start;
+ // "GMT"
+ if (text.compare(start, kGmtLen, gGmt) != 0) {
+ pos.setErrorIndex(start);
+ return 0;
+ }
+ cur += kGmtLen;
+ // Sign
+ UBool negative = FALSE;
+ if (text.charAt(cur) == (UChar)0x002D /* minus */) {
+ negative = TRUE;
+ } else if (text.charAt(cur) != (UChar)0x002B /* plus */) {
+ pos.setErrorIndex(cur);
+ return 0;
+ }
+ cur++;
+
+ // Numbers
+ int32_t numLen;
+ pos.setIndex(cur);
+
+ Formattable number;
+ parseInt(text, number, 6, pos, FALSE);
+ numLen = pos.getIndex() - cur;
+
+ if (numLen <= 0) {
+ pos.setIndex(start);
+ pos.setErrorIndex(cur);
+ return 0;
+ }
+
+ int32_t numVal = number.getLong();
+
+ int32_t hour = 0;
+ int32_t min = 0;
+ int32_t sec = 0;
+
+ if (numLen <= 2) {
+ // H[H][:mm[:ss]]
+ hour = numVal;
+ cur += numLen;
+ if (cur + 2 < text.length() && text.charAt(cur) == (UChar)0x003A /* colon */) {
+ cur++;
+ pos.setIndex(cur);
+ parseInt(text, number, 2, pos, FALSE);
+ numLen = pos.getIndex() - cur;
+ if (numLen == 2) {
+ // got minute field
+ min = number.getLong();
+ cur += numLen;
+ if (cur + 2 < text.length() && text.charAt(cur) == (UChar)0x003A /* colon */) {
+ cur++;
+ pos.setIndex(cur);
+ parseInt(text, number, 2, pos, FALSE);
+ numLen = pos.getIndex() - cur;
+ if (numLen == 2) {
+ // got second field
+ sec = number.getLong();
+ } else {
+ // reset position
+ pos.setIndex(cur - 1);
+ pos.setErrorIndex(-1);
+ }
+ }
+ } else {
+ // reset postion
+ pos.setIndex(cur - 1);
+ pos.setErrorIndex(-1);
+ }
+ }
+ } else if (numLen == 3 || numLen == 4) {
+ // Hmm or HHmm
+ hour = numVal / 100;
+ min = numVal % 100;
+ } else if (numLen == 5 || numLen == 6) {
+ // Hmmss or HHmmss
+ hour = numVal / 10000;
+ min = (numVal % 10000) / 100;
+ sec = numVal % 100;
+ } else {
+ // HHmmss followed by bogus numbers
+ pos.setIndex(cur + 6);
+
+ int32_t shift = numLen - 6;
+ while (shift > 0) {
+ numVal /= 10;
+ shift--;
+ }
+ hour = numVal / 10000;
+ min = (numVal % 10000) / 100;
+ sec = numVal % 100;
+ }
+
+ int32_t offset = ((hour*60 + min)*60 + sec)*1000;
+ if (negative) {
+ offset = -offset;
+ }
+ return offset;
+}
+
+UBool
+SimpleDateFormat::isDefaultGMTFormat() const {
+ // GMT pattern
+ if (fSymbols->fGmtFormat.length() == 0) {
+ // No GMT pattern is set
+ return TRUE;
+ } else if (fSymbols->fGmtFormat.compare(gDefGmtPat, kGmtPatLen) != 0) {
+ return FALSE;
+ }
+ // Hour patterns
+ if (fSymbols->fGmtHourFormats == NULL || fSymbols->fGmtHourFormatsCount != DateFormatSymbols::GMT_HOUR_COUNT) {
+ // No Hour pattern is set
+ return TRUE;
+ } else if ((fSymbols->fGmtHourFormats[DateFormatSymbols::GMT_NEGATIVE_HMS].compare(gDefGmtNegHmsPat, kNegHmsLen) != 0)
+ || (fSymbols->fGmtHourFormats[DateFormatSymbols::GMT_NEGATIVE_HM].compare(gDefGmtNegHmPat, kNegHmLen) != 0)
+ || (fSymbols->fGmtHourFormats[DateFormatSymbols::GMT_POSITIVE_HMS].compare(gDefGmtPosHmsPat, kPosHmsLen) != 0)
+ || (fSymbols->fGmtHourFormats[DateFormatSymbols::GMT_POSITIVE_HM].compare(gDefGmtPosHmPat, kPosHmLen) != 0)) {
+ return FALSE;
+ }
+ return TRUE;
+}
+
+void
+SimpleDateFormat::formatRFC822TZ(UnicodeString &appendTo, int32_t offset) const {
+ UChar sign = 0x002B /* '+' */;
+ if (offset < 0) {
+ offset = -offset;
+ sign = 0x002D /* '-' */;
+ }
+ appendTo.append(sign);
+
+ int32_t offsetH = offset / U_MILLIS_PER_HOUR;
+ offset = offset % U_MILLIS_PER_HOUR;
+ int32_t offsetM = offset / U_MILLIS_PER_MINUTE;
+ offset = offset % U_MILLIS_PER_MINUTE;
+ int32_t offsetS = offset / U_MILLIS_PER_SECOND;
+
+ int32_t num = 0, denom = 0;
+ if (offsetS == 0) {
+ offset = offsetH*100 + offsetM; // HHmm
+ num = offset % 10000;
+ denom = 1000;
+ } else {
+ offset = offsetH*10000 + offsetM*100 + offsetS; // HHmmss
+ num = offset % 1000000;
+ denom = 100000;
+ }
+ while (denom >= 1) {
+ UChar digit = (UChar)0x0030 + (num / denom);
+ appendTo.append(digit);
+ num = num % denom;
+ denom /= 10;
+ }
+}
+
+void
+SimpleDateFormat::initGMTFormatters(UErrorCode &status) {
+ if (U_FAILURE(status)) {
+ return;
+ }
+ umtx_lock(&LOCK);
+ if (fGMTFormatters == NULL) {
+ fGMTFormatters = (MessageFormat**)uprv_malloc(kNumGMTFormatters * sizeof(MessageFormat*));
+ if (fGMTFormatters) {
+ for (int32_t i = 0; i < kNumGMTFormatters; i++) {
+ const UnicodeString *hourPattern;
+ switch (i) {
+ case kGMTNegativeHMS:
+ hourPattern = &(fSymbols->fGmtHourFormats[DateFormatSymbols::GMT_NEGATIVE_HMS]);
+ break;
+ case kGMTNegativeHM:
+ hourPattern = &(fSymbols->fGmtHourFormats[DateFormatSymbols::GMT_NEGATIVE_HM]);
+ break;
+ case kGMTPositiveHMS:
+ hourPattern = &(fSymbols->fGmtHourFormats[DateFormatSymbols::GMT_POSITIVE_HMS]);
+ break;
+ case kGMTPositiveHM:
+ hourPattern = &(fSymbols->fGmtHourFormats[DateFormatSymbols::GMT_POSITIVE_HM]);
+ break;
+ }
+ fGMTFormatters[i] = new MessageFormat(fSymbols->fGmtFormat, status);
+ if (U_FAILURE(status)) {
+ break;
+ }
+ SimpleDateFormat *sdf = (SimpleDateFormat*)this->clone();
+ sdf->adoptTimeZone(TimeZone::createTimeZone(UnicodeString(gEtcUTC)));
+ sdf->applyPattern(*hourPattern);
+ fGMTFormatters[i]->adoptFormat(0, sdf);
+ }
+ } else {
+ status = U_MEMORY_ALLOCATION_ERROR;
+ }
+ }
+ umtx_unlock(&LOCK);
}
//---------------------------------------------------------------------
@@ -775,145 +1125,57 @@ SimpleDateFormat::subFormat(UnicodeString &appendTo,
// then the time zone shows up as "GMT+hh:mm" or "GMT-hh:mm" (where "hh:mm" is the
// offset from GMT) regardless of how many z's were in the pattern symbol
case UDAT_TIMEZONE_FIELD:
+ case UDAT_TIMEZONE_GENERIC_FIELD:
case UDAT_TIMEZONE_SPECIAL_FIELD:
- case UDAT_TIMEZONE_GENERIC_FIELD: {
- UnicodeString str;
- UnicodeString zid;
- UnicodeString mzid;
- UnicodeString displayString;
- zid = fSymbols->getZoneID(cal.getTimeZone().getID(str), zid, status);
- if(U_FAILURE(status)){
- break;
- }
- if (zid.length() == 0) {
- appendGMT(appendTo, cal, status);
- }
- else {
- zoneIDCanonicalize(zid);
- if (patternCharIndex == UDAT_TIMEZONE_GENERIC_FIELD) {
- if(count < 4){
- if (cal.getTimeZone().useDaylightTime()) {
- fSymbols->getZoneString(zid, DateFormatSymbols::TIMEZONE_SHORT_GENERIC, displayString, status);
- if ( !fSymbols->isCommonlyUsed(zid)) {
- displayString.remove();
- }
- if(displayString.length()==0) {
- mzid = fSymbols->getMetazoneString(zid, DateFormatSymbols::TIMEZONE_SHORT_GENERIC, cal, displayString, status);
- if ( !fSymbols->isCommonlyUsed(mzid)) {
- displayString.remove();
- }
- }
- }
- else {
- fSymbols->getZoneString(zid, DateFormatSymbols::TIMEZONE_SHORT_STANDARD, displayString, status);
- if ( !fSymbols->isCommonlyUsed(zid)) {
- displayString.remove();
- }
- if(displayString.length()==0) {
- mzid = fSymbols->getMetazoneString(zid, DateFormatSymbols::TIMEZONE_SHORT_STANDARD, cal, displayString, status);
- if ( !fSymbols->isCommonlyUsed(mzid)) {
- displayString.remove();
- }
- }
- }
- if(displayString.length()==0) {
- fSymbols->getFallbackString(zid, displayString, status);
- }
- }else{
- if (cal.getTimeZone().useDaylightTime()) {
- fSymbols->getZoneString(zid, DateFormatSymbols::TIMEZONE_LONG_GENERIC, displayString, status);
- if(displayString.length()==0) {
- mzid = fSymbols->getMetazoneString(zid, DateFormatSymbols::TIMEZONE_LONG_GENERIC, cal, displayString, status);
- }
+ {
+ UnicodeString zoneString;
+ const ZoneStringFormat *zsf = fSymbols->getZoneStringFormat();
+ if (zsf) {
+ if (patternCharIndex == UDAT_TIMEZONE_FIELD) {
+ if (count < 4) {
+ // "z", "zz", "zzz"
+ zsf->getSpecificShortString(cal, TRUE /*commonly used only*/,
+ zoneString, status);
+ } else {
+ // "zzzz"
+ zsf->getSpecificLongString(cal, zoneString, status);
}
- else {
- fSymbols->getZoneString(zid, DateFormatSymbols::TIMEZONE_LONG_STANDARD, displayString, status);
- if(displayString.length()==0) {
- mzid = fSymbols->getMetazoneString(zid, DateFormatSymbols::TIMEZONE_LONG_STANDARD, cal, displayString, status);
- }
+ } else if (patternCharIndex == UDAT_TIMEZONE_GENERIC_FIELD) {
+ if (count == 1) {
+ // "v"
+ zsf->getGenericShortString(cal, TRUE /*commonly used only*/,
+ zoneString, status);
+ } else if (count == 4) {
+ // "vvvv"
+ zsf->getGenericLongString(cal, zoneString, status);
}
-
- if(displayString.length()==0) {
- fSymbols->getFallbackString(zid, displayString, status);
+ } else { // patternCharIndex == UDAT_TIMEZONE_SPECIAL_FIELD
+ if (count == 1) {
+ // "V"
+ zsf->getSpecificShortString(cal, FALSE /*ignore commonly used*/,
+ zoneString, status);
+ } else if (count == 4) {
+ // "VVVV"
+ zsf->getGenericLocationString(cal, zoneString, status);
}
}
}
- else if (patternCharIndex == UDAT_TIMEZONE_SPECIAL_FIELD) {
- if(count == 4){ // VVVV format - always get fallback string.
- fSymbols->getFallbackString(zid, displayString, status);
- }
- else if (count == 1){ // V format - ignore commonlyUsed
- if (cal.get(UCAL_DST_OFFSET, status) != 0) {
- fSymbols->getZoneString(zid, DateFormatSymbols::TIMEZONE_SHORT_DAYLIGHT, displayString, status);
- if(displayString.length()==0) {
- mzid = fSymbols->getMetazoneString(zid, DateFormatSymbols::TIMEZONE_SHORT_DAYLIGHT, cal, displayString, status);
- }
- }
- else {
- fSymbols->getZoneString(zid, DateFormatSymbols::TIMEZONE_SHORT_STANDARD, displayString, status);
- if(displayString.length()==0) {
- mzid = fSymbols->getMetazoneString(zid, DateFormatSymbols::TIMEZONE_SHORT_STANDARD, cal, displayString, status);
- }
- }
- }
- } else {
- if (cal.get(UCAL_DST_OFFSET, status) != 0) {
- if(count < 4){
- fSymbols->getZoneString(zid, DateFormatSymbols::TIMEZONE_SHORT_DAYLIGHT, displayString, status);
- if ( fSymbols->isCommonlyUsed(zid) == FALSE ) {
- displayString.remove();
- }
- if(displayString.length()==0) {
- mzid = fSymbols->getMetazoneString(zid, DateFormatSymbols::TIMEZONE_SHORT_DAYLIGHT, cal, displayString, status);
- if ( fSymbols->isCommonlyUsed(mzid) == FALSE ) {
- displayString.remove();
- }
- }
- }else{
- fSymbols->getZoneString(zid, DateFormatSymbols::TIMEZONE_LONG_DAYLIGHT, displayString, status);
- if(displayString.length()==0)
- fSymbols->getMetazoneString(zid, DateFormatSymbols::TIMEZONE_LONG_DAYLIGHT, cal, displayString, status);
- }
- }else{
- if(count < 4){
- fSymbols->getZoneString(zid, DateFormatSymbols::TIMEZONE_SHORT_STANDARD, displayString, status);
- if ( !fSymbols->isCommonlyUsed(zid)) {
- displayString.remove();
- }
- if(displayString.length()==0) {
- mzid = fSymbols->getMetazoneString(zid, DateFormatSymbols::TIMEZONE_SHORT_STANDARD, cal, displayString, status);
- if ( fSymbols->isCommonlyUsed(mzid) == FALSE ) {
- displayString.remove();
- }
- }
- }else{
- fSymbols->getZoneString(zid, DateFormatSymbols::TIMEZONE_LONG_STANDARD, displayString, status);
- if(displayString.length()==0)
- fSymbols->getMetazoneString(zid, DateFormatSymbols::TIMEZONE_LONG_STANDARD, cal, displayString, status);
- }
- }
- }
- if(displayString.length()==0){
+ if (zoneString.isEmpty()) {
appendGMT(appendTo, cal, status);
- }else{
- appendTo += displayString;
+ } else {
+ appendTo += zoneString;
}
}
- }
- break;
-
- case 23: // 'Z' - TIMEZONE_RFC
- {
- UChar sign = 43/*'+'*/;
- value = (cal.get(UCAL_ZONE_OFFSET, status) +
- cal.get(UCAL_DST_OFFSET, status)) / U_MILLIS_PER_MINUTE;
- if (value < 0) {
- value = -value;
- sign = 45/*'-'*/;
- }
- value = (value / 60) * 100 + (value % 60); // minutes => KKmm
- appendTo += sign;
- zeroPaddingNumber(appendTo, value, 4, 4);
+ break;
+
+ case UDAT_TIMEZONE_RFC_FIELD: // 'Z' - TIMEZONE_RFC
+ if (count < 4) {
+ // RFC822 format, must use ASCII digits
+ value = (cal.get(UCAL_ZONE_OFFSET, status) + cal.get(UCAL_DST_OFFSET, status));
+ formatRFC822TZ(appendTo, value);
+ } else {
+ // long form, localized GMT pattern
+ appendGMT(appendTo, cal, status);
}
break;
@@ -955,88 +1217,8 @@ SimpleDateFormat::subFormat(UnicodeString &appendTo,
pos.setEndIndex(appendTo.length());
}
}
-//----------------------------------------------------------------------
-void
-SimpleDateFormat::zoneIDCanonicalize(UnicodeString &zid) const
-{
- UnicodeString colon = UNICODE_STRING_SIMPLE(":");
- UnicodeString solidus = UNICODE_STRING_SIMPLE("/");
- char zidkey[ZID_KEY_MAX];
- int32_t len;
-
- UErrorCode status = U_ZERO_ERROR;
- UResourceBundle* supplementalDataBundle = ures_openDirect(NULL, kSUPPLEMENTAL, &status);
- if (U_FAILURE(status)) {
- return;
- }
-
- UResourceBundle* zoneFormatting = ures_getByKey(supplementalDataBundle, gZoneFormattingTag, NULL, &status);
-
- // First try to lookup the zone itself, since most of the time the canonical ID and the ID itself
- // will be the same. This should save us a significant amount of time.
-
- len = zid.length();
- len = (len >= (ZID_KEY_MAX-1) ? ZID_KEY_MAX-1 : len);
- u_UCharsToChars(zid.getBuffer(), zidkey, len);
- zidkey[len] = 0; // NULL terminate
-
- // Replace / with : for zid
- len = (int32_t)uprv_strlen(zidkey);
- for (int i = 0; i < len; i++) {
- if (zidkey[i] == '/') {
- zidkey[i] = ':';
- }
- }
-
- UResourceBundle* tryThisZone = ures_getByKey(zoneFormatting,zidkey,NULL,&status);
- if (U_SUCCESS(status)) {
- ures_close(tryThisZone);
- ures_close(zoneFormatting);
- ures_close(supplementalDataBundle);
- return;
- }
- else {
- status = U_ZERO_ERROR;
- }
-
- // Didn't find it, so go searching for an alias
-
- while ( ures_hasNext(zoneFormatting)) {
- UResourceBundle *currentZone = ures_getNextResource(zoneFormatting,NULL,&status);
- if (U_FAILURE(status)) {
- break;
- }
-
- const char *currentZoneString= ures_getKey(currentZone);
-
- UResourceBundle *zoneAliases = ures_getByKey(currentZone,gAliases,NULL, &status);
- if (U_FAILURE(status)) {
- status = U_ZERO_ERROR;
- ures_close(currentZone);
- continue;
- }
- while ( ures_hasNext(zoneAliases)) {
- int32_t len;
- const UChar* alias = ures_getNextString(zoneAliases,&len,NULL,&status);
- if ( zid.compare(alias)==0 ) {
- zid.setTo(UnicodeString(currentZoneString, -1, US_INV));
- zid.findAndReplace(colon,solidus);
- ures_close(zoneAliases);
- ures_close(currentZone);
- ures_close(zoneFormatting);
- ures_close(supplementalDataBundle);
- return;
- }
- }
- ures_close(zoneAliases);
- ures_close(currentZone);
- }
- ures_close(zoneFormatting);
- ures_close(supplementalDataBundle);
-}
//----------------------------------------------------------------------
-
void
SimpleDateFormat::zeroPaddingNumber(UnicodeString &appendTo, int32_t value, int32_t minDigits, int32_t maxDigits) const
{
@@ -1073,9 +1255,8 @@ SimpleDateFormat::parse(const UnicodeString& text, Calendar& cal, ParsePosition&
UBool ambiguousYear[] = { FALSE };
int32_t count = 0;
- // hack, clear parsedTimeZone, cast away const
- delete parsedTimeZone;
- ((SimpleDateFormat*)this)->parsedTimeZone = NULL;
+ // hack, reset tztype, cast away const
+ ((SimpleDateFormat*)this)->tztype = TZTYPE_UNK;
// For parsing abutting numeric fields. 'abutPat' is the
// offset into 'pattern' of the first of 2 or more abutting
@@ -1271,39 +1452,127 @@ SimpleDateFormat::parse(const UnicodeString& text, Calendar& cal, ParsePosition&
// front or the back of the default century. This only works because we adjust
// the year correctly to start with in other cases -- see subParse().
UErrorCode status = U_ZERO_ERROR;
- if (ambiguousYear[0] || parsedTimeZone != NULL) // If this is true then the two-digit year == the default start year
+ if (ambiguousYear[0] || tztype != TZTYPE_UNK) // If this is true then the two-digit year == the default start year
{
// We need a copy of the fields, and we need to avoid triggering a call to
// complete(), which will recalculate the fields. Since we can't access
// the fields[] array in Calendar, we clone the entire object. This will
// stop working if Calendar.clone() is ever rewritten to call complete().
- Calendar *copy = cal.clone();
+ Calendar *copy;
if (ambiguousYear[0]) {
+ copy = cal.clone();
UDate parsedDate = copy->getTime(status);
// {sfb} check internalGetDefaultCenturyStart
if (fHaveDefaultCentury && (parsedDate < fDefaultCenturyStart)) {
// We can't use add here because that does a complete() first.
cal.set(UCAL_YEAR, fDefaultCenturyStartYear + 100);
}
+ delete copy;
}
- if (parsedTimeZone != NULL) {
- TimeZone *tz = parsedTimeZone;
+ if (tztype != TZTYPE_UNK) {
+ copy = cal.clone();
+ const TimeZone & tz = cal.getTimeZone();
+ BasicTimeZone *btz = NULL;
- // the calendar represents the parse as gmt time
- // we need to turn this into local time, so we add the raw offset
- // then we ask the timezone to handle this local time
- int32_t rawOffset = 0;
- int32_t dstOffset = 0;
- tz->getOffset(copy->getTime(status)+tz->getRawOffset(), TRUE,
- rawOffset, dstOffset, status);
- if (U_SUCCESS(status)) {
- cal.set(UCAL_ZONE_OFFSET, rawOffset);
- cal.set(UCAL_DST_OFFSET, dstOffset);
+ if (tz.getDynamicClassID() == OlsonTimeZone::getStaticClassID()
+ || tz.getDynamicClassID() == SimpleTimeZone::getStaticClassID()
+ || tz.getDynamicClassID() == RuleBasedTimeZone::getStaticClassID()
+ || tz.getDynamicClassID() == VTimeZone::getStaticClassID()) {
+ btz = (BasicTimeZone*)&tz;
}
- }
- delete copy;
+ // Get local millis
+ copy->set(UCAL_ZONE_OFFSET, 0);
+ copy->set(UCAL_DST_OFFSET, 0);
+ UDate localMillis = copy->getTime(status);
+
+ // Make sure parsed time zone type (Standard or Daylight)
+ // matches the rule used by the parsed time zone.
+ int32_t raw, dst;
+ if (btz != NULL) {
+ if (tztype == TZTYPE_STD) {
+ btz->getOffsetFromLocal(localMillis,
+ BasicTimeZone::kStandard, BasicTimeZone::kStandard, raw, dst, status);
+ } else {
+ btz->getOffsetFromLocal(localMillis,
+ BasicTimeZone::kDaylight, BasicTimeZone::kDaylight, raw, dst, status);
+ }
+ } else {
+ // No good way to resolve ambiguous time at transition,
+ // but following code work in most case.
+ tz.getOffset(localMillis, TRUE, raw, dst, status);
+ }
+
+ // Now, compare the results with parsed type, either standard or daylight saving time
+ int32_t resolvedSavings = dst;
+ if (tztype == TZTYPE_STD) {
+ if (dst != 0) {
+ // Override DST_OFFSET = 0 in the result calendar
+ resolvedSavings = 0;
+ }
+ } else { // tztype == TZTYPE_DST
+ if (dst == 0) {
+ if (btz != NULL) {
+ UDate time = localMillis + raw;
+ // We use the nearest daylight saving time rule.
+ TimeZoneTransition beforeTrs, afterTrs;
+ UDate beforeT = time, afterT = time;
+ int32_t beforeSav = 0, afterSav = 0;
+ UBool beforeTrsAvail, afterTrsAvail;
+
+ // Search for DST rule before or on the time
+ while (TRUE) {
+ beforeTrsAvail = btz->getPreviousTransition(beforeT, TRUE, beforeTrs);
+ if (!beforeTrsAvail) {
+ break;
+ }
+ beforeT = beforeTrs.getTime() - 1;
+ beforeSav = beforeTrs.getFrom()->getDSTSavings();
+ if (beforeSav != 0) {
+ break;
+ }
+ }
+
+ // Search for DST rule after the time
+ while (TRUE) {
+ afterTrsAvail = btz->getNextTransition(afterT, FALSE, afterTrs);
+ if (!afterTrsAvail) {
+ break;
+ }
+ afterT = afterTrs.getTime();
+ afterSav = afterTrs.getTo()->getDSTSavings();
+ if (afterSav != 0) {
+ break;
+ }
+ }
+
+ if (beforeTrsAvail && afterTrsAvail) {
+ if (time - beforeT > afterT - time) {
+ resolvedSavings = afterSav;
+ } else {
+ resolvedSavings = beforeSav;
+ }
+ } else if (beforeTrsAvail && beforeSav != 0) {
+ resolvedSavings = beforeSav;
+ } else if (afterTrsAvail && afterSav != 0) {
+ resolvedSavings = afterSav;
+ } else {
+ resolvedSavings = btz->getDSTSavings();
+ }
+ } else {
+ resolvedSavings = tz.getDSTSavings();
+ }
+ if (resolvedSavings == 0) {
+ // final fallback
+ resolvedSavings = U_MILLIS_PER_HOUR;
+ }
+ }
+ }
+ cal.set(UCAL_ZONE_OFFSET, raw);
+ cal.set(UCAL_DST_OFFSET, resolvedSavings);
+ delete copy;
+ }
}
// If any Calendar calls failed, we pretend that we
@@ -1785,125 +2054,151 @@ int32_t SimpleDateFormat::subParse(const UnicodeString& text, int32_t& start, UC
case UDAT_TIMEZONE_FIELD:
case UDAT_TIMEZONE_RFC_FIELD:
case UDAT_TIMEZONE_GENERIC_FIELD:
+ case UDAT_TIMEZONE_SPECIAL_FIELD:
{
- // First try to parse generic forms such as GMT-07:00. Do this first
- // in case localized DateFormatZoneData contains the string "GMT"
- // for a zone; in that case, we don't want to match the first three
- // characters of GMT+/-HH:MM etc.
-
- int32_t sign = 0;
- int32_t offset;
- int32_t gmtLen = u_strlen(gGmt);
-
- // For time zones that have no known names, look for strings
- // of the form:
- // GMT[+-]hours:minutes or
- // GMT[+-]hhmm or
- // GMT.
-
- if ((text.length() - start) >= gmtLen &&
- (text.caseCompare(start, gmtLen, gGmt, 0, gmtLen, U_FOLD_CASE_DEFAULT)) == 0)
- {
- cal.set(UCAL_DST_OFFSET, 0);
+ int32_t offset = 0;
+ UBool parsed = FALSE;
+
+ // Step 1
+ // Check if this is a long GMT offset string (either localized or default)
+ offset = parseGMT(text, pos);
+ if (pos.getIndex() - start > 0) {
+ parsed = TRUE;
+ }
+ if (!parsed) {
+ // Step 2
+ // Check if this is an RFC822 time zone offset.
+ // ICU supports the standard RFC822 format [+|-]HHmm
+ // and its extended form [+|-]HHmmSS.
+ do {
+ int32_t sign = 0;
+ UChar signChar = text.charAt(start);
+ if (signChar == (UChar)0x002B /* '+' */) {
+ sign = 1;
+ } else if (signChar == (UChar)0x002D /* '-' */) {
+ sign = -1;
+ } else {
+ // Not an RFC822 offset string
+ break;
+ }
- pos.setIndex(start + gmtLen);
+ // Parse digits
+ int32_t orgPos = start + 1;
+ pos.setIndex(orgPos);
+ parseInt(text, number, 6, pos, FALSE);
+ int32_t numLen = pos.getIndex() - orgPos;
+ if (numLen <= 0) {
+ break;
+ }
- if( text[pos.getIndex()] == 0x002B /*'+'*/ )
- sign = 1;
- else if( text[pos.getIndex()] == 0x002D /*'-'*/ )
- sign = -1;
- else {
- cal.set(UCAL_ZONE_OFFSET, 0 );
- return pos.getIndex();
- }
+ // Followings are possible format (excluding sign char)
+ // HHmmSS
+ // HmmSS
+ // HHmm
+ // Hmm
+ // HH
+ // H
+ int32_t val = number.getLong();
+ int32_t hour = 0, min = 0, sec = 0;
+ switch(numLen) {
+ case 1: // H
+ case 2: // HH
+ hour = val;
+ break;
+ case 3: // Hmm
+ case 4: // HHmm
+ hour = val / 100;
+ min = val % 100;
+ break;
+ case 5: // Hmmss
+ case 6: // HHmmss
+ hour = val / 10000;
+ min = (val % 10000) / 100;
+ sec = val % 100;
+ break;
+ }
+ if (hour > 23 || min > 59 || sec > 59) {
+ // Invalid value range
+ break;
+ }
+ offset = (((hour * 60) + min) * 60 + sec) * 1000 * sign;
+ parsed = TRUE;
+ } while (FALSE);
- // Look for hours:minutes or hhmm.
- pos.setIndex(pos.getIndex() + 1);
- int32_t parseStart = pos.getIndex();
- Formattable tzNumber;
- fNumberFormat->parse(text, tzNumber, pos);
- if( pos.getIndex() == parseStart) {
- return -start;
- }
- if( text[pos.getIndex()] == 0x003A /*':'*/ ) {
- // This is the hours:minutes case
- offset = tzNumber.getLong() * 60;
- pos.setIndex(pos.getIndex() + 1);
- parseStart = pos.getIndex();
- fNumberFormat->parse(text, tzNumber, pos);
- if( pos.getIndex() == parseStart) {
- return -start;
- }
- offset += tzNumber.getLong();
+ if (!parsed) {
+ // Failed to parse. Reset the position.
+ pos.setIndex(start);
+ }
}
- else {
- // This is the hhmm case.
- offset = tzNumber.getLong();
- if( offset < 24 )
- offset *= 60;
- else
- offset = offset % 100 + offset / 100 * 60;
+
+ if (parsed) {
+ // offset was successfully parsed as either a long GMT string or RFC822 zone offset
+ // string. Create normalized zone ID for the offset.
+
+ UnicodeString tzID(gGmt);
+ formatRFC822TZ(tzID, offset);
+ //TimeZone *customTZ = TimeZone::createTimeZone(tzID);
+ TimeZone *customTZ = new SimpleTimeZone(offset, tzID); // faster than TimeZone::createTimeZone
+ cal.adoptTimeZone(customTZ);
+
+ return pos.getIndex();
}
- // Fall through for final processing below of 'offset' and 'sign'.
- }
- else {
+ // Step 3
// At this point, check for named time zones by looking through
// the locale data from the DateFormatZoneData strings.
// Want to be able to parse both short and long forms.
- // !!! side effect, might set parsedZoneString
- UErrorCode status = U_ZERO_ERROR;
- int32_t result = subParseZoneString(text, start, cal, status);
- if (result != 0) {
- return result;
- }
-
- // As a last resort, look for numeric timezones of the form
- // [+-]hhmm as specified by RFC 822. This code is actually
- // a little more permissive than RFC 822. It will try to do
- // its best with numbers that aren't strictly 4 digits long
- DecimalFormat fmt(UNICODE_STRING_SIMPLE("+####;-####"), status);
- if(U_FAILURE(status))
- return -start;
- fmt.setParseIntegerOnly(TRUE);
- int32_t parseStart = pos.getIndex();
- Formattable tzNumber;
- fmt.parse( text, tzNumber, pos );
- if( pos.getIndex() == parseStart) {
- return -start; // Wasn't actually a number.
- }
- offset = tzNumber.getLong();
- sign = 1;
- if( offset < 0 ) {
- sign = -1;
- offset = -offset;
- }
- if( offset < 24 )
- offset = offset * 60;
- else
- offset = offset % 100 + offset / 100 * 60;
-
- // Fall through for final processing below of 'offset' and 'sign'.
- }
-
- // Do the final processing for both of the above cases. We only
- // arrive here if the form GMT+/-... or an RFC 822 form was seen.
- if (sign != 0)
- {
- offset *= U_MILLIS_PER_MINUTE * sign;
+ // optimize for calendar's current time zone
+ const ZoneStringFormat *zsf = fSymbols->getZoneStringFormat();
+ if (zsf) {
+ UErrorCode status = U_ZERO_ERROR;
+ const ZoneStringInfo *zsinfo = NULL;
+ int32_t matchLen;
+
+ switch (patternCharIndex) {
+ case UDAT_TIMEZONE_FIELD: // 'z'
+ if (count < 4) {
+ zsinfo = zsf->findSpecificShort(text, start, matchLen, status);
+ } else {
+ zsinfo = zsf->findSpecificLong(text, start, matchLen, status);
+ }
+ break;
+ case UDAT_TIMEZONE_GENERIC_FIELD: // 'v'
+ if (count == 1) {
+ zsinfo = zsf->findGenericShort(text, start, matchLen, status);
+ } else if (count == 4) {
+ zsinfo = zsf->findGenericLong(text, start, matchLen, status);
+ }
+ break;
+ case UDAT_TIMEZONE_SPECIAL_FIELD: // 'V'
+ if (count == 1) {
+ zsinfo = zsf->findSpecificShort(text, start, matchLen, status);
+ } else if (count == 4) {
+ zsinfo = zsf->findGenericLocation(text, start, matchLen, status);
+ }
+ break;
+ }
- if (cal.getTimeZone().useDaylightTime())
- {
- cal.set(UCAL_DST_OFFSET, U_MILLIS_PER_HOUR);
- offset -= U_MILLIS_PER_HOUR;
+ if (U_SUCCESS(status) && zsinfo != NULL) {
+ if (zsinfo->isStandard()) {
+ ((SimpleDateFormat*)this)->tztype = TZTYPE_STD;
+ } else if (zsinfo->isDaylight()) {
+ ((SimpleDateFormat*)this)->tztype = TZTYPE_DST;
+ }
+ UnicodeString tzid;
+ zsinfo->getID(tzid);
+
+ UnicodeString current;
+ cal.getTimeZone().getID(current);
+ if (tzid != current) {
+ TimeZone *tz = TimeZone::createTimeZone(tzid);
+ cal.adoptTimeZone(tz);
+ }
+ return start + matchLen;
+ }
}
- cal.set(UCAL_ZONE_OFFSET, offset);
-
- return pos.getIndex();
- }
-
- // All efforts to parse a zone failed.
- return -start;
+ // complete failure
+ return -start;
}
default:
@@ -1928,61 +2223,6 @@ int32_t SimpleDateFormat::subParse(const UnicodeString& text, int32_t& start, UC
}
}
-int32_t
-SimpleDateFormat::subParseZoneString(const UnicodeString& text, int32_t start, Calendar& cal, UErrorCode& status) const
-{
- // At this point, check for named time zones by looking through
- // the locale data from the DateFormatZoneData strings.
- // Want to be able to parse both short and long forms.
-
- // optimize for calendar's current time zone
- TimeZone *tz = NULL;
- UnicodeString id;
- UnicodeString zid, value;
- DateFormatSymbols::TimeZoneTranslationType type = DateFormatSymbols::TIMEZONE_COUNT;
- fSymbols->getZoneID(getTimeZone().getID(id), zid, status);
- if(zid.length() > 0){
- fSymbols->findZoneIDTypeValue(zid, text, start, type, value, status);
- if(type != DateFormatSymbols::TIMEZONE_COUNT) {
- tz = TimeZone::createTimeZone(zid);
- }
- }
-
- if(U_FAILURE(status)){
- return 0;
- }
- if (tz != NULL) { // Matched any ?
- // always set zone offset, needed to get correct hour in wall time
- // when checking daylight savings
- cal.set(UCAL_ZONE_OFFSET, tz->getRawOffset());
- if (type==DateFormatSymbols::TIMEZONE_SHORT_STANDARD || type==DateFormatSymbols::TIMEZONE_LONG_STANDARD ) {
- // standard time
- cal.set(UCAL_DST_OFFSET, 0);
- delete tz; tz = NULL;
- } else if (type==DateFormatSymbols::TIMEZONE_SHORT_DAYLIGHT || type==DateFormatSymbols::TIMEZONE_LONG_DAYLIGHT ) {
- // daylight time
- // !!! todo - no getDSTSavings() in ICU's timezone
- // use the correct DST SAVINGS for the zone.
- // cal.set(UCAL_DST_OFFSET, tz->getDSTSavings());
- cal.set(UCAL_DST_OFFSET, U_MILLIS_PER_HOUR);
- delete tz; tz = NULL;
- } else {
- // either standard or daylight
- // need to finish getting the date, then compute dst offset as appropriate
-
- // !!! hack for api compatibility, can't modify subParse(...) so can't
- // pass this back any other way. cast away const.
- ((SimpleDateFormat*)this)->parsedTimeZone = tz;
- }
-
- return start + value.length();
- }
-
-
- // complete failure
- return 0;
-}
-
/**
* Parse an integer using fNumberFormat. This method is semantically
* const, but actually may modify fNumberFormat.
@@ -1991,6 +2231,17 @@ void SimpleDateFormat::parseInt(const UnicodeString& text,
Formattable& number,
ParsePosition& pos,
UBool allowNegative) const {
+ parseInt(text, number, -1, pos, allowNegative);
+}
+
+/**
+ * Parse an integer using fNumberFormat up to maxDigits.
+ */
+void SimpleDateFormat::parseInt(const UnicodeString& text,
+ Formattable& number,
+ int32_t maxDigits,
+ ParsePosition& pos,
+ UBool allowNegative) const {
UnicodeString oldPrefix;
DecimalFormat* df = NULL;
if (!allowNegative &&
@@ -1999,10 +2250,27 @@ void SimpleDateFormat::parseInt(const UnicodeString& text,
df->getNegativePrefix(oldPrefix);
df->setNegativePrefix(SUPPRESS_NEGATIVE_PREFIX);
}
+ int32_t oldPos = pos.getIndex();
fNumberFormat->parse(text, number, pos);
if (df != NULL) {
df->setNegativePrefix(oldPrefix);
}
+
+ if (maxDigits > 0) {
+ // adjust the result to fit into
+ // the maxDigits and move the position back
+ int32_t nDigits = pos.getIndex() - oldPos;
+ if (nDigits > maxDigits) {
+ int32_t val = number.getLong();
+ nDigits -= maxDigits;
+ while (nDigits > 0) {
+ val /= 10;
+ nDigits--;
+ }
+ pos.setIndex(oldPos + maxDigits);
+ number.setLong(val);
+ }
+ }
}
//----------------------------------------------------------------------
diff --git a/i18n/timezone.cpp b/i18n/timezone.cpp
index fc997ed0..56656117 100644
--- a/i18n/timezone.cpp
+++ b/i18n/timezone.cpp
@@ -88,6 +88,7 @@ static char gStrBuf[256];
#define kDEFAULT "Default"
#define kMAX_CUSTOM_HOUR 23
#define kMAX_CUSTOM_MIN 59
+#define kMAX_CUSTOM_SEC 59
#define MINUS 0x002D
#define PLUS 0x002B
#define ZERO_DIGIT 0x0030
@@ -100,6 +101,7 @@ static const UChar ZZZZ_STR[] = {0x7A, 0x7A, 0x7A, 0x7A, 0x00}; /* "zzzz
static const int32_t GMT_ID_LENGTH = 3;
static UMTX LOCK;
+static UMTX TZSET_LOCK;
static U_NAMESPACE_QUALIFIER TimeZone* DEFAULT_ZONE = NULL;
static U_NAMESPACE_QUALIFIER TimeZone* _GMT = NULL; // cf. TimeZone::GMT
@@ -129,6 +131,10 @@ static UBool U_CALLCONV timeZone_cleanup(void)
umtx_destroy(&LOCK);
LOCK = NULL;
}
+ if (TZSET_LOCK) {
+ umtx_destroy(&TZSET_LOCK);
+ TZSET_LOCK = NULL;
+ }
return TRUE;
}
@@ -511,12 +517,19 @@ TimeZone::initDefault()
// First, try to create a system timezone, based
// on the string ID in tzname[0].
{
- // NOTE: Global mutex here; TimeZone mutex above
- // mutexed to avoid threading issues in the platform fcns.
- // Some of the locale/timezone OS functions may not be thread safe,
- // so the intent is that any setting from anywhere within ICU
- // happens with the ICU global mutex held.
- Mutex lock;
+ // NOTE: Local mutex here. TimeZone mutex below
+ // mutexed to avoid threading issues in the platform functions.
+ // Some of the locale/timezone OS functions may not be thread safe,
+ // so the intent is that any setting from anywhere within ICU
+ // happens while the ICU mutex is held.
+ // The operating system might actually use ICU to implement timezones.
+ // So we may have ICU calling ICU here, like on AIX.
+ // In order to prevent a double lock of a non-reentrant mutex in a
+ // different part of ICU, we use TZSET_LOCK to allow only one instance
+ // of ICU to query these thread unsafe OS functions at any given time.
+ Mutex lock(&TZSET_LOCK);
+
+ ucln_i18n_registerCleanup(UCLN_I18N_TIMEZONE, timeZone_cleanup);
uprv_tzset(); // Initialize tz... system data
// Get the timezone ID from the host. This function should do
@@ -529,6 +542,13 @@ TimeZone::initDefault()
rawOffset = uprv_timezone() * -U_MILLIS_PER_SECOND;
}
+ UBool initialized;
+ UMTX_CHECK(&LOCK, (DEFAULT_ZONE != NULL), initialized);
+ if (initialized) {
+ /* Hrmph? Either a race condition happened, or tzset initialized ICU. */
+ return;
+ }
+
TimeZone* default_zone = NULL;
/* Make sure that the string is NULL terminated to prevent BoundsChecker/Purify warnings. */
@@ -617,13 +637,13 @@ TimeZone::createDefault()
{
/* This is here to prevent race conditions. */
UBool needsInit;
- UMTX_CHECK(&LOCK, (DEFAULT_ZONE == 0), needsInit);
+ UMTX_CHECK(&LOCK, (DEFAULT_ZONE == NULL), needsInit);
if (needsInit) {
initDefault();
}
Mutex lock(&LOCK); // In case adoptDefault is called
- return DEFAULT_ZONE->clone();
+ return (DEFAULT_ZONE != NULL) ? DEFAULT_ZONE->clone() : NULL;
}
// -------------------------------------
@@ -668,36 +688,40 @@ void TimeZone::getOffset(UDate date, UBool local, int32_t& rawOffset,
}
rawOffset = getRawOffset();
-
- // Convert to local wall millis if necessary
if (!local) {
date += rawOffset; // now in local standard millis
}
- // When local==FALSE, we might have to recompute. This loop is
- // executed once, unless a recomputation is required; then it is
- // executed twice.
+ // 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, month, dom, dow;
double day = uprv_floor(date / U_MILLIS_PER_DAY);
int32_t millis = (int32_t) (date - day * U_MILLIS_PER_DAY);
-
+
Grego::dayToFields(day, year, month, dom, dow);
-
+
dstOffset = getOffset(GregorianCalendar::AD, year, month, dom,
(uint8_t) dow, millis,
Grego::monthLength(year, month),
ec) - rawOffset;
- // Recompute if local==FALSE, dstOffset!=0, and addition of
- // the dstOffset puts us in a different day.
- if (pass!=0 || local || dstOffset==0) {
- break;
- }
- date += dstOffset;
- if (uprv_floor(date / U_MILLIS_PER_DAY) == day) {
+ // Recompute if local==TRUE, dstOffset!=0.
+ if (pass!=0 || !local || dstOffset == 0) {
break;
}
+ // adjust to local standard millis
+ date -= dstOffset;
}
}
@@ -1074,6 +1098,33 @@ TimeZone::getEquivalentID(const UnicodeString& id, int32_t index) {
// ---------------------------------------
+UnicodeString&
+TimeZone::getOlsonCanonicalID(const UnicodeString &id, UnicodeString &canonical) {
+ UErrorCode ec = U_ZERO_ERROR;
+ canonical.remove();
+ UResourceBundle *top = ures_openDirect(0, kZONEINFO, &ec);
+ UResourceBundle *res = getZoneByName(top, id, NULL, ec);
+ if (U_SUCCESS(ec)) {
+ if (ures_getSize(res) == 1) {
+ int32_t deref = ures_getInt(res, &ec);
+ UResourceBundle *nres = ures_getByKey(top, kNAMES, NULL, &ec); // dereference Names section
+ int32_t len;
+ const UChar* tmp = ures_getStringByIndex(nres, deref, &len, &ec);
+ if (U_SUCCESS(ec)) {
+ canonical.setTo(tmp, len);
+ }
+ ures_close(nres);
+ } else {
+ canonical.setTo(id);
+ }
+ }
+ ures_close(res);
+ ures_close(top);
+ return canonical;
+}
+
+// ---------------------------------------
+
UnicodeString&
TimeZone::getDisplayName(UnicodeString& result) const
@@ -1173,11 +1224,12 @@ TimeZone::createCustomTimeZone(const UnicodeString& id)
UBool negative = FALSE;
int32_t hour = 0;
int32_t min = 0;
+ int32_t sec = 0;
if (id[pos.getIndex()] == MINUS /*'-'*/)
negative = TRUE;
else if (id[pos.getIndex()] != PLUS /*'+'*/)
- return 0;
+ return NULL;
pos.setIndex(pos.getIndex() + 1);
UErrorCode success = U_ZERO_ERROR;
@@ -1196,75 +1248,125 @@ TimeZone::createCustomTimeZone(const UnicodeString& id)
numberFormat->parse(id, n, pos);
if (pos.getIndex() == start) {
delete numberFormat;
- return 0;
+ return NULL;
}
hour = n.getLong();
- if (pos.getIndex() < id.length() &&
- id[pos.getIndex()] == 0x003A /*':'*/)
- {
+ if (pos.getIndex() < id.length()) {
+ if (pos.getIndex() - start > 2
+ || id[pos.getIndex()] != 0x003A /*':'*/) {
+ delete numberFormat;
+ return NULL;
+ }
// hh:mm
pos.setIndex(pos.getIndex() + 1);
int32_t oldPos = pos.getIndex();
n.setLong(kParseFailed);
numberFormat->parse(id, n, pos);
- if (pos.getIndex() == oldPos) {
+ if ((pos.getIndex() - oldPos) != 2) {
+ // must be 2 digits
delete numberFormat;
- return 0;
+ return NULL;
}
min = n.getLong();
- }
- else
- {
- // hhmm or hh
-
- // Be strict about interpreting something as hh; it must be
- // an offset < 23, and it must be one or two digits. Thus
- // 0010 is interpreted as 00:10, but 10 is interpreted as
- // 10:00.
- if (hour > kMAX_CUSTOM_HOUR || (pos.getIndex() - start) > 2) {
- min = hour % 100;
- hour /= 100;
+ if (pos.getIndex() < id.length()) {
+ if (id[pos.getIndex()] != 0x003A /*':'*/) {
+ delete numberFormat;
+ return NULL;
+ }
+ // [:ss]
+ pos.setIndex(pos.getIndex() + 1);
+ oldPos = pos.getIndex();
+ n.setLong(kParseFailed);
+ numberFormat->parse(id, n, pos);
+ if (pos.getIndex() != id.length()
+ || (pos.getIndex() - oldPos) != 2) {
+ delete numberFormat;
+ return NULL;
+ }
+ sec = n.getLong();
+ }
+ } else {
+ // Supported formats are below -
+ //
+ // HHmmss
+ // Hmmss
+ // HHmm
+ // Hmm
+ // HH
+ // H
+
+ int32_t length = pos.getIndex() - start;
+ if (length <= 0 || 6 < length) {
+ // invalid length
+ delete numberFormat;
+ return NULL;
+ }
+ switch (length) {
+ case 1:
+ case 2:
+ // already set to hour
+ break;
+ case 3:
+ case 4:
+ min = hour % 100;
+ hour /= 100;
+ break;
+ case 5:
+ case 6:
+ sec = hour % 100;
+ min = (hour/100) % 100;
+ hour /= 10000;
+ break;
}
}
delete numberFormat;
- if (hour > kMAX_CUSTOM_HOUR || min > kMAX_CUSTOM_MIN) {
+ if (hour > kMAX_CUSTOM_HOUR || min > kMAX_CUSTOM_MIN || sec > kMAX_CUSTOM_SEC) {
return 0;
}
- // Create time zone ID in RFC822 format - GMT[+|-]hhmm
- UnicodeString tzRFC(GMT_ID);
- if (hour|min) {
+ // Create time zone ID - GMT[+|-]hhmm[ss]
+ UnicodeString tzID(GMT_ID);
+ if (hour | min | sec) {
if (negative) {
- tzRFC += (UChar)MINUS;
+ tzID += (UChar)MINUS;
} else {
- tzRFC += (UChar)PLUS;
+ tzID += (UChar)PLUS;
}
if (hour < 10) {
- tzRFC += (UChar)ZERO_DIGIT;
+ tzID += (UChar)ZERO_DIGIT;
} else {
- tzRFC += (UChar)(ZERO_DIGIT + hour/10);
+ tzID += (UChar)(ZERO_DIGIT + hour/10);
}
- tzRFC += (UChar)(ZERO_DIGIT + hour%10);
+ tzID += (UChar)(ZERO_DIGIT + hour%10);
if (min < 10) {
- tzRFC += (UChar)ZERO_DIGIT;
+ tzID += (UChar)ZERO_DIGIT;
} else {
- tzRFC += (UChar)(ZERO_DIGIT + min/10);
+ tzID += (UChar)(ZERO_DIGIT + min/10);
+ }
+ tzID += (UChar)(ZERO_DIGIT + min%10);
+
+ if (sec) {
+ if (sec < 10) {
+ tzID += (UChar)ZERO_DIGIT;
+ } else {
+ tzID += (UChar)(ZERO_DIGIT + sec/10);
+ }
+ tzID += (UChar)(ZERO_DIGIT + sec%10);
}
- tzRFC += (UChar)(ZERO_DIGIT + min%10);
}
- int32_t offset = (hour * 60 + min) * 60 * 1000;
- if(negative) {
+ int32_t offset = ((hour * 60 + min) * 60 + sec) * 1000;
+ if (negative) {
offset = -offset;
}
- return new SimpleTimeZone(offset, tzRFC);
+ return new SimpleTimeZone(offset, tzID);
}
- return 0;
+ return NULL;
}
diff --git a/i18n/ucln_in.h b/i18n/ucln_in.h
index 6feae009..66138191 100644
--- a/i18n/ucln_in.h
+++ b/i18n/ucln_in.h
@@ -39,6 +39,8 @@ typedef enum ECleanupI18NType {
UCLN_I18N_UCOL,
UCLN_I18N_UCOL_BLD,
UCLN_I18N_CSDET,
+ UCLN_I18N_ZONEMETA,
+ UCLN_I18N_ZSFORMAT,
UCLN_I18N_COUNT /* This must be last */
} ECleanupI18NType;
diff --git a/i18n/ucol.cpp b/i18n/ucol.cpp
index 96726a25..9e443481 100644
--- a/i18n/ucol.cpp
+++ b/i18n/ucol.cpp
@@ -6054,6 +6054,8 @@ ucol_nextSortKeyPart(const UCollator *coll,
level = UCOL_PSK_QUIN;
break;
}
+ if(CE==0)
+ continue;
if(isShiftedCE(CE, LVT, &wasShifted)) {
CE >>= 16; /* get primary */
if(CE != 0) {
diff --git a/i18n/ucol_bld.cpp b/i18n/ucol_bld.cpp
index 6d493b36..83f33f49 100644
--- a/i18n/ucol_bld.cpp
+++ b/i18n/ucol_bld.cpp
@@ -27,6 +27,7 @@
#include "ucln_in.h"
#include "umutex.h"
#include "unicode/uniset.h"
+#include "unormimp.h"
static const InverseUCATableHeader* _staticInvUCA = NULL;
static UDataMemory* invUCA_DATA_MEM = NULL;
@@ -825,6 +826,7 @@ U_CFUNC void ucol_createElements(UColTokenParser *src, tempUCATable *t, UColTokL
UColToken *tok = lh->first;
UColToken *expt = NULL;
uint32_t i = 0, j = 0;
+ const uint16_t *fcdTrieData = unorm_getFCDTrie(status);
while(tok != NULL && U_SUCCESS(*status)) {
/* first, check if there are any expansions */
@@ -912,10 +914,25 @@ U_CFUNC void ucol_createElements(UColTokenParser *src, tempUCATable *t, UColTokL
uprv_memcpy(el.uchars, (tok->source & 0x00FFFFFF) + src->source, el.cSize*sizeof(UChar));
}
if(src->UCA != NULL) {
+ UBool containCombinMarks = FALSE;
for(i = 0; i<el.cSize; i++) {
if(UCOL_ISJAMO(el.cPoints[i])) {
t->image->jamoSpecial = TRUE;
}
+ if ( !src->buildCCTabFlag ) {
+ // check combining class
+ int16_t fcd = unorm_getFCD16(fcdTrieData, el.cPoints[i]);
+ if ( (fcd && 0xff) == 0 ) {
+ // reset flag when current char is not combining mark.
+ containCombinMarks = FALSE;
+ }
+ else {
+ containCombinMarks = TRUE;
+ }
+ }
+ }
+ if ( !src->buildCCTabFlag && containCombinMarks ) {
+ src->buildCCTabFlag = TRUE;
}
}
@@ -1214,10 +1231,8 @@ UCATableHeader *ucol_assembleTailoringTable(UColTokenParser *src, UErrorCode *st
// Add completely ignorable elements
utrie_enum(&t->UCA->mapping, NULL, _processUCACompleteIgnorables, t);
-
- // canonical closure
- uprv_uca_canonicalClosure(t, status);
-
+ // add tailoring characters related canonical closures
+ uprv_uca_canonicalClosure(t, src, status);
/* still need to produce compatibility closure */
diff --git a/i18n/ucol_elm.cpp b/i18n/ucol_elm.cpp
index ecb7b5f9..929a4db8 100644
--- a/i18n/ucol_elm.cpp
+++ b/i18n/ucol_elm.cpp
@@ -1,7 +1,8 @@
+
/*
*******************************************************************************
*
-* Copyright (C) 2001-2006, International Business Machines
+* Copyright (C) 2001-2007, International Business Machines
* Corporation and others. All Rights Reserved.
*
*******************************************************************************
@@ -32,6 +33,7 @@
#include "unicode/ucoleitr.h"
#include "unicode/normlzr.h"
#include "ucol_elm.h"
+#include "ucol_tok.h"
#include "unormimp.h"
#include "unicode/caniter.h"
#include "cmemory.h"
@@ -203,6 +205,7 @@ uprv_uca_initTempTable(UCATableHeader *image, UColOptionSet *opts, const UCollat
}
uprv_memset(t->unsafeCP, 0, UCOL_UNSAFECP_TABLE_SIZE);
uprv_memset(t->contrEndCP, 0, UCOL_UNSAFECP_TABLE_SIZE);
+ t->cmLookup = NULL;
return t;
allocation_failure:
@@ -391,6 +394,11 @@ uprv_uca_closeTempTable(tempUCATable *t) {
uprv_free(t->unsafeCP);
uprv_free(t->contrEndCP);
+
+ if (t->cmLookup != NULL) {
+ uprv_free(t->cmLookup->cPoints);
+ uprv_free(t->cmLookup);
+ }
uprv_free(t);
}
@@ -675,25 +683,94 @@ static void unsafeCPSet(uint8_t *table, UChar c) {
*htByte |= (1 << (hash & 7));
}
+static void
+uprv_uca_createCMTable(tempUCATable *t, int32_t noOfCM, UErrorCode *status) {
+ t->cmLookup = (CombinClassTable *)uprv_malloc(sizeof(CombinClassTable));
+ if (t->cmLookup==NULL) {
+ *status = U_MEMORY_ALLOCATION_ERROR;
+ return;
+ }
+ t->cmLookup->cPoints=(UChar *)uprv_malloc(noOfCM*sizeof(UChar));
+ if (t->cmLookup->cPoints ==NULL) {
+ uprv_free(t->cmLookup);
+ t->cmLookup = NULL;
+ *status = U_MEMORY_ALLOCATION_ERROR;
+ return;
+ }
+
+ t->cmLookup->size=noOfCM;
+ uprv_memset(t->cmLookup->index, 0, sizeof(t->cmLookup->index));
+
+ return;
+}
+
+static void
+uprv_uca_copyCMTable(tempUCATable *t, UChar *cm, uint16_t *index) {
+ int32_t count=0;
-/* to the UnsafeCP hash table, add all chars with combining class != 0 */
+ for (int32_t i=0; i<256; ++i) {
+ if (index[i]>0) {
+ // cPoints is ordered by combining class value.
+ uprv_memcpy(t->cmLookup->cPoints+count, cm+(i<<8), index[i]*sizeof(UChar));
+ count += index[i];
+ }
+ t->cmLookup->index[i]=count;
+ }
+ return;
+}
+
+/* 1. to the UnsafeCP hash table, add all chars with combining class != 0 */
+/* 2. build combining marks table for all chars with combining class != 0 */
static void uprv_uca_unsafeCPAddCCNZ(tempUCATable *t, UErrorCode *status) {
UChar c;
uint16_t fcd; // Hi byte is lead combining class.
// lo byte is trailing combing class.
const uint16_t *fcdTrieData;
-
+ UBool buildCMTable = (t->cmLookup==NULL); // flag for building combining class table
+ UChar *cm=NULL;
+ uint16_t index[256];
+ int32_t count=0;
fcdTrieData = unorm_getFCDTrie(status);
if (U_FAILURE(*status)) {
return;
}
+ if (buildCMTable) {
+ if (cm==NULL) {
+ cm = (UChar *)uprv_malloc(sizeof(UChar)*UCOL_MAX_CM_TAB);
+ if (cm==NULL) {
+ *status = U_MEMORY_ALLOCATION_ERROR;
+ return;
+ }
+ }
+ uprv_memset(index, 0, sizeof(index));
+ }
for (c=0; c<0xffff; c++) {
fcd = unorm_getFCD16(fcdTrieData, c);
if (fcd >= 0x100 || // if the leading combining class(c) > 0 ||
- (UTF_IS_LEAD(c) && fcd != 0)) // c is a leading surrogate with some FCD data
+ (UTF_IS_LEAD(c) && fcd != 0)) {// c is a leading surrogate with some FCD data
+ if (buildCMTable) {
+ uint32_t cClass = fcd & 0xff;
+ uint32_t temp=(cClass<<8)+index[cClass];
+ cm[(cClass<<8)+index[cClass]] = c; //
+ index[cClass]++;
+ count++;
+ }
unsafeCPSet(t->unsafeCP, c);
+ }
+ }
+
+ // copy to cm table
+ if (buildCMTable) {
+ uprv_uca_createCMTable(t, count, status);
+ if(U_FAILURE(*status)) {
+ if (cm!=NULL) {
+ uprv_free(cm);
+ }
+ return;
+ }
+ uprv_uca_copyCMTable(t, cm, index);
}
if(t->prefixLookup != NULL) {
@@ -705,21 +782,25 @@ static void uprv_uca_unsafeCPAddCCNZ(tempUCATable *t, UErrorCode *status) {
while((e = uhash_nextElement(t->prefixLookup, &i)) != NULL) {
element = (UCAElements *)e->value.pointer;
// codepoints here are in the NFD form. We need to add the
- // first code point of the NFC form to unsafe, because
+ // first code point of the NFC form to unsafe, because
// strcoll needs to backup over them.
NFCbufLen = unorm_normalize(element->cPoints, element->cSize, UNORM_NFC, 0,
NFCbuf, 256, status);
unsafeCPSet(t->unsafeCP, NFCbuf[0]);
- }
+ }
+ }
+
+ if (cm!=NULL) {
+ uprv_free(cm);
}
}
-static uint32_t uprv_uca_addPrefix(tempUCATable *t, uint32_t CE,
+static uint32_t uprv_uca_addPrefix(tempUCATable *t, uint32_t CE,
UCAElements *element, UErrorCode *status)
{
// currently the longest prefix we're supporting in Japanese is two characters
// long. Although this table could quite easily mimic complete contraction stuff
- // there is no good reason to make a general solution, as it would require some
+ // there is no good reason to make a general solution, as it would require some
// error prone messing.
CntTable *contractions = t->contractions;
UChar32 cp;
@@ -744,7 +825,7 @@ static uint32_t uprv_uca_addPrefix(tempUCATable *t, uint32_t CE,
#endif
for (j = 1; j<element->prefixSize; j++) { /* First add NFD prefix chars to unsafe CP hash table */
- // Unless it is a trail surrogate, which is handled algoritmically and
+ // Unless it is a trail surrogate, which is handled algoritmically and
// shouldn't take up space in the table.
if(!(UTF_IS_TRAIL(element->prefix[j]))) {
unsafeCPSet(t->unsafeCP, element->prefix[j]);
@@ -784,7 +865,7 @@ static uint32_t uprv_uca_addPrefix(tempUCATable *t, uint32_t CE,
element->cSize = element->prefixSize;
// Add the last char of the contraction to the contraction-end hash table.
- // unless it is a trail surrogate, which is handled algorithmically and
+ // unless it is a trail surrogate, which is handled algorithmically and
// shouldn't be in the table
if(!(UTF_IS_TRAIL(element->cPoints[element->cSize -1]))) {
ContrEndCPSet(t->contrEndCP, element->cPoints[element->cSize -1]);
@@ -793,7 +874,7 @@ static uint32_t uprv_uca_addPrefix(tempUCATable *t, uint32_t CE,
// First we need to check if contractions starts with a surrogate
UTF_NEXT_CHAR(element->cPoints, cpsize, element->cSize, cp);
- // If there are any Jamos in the contraction, we should turn on special
+ // If there are any Jamos in the contraction, we should turn on special
// processing for Jamos
if(UCOL_ISJAMO(element->prefix[0])) {
t->image->jamoSpecial = TRUE;
@@ -801,7 +882,7 @@ static uint32_t uprv_uca_addPrefix(tempUCATable *t, uint32_t CE,
/* then we need to deal with it */
/* we could aready have something in table - or we might not */
- if(!isPrefix(CE)) {
+ if(!isPrefix(CE)) {
/* if it wasn't contraction, we wouldn't end up here*/
int32_t firstContractionOffset = 0;
firstContractionOffset = uprv_cnttab_addContraction(contractions, UPRV_CNTTAB_NEWELEMENT, 0, CE, status);
@@ -1557,45 +1638,348 @@ _enumCategoryRangeClosureCategory(const void *context, UChar32 start, UChar32 li
}
U_CDECL_END
+static void
+uprv_uca_setMapCE(tempUCATable *t, UCAElements *element, UErrorCode *status) {
+ uint32_t expansion = 0;
+ int32_t j;
+
+ ExpansionTable *expansions = t->expansions;
+ if(element->noOfCEs == 2 // a two CE expansion
+ && isContinuation(element->CEs[1]) // which is a continuation
+ && (element->CEs[1] & (~(0xFF << 24 | UCOL_CONTINUATION_MARKER))) == 0 // that has only primaries in continuation,
+ && (((element->CEs[0]>>8) & 0xFF) == UCOL_BYTE_COMMON) // a common secondary
+ && ((element->CEs[0] & 0xFF) == UCOL_BYTE_COMMON) // and a common tertiary
+ ) {
+ element->mapCE = UCOL_SPECIAL_FLAG | (LONG_PRIMARY_TAG<<24) // a long primary special
+ | ((element->CEs[0]>>8) & 0xFFFF00) // first and second byte of primary
+ | ((element->CEs[1]>>24) & 0xFF); // third byte of primary
+ } else {
+ expansion = (uint32_t)(UCOL_SPECIAL_FLAG | (EXPANSION_TAG<<UCOL_TAG_SHIFT)
+ | ((uprv_uca_addExpansion(expansions, element->CEs[0], status)+(headersize>>2))<<4)
+ & 0xFFFFF0);
+
+ for(j = 1; j<(int32_t)element->noOfCEs; j++) {
+ uprv_uca_addExpansion(expansions, element->CEs[j], status);
+ }
+ if(element->noOfCEs <= 0xF) {
+ expansion |= element->noOfCEs;
+ } else {
+ uprv_uca_addExpansion(expansions, 0, status);
+ }
+ element->mapCE = expansion;
+ uprv_uca_setMaxExpansion(element->CEs[element->noOfCEs - 1],
+ (uint8_t)element->noOfCEs,
+ t->maxExpansions,
+ status);
+ }
+}
+
+static void
+uprv_uca_addFCD4AccentedContractions(tempUCATable *t,
+ UCollationElements* colEl,
+ UChar *data,
+ int32_t len,
+ UCAElements *el,
+ UErrorCode *status) {
+ UChar decomp[256], comp[256];
+ int32_t decLen, compLen;
+
+ decLen = unorm_normalize(data, len, UNORM_NFD, 0, decomp, 256, status);
+ compLen = unorm_normalize(data, len, UNORM_NFC, 0, comp, 256, status);
+ decomp[decLen] = comp[compLen] = 0;
+
+ el->cPoints = decomp;
+ el->cSize = decLen;
+ el->noOfCEs = 0;
+ el->prefixSize = 0;
+ el->prefix = el->prefixChars;
+
+ UCAElements *prefix=(UCAElements *)uhash_get(t->prefixLookup, el);
+ el->cPoints = comp;
+ el->cSize = compLen;
+ el->prefix = el->prefixChars;
+ el->prefixSize = 0;
+ if(prefix == NULL) {
+ el->noOfCEs = 0;
+ ucol_setText(colEl, decomp, decLen, status);
+ while((el->CEs[el->noOfCEs] = ucol_next(colEl, status)) != (uint32_t)UCOL_NULLORDER) {
+ el->noOfCEs++;
+ }
+ uprv_uca_setMapCE(t, el, status);
+ uprv_uca_addAnElement(t, el, status);
+ }
+}
+
+static void
+uprv_uca_addMultiCMContractions(tempUCATable *t,
+ UCollationElements* colEl,
+ tempTailorContext *c,
+ UCAElements *el,
+ UErrorCode *status) {
+ CombinClassTable *cmLookup = t->cmLookup;
+ UChar newDecomp[256];
+ int32_t maxComp, newDecLen;
+ const uint16_t *fcdTrieData = unorm_getFCDTrie(status);
+ int16_t curClass = (unorm_getFCD16(fcdTrieData, c->tailoringCM) & 0xff);
+ CompData *precomp = c->precomp;
+ int32_t compLen = c->compLen;
+ UChar *comp = c->comp;
+ maxComp = c->precompLen;
+
+ for (int32_t j=0; j < maxComp; j++) {
+ int32_t count=0;
+ int32_t newClass=0;
+ do {
+ if ( count == 0 ) { // Decompose the saved precomposed char.
+ UChar temp[2];
+ temp[0]=precomp[j].cp;
+ temp[1]=0;
+ newDecLen = unorm_normalize(temp, 1, UNORM_NFD, 0,
+ newDecomp, sizeof(newDecomp)/sizeof(UChar), status);
+ newDecomp[newDecLen++] = cmLookup->cPoints[c->cmPos];
+ }
+ else { // swap 2 combining marks when they are equal.
+ uprv_memcpy(newDecomp, c->decomp, sizeof(UChar)*(c->decompLen));
+ newDecLen = c->decompLen;
+ newDecomp[newDecLen++] = precomp[j].cClass;
+ }
+ newDecomp[newDecLen] = 0;
+ compLen = unorm_normalize(newDecomp, newDecLen, UNORM_NFC, 0,
+ comp, 256, status);
+ if (compLen==1) {
+ comp[compLen++] = newDecomp[newDecLen++] = c->tailoringCM;
+ comp[compLen] = newDecomp[newDecLen] = 0;
+ el->cPoints = newDecomp;
+ el->cSize = newDecLen;
+
+ UCAElements *prefix=(UCAElements *)uhash_get(t->prefixLookup, el);
+ el->cPoints = c->comp;
+ el->cSize = compLen;
+ el->prefix = el->prefixChars;
+ el->prefixSize = 0;
+ if(prefix == NULL) {
+ el->noOfCEs = 0;
+ ucol_setText(colEl, newDecomp, newDecLen, status);
+ while((el->CEs[el->noOfCEs] = ucol_next(colEl, status)) != (uint32_t)UCOL_NULLORDER) {
+ el->noOfCEs++;
+ }
+ uprv_uca_setMapCE(t, el, status);
+ uprv_uca_finalizeAddition(t, el, status);
+
+ // Save the current precomposed char and its class to find any
+ // other combining mark combinations.
+ precomp[c->precompLen].cp=comp[0];
+ precomp[c->precompLen].cClass = curClass;
+ c->precompLen++;
+ }
+ }
+ } while (++count<2 && (precomp[j].cClass == curClass));
+ }
+
+}
+
+static void
+uprv_uca_addTailCanonicalClosures(tempUCATable *t,
+ UCollationElements* colEl,
+ UChar baseCh,
+ UChar cMark,
+ UCAElements *el,
+ UErrorCode *status) {
+ CombinClassTable *cmLookup = t->cmLookup;
+ const uint16_t *fcdTrieData = unorm_getFCDTrie(status);
+ int16_t maxIndex = (unorm_getFCD16(fcdTrieData, cMark) & 0xff );
+ UCAElements element;
+ uint16_t *index;
+ UChar decomp[256];
+ UChar comp[256];
+ CompData precomp[256]; // precomposed array
+ int32_t precompLen = 0; // count for precomp
+ int32_t i, len, decompLen, curClass, replacedPos;
+ tempTailorContext c;
+
+ if ( cmLookup == NULL ) {
+ return;
+ }
+ index = cmLookup->index;
+ int32_t cClass=(unorm_getFCD16(fcdTrieData, cMark) & 0xff);
+ maxIndex = (int32_t)index[(unorm_getFCD16(fcdTrieData, cMark) & 0xff)-1];
+ c.comp = comp;
+ c.decomp = decomp;
+ c.precomp = precomp;
+ c.tailoringCM = cMark;
+
+ if (cClass>0) {
+ maxIndex = (int32_t)index[cClass-1];
+ }
+ else {
+ maxIndex=0;
+ }
+ decomp[0]=baseCh;
+ for ( i=0; i<maxIndex ; i++ ) {
+ decomp[1] = cmLookup->cPoints[i];
+ decomp[2]=0;
+ decompLen=2;
+ len = unorm_normalize(decomp, decompLen, UNORM_NFC, 0, comp, 256, status);
+ if (len==1) {
+ // Save the current precomposed char and its class to find any
+ // other combining mark combinations.
+ precomp[precompLen].cp=comp[0];
+ curClass = precomp[precompLen].cClass =
+ index[unorm_getFCD16(fcdTrieData, decomp[1]) & 0xff];
+ precompLen++;
+ replacedPos=0;
+ for (decompLen=0; decompLen< (int32_t)el->cSize; decompLen++) {
+ decomp[decompLen] = el->cPoints[decompLen];
+ if (decomp[decompLen]==cMark) {
+ replacedPos = decompLen; // record the position for later use
+ }
+ }
+ if ( replacedPos != 0 ) {
+ decomp[replacedPos]=cmLookup->cPoints[i];
+ }
+ decomp[decompLen] = 0;
+ len = unorm_normalize(decomp, decompLen, UNORM_NFC, 0, comp, 256, status);
+ comp[len++] = decomp[decompLen++] = cMark;
+ comp[len] = decomp[decompLen] = 0;
+ element.cPoints = decomp;
+ element.cSize = decompLen;
+ element.noOfCEs = 0;
+ element.prefix = el->prefixChars;
+ element.prefixSize = 0;
+
+ UCAElements *prefix=(UCAElements *)uhash_get(t->prefixLookup, &element);
+ element.cPoints = comp;
+ element.cSize = len;
+ element.prefix = el->prefixChars;
+ element.prefixSize = 0;
+ if(prefix == NULL) {
+ element.noOfCEs = 0;
+ ucol_setText(colEl, decomp, decompLen, status);
+ while((element.CEs[element.noOfCEs] = ucol_next(colEl, status)) != (uint32_t)UCOL_NULLORDER) {
+ element.noOfCEs++;
+ }
+ uprv_uca_setMapCE(t, &element, status);
+ uprv_uca_finalizeAddition(t, &element, status);
+ }
+
+ // This is a fix for tailoring contractions with accented
+ // character at the end of contraction string.
+ if ((len>2) &&
+ (unorm_getFCD16(fcdTrieData, comp[len-2]) & 0xff00)==0) {
+ uprv_uca_addFCD4AccentedContractions(t, colEl, comp, len, &element, status);
+ }
+
+ if (precompLen >1) {
+ c.compLen = len;
+ c.decompLen = decompLen;
+ c.precompLen = precompLen;
+ c.cmPos = i;
+ uprv_uca_addMultiCMContractions(t, colEl, &c, &element, status);
+ precompLen = c.precompLen;
+ }
+ }
+ }
+}
+
U_CAPI int32_t U_EXPORT2
-uprv_uca_canonicalClosure(tempUCATable *t, UErrorCode *status)
+uprv_uca_canonicalClosure(tempUCATable *t,
+ UColTokenParser *src,
+ UErrorCode *status)
{
enumStruct context;
context.noOfClosures = 0;
- if(U_SUCCESS(*status)) {
- UCollator *tempColl = NULL;
- tempUCATable *tempTable = uprv_uca_cloneTempTable(t, status);
-
- UCATableHeader *tempData = uprv_uca_assembleTable(tempTable, status);
- tempColl = ucol_initCollator(tempData, 0, t->UCA, status);
- uprv_uca_closeTempTable(tempTable);
-
- if(U_SUCCESS(*status)) {
- tempColl->rb = NULL;
- tempColl->elements = NULL;
- tempColl->validLocale = NULL;
- tempColl->requestedLocale = NULL;
- tempColl->hasRealData = TRUE;
- tempColl->freeImageOnClose = TRUE;
- } else if(tempData != 0) {
- uprv_free(tempData);
- }
+ UCAElements el;
+ UColToken *tok;
+ UColToken *expt = NULL;
+ uint32_t i = 0, j = 0;
+ UChar baseChar, firstCM;
+ const uint16_t *fcdTrieData = unorm_getFCDTrie(status);
+
+ if(!U_SUCCESS(*status)) {
+ return 0;
+ }
- /* produce canonical closure */
- UCollationElements* colEl = ucol_openElements(tempColl, NULL, 0, status);
+ UCollator *tempColl = NULL;
+ tempUCATable *tempTable = uprv_uca_cloneTempTable(t, status);
- context.t = t;
- context.tempColl = tempColl;
- context.colEl = colEl;
- context.status = status;
- u_enumCharTypes(_enumCategoryRangeClosureCategory, &context);
+ UCATableHeader *tempData = uprv_uca_assembleTable(tempTable, status);
+ tempColl = ucol_initCollator(tempData, 0, t->UCA, status);
+ if ( tempTable->cmLookup != NULL ) {
+ t->cmLookup = tempTable->cmLookup; // copy over to t
+ tempTable->cmLookup = NULL;
+ }
+ uprv_uca_closeTempTable(tempTable);
+ if(U_SUCCESS(*status)) {
+ tempColl->rb = NULL;
+ tempColl->elements = NULL;
+ tempColl->validLocale = NULL;
+ tempColl->requestedLocale = NULL;
+ tempColl->hasRealData = TRUE;
+ tempColl->freeImageOnClose = TRUE;
+ } else if(tempData != 0) {
+ uprv_free(tempData);
+ }
+
+ /* produce canonical closure */
+ UCollationElements* colEl = ucol_openElements(tempColl, NULL, 0, status);
+
+ context.t = t;
+ context.tempColl = tempColl;
+ context.colEl = colEl;
+ context.status = status;
+ u_enumCharTypes(_enumCategoryRangeClosureCategory, &context);
+
+ if ( (src==NULL) || !src->buildCCTabFlag ) {
ucol_closeElements(colEl);
ucol_close(tempColl);
+ return context.noOfClosures; // no extra contraction needed to add
+ }
+
+ for (i=0; i < src->resultLen; i++) {
+ baseChar = firstCM= (UChar)0;
+ tok = src->lh[i].first;
+ while (tok != NULL && U_SUCCESS(*status)) {
+ el.prefix = el.prefixChars;
+ el.cPoints = el.uchars;
+ if(tok->prefix != 0) {
+ el.prefixSize = tok->prefix>>24;
+ uprv_memcpy(el.prefix, src->source + (tok->prefix & 0x00FFFFFF), el.prefixSize*sizeof(UChar));
+
+ el.cSize = (tok->source >> 24)-(tok->prefix>>24);
+ uprv_memcpy(el.uchars, (tok->source & 0x00FFFFFF)+(tok->prefix>>24) + src->source, el.cSize*sizeof(UChar));
+ } else {
+ el.prefixSize = 0;
+ *el.prefix = 0;
+
+ el.cSize = (tok->source >> 24);
+ uprv_memcpy(el.uchars, (tok->source & 0x00FFFFFF) + src->source, el.cSize*sizeof(UChar));
+ }
+ if(src->UCA != NULL) {
+ for(j = 0; j<el.cSize; j++) {
+ int16_t fcd = unorm_getFCD16(fcdTrieData, el.cPoints[j]);
+ if ( (fcd & 0xff) == 0 ) {
+ baseChar = el.cPoints[j]; // last base character
+ firstCM=0; // reset combining mark value
+ }
+ else {
+ if ( (baseChar!=0) && (firstCM==0) ) {
+ firstCM = el.cPoints[j]; // first combining mark
+ }
+ }
+ }
+ }
+ if ( (baseChar!= (UChar)0) && (firstCM != (UChar)0) ) {
+ // find all the canonical rules
+ uprv_uca_addTailCanonicalClosures(t, colEl, baseChar, firstCM, &el, status);
+ }
+ tok = tok->next;
+ }
}
+ ucol_closeElements(colEl);
+ ucol_close(tempColl);
+
return context.noOfClosures;
}
#endif /* #if !UCONFIG_NO_COLLATION */
-
-
diff --git a/i18n/ucol_elm.h b/i18n/ucol_elm.h
index 9de80da8..fbe966ea 100644
--- a/i18n/ucol_elm.h
+++ b/i18n/ucol_elm.h
@@ -22,6 +22,7 @@
#define UCOL_UCAELEMS_H
#include "unicode/utypes.h"
+#include "ucol_tok.h"
#if !UCONFIG_NO_COLLATION
@@ -43,6 +44,13 @@ a problem. Normally, less than 32K are tailored.
*/
#define UCOL_ELM_TRIE_CAPACITY 0x40000
+/* This is the maxmun capacity for temparay combining class
+ * table. The table will be compacted after scanning all the
+ * Unicode codepoints.
+*/
+#define UCOL_MAX_CM_TAB 0x10000
+
+
typedef struct {
uint32_t *CEs;
int32_t position;
@@ -85,6 +93,12 @@ typedef struct {
} MaxExpansionTable;
typedef struct {
+ uint16_t index[256]; /* index of cPoints by combining class 0-255. */
+ UChar *cPoints; /* code point array of all combining marks */
+ uint32_t size; /* total number of combining marks */
+} CombinClassTable;
+
+typedef struct {
/*CompactEIntArray *mapping; */
UNewTrie *mapping;
ExpansionTable *expansions;
@@ -97,17 +111,33 @@ typedef struct {
uint8_t *contrEndCP;
const UCollator *UCA;
UHashtable *prefixLookup;
+ CombinClassTable *cmLookup; /* combining class lookup for tailoring. */
} tempUCATable;
+typedef struct {
+ UChar cp;
+ uint16_t cClass; // combining class
+}CompData;
+
+typedef struct {
+ CompData *precomp;
+ int32_t precompLen;
+ UChar *decomp;
+ int32_t decompLen;
+ UChar *comp;
+ int32_t compLen;
+ uint16_t curClass;
+ uint16_t tailoringCM;
+ int32_t cmPos;
+}tempTailorContext;
+
U_CAPI tempUCATable * U_EXPORT2 uprv_uca_initTempTable(UCATableHeader *image, UColOptionSet *opts, const UCollator *UCA, UColCETags initTag, UColCETags supplementaryInitTag, UErrorCode *status);
U_CAPI tempUCATable * U_EXPORT2 uprv_uca_cloneTempTable(tempUCATable *t, UErrorCode *status);
U_CAPI void U_EXPORT2 uprv_uca_closeTempTable(tempUCATable *t);
U_CAPI uint32_t U_EXPORT2 uprv_uca_addAnElement(tempUCATable *t, UCAElements *element, UErrorCode *status);
U_CAPI UCATableHeader * U_EXPORT2 uprv_uca_assembleTable(tempUCATable *t, UErrorCode *status);
U_CAPI int32_t U_EXPORT2
-uprv_uca_canonicalClosure(tempUCATable *t, UErrorCode *status);
-
-
+uprv_uca_canonicalClosure(tempUCATable *t, UColTokenParser *src, UErrorCode *status);
#define paddedsize(something) ((something)+((((something)%4)!=0)?(4-(something)%4):0))
#define headersize (paddedsize(sizeof(UCATableHeader))+paddedsize(sizeof(UColOptionSet)))
diff --git a/i18n/ucol_tok.cpp b/i18n/ucol_tok.cpp
index d7d993a1..77e2c860 100644
--- a/i18n/ucol_tok.cpp
+++ b/i18n/ucol_tok.cpp
@@ -1817,7 +1817,7 @@ void ucol_tok_initTokenList(UColTokenParser *src, const UChar *rules, const uint
src->parsedToken.prefixOffset = 0;
src->parsedToken.flags = 0;
src->parsedToken.strength = UCOL_TOK_UNSET;
-
+ src->buildCCTabFlag = FALSE;
if(U_FAILURE(*status)) {
return;
diff --git a/i18n/ucol_tok.h b/i18n/ucol_tok.h
index efd0a021..fc252e94 100644
--- a/i18n/ucol_tok.h
+++ b/i18n/ucol_tok.h
@@ -1,7 +1,7 @@
/*
*******************************************************************************
*
-* Copyright (C) 2001-2006, International Business Machines
+* Copyright (C) 2001-2007, International Business Machines
* Corporation and others. All Rights Reserved.
*
*******************************************************************************
@@ -122,6 +122,7 @@ typedef struct {
UColToken *varTop;
USet *copySet;
USet *removeSet;
+ UBool buildCCTabFlag; /* Tailoring rule requirs building combining class table. */
} UColTokenParser;
typedef struct {
diff --git a/i18n/unicode/basictz.h b/i18n/unicode/basictz.h
index 61e05b61..a49effd5 100644
--- a/i18n/unicode/basictz.h
+++ b/i18n/unicode/basictz.h
@@ -140,7 +140,36 @@ public:
virtual void getSimpleRulesNear(UDate date, InitialTimeZoneRule*& initial,
AnnualTimeZoneRule*& std, AnnualTimeZoneRule*& dst, UErrorCode& status) /*const*/;
+
+ /**
+ * The time type option bit flags used by getOffsetFromLocal
+ * @internal
+ */
+ enum {
+ kStandard = 0x01,
+ kDaylight = 0x03,
+ kFormer = 0x04,
+ kLatter = 0x0C
+ };
+
+ /**
+ * Get time zone offsets from local wall time.
+ * @internal
+ */
+ virtual void getOffsetFromLocal(UDate date, int32_t nonExistingTimeOpt, int32_t duplicatedTimeOpt,
+ int32_t& rawOffset, int32_t& dstOffset, UErrorCode& status) /*const*/;
+
protected:
+
+ /**
+ * The time type option bit masks used by getOffsetFromLocal
+ * @internal
+ */
+ enum {
+ kStdDstMask = kDaylight,
+ kFormerLatterMask = kLatter
+ };
+
/**
* Default constructor.
* @draft ICU 3.8
diff --git a/i18n/unicode/dtfmtsym.h b/i18n/unicode/dtfmtsym.h
index 5377723f..5bc73fef 100644
--- a/i18n/unicode/dtfmtsym.h
+++ b/i18n/unicode/dtfmtsym.h
@@ -37,6 +37,8 @@ U_NAMESPACE_BEGIN
/* forward declaration */
class SimpleDateFormat;
class Hashtable;
+class ZoneStringFormat;
+class SafeZoneStringFormatPtr;
/**
* DateFormatSymbols is a public class for encapsulating localizable date-time
@@ -445,100 +447,6 @@ public:
*/
static UClassID U_EXPORT2 getStaticClassID();
- /**
- * The translation type of the translated zone strings
- * @internal ICU 3.6
- */
- enum TimeZoneTranslationType {
- TIMEZONE_SHORT_GENERIC,
- TIMEZONE_SHORT_STANDARD,
- TIMEZONE_SHORT_DAYLIGHT,
- TIMEZONE_LONG_GENERIC,
- TIMEZONE_LONG_STANDARD,
- TIMEZONE_LONG_DAYLIGHT,
- TIMEZONE_EXEMPLAR_CITY,
- TIMEZONE_COUNT
- };
-
- /**
- * Creates an enumeration of time zone IDs. The object is owned by the caller and should delete it after use.
- * The time zone IDs are just for programmatic lookup. NOT LOCALIZED!!!
- * @param status Input/output parameter, set to success or
- * failure code upon return.
- * @return A new StringEnumeration object
- * @internal ICU 3.6
- */
- virtual StringEnumeration* createZoneStringIDs(UErrorCode &status);
-
- /**
- * Gets timezone string give the key and translation type
- * @param ID The ID of zone strings, e.g: "America/Los_Angeles".
- * The time zone ID is for programmatic lookup.
- * @param type The translation type requested
- * @param result Output parameter to recieve the translation string
- * @param status Input/output parameter, set to success or
- * failure code upon return.
- * @return the input UnicodeString parameter for chaining
- * @internal ICU 3.8
- */
- UnicodeString& getZoneString(const UnicodeString &ID, const TimeZoneTranslationType type, UnicodeString &result, UErrorCode &status);
-
- /**
- * Gets metazone string given the key and translation type and calendar
- * @param ID The ID of zone strings, e.g: "America/Los_Angeles".
- * The time zone ID is for programmatic lookup.
- * @param type The translation type requested
- * @param cal The calendar
- * @param result Output parameter to recieve the translation string
- * @param status Input/output parameter, set to success or
- * failure code upon return.
- * @return the input UnicodeString parameter for chaining
- * @internal ICU 3.8
- */
- UnicodeString getMetazoneString(const UnicodeString &ID, const TimeZoneTranslationType type, Calendar &cal, UnicodeString &result, UErrorCode &status);
-
- /**
- * Gets fallback string given the key
- * @param ID The ID of zone strings, e.g: "America/Los_Angeles".
- * The time zone ID is for programmatic lookup.
- * @param result Output parameter to recieve the translation string
- * @param status Input/output parameter, set to success or
- * failure code upon return.
- * @return the input UnicodeString parameter for chaining
- * @internal ICU 3.8
- */
- UnicodeString& getFallbackString(const UnicodeString &ID, UnicodeString &result, UErrorCode &status);
-
- /**
- * Sets timezone string for the given the ID and translation type
- * @param ID The ID of zone strings, e.g: "America/Los_Angeles".
- * The time zone ID is for programmatic lookup.
- * @param type The translation type to set the value for
- * @param value The string with which current translation needs to be replaced
- * @param status Input/output parameter, set to success or
- * @internal ICU 3.6
- */
-
- /**
- * Determines if the Commonly Used flag is set for this zone
- * @param zid The ID of zone strings, e.g: "America/Los_Angeles".
- * The time zone ID is for programmatic lookup.
- * @return A boolean value indicating if the zone is commonlyUsed or not.
- * @internal ICU 3.8
- */
- UBool isCommonlyUsed(const UnicodeString &zid);
-
- /**
- * Sets timezone string for the given the ID and translation type
- * @param ID The ID of zone strings, e.g: "America/Los_Angeles".
- * The time zone ID is for programmatic lookup.
- * @param type The translation type to set the value for
- * @param value The string with which current translation needs to be replaced
- * @param status Input/output parameter, set to success or
- * @internal ICU 3.6
- */
- void setZoneString(const UnicodeString &ID, const TimeZoneTranslationType type, const UnicodeString &value, UErrorCode &status);
-
private:
friend class SimpleDateFormat;
@@ -661,13 +569,34 @@ private:
/**
* The format data of all the timezones in this locale.
*/
- UnicodeString** fZoneStrings;
+ UnicodeString **fZoneStrings; // Zone string array set by setZoneStrings
+ UnicodeString **fLocaleZoneStrings; // Zone string array created by the locale
int32_t fZoneStringsRowCount;
int32_t fZoneStringsColCount;
- StringEnumeration* fZoneIDEnumeration;
- Hashtable* fZoneStringsHash;
- UResourceBundle* fResourceBundle;
- const char* fCountry;
+
+ const ZoneStringFormat *fZoneStringFormat;
+ ZoneStringFormat *fZSFLocal; // Local ZoneStringFormat instance
+ SafeZoneStringFormatPtr *fZSFCachePtr; // Cached ZoneStringFormat
+ Locale fZSFLocale; // Locale used for getting ZoneStringFormat
+
+ /**
+ * Pattern string used for localized time zone GMT format. For example, "GMT{0}"
+ */
+ UnicodeString fGmtFormat;
+
+ /**
+ * Pattern strings used for formatting zone offset in a localized time zone GMT string.
+ */
+ UnicodeString *fGmtHourFormats;
+ int32_t fGmtHourFormatsCount;
+
+ enum GMTHourType {
+ GMT_NEGATIVE_HMS = 0,
+ GMT_NEGATIVE_HM,
+ GMT_POSITIVE_HMS,
+ GMT_POSITIVE_HM,
+ GMT_HOUR_COUNT
+ };
/**
* Localized date-time pattern characters. For example: use 'u' as 'y'.
@@ -729,21 +658,6 @@ private:
void createZoneStrings(const UnicodeString *const * otherStrings);
/**
- * Package private: used by SimpleDateFormat
- * Gets the index for the given time zone ID to obtain the timezone
- * strings for formatting. The time zone ID is just for programmatic
- * lookup. NOT LOCALIZED!!!
- * @param ID the given time zone ID.
- * @return the index of the given time zone ID. Returns -1 if
- * the given time zone ID can't be located in the DateFormatSymbols object.
- * @see java.util.SimpleTimeZone
- */
- int32_t getZoneIndex(const UnicodeString& ID) const;
-
- // Internal method; see source for documentation
- int32_t _getZoneIndex(const UnicodeString& id) const;
-
- /**
* Delete all the storage owned by this object.
*/
void dispose(void);
@@ -754,67 +668,26 @@ private:
*/
void copyData(const DateFormatSymbols& other);
- /**
- * Delete just the zone strings.
- */
- void disposeZoneStrings(void);
/**
- * Initializes the zoneStrings hash and keys StringEnumeration after reading the zoneStrings resource
- */
- void initZoneStrings(UErrorCode &status);
- /**
- * initialzes the zoneStrings has and keys enumeration after reading the strings[][]. Required for backwards
- * compatibility of setZoneStrings method
- */
- void initZoneStrings(const UnicodeString** strings, int32_t rowCount, int32_t collumnCount, UErrorCode& status);
- /**
- * initialization of the fZoneStrings data member
- */
- void initZoneStringsArray(UErrorCode& status);
- /**
- * Creates a deep clone of the Hashtable
+ * Returns a ZoneStringFormat, used only by SimpleDateFormat for now.
*/
- Hashtable* createZoneStringsHash(const Hashtable* otherHash);
-
+ const ZoneStringFormat* getZoneStringFormat(void) const;
+
/**
- * Fetches the key from the hashtable for a given ID.
- * e.g: for a given ID such as PST returns "Americal/Los_Angeles"
- * Used by SimpleDateFormat class.
- * @param ID The id of the time zone for which the key needs to be fetched
- * @param result Output parameter to recieve the key.
- * @return the input UnicodeString object for chaining
+ * Create a ZoneStringFormat by locale if not yet availble
*/
- UnicodeString& getZoneID(const UnicodeString& zid, UnicodeString& result, UErrorCode& status);
-
+ void initZoneStringFormat(void);
+
/**
- * Fetches the zone type and zone string from the hashtable for a given key.
- * e.g: for key: "Americal/Los_Angeles", text: "2004/1/1 PT 1:00" and start:9
- * returns TIMEZONE_SHORT_GENERIC and "PT".
- * Used by SimpleDateFormat class.
- * @param ID the name of the timezone
- * @param text the string containing the time zone translation
- * @param start The position in string where time zone string starts
- * @param type output parameter to recieve the type of time zone string
- * @param value output parameter to recieve the the acutal time zone string
+ * Create zone strings array by locale if not yet available
*/
- void getZoneType(const UnicodeString& zid, const UnicodeString& text, int32_t start, TimeZoneTranslationType& type, UnicodeString& value, UErrorCode& status);
-
+ void initZoneStringsArray(void);
+
/**
- * Fetches the zone type and zone string from the hashtable by cycling through all elements in the hashtable.
- * e.g: text: "2004/1/1 PT 1:00" and start:9
- * returns "Americal/Los_Angeles", TIMEZONE_SHORT_GENERIC and "PT". Used by SimpleDateFormat class.
- * Used by SimpleDateFormat class.
- * @param ID output parameter to recieve the key name of the time zone
- * @param text the string containing the time zone translation
- * @param start The position in string where time zone string starts
- * @param type output parameter to recieve the type of time zone string
- * @param value output parameter to recieve the the acutal time zone string
- * @param status output parameter to recive the error information
+ * Delete just the zone strings.
*/
- void findZoneIDTypeValue(UnicodeString& zid, const UnicodeString& text, int32_t start, TimeZoneTranslationType& type, UnicodeString& value, UErrorCode& status);
-
- UnicodeString resolveParsedMetazone(const UnicodeString& zid);
+ void disposeZoneStrings(void);
};
U_NAMESPACE_END
diff --git a/i18n/unicode/rbtz.h b/i18n/unicode/rbtz.h
index 791f9851..d7f342d5 100644
--- a/i18n/unicode/rbtz.h
+++ b/i18n/unicode/rbtz.h
@@ -23,6 +23,7 @@ U_NAMESPACE_BEGIN
// forward declaration
class UVector;
+struct Transition;
class U_I18N_API RuleBasedTimeZone : public BasicTimeZone {
public:
@@ -289,13 +290,27 @@ public:
virtual void getTimeZoneRules(const InitialTimeZoneRule*& initial,
const TimeZoneRule* trsrules[], int32_t& trscount, UErrorCode& status) /*const*/;
+ /**
+ * Get time zone offsets from local wall time.
+ * @internal
+ */
+ virtual void getOffsetFromLocal(UDate date, int32_t nonExistingTimeOpt, int32_t duplicatedTimeOpt,
+ int32_t& rawOffset, int32_t& dstOffset, UErrorCode& status) /*const*/;
+
private:
void deleteRules(void);
void deleteTransitions(void);
UVector* copyRules(UVector* source);
- TimeZoneRule* findRuleInFinal(UDate date, UBool local) const;
+ TimeZoneRule* findRuleInFinal(UDate date, UBool local,
+ int32_t NonExistingTimeOpt, int32_t DuplicatedTimeOpt) const;
UBool findNext(UDate base, UBool inclusive, UDate& time, TimeZoneRule*& from, TimeZoneRule*& to) const;
UBool findPrev(UDate base, UBool inclusive, UDate& time, TimeZoneRule*& from, TimeZoneRule*& to) const;
+ int32_t getLocalDelta(int32_t rawBefore, int32_t dstBefore, int32_t rawAfter, int32_t dstAfter,
+ int32_t NonExistingTimeOpt, int32_t DuplicatedTimeOpt) const;
+ UDate getTransitionTime(Transition* transition, UBool local,
+ int32_t NonExistingTimeOpt, int32_t DuplicatedTimeOpt) const;
+ void getOffsetInternal(UDate date, UBool local, int32_t NonExistingTimeOpt, int32_t DuplicatedTimeOpt,
+ int32_t& rawOffset, int32_t& dstOffset, UErrorCode& ec) const;
InitialTimeZoneRule *fInitialRule;
UVector *fHistoricRules;
diff --git a/i18n/unicode/simpletz.h b/i18n/unicode/simpletz.h
index c5411326..08fdd208 100644
--- a/i18n/unicode/simpletz.h
+++ b/i18n/unicode/simpletz.h
@@ -617,6 +617,13 @@ public:
int32_t& dstOffset, UErrorCode& ec) const;
/**
+ * Get time zone offsets from local wall time.
+ * @internal
+ */
+ virtual void getOffsetFromLocal(UDate date, int32_t nonExistingTimeOpt, int32_t duplicatedTimeOpt,
+ int32_t& rawOffset, int32_t& dstOffset, UErrorCode& status) /*const*/;
+
+ /**
* Returns the TimeZone's raw GMT offset (i.e., the number of milliseconds to add
* to GMT to get local time, before taking daylight savings time into account).
*
diff --git a/i18n/unicode/smpdtfmt.h b/i18n/unicode/smpdtfmt.h
index b0bbc41f..2fb7c8eb 100644
--- a/i18n/unicode/smpdtfmt.h
+++ b/i18n/unicode/smpdtfmt.h
@@ -38,6 +38,7 @@ U_NAMESPACE_BEGIN
class DateFormatSymbols;
class DateFormat;
+class MessageFormat;
/**
*
@@ -650,13 +651,6 @@ private:
UErrorCode& status) const; // in case of illegal argument
/**
- * Used to resolve Time Zone aliases
- *
- * @param zid Time Zone ID to Canonicalize ( resolve aliases )
- */
- void zoneIDCanonicalize( UnicodeString & ) const;
-
- /**
* Used by subFormat() to format a numeric value.
* Appends to toAppendTo a string representation of "value"
* having a number of digits between "minDigits" and
@@ -767,6 +761,12 @@ private:
ParsePosition& pos,
UBool allowNegative) const;
+ void parseInt(const UnicodeString& text,
+ Formattable& number,
+ int32_t maxDigits,
+ ParsePosition& pos,
+ UBool allowNegative) const;
+
/**
* Translate a pattern, mapping each character in the from string to the
* corresponding character in the to string. Return an error if the original
@@ -793,25 +793,22 @@ private:
* if the operation succeeds.
*/
void parseAmbiguousDatesAsAfter(UDate startDate, UErrorCode& status);
-
- /**
- * Given text, a start in the text, and a row index, return the column index that
- * of the zone name that matches (case insensitive) at start, or 0 if none matches.
- *
- int32_t matchZoneString(const UnicodeString& text, int32_t start, int32_t zi) const;
- */
-
+
/**
- * Given text, a start in the text, and a calendar, return the next offset in the text
- * after matching the zone string. If we fail to match, return 0. Update the calendar
- * as appropriate.
+ * Private methods for formatting/parsing GMT string
*/
- int32_t subParseZoneString(const UnicodeString& text, int32_t start, Calendar& cal, UErrorCode& status) const;
-
+ void appendGMT(UnicodeString &appendTo, Calendar& cal, UErrorCode& status) const;
+ void formatGMTDefault(UnicodeString &appendTo, int32_t offset) const;
+ int32_t parseGMT(const UnicodeString &text, ParsePosition &pos) const;
+ int32_t parseGMTDefault(const UnicodeString &text, ParsePosition &pos) const;
+ UBool isDefaultGMTFormat() const;
+
+ void formatRFC822TZ(UnicodeString &appendTo, int32_t offset) const;
+
/**
- * append the gmt string
+ * Initialize MessageFormat instances used for GMT formatting/parsing
*/
- inline void appendGMT(UnicodeString &appendTo, Calendar& cal, UErrorCode& status) const;
+ void initGMTFormatters(UErrorCode &status);
/**
* Used to map pattern characters to Calendar field identifiers.
@@ -854,7 +851,18 @@ private:
*/
/*transient*/ int32_t fDefaultCenturyStartYear;
- /*transient*/ TimeZone* parsedTimeZone; // here to avoid api change
+ enum ParsedTZType {
+ TZTYPE_UNK,
+ TZTYPE_STD,
+ TZTYPE_DST
+ };
+
+ ParsedTZType tztype; // here to avoid api change
+
+ /*
+ * MessageFormat instances used for localized GMT format
+ */
+ MessageFormat **fGMTFormatters;
UBool fHaveDefaultCentury;
};
diff --git a/i18n/unicode/timezone.h b/i18n/unicode/timezone.h
index aed21e05..e771cb90 100644
--- a/i18n/unicode/timezone.h
+++ b/i18n/unicode/timezone.h
@@ -670,6 +670,15 @@ protected:
static UResourceBundle* loadRule(const UResourceBundle* top, const UnicodeString& ruleid, UResourceBundle* oldbundle, UErrorCode&status);
private:
+ friend class ZoneMeta;
+
+ /**
+ * Get a canonical Olson zone ID for the given ID. If the given ID is not valid,
+ * this method returns empty string as the result. If the given ID is a link, then the
+ * referenced ID (canonical ID) is returned.
+ */
+ static UnicodeString& getOlsonCanonicalID(const UnicodeString &id, UnicodeString &canonical);
+
static TimeZone* createCustomTimeZone(const UnicodeString&); // Creates a time zone based on the string.
/**
diff --git a/i18n/usearch.cpp b/i18n/usearch.cpp
index f974d9ac..df44aa8b 100644
--- a/i18n/usearch.cpp
+++ b/i18n/usearch.cpp
@@ -1,6 +1,6 @@
/*
**********************************************************************
-* Copyright (C) 2001-2006 IBM and others. All rights reserved.
+* Copyright (C) 2001-2007 IBM and others. All rights reserved.
**********************************************************************
* Date Name Description
* 07/02/2001 synwee Creation.
@@ -320,13 +320,19 @@ inline int16_t initializePattern(UStringSearch *strsrch, UErrorCode *status)
const UChar *patterntext = pattern->text;
int32_t length = pattern->textLength;
int32_t index = 0;
-
- pattern->hasPrefixAccents = getFCD(patterntext, &index, length) >>
- SECOND_LAST_BYTE_SHIFT_;
- index = length;
- UTF_BACK_1(patterntext, 0, index);
- pattern->hasSuffixAccents = getFCD(patterntext, &index, length) &
- LAST_BYTE_MASK_;
+
+ // Since the strength is primary, accents are ignored in the pattern.
+ if (strsrch->strength == UCOL_PRIMARY) {
+ pattern->hasPrefixAccents = 0;
+ pattern->hasSuffixAccents = 0;
+ } else {
+ pattern->hasPrefixAccents = getFCD(patterntext, &index, length) >>
+ SECOND_LAST_BYTE_SHIFT_;
+ index = length;
+ UTF_BACK_1(patterntext, 0, index);
+ pattern->hasSuffixAccents = getFCD(patterntext, &index, length) &
+ LAST_BYTE_MASK_;
+ }
// since intializePattern is an internal method status is a success.
return initializePatternCETable(strsrch, status);
}
@@ -426,6 +432,35 @@ inline void initialize(UStringSearch *strsrch, UErrorCode *status)
}
/**
+* Check to make sure that the match length is at the end of the character by
+* using the breakiterator.
+* @param strsrch string search data
+* @param start target text start offset
+* @param end target text end offset
+*/
+static
+void checkBreakBoundary(const UStringSearch *strsrch, int32_t *start,
+ int32_t *end)
+{
+#if !UCONFIG_NO_BREAK_ITERATION
+ UBreakIterator *breakiterator = strsrch->search->internalBreakIter;
+ if (breakiterator) {
+ int32_t matchend = *end;
+ int32_t matchstart = *start;
+
+ if (!ubrk_isBoundary(breakiterator, matchend))
+ *end = ubrk_following(breakiterator, matchend);
+
+ /* Check the start of the matched text to make sure it doesn't have any accents
+ * before it. This code may not be necessary and so it is commented out */
+ /*if (!ubrk_isBoundary(breakiterator, matchstart) && !ubrk_isBoundary(breakiterator, matchstart-1)) {
+ *start = ubrk_preceding(breakiterator, matchstart);
+ }*/
+ }
+#endif
+}
+
+/**
* Determine whether the target text in UStringSearch bounded by the offset
* start and end is one or more whole units of text as
* determined by the breakiterator in UStringSearch.
@@ -439,6 +474,7 @@ UBool isBreakUnit(const UStringSearch *strsrch, int32_t start,
{
#if !UCONFIG_NO_BREAK_ITERATION
UBreakIterator *breakiterator = strsrch->search->breakIter;
+ //TODO: Add here.
if (breakiterator) {
int32_t startindex = ubrk_first(breakiterator);
int32_t endindex = ubrk_last(breakiterator);
@@ -705,7 +741,7 @@ UBool checkExtraMatchAccents(const UStringSearch *strsrch, int32_t start,
uint32_t firstce = strsrch->pattern.CE[0];
UBool ignorable = TRUE;
uint32_t ce = UCOL_IGNORABLE;
- while (U_SUCCESS(*status) && ce != firstce) {
+ while (U_SUCCESS(*status) && ce != firstce && ce != UCOL_NULLORDER) {
offset = ucol_getOffset(coleiter);
if (ce != firstce && ce != UCOL_IGNORABLE) {
ignorable = FALSE;
@@ -858,11 +894,14 @@ UBool hasAccentsAfterMatch(const UStringSearch *strsrch, int32_t start,
}
count ++;
}
- int32_t ce = getCE(strsrch, ucol_next(coleiter, &status));
+ int32_t ce = ucol_next(coleiter, &status);
if (U_FAILURE(status)) {
return TRUE;
}
if (ce != UCOL_NULLORDER && ce != UCOL_IGNORABLE) {
+ ce = getCE(strsrch, ce);
+ }
+ if (ce != UCOL_NULLORDER && ce != UCOL_IGNORABLE) {
if (ucol_getOffset(coleiter) <= end) {
return TRUE;
}
@@ -1120,6 +1159,11 @@ inline UBool checkNextExactMatch(UStringSearch *strsrch,
*textoffset = getNextUStringSearchBaseOffset(strsrch, *textoffset);
return FALSE;
}
+
+ //Add breakiterator boundary check for primary strength search.
+ if (!strsrch->search->breakIter && strsrch->strength == UCOL_PRIMARY) {
+ checkBreakBoundary(strsrch, &start, textoffset);
+ }
// totally match, we will get rid of the ending ignorables.
strsrch->search->matchedIndex = start;
@@ -1963,6 +2007,12 @@ inline UBool checkPreviousExactMatch(UStringSearch *strsrch,
*textoffset);
return FALSE;
}
+
+ //Add breakiterator boundary check for primary strength search.
+ if (!strsrch->search->breakIter && strsrch->strength == UCOL_PRIMARY) {
+ checkBreakBoundary(strsrch, textoffset, &end);
+ }
+
strsrch->search->matchedIndex = *textoffset;
strsrch->search->matchedLength = end - *textoffset;
return TRUE;
@@ -2552,8 +2602,9 @@ U_CAPI UStringSearch * U_EXPORT2 usearch_openFromCollator(
result->search->breakIter = breakiter;
#if !UCONFIG_NO_BREAK_ITERATION
+ result->search->internalBreakIter = ubrk_open(UBRK_CHARACTER, ucol_getLocale(result->collator, ULOC_VALID_LOCALE, status), text, textlength, status);
if (breakiter) {
- ubrk_setText(breakiter, text, textlength, status);
+ ubrk_setText(breakiter, text, textlength, status);
}
#endif
@@ -2598,6 +2649,9 @@ U_CAPI void U_EXPORT2 usearch_close(UStringSearch *strsrch)
if (strsrch->ownCollator && strsrch->collator) {
ucol_close((UCollator *)strsrch->collator);
}
+ if (strsrch->search->internalBreakIter) {
+ ubrk_close(strsrch->search->internalBreakIter);
+ }
uprv_free(strsrch->search);
uprv_free(strsrch);
}
@@ -2736,7 +2790,7 @@ U_CAPI void U_EXPORT2 usearch_setBreakIterator(UStringSearch *strsrch,
UErrorCode *status)
{
if (U_SUCCESS(*status) && strsrch) {
- strsrch->search->breakIter = breakiter;
+ strsrch->search->breakIter = breakiter;
if (breakiter) {
ubrk_setText(breakiter, strsrch->search->text,
strsrch->search->textLength, status);
@@ -2780,6 +2834,7 @@ U_CAPI void U_EXPORT2 usearch_setText( UStringSearch *strsrch,
ubrk_setText(strsrch->search->breakIter, text,
textlength, status);
}
+ ubrk_setText(strsrch->search->internalBreakIter, text, textlength, status);
#endif
}
}
@@ -2812,6 +2867,11 @@ U_CAPI void U_EXPORT2 usearch_setCollator( UStringSearch *strsrch,
strsrch->collator = collator;
strsrch->strength = ucol_getStrength(collator);
strsrch->ceMask = getMask(strsrch->strength);
+#if !UCONFIG_NO_BREAK_ITERATION
+ ubrk_close(strsrch->search->internalBreakIter);
+ strsrch->search->internalBreakIter = ubrk_open(UBRK_CHARACTER, ucol_getLocale(collator, ULOC_VALID_LOCALE, status),
+ strsrch->search->text, strsrch->search->textLength, status);
+#endif
// if status is a failure, ucol_getAttribute returns UCOL_DEFAULT
strsrch->toShift =
ucol_getAttribute(collator, UCOL_ALTERNATE_HANDLING, status) ==
@@ -3229,9 +3289,10 @@ UBool usearch_handleNextExact(UStringSearch *strsrch, UErrorCode *status)
}
}
- targetce = lastce;
+ //targetce = lastce;
while (found && patternceindex > 0) {
+ lastce = targetce;
targetce = ucol_previous(coleiter, status);
if (U_FAILURE(*status) || targetce == UCOL_NULLORDER) {
found = FALSE;
@@ -3245,6 +3306,8 @@ UBool usearch_handleNextExact(UStringSearch *strsrch, UErrorCode *status)
patternceindex --;
found = found && targetce == patternce[patternceindex];
}
+
+ targetce = lastce;
if (!found) {
if (U_FAILURE(*status)) {
@@ -3411,7 +3474,7 @@ UBool usearch_handlePreviousExact(UStringSearch *strsrch, UErrorCode *status)
if (firstce == UCOL_NULLORDER || firstce == UCOL_IGNORABLE) {
firstce = targetce;
}
- if (targetce == UCOL_IGNORABLE) {
+ if (targetce == UCOL_IGNORABLE && strsrch->strength != UCOL_PRIMARY) {
continue;
}
if (targetce == patternce[0]) {
@@ -3425,9 +3488,10 @@ UBool usearch_handlePreviousExact(UStringSearch *strsrch, UErrorCode *status)
}
}
- targetce = firstce;
+ //targetce = firstce;
while (found && (patternceindex < patterncelength)) {
+ firstce = targetce;
targetce = ucol_next(coleiter, status);
if (U_FAILURE(*status) || targetce == UCOL_NULLORDER) {
found = FALSE;
@@ -3441,11 +3505,14 @@ UBool usearch_handlePreviousExact(UStringSearch *strsrch, UErrorCode *status)
found = found && targetce == patternce[patternceindex];
patternceindex ++;
}
+
+ targetce = firstce;
if (!found) {
if (U_FAILURE(*status)) {
break;
}
+
textoffset = reverseShift(strsrch, textoffset, targetce,
patternceindex);
patternceindex = 0;
diff --git a/i18n/usrchimp.h b/i18n/usrchimp.h
index 94968a71..fff52b21 100644
--- a/i18n/usrchimp.h
+++ b/i18n/usrchimp.h
@@ -1,6 +1,6 @@
/*
**********************************************************************
-* Copyright (C) 2001-2004 IBM and others. All rights reserved.
+* Copyright (C) 2001-2007 IBM and others. All rights reserved.
**********************************************************************
* Date Name Description
* 08/13/2001 synwee Creation.
@@ -26,6 +26,7 @@ struct USearch {
int32_t textLength; // exact length
UBool isOverlap;
UBool isCanonicalMatch;
+ UBreakIterator *internalBreakIter; //internal character breakiterator
UBreakIterator *breakIter;
// value USEARCH_DONE is the default value
// if we are not at the start of the text or the end of the text,
diff --git a/i18n/zonemeta.cpp b/i18n/zonemeta.cpp
new file mode 100644
index 00000000..0e67c687
--- /dev/null
+++ b/i18n/zonemeta.cpp
@@ -0,0 +1,873 @@
+/*
+*******************************************************************************
+* Copyright (C) 2007, International Business Machines Corporation and *
+* others. All Rights Reserved. *
+*******************************************************************************
+*/
+
+#include "unicode/utypes.h"
+
+#if !UCONFIG_NO_FORMATTING
+
+#include "zonemeta.h"
+
+#include "unicode/timezone.h"
+#include "unicode/ustring.h"
+#include "unicode/putil.h"
+
+#include "umutex.h"
+#include "uvector.h"
+#include "cmemory.h"
+#include "gregoimp.h"
+#include "cstring.h"
+#include "ucln_in.h"
+
+static UBool gZoneMetaInitialized = FALSE;
+
+// Metazone mapping tables
+static UMTX gZoneMetaLock = NULL;
+static U_NAMESPACE_QUALIFIER Hashtable *gCanonicalMap = NULL;
+static U_NAMESPACE_QUALIFIER Hashtable *gOlsonToMeta = NULL;
+static U_NAMESPACE_QUALIFIER Hashtable *gMetaToOlson = NULL;
+
+U_CDECL_BEGIN
+/**
+ * Cleanup callback func
+ */
+static UBool U_CALLCONV zoneMeta_cleanup(void)
+{
+ umtx_destroy(&gZoneMetaLock);
+
+ if (gCanonicalMap != NULL) {
+ delete gCanonicalMap;
+ gCanonicalMap = NULL;
+ }
+
+ if (gOlsonToMeta != NULL) {
+ delete gOlsonToMeta;
+ gOlsonToMeta = NULL;
+ }
+
+ if (gMetaToOlson != NULL) {
+ delete gMetaToOlson;
+ gMetaToOlson = NULL;
+ }
+
+ gZoneMetaInitialized = FALSE;
+
+ return TRUE;
+}
+
+/**
+ * Deleter for UVector
+ */
+static void U_CALLCONV
+deleteUVector(void *obj) {
+ delete (U_NAMESPACE_QUALIFIER UVector*) obj;
+}
+
+/**
+ * Deleter for CanonicalMapEntry
+ */
+static void U_CALLCONV
+deleteCanonicalMapEntry(void *obj) {
+ U_NAMESPACE_QUALIFIER CanonicalMapEntry *entry = (U_NAMESPACE_QUALIFIER CanonicalMapEntry*)obj;
+ uprv_free(entry->id);
+ uprv_free(entry);
+}
+
+/**
+ * Deleter for OlsonToMetaMappingEntry
+ */
+static void U_CALLCONV
+deleteOlsonToMetaMappingEntry(void *obj) {
+ U_NAMESPACE_QUALIFIER OlsonToMetaMappingEntry *entry = (U_NAMESPACE_QUALIFIER OlsonToMetaMappingEntry*)obj;
+ uprv_free(entry);
+}
+
+/**
+ * Deleter for MetaToOlsonMappingEntry
+ */
+static void U_CALLCONV
+deleteMetaToOlsonMappingEntry(void *obj) {
+ U_NAMESPACE_QUALIFIER MetaToOlsonMappingEntry *entry = (U_NAMESPACE_QUALIFIER MetaToOlsonMappingEntry*)obj;
+ uprv_free(entry->territory);
+ uprv_free(entry);
+}
+U_CDECL_END
+
+U_NAMESPACE_BEGIN
+
+#define ZID_KEY_MAX 128
+static const char gZoneStringsTag[] = "zoneStrings";
+static const char gUseMetazoneTag[] = "um";
+
+static const char gSupplementalData[] = "supplementalData";
+static const char gMapTimezonesTag[] = "mapTimezones";
+static const char gMetazonesTag[] = "metazones";
+static const char gZoneFormattingTag[] = "zoneFormatting";
+static const char gTerritoryTag[] = "territory";
+static const char gAliasesTag[] = "aliases";
+static const char gMultizoneTag[] = "multizone";
+
+static const char gMetazoneInfo[] = "metazoneInfo";
+static const char gMetazoneMappings[] = "metazoneMappings";
+
+#define MZID_PREFIX_LEN 5
+static const char gMetazoneIdPrefix[] = "meta:";
+
+static const UChar gWorld[] = {0x30, 0x30, 0x31, 0x00}; // "001"
+
+#define ASCII_DIGIT(c) (((c)>=0x30 && (c)<=0x39) ? (c)-0x30 : -1)
+
+/*
+ * Convert a date string used by metazone mappings to UDate.
+ * The format used by CLDR metazone mapping is "yyyy-MM-dd HH:mm".
+ */
+ static UDate parseDate (const UChar *text, UErrorCode &status) {
+ if (U_FAILURE(status)) {
+ return 0;
+ }
+ int32_t len = u_strlen(text);
+ if (len != 16 && len != 10) {
+ // It must be yyyy-MM-dd HH:mm (length 16) or yyyy-MM-dd (length 10)
+ status = U_INVALID_FORMAT_ERROR;
+ return 0;
+ }
+
+ int32_t year = 0, month = 0, day = 0, hour = 0, min = 0, n;
+ int32_t idx;
+
+ // "yyyy" (0 - 3)
+ for (idx = 0; idx <= 3 && U_SUCCESS(status); idx++) {
+ n = ASCII_DIGIT(text[idx]);
+ if (n >= 0) {
+ year = 10*year + n;
+ } else {
+ status = U_INVALID_FORMAT_ERROR;
+ }
+ }
+ // "MM" (5 - 6)
+ for (idx = 5; idx <= 6 && U_SUCCESS(status); idx++) {
+ n = ASCII_DIGIT(text[idx]);
+ if (n >= 0) {
+ month = 10*month + n;
+ } else {
+ status = U_INVALID_FORMAT_ERROR;
+ }
+ }
+ // "dd" (8 - 9)
+ for (idx = 8; idx <= 9 && U_SUCCESS(status); idx++) {
+ n = ASCII_DIGIT(text[idx]);
+ if (n >= 0) {
+ day = 10*day + n;
+ } else {
+ status = U_INVALID_FORMAT_ERROR;
+ }
+ }
+ if (len == 16) {
+ // "HH" (11 - 12)
+ for (idx = 11; idx <= 12 && U_SUCCESS(status); idx++) {
+ n = ASCII_DIGIT(text[idx]);
+ if (n >= 0) {
+ hour = 10*hour + n;
+ } else {
+ status = U_INVALID_FORMAT_ERROR;
+ }
+ }
+ // "mm" (14 - 15)
+ for (idx = 14; idx <= 15 && U_SUCCESS(status); idx++) {
+ n = ASCII_DIGIT(text[idx]);
+ if (n >= 0) {
+ min = 10*min + n;
+ } else {
+ status = U_INVALID_FORMAT_ERROR;
+ }
+ }
+ }
+
+ if (U_SUCCESS(status)) {
+ UDate date = Grego::fieldsToDay(year, month - 1, day) * U_MILLIS_PER_DAY
+ + hour * U_MILLIS_PER_HOUR + min * U_MILLIS_PER_MINUTE;
+ return date;
+ }
+ return 0;
+}
+
+ /*
+ * Initialize global objects
+ */
+void
+ZoneMeta::initialize(void) {
+ UBool initialized;
+ UMTX_CHECK(&gZoneMetaLock, gZoneMetaInitialized, initialized);
+ if (initialized) {
+ return;
+ }
+
+ // Initialize hash tables
+ Hashtable *tmpCanonicalMap = createCanonicalMap();
+ Hashtable *tmpOlsonToMeta = createOlsonToMetaMap();
+ if (tmpOlsonToMeta == NULL) {
+ // With ICU 3.8 data
+ tmpOlsonToMeta = createOlsonToMetaMapOld();
+ }
+ Hashtable *tmpMetaToOlson = createMetaToOlsonMap();
+
+ umtx_lock(&gZoneMetaLock);
+ if (gZoneMetaInitialized) {
+ // Another thread already created mappings
+ delete tmpCanonicalMap;
+ delete tmpOlsonToMeta;
+ delete tmpMetaToOlson;
+ } else {
+ gZoneMetaInitialized = TRUE;
+ gCanonicalMap = tmpCanonicalMap;
+ gOlsonToMeta = tmpOlsonToMeta;
+ gMetaToOlson = tmpMetaToOlson;
+ ucln_i18n_registerCleanup(UCLN_I18N_ZONEMETA, zoneMeta_cleanup);
+ }
+ umtx_unlock(&gZoneMetaLock);
+}
+
+Hashtable*
+ZoneMeta::createCanonicalMap(void) {
+ UErrorCode status = U_ZERO_ERROR;
+
+ Hashtable *canonicalMap = NULL;
+ UResourceBundle *zoneFormatting = NULL;
+ UResourceBundle *tzitem = NULL;
+ UResourceBundle *aliases = NULL;
+
+ canonicalMap = new Hashtable(uhash_compareUnicodeString, NULL, status);
+ if (U_FAILURE(status)) {
+ return NULL;
+ }
+ canonicalMap->setValueDeleter(deleteCanonicalMapEntry);
+
+ zoneFormatting = ures_openDirect(NULL, gSupplementalData, &status);
+ zoneFormatting = ures_getByKey(zoneFormatting, gZoneFormattingTag, zoneFormatting, &status);
+ if (U_FAILURE(status)) {
+ goto error_cleanup;
+ }
+
+ while (ures_hasNext(zoneFormatting)) {
+ tzitem = ures_getNextResource(zoneFormatting, tzitem, &status);
+ if (U_FAILURE(status)) {
+ status = U_ZERO_ERROR;
+ continue;
+ }
+ if (ures_getType(tzitem) != URES_TABLE) {
+ continue;
+ }
+
+ int32_t territoryLen;
+ const UChar *territory = ures_getStringByKey(tzitem, gTerritoryTag, &territoryLen, &status);
+ if (U_FAILURE(status)) {
+ territory = NULL;
+ status = U_ZERO_ERROR;
+ }
+
+ int32_t tzidLen = 0;
+ char tzid[ZID_KEY_MAX];
+ const char *tzkey = ures_getKey(tzitem);
+ uprv_strcpy(tzid, tzkey);
+ // Replace ':' with '/'
+ char *p = tzid;
+ while (*p) {
+ if (*p == ':') {
+ *p = '/';
+ }
+ p++;
+ tzidLen++;
+ }
+
+ // Create canonical map entry
+ CanonicalMapEntry *entry = (CanonicalMapEntry*)uprv_malloc(sizeof(CanonicalMapEntry));
+ if (entry == NULL) {
+ status = U_MEMORY_ALLOCATION_ERROR;
+ goto error_cleanup;
+ }
+ entry->id = (UChar*)uprv_malloc((tzidLen + 1) * sizeof(UChar));
+ if (entry->id == NULL) {
+ status = U_MEMORY_ALLOCATION_ERROR;
+ uprv_free(entry);
+ goto error_cleanup;
+ }
+ u_charsToUChars(tzid, entry->id, tzidLen + 1);
+
+ if (territory == NULL || u_strcmp(territory, gWorld) == 0) {
+ entry->country = NULL;
+ } else {
+ entry->country = territory;
+ }
+
+ // Put this entry to the table
+ canonicalMap->put(UnicodeString(entry->id), entry, status);
+ if (U_FAILURE(status)) {
+ deleteCanonicalMapEntry(entry);
+ goto error_cleanup;
+ }
+
+ // Get aliases
+ aliases = ures_getByKey(tzitem, gAliasesTag, aliases, &status);
+ if (U_FAILURE(status)) {
+ // No aliases
+ status = U_ZERO_ERROR;
+ continue;
+ }
+
+ while (ures_hasNext(aliases)) {
+ const UChar* alias = ures_getNextString(aliases, NULL, NULL, &status);
+ if (U_FAILURE(status)) {
+ status = U_ZERO_ERROR;
+ continue;
+ }
+ // Create canonical map entry for this alias
+ entry = (CanonicalMapEntry*)uprv_malloc(sizeof(CanonicalMapEntry));
+ if (entry == NULL) {
+ status = U_MEMORY_ALLOCATION_ERROR;
+ goto error_cleanup;
+ }
+ entry->id = (UChar*)uprv_malloc((tzidLen + 1) * sizeof(UChar));
+ if (entry->id == NULL) {
+ status = U_MEMORY_ALLOCATION_ERROR;
+ uprv_free(entry);
+ goto error_cleanup;
+ }
+ u_charsToUChars(tzid, entry->id, tzidLen + 1);
+
+ if (territory == NULL || u_strcmp(territory, gWorld) == 0) {
+ entry->country = NULL;
+ } else {
+ entry->country = territory;
+ }
+ canonicalMap->put(UnicodeString(alias), entry, status);
+ if (U_FAILURE(status)) {
+ deleteCanonicalMapEntry(entry);
+ goto error_cleanup;
+ }
+ }
+ }
+
+normal_cleanup:
+ ures_close(aliases);
+ ures_close(tzitem);
+ ures_close(zoneFormatting);
+ return canonicalMap;
+
+error_cleanup:
+ delete canonicalMap;
+ canonicalMap = NULL;
+
+ goto normal_cleanup;
+}
+
+/*
+ * Creating Olson tzid to metazone mappings from resource (3.8.1 and beyond)
+ */
+Hashtable*
+ZoneMeta::createOlsonToMetaMap(void) {
+ UErrorCode status = U_ZERO_ERROR;
+
+ Hashtable *olsonToMeta = NULL;
+ UResourceBundle *metazoneMappings = NULL;
+ UResourceBundle *zoneItem = NULL;
+ UResourceBundle *mz = NULL;
+ StringEnumeration *tzids = NULL;
+
+ olsonToMeta = new Hashtable(uhash_compareUnicodeString, NULL, status);
+ if (U_FAILURE(status)) {
+ return NULL;
+ }
+ olsonToMeta->setValueDeleter(deleteUVector);
+
+ // Read metazone mappings from metazoneInfo bundle
+ metazoneMappings = ures_openDirect(NULL, gMetazoneInfo, &status);
+ metazoneMappings = ures_getByKey(metazoneMappings, gMetazoneMappings, metazoneMappings, &status);
+ if (U_FAILURE(status)) {
+ goto error_cleanup;
+ }
+
+ // Walk through all canonical tzids
+ char zidkey[ZID_KEY_MAX];
+
+ tzids = TimeZone::createEnumeration();
+ const UnicodeString *tzid;
+ while ((tzid = tzids->snext(status))) {
+ if (U_FAILURE(status)) {
+ goto error_cleanup;
+ }
+ // We may skip aliases, because the bundle
+ // contains only canonical IDs. For now, try
+ // all of them.
+ tzid->extract(0, tzid->length(), zidkey, sizeof(zidkey), US_INV);
+ zidkey[sizeof(zidkey)-1] = 0; // NULL terminate just in case.
+
+ // Replace '/' with ':'
+ UBool foundSep = FALSE;
+ char *p = zidkey;
+ while (*p) {
+ if (*p == '/') {
+ *p = ':';
+ foundSep = TRUE;
+ }
+ p++;
+ }
+ if (!foundSep) {
+ // A valid time zone key has at least one separator
+ continue;
+ }
+
+ zoneItem = ures_getByKey(metazoneMappings, zidkey, zoneItem, &status);
+ if (U_FAILURE(status)) {
+ status = U_ZERO_ERROR;
+ continue;
+ }
+
+ UVector *mzMappings = NULL;
+ while (ures_hasNext(zoneItem)) {
+ mz = ures_getNextResource(zoneItem, mz, &status);
+ const UChar *mz_name = ures_getStringByIndex(mz, 0, NULL, &status);
+ const UChar *mz_from = ures_getStringByIndex(mz, 1, NULL, &status);
+ const UChar *mz_to = ures_getStringByIndex(mz, 2, NULL, &status);
+
+ if(U_FAILURE(status)){
+ status = U_ZERO_ERROR;
+ continue;
+ }
+ // We do not want to use SimpleDateformat to parse boundary dates,
+ // because this code could be triggered by the initialization code
+ // used by SimpleDateFormat.
+ UDate from = parseDate(mz_from, status);
+ UDate to = parseDate(mz_to, status);
+ if (U_FAILURE(status)) {
+ status = U_ZERO_ERROR;
+ continue;
+ }
+
+ OlsonToMetaMappingEntry *entry = (OlsonToMetaMappingEntry*)uprv_malloc(sizeof(OlsonToMetaMappingEntry));
+ if (entry == NULL) {
+ status = U_MEMORY_ALLOCATION_ERROR;
+ break;
+ }
+ entry->mzid = mz_name;
+ entry->from = from;
+ entry->to = to;
+
+ if (mzMappings == NULL) {
+ mzMappings = new UVector(deleteOlsonToMetaMappingEntry, NULL, status);
+ if (U_FAILURE(status)) {
+ delete mzMappings;
+ deleteOlsonToMetaMappingEntry(entry);
+ uprv_free(entry);
+ break;
+ }
+ }
+
+ mzMappings->addElement(entry, status);
+ if (U_FAILURE(status)) {
+ break;
+ }
+ }
+
+ if (U_FAILURE(status)) {
+ if (mzMappings != NULL) {
+ delete mzMappings;
+ }
+ goto error_cleanup;
+ }
+ if (mzMappings != NULL) {
+ olsonToMeta->put(*tzid, mzMappings, status);
+ if (U_FAILURE(status)) {
+ delete mzMappings;
+ goto error_cleanup;
+ }
+ }
+ }
+
+normal_cleanup:
+ if (tzids != NULL) {
+ delete tzids;
+ }
+ ures_close(zoneItem);
+ ures_close(mz);
+ ures_close(metazoneMappings);
+ return olsonToMeta;
+
+error_cleanup:
+ if (olsonToMeta != NULL) {
+ delete olsonToMeta;
+ olsonToMeta = NULL;
+ }
+ goto normal_cleanup;
+}
+
+/*
+ * Creating Olson tzid to metazone mappings from ICU resource (3.8)
+ */
+Hashtable*
+ZoneMeta::createOlsonToMetaMapOld(void) {
+ UErrorCode status = U_ZERO_ERROR;
+
+ Hashtable *olsonToMeta = NULL;
+ UResourceBundle *zoneStringsArray = NULL;
+ UResourceBundle *mz = NULL;
+ UResourceBundle *zoneItem = NULL;
+ UResourceBundle *useMZ = NULL;
+ StringEnumeration *tzids = NULL;
+
+ olsonToMeta = new Hashtable(uhash_compareUnicodeString, NULL, status);
+ if (U_FAILURE(status)) {
+ return NULL;
+ }
+ olsonToMeta->setValueDeleter(deleteUVector);
+
+ // Read metazone mappings from root bundle
+ zoneStringsArray = ures_openDirect(NULL, "", &status);
+ zoneStringsArray = ures_getByKey(zoneStringsArray, gZoneStringsTag, zoneStringsArray, &status);
+ if (U_FAILURE(status)) {
+ goto error_cleanup;
+ }
+
+ // Walk through all canonical tzids
+ char zidkey[ZID_KEY_MAX];
+
+ tzids = TimeZone::createEnumeration();
+ const UnicodeString *tzid;
+ while ((tzid = tzids->snext(status))) {
+ if (U_FAILURE(status)) {
+ goto error_cleanup;
+ }
+ // We may skip aliases, because the bundle
+ // contains only canonical IDs. For now, try
+ // all of them.
+ tzid->extract(0, tzid->length(), zidkey, sizeof(zidkey), US_INV);
+ zidkey[sizeof(zidkey)-1] = 0; // NULL terminate just in case.
+
+ // Replace '/' with ':'
+ UBool foundSep = FALSE;
+ char *p = zidkey;
+ while (*p) {
+ if (*p == '/') {
+ *p = ':';
+ foundSep = TRUE;
+ }
+ p++;
+ }
+ if (!foundSep) {
+ // A valid time zone key has at least one separator
+ continue;
+ }
+
+ zoneItem = ures_getByKey(zoneStringsArray, zidkey, zoneItem, &status);
+ useMZ = ures_getByKey(zoneItem, gUseMetazoneTag, useMZ, &status);
+ if (U_FAILURE(status)) {
+ status = U_ZERO_ERROR;
+ continue;
+ }
+
+ UVector *mzMappings = NULL;
+ while (ures_hasNext(useMZ)) {
+ mz = ures_getNextResource(useMZ, mz, &status);
+ const UChar *mz_name = ures_getStringByIndex(mz, 0, NULL, &status);
+ const UChar *mz_from = ures_getStringByIndex(mz, 1, NULL, &status);
+ const UChar *mz_to = ures_getStringByIndex(mz, 2, NULL, &status);
+
+ if(U_FAILURE(status)){
+ status = U_ZERO_ERROR;
+ continue;
+ }
+ // We do not want to use SimpleDateformat to parse boundary dates,
+ // because this code could be triggered by the initialization code
+ // used by SimpleDateFormat.
+ UDate from = parseDate(mz_from, status);
+ UDate to = parseDate(mz_to, status);
+ if (U_FAILURE(status)) {
+ status = U_ZERO_ERROR;
+ continue;
+ }
+
+ OlsonToMetaMappingEntry *entry = (OlsonToMetaMappingEntry*)uprv_malloc(sizeof(OlsonToMetaMappingEntry));
+ if (entry == NULL) {
+ status = U_MEMORY_ALLOCATION_ERROR;
+ break;
+ }
+ entry->mzid = mz_name;
+ entry->from = from;
+ entry->to = to;
+
+ if (mzMappings == NULL) {
+ mzMappings = new UVector(deleteOlsonToMetaMappingEntry, NULL, status);
+ if (U_FAILURE(status)) {
+ delete mzMappings;
+ deleteOlsonToMetaMappingEntry(entry);
+ uprv_free(entry);
+ break;
+ }
+ }
+
+ mzMappings->addElement(entry, status);
+ if (U_FAILURE(status)) {
+ break;
+ }
+ }
+
+ if (U_FAILURE(status)) {
+ if (mzMappings != NULL) {
+ delete mzMappings;
+ }
+ goto error_cleanup;
+ }
+ if (mzMappings != NULL) {
+ olsonToMeta->put(*tzid, mzMappings, status);
+ if (U_FAILURE(status)) {
+ delete mzMappings;
+ goto error_cleanup;
+ }
+ }
+ }
+
+normal_cleanup:
+ if (tzids != NULL) {
+ delete tzids;
+ }
+ ures_close(zoneItem);
+ ures_close(useMZ);
+ ures_close(mz);
+ ures_close(zoneStringsArray);
+ return olsonToMeta;
+
+error_cleanup:
+ if (olsonToMeta != NULL) {
+ delete olsonToMeta;
+ }
+ goto normal_cleanup;
+}
+
+Hashtable*
+ZoneMeta::createMetaToOlsonMap(void) {
+ UErrorCode status = U_ZERO_ERROR;
+
+ Hashtable *metaToOlson = NULL;
+ UResourceBundle *metazones = NULL;
+ UResourceBundle *mz = NULL;
+
+ metaToOlson = new Hashtable(uhash_compareUnicodeString, NULL, status);
+ if (U_FAILURE(status)) {
+ return NULL;
+ }
+ metaToOlson->setValueDeleter(deleteUVector);
+
+ metazones = ures_openDirect(NULL, gSupplementalData, &status);
+ metazones = ures_getByKey(metazones, gMapTimezonesTag, metazones, &status);
+ metazones = ures_getByKey(metazones, gMetazonesTag, metazones, &status);
+ if (U_FAILURE(status)) {
+ goto error_cleanup;
+ }
+
+ while (ures_hasNext(metazones)) {
+ mz = ures_getNextResource(metazones, mz, &status);
+ if (U_FAILURE(status)) {
+ status = U_ZERO_ERROR;
+ continue;
+ }
+ const char *mzkey = ures_getKey(mz);
+ if (uprv_strncmp(mzkey, gMetazoneIdPrefix, MZID_PREFIX_LEN) == 0) {
+ const char *mzid = mzkey + MZID_PREFIX_LEN;
+ const char *territory = uprv_strrchr(mzid, '_');
+ int32_t mzidLen = 0;
+ int32_t territoryLen = 0;
+ if (territory) {
+ mzidLen = territory - mzid;
+ territory++;
+ territoryLen = uprv_strlen(territory);
+ }
+ if (mzidLen > 0 && territoryLen > 0) {
+ int32_t tzidLen;
+ const UChar *tzid = ures_getStringByIndex(mz, 0, &tzidLen, &status);
+ if (U_SUCCESS(status)) {
+ // Create MetaToOlsonMappingEntry
+ MetaToOlsonMappingEntry *entry = (MetaToOlsonMappingEntry*)uprv_malloc(sizeof(MetaToOlsonMappingEntry));
+ if (entry == NULL) {
+ status = U_MEMORY_ALLOCATION_ERROR;
+ goto error_cleanup;
+ }
+ entry->id = tzid;
+ entry->territory = (UChar*)uprv_malloc((territoryLen + 1) * sizeof(UChar));
+ if (entry->territory == NULL) {
+ status = U_MEMORY_ALLOCATION_ERROR;
+ uprv_free(entry);
+ goto error_cleanup;
+ }
+ u_charsToUChars(territory, entry->territory, territoryLen + 1);
+
+ // Check if mapping entries for metazone is already available
+ UnicodeString mzidStr(mzid, mzidLen, US_INV);
+ UVector *tzMappings = (UVector*)metaToOlson->get(mzidStr);
+ if (tzMappings == NULL) {
+ // Create new UVector and put it into the hashtable
+ tzMappings = new UVector(deleteMetaToOlsonMappingEntry, NULL, status);
+ metaToOlson->put(mzidStr, tzMappings, status);
+ if (U_FAILURE(status)) {
+ if (tzMappings != NULL) {
+ delete tzMappings;
+ }
+ deleteMetaToOlsonMappingEntry(entry);
+ goto error_cleanup;
+ }
+ }
+ tzMappings->addElement(entry, status);
+ if (U_FAILURE(status)) {
+ goto error_cleanup;
+ }
+ } else {
+ status = U_ZERO_ERROR;
+ }
+ }
+ }
+ }
+
+normal_cleanup:
+ ures_close(mz);
+ ures_close(metazones);
+ return metaToOlson;
+
+error_cleanup:
+ if (metaToOlson != NULL) {
+ delete metaToOlson;
+ }
+ goto normal_cleanup;
+}
+
+UnicodeString&
+ZoneMeta::getCanonicalID(const UnicodeString &tzid, UnicodeString &canonicalID) {
+ const CanonicalMapEntry *entry = getCanonicalInfo(tzid);
+ if (entry != NULL) {
+ canonicalID.setTo(entry->id);
+ } else {
+ // Use the input tzid
+ canonicalID.setTo(tzid);
+ }
+ return canonicalID;
+}
+
+UnicodeString&
+ZoneMeta::getCanonicalCountry(const UnicodeString &tzid, UnicodeString &canonicalCountry) {
+ const CanonicalMapEntry *entry = getCanonicalInfo(tzid);
+ if (entry != NULL && entry->country != NULL) {
+ canonicalCountry.setTo(entry->country);
+ } else {
+ // Use the input tzid
+ canonicalCountry.remove();
+ }
+ return canonicalCountry;
+}
+
+const CanonicalMapEntry*
+ZoneMeta::getCanonicalInfo(const UnicodeString &tzid) {
+ initialize();
+ CanonicalMapEntry *entry = NULL;
+ UnicodeString canonicalOlsonId;
+ TimeZone::getOlsonCanonicalID(tzid, canonicalOlsonId);
+ if (!canonicalOlsonId.isEmpty()) {
+ if (gCanonicalMap != NULL) {
+ entry = (CanonicalMapEntry*)gCanonicalMap->get(tzid);
+ }
+ }
+ return entry;
+}
+
+UnicodeString&
+ZoneMeta::getSingleCountry(const UnicodeString &tzid, UnicodeString &country) {
+ UErrorCode status = U_ZERO_ERROR;
+
+ // Get canonical country for the zone
+ getCanonicalCountry(tzid, country);
+
+ if (!country.isEmpty()) {
+ UResourceBundle *supplementalDataBundle = ures_openDirect(NULL, gSupplementalData, &status);
+ UResourceBundle *zoneFormatting = ures_getByKey(supplementalDataBundle, gZoneFormattingTag, NULL, &status);
+ UResourceBundle *multizone = ures_getByKey(zoneFormatting, gMultizoneTag, NULL, &status);
+
+ if (U_SUCCESS(status)) {
+ while (ures_hasNext(multizone)) {
+ int32_t len;
+ const UChar* multizoneCountry = ures_getNextString(multizone, &len, NULL, &status);
+ if (country.compare(multizoneCountry, len) == 0) {
+ // Included in the multizone country list
+ country.remove();
+ break;
+ }
+ }
+ }
+
+ ures_close(multizone);
+ ures_close(zoneFormatting);
+ ures_close(supplementalDataBundle);
+ }
+
+ return country;
+}
+
+UnicodeString&
+ZoneMeta::getMetazoneID(const UnicodeString &tzid, UDate date, UnicodeString &result) {
+ UBool isSet = FALSE;
+ const UVector *mappings = getMetazoneMappings(tzid);
+ if (mappings != NULL) {
+ for (int32_t i = 0; i < mappings->size(); i++) {
+ OlsonToMetaMappingEntry *mzm = (OlsonToMetaMappingEntry*)mappings->elementAt(i);
+ if (mzm->from <= date && mzm->to > date) {
+ result.setTo(mzm->mzid, -1);
+ isSet = TRUE;
+ break;
+ }
+ }
+ }
+ if (!isSet) {
+ result.remove();
+ }
+ return result;
+}
+
+const UVector*
+ZoneMeta::getMetazoneMappings(const UnicodeString &tzid) {
+ initialize();
+ const UVector *result = NULL;
+ if (gOlsonToMeta != NULL) {
+ result = (UVector*)gOlsonToMeta->get(tzid);
+ }
+ return result;
+}
+
+UnicodeString&
+ZoneMeta::getZoneIdByMetazone(const UnicodeString &mzid, const UnicodeString &region, UnicodeString &result) {
+ initialize();
+ UBool isSet = FALSE;
+ if (gMetaToOlson != NULL) {
+ UVector *mappings = (UVector*)gMetaToOlson->get(mzid);
+ if (mappings != NULL) {
+ // Find a preferred time zone for the given region.
+ for (int32_t i = 0; i < mappings->size(); i++) {
+ MetaToOlsonMappingEntry *olsonmap = (MetaToOlsonMappingEntry*)mappings->elementAt(i);
+ if (region.compare(olsonmap->territory, -1) == 0) {
+ result.setTo(olsonmap->id);
+ isSet = TRUE;
+ break;
+ } else if (u_strcmp(olsonmap->territory, gWorld) == 0) {
+ result.setTo(olsonmap->id);
+ isSet = TRUE;
+ }
+ }
+ }
+ }
+ if (!isSet) {
+ result.remove();
+ }
+ return result;
+}
+
+
+U_NAMESPACE_END
+
+#endif /* #if !UCONFIG_NO_FORMATTING */
diff --git a/i18n/zonemeta.h b/i18n/zonemeta.h
new file mode 100644
index 00000000..d7cb1a48
--- /dev/null
+++ b/i18n/zonemeta.h
@@ -0,0 +1,84 @@
+/*
+*******************************************************************************
+* Copyright (C) 2007, International Business Machines Corporation and *
+* others. All Rights Reserved. *
+*******************************************************************************
+*/
+#ifndef ZONEMETA_H
+#define ZONEMETA_H
+
+#include "unicode/utypes.h"
+
+#if !UCONFIG_NO_FORMATTING
+
+#include "unicode/unistr.h"
+#include "hash.h"
+
+U_NAMESPACE_BEGIN
+
+typedef struct CanonicalMapEntry {
+ UChar *id;
+ const UChar *country; // const because it's a reference to a resource bundle string.
+} CanonicalMapEntry;
+
+typedef struct OlsonToMetaMappingEntry {
+ const UChar *mzid; // const because it's a reference to a resource bundle string.
+ UDate from;
+ UDate to;
+} OlsonToMetaMappingEntry;
+
+typedef struct MetaToOlsonMappingEntry {
+ const UChar *id; // const because it's a reference to a resource bundle string.
+ UChar *territory;
+} MetaToOlsonMappingEntry;
+
+class UVector;
+
+class U_I18N_API ZoneMeta {
+public:
+ /**
+ * Return the canonical id for this tzid, which might be the id itself.
+ * If there is no canonical id for it, return the passed-in id.
+ */
+ static UnicodeString& getCanonicalID(const UnicodeString &tzid, UnicodeString &canonicalID);
+
+ /**
+ * Return the canonical country code for this tzid. If we have none, or if the time zone
+ * is not associated with a country, return null.
+ */
+ static UnicodeString& getCanonicalCountry(const UnicodeString &tzid, UnicodeString &canonicalCountry);
+
+ /**
+ * Return the country code if this is a 'single' time zone that can fallback to just
+ * the country, otherwise return empty string. (Note, one must also check the locale data
+ * to see that there is a localization for the country in order to implement
+ * tr#35 appendix J step 5.)
+ */
+ static UnicodeString& getSingleCountry(const UnicodeString &tzid, UnicodeString &country);
+
+ /**
+ * Returns a CLDR metazone ID for the given Olson tzid and time.
+ */
+ static UnicodeString& getMetazoneID(const UnicodeString &tzid, UDate date, UnicodeString &result);
+ /**
+ * Returns an Olson ID for the ginve metazone and region
+ */
+ static UnicodeString& getZoneIdByMetazone(const UnicodeString &mzid, const UnicodeString &region, UnicodeString &result);
+
+ static const UVector* getMetazoneMappings(const UnicodeString &tzid);
+
+private:
+ static void initialize(void);
+
+ static const CanonicalMapEntry* getCanonicalInfo(const UnicodeString &tzid);
+
+ static Hashtable* createCanonicalMap(void);
+ static Hashtable* createOlsonToMetaMapOld(void);
+ static Hashtable* createOlsonToMetaMap(void);
+ static Hashtable* createMetaToOlsonMap(void);
+};
+
+U_NAMESPACE_END
+
+#endif /* #if !UCONFIG_NO_FORMATTING */
+#endif // ZONEMETA_H
diff --git a/i18n/zstrfmt.cpp b/i18n/zstrfmt.cpp
new file mode 100644
index 00000000..b42bbfb2
--- /dev/null
+++ b/i18n/zstrfmt.cpp
@@ -0,0 +1,1604 @@
+/*
+*******************************************************************************
+* Copyright (C) 2007, International Business Machines Corporation and *
+* others. All Rights Reserved. *
+*******************************************************************************
+*/
+
+#include "unicode/utypes.h"
+
+#if !UCONFIG_NO_FORMATTING
+
+#include "zstrfmt.h"
+
+#include "unicode/ustring.h"
+#include "unicode/putil.h"
+#include "unicode/msgfmt.h"
+#include "unicode/basictz.h"
+#include "unicode/simpletz.h"
+#include "unicode/rbtz.h"
+#include "unicode/vtzone.h"
+
+#include "uvector.h"
+#include "cstring.h"
+#include "cmemory.h"
+#include "uresimp.h"
+#include "zonemeta.h"
+#include "olsontz.h"
+#include "umutex.h"
+#include "ucln_in.h"
+
+/**
+ * global ZoneStringFormatCache stuffs
+ */
+static UMTX gZSFCacheLock = NULL;
+static U_NAMESPACE_QUALIFIER ZSFCache *gZoneStringFormatCache = NULL;
+
+U_CDECL_BEGIN
+/**
+ * ZoneStringFormatCache cleanup callback func
+ */
+static UBool U_CALLCONV zoneStringFormat_cleanup(void)
+{
+ umtx_destroy(&gZSFCacheLock);
+ if (gZoneStringFormatCache != NULL) {
+ delete gZoneStringFormatCache;
+ gZoneStringFormatCache = NULL;
+ }
+ gZoneStringFormatCache = NULL;
+ return TRUE;
+}
+
+/**
+ * Deleter for ZoneStringInfo
+ */
+static void U_CALLCONV
+deleteZoneStringInfo(void *obj) {
+ delete (U_NAMESPACE_QUALIFIER ZoneStringInfo*)obj;
+}
+
+/**
+ * Deleter for ZoneStrings
+ */
+static void U_CALLCONV
+deleteZoneStrings(void *obj) {
+ delete (U_NAMESPACE_QUALIFIER ZoneStrings*)obj;
+}
+U_CDECL_END
+
+U_NAMESPACE_BEGIN
+
+#define ZID_KEY_MAX 128
+
+static const char gCountriesTag[] = "Countries";
+static const char gZoneStringsTag[] = "zoneStrings";
+static const char gShortGenericTag[] = "sg";
+static const char gShortStandardTag[] = "ss";
+static const char gShortDaylightTag[] = "sd";
+static const char gLongGenericTag[] = "lg";
+static const char gLongStandardTag[] = "ls";
+static const char gLongDaylightTag[] = "ld";
+static const char gExemplarCityTag[] = "ec";
+static const char gCommonlyUsedTag[] = "cu";
+static const char gFallbackFormatTag[] = "fallbackFormat";
+static const char gRegionFormatTag[] = "regionFormat";
+
+#define MZID_PREFIX_LEN 5
+static const char gMetazoneIdPrefix[] = "meta:";
+
+#define MAX_METAZONES_PER_ZONE 10
+
+static const UChar gDefFallbackPattern[] = {0x7B, 0x31, 0x7D, 0x20, 0x28, 0x7B, 0x30, 0x7D, 0x29, 0x00}; // "{1} ({0})"
+static const UChar gDefRegionPattern[] = {0x7B, 0x30, 0x7D, 0x00}; // "{0}"
+static const UChar gCommonlyUsedTrue[] = {0x31, 0x00}; // "1"
+
+static const double kDstCheckRange = (double)184*U_MILLIS_PER_DAY;
+
+static int32_t
+getTimeZoneTranslationTypeIndex(TimeZoneTranslationType type) {
+ int32_t typeIdx = 0;
+ switch (type) {
+ case LOCATION:
+ typeIdx = ZSIDX_LOCATION;
+ break;
+ case GENERIC_LONG:
+ typeIdx = ZSIDX_LONG_GENERIC;
+ break;
+ case GENERIC_SHORT:
+ typeIdx = ZSIDX_SHORT_GENERIC;
+ break;
+ case STANDARD_LONG:
+ typeIdx = ZSIDX_LONG_STANDARD;
+ break;
+ case STANDARD_SHORT:
+ typeIdx = ZSIDX_SHORT_STANDARD;
+ break;
+ case DAYLIGHT_LONG:
+ typeIdx = ZSIDX_LONG_DAYLIGHT;
+ break;
+ case DAYLIGHT_SHORT:
+ typeIdx = ZSIDX_SHORT_DAYLIGHT;
+ break;
+ }
+ return typeIdx;
+}
+
+static int32_t
+getTimeZoneTranslationType(TimeZoneTranslationTypeIndex typeIdx) {
+ int32_t type = 0;
+ switch (typeIdx) {
+ case ZSIDX_LOCATION:
+ type = LOCATION;
+ break;
+ case ZSIDX_LONG_GENERIC:
+ type = GENERIC_LONG;
+ break;
+ case ZSIDX_SHORT_GENERIC:
+ type = GENERIC_SHORT;
+ break;
+ case ZSIDX_LONG_STANDARD:
+ type = STANDARD_LONG;
+ break;
+ case ZSIDX_SHORT_STANDARD:
+ type = STANDARD_SHORT;
+ break;
+ case ZSIDX_LONG_DAYLIGHT:
+ type = DAYLIGHT_LONG;
+ break;
+ case ZSIDX_SHORT_DAYLIGHT:
+ type = DAYLIGHT_SHORT;
+ break;
+ }
+ return type;
+}
+
+#define DEFAULT_CHARACTERNODE_CAPACITY 1
+
+// ----------------------------------------------------------------------------
+CharacterNode::CharacterNode(UChar32 c, UObjectDeleter *valueDeleterFunc, UErrorCode &status)
+: UMemory(),
+ fChildren(valueDeleterFunc, NULL, DEFAULT_CHARACTERNODE_CAPACITY, status),
+ fValues(valueDeleterFunc, NULL, DEFAULT_CHARACTERNODE_CAPACITY, status),
+ fValueDeleter(valueDeleterFunc),
+ fCharacter(c)
+{
+}
+
+CharacterNode::~CharacterNode() {
+ while (!fChildren.isEmpty()) {
+ CharacterNode *node = (CharacterNode*)fChildren.orphanElementAt(0);
+ delete node;
+ }
+}
+
+void
+CharacterNode::addValue(void *value, UErrorCode &status) {
+ if (U_FAILURE(status)) {
+ return;
+ }
+ fValues.addElement(value, status);
+}
+
+CharacterNode*
+CharacterNode::addChildNode(UChar32 c, UErrorCode &status) {
+ if (U_FAILURE(status)) {
+ return NULL;
+ }
+ CharacterNode *result = NULL;
+ for (int32_t i = 0; i < fChildren.size(); i++) {
+ CharacterNode *node = (CharacterNode*)fChildren.elementAt(i);
+ if (node->getCharacter() == c) {
+ result = node;
+ break;
+ }
+ }
+ if (result == NULL) {
+ result = new CharacterNode(c, fValueDeleter, status);
+ fChildren.addElement(result, status);
+ }
+
+ return result;
+}
+
+CharacterNode*
+CharacterNode::getChildNode(UChar32 c) const {
+ CharacterNode *result = NULL;
+ for (int32_t i = 0; i < fChildren.size(); i++) {
+ CharacterNode *node = (CharacterNode*)fChildren.elementAt(i);
+ if (node->getCharacter() == c) {
+ result = node;
+ break;
+ }
+ }
+ return result;
+}
+
+// ----------------------------------------------------------------------------
+TextTrieMap::TextTrieMap(UBool ignoreCase, UObjectDeleter *valueDeleterFunc)
+: UMemory(), fIgnoreCase(ignoreCase), fValueDeleter(valueDeleterFunc), fRoot(NULL) {
+}
+
+TextTrieMap::~TextTrieMap() {
+ if (fRoot != NULL) {
+ delete fRoot;
+ }
+}
+
+void
+TextTrieMap::put(const UnicodeString &key, void *value, UErrorCode &status) {
+ if (fRoot == NULL) {
+ fRoot = new CharacterNode(0, fValueDeleter, status);
+ }
+
+ UnicodeString keyString(key);
+ if (fIgnoreCase) {
+ keyString.foldCase();
+ }
+
+ CharacterNode *node = fRoot;
+ int32_t index = 0;
+ while (index < keyString.length()) {
+ UChar32 c = keyString.char32At(index);
+ node = node->addChildNode(c, status);
+ if (U_FAILURE(status)) {
+ return;
+ }
+ index = keyString.moveIndex32(index, 1);
+ }
+ node->addValue(value, status);
+}
+
+void
+TextTrieMap::search(const UnicodeString &text, int32_t start,
+ TextTrieMapSearchResultHandler *handler, UErrorCode &status) const {
+ if (fRoot == NULL) {
+ return;
+ }
+ search(fRoot, text, start, start, handler, status);
+}
+
+void
+TextTrieMap::search(CharacterNode *node, const UnicodeString &text, int32_t start,
+ int32_t index, TextTrieMapSearchResultHandler *handler, UErrorCode &status) const {
+ if (U_FAILURE(status)) {
+ return;
+ }
+ const UVector *values = node->getValues();
+ if (values != NULL) {
+ if (!handler->handleMatch(index - start, values, status)) {
+ return;
+ }
+ if (U_FAILURE(status)) {
+ return;
+ }
+ }
+ UChar32 c = text.char32At(index);
+ if (fIgnoreCase) {
+ // size of character may grow after fold operation
+ UnicodeString tmp(c);
+ tmp.foldCase();
+ int32_t tmpidx = 0;
+ while (tmpidx < tmp.length()) {
+ c = tmp.char32At(tmpidx);
+ node = node->getChildNode(c);
+ if (node == NULL) {
+ break;
+ }
+ tmpidx = tmp.moveIndex32(tmpidx, 1);
+ }
+ } else {
+ node = node->getChildNode(c);
+ }
+ if (node != NULL) {
+ search(node, text, start, index+1, handler, status);
+ }
+}
+
+// ----------------------------------------------------------------------------
+ZoneStringInfo::ZoneStringInfo(const UnicodeString &id, const UnicodeString &str,
+ TimeZoneTranslationType type)
+: UMemory(), fId(id), fStr(str), fType(type) {
+}
+
+ZoneStringInfo::~ZoneStringInfo() {
+}
+// ----------------------------------------------------------------------------
+ZoneStringSearchResultHandler::ZoneStringSearchResultHandler(UErrorCode &status)
+: UMemory(), fResults(status)
+{
+ clear();
+}
+
+ZoneStringSearchResultHandler::~ZoneStringSearchResultHandler() {
+ clear();
+}
+
+UBool
+ZoneStringSearchResultHandler::handleMatch(int32_t matchLength, const UVector *values, UErrorCode &status) {
+ if (U_FAILURE(status)) {
+ return FALSE;
+ }
+ if (values != NULL) {
+ for (int32_t i = 0; values->size(); i++) {
+ ZoneStringInfo *zsinfo = (ZoneStringInfo*)values->elementAt(i);
+ if (zsinfo == NULL) {
+ break;
+ }
+ // Update the results
+ UBool foundType = FALSE;
+ for (int32_t j = 0; j < fResults.size(); j++) {
+ ZoneStringInfo *tmp = (ZoneStringInfo*)fResults.elementAt(j);
+ if (zsinfo->fType == tmp->fType) {
+ int32_t lenidx = getTimeZoneTranslationTypeIndex(tmp->fType);
+ if (matchLength > fMatchLen[lenidx]) {
+ // Same type, longer match
+ fResults.setElementAt(zsinfo, j);
+ fMatchLen[lenidx] = matchLength;
+ }
+ foundType = TRUE;
+ break;
+ }
+ }
+ if (!foundType) {
+ // not found in the current list
+ fResults.addElement(zsinfo, status);
+ fMatchLen[getTimeZoneTranslationTypeIndex(zsinfo->fType)] = matchLength;
+ }
+ }
+ }
+ return TRUE;
+}
+
+int32_t
+ZoneStringSearchResultHandler::countMatches(void) {
+ return fResults.size();
+}
+
+const ZoneStringInfo*
+ZoneStringSearchResultHandler::getMatch(int32_t index, int32_t &matchLength) {
+ ZoneStringInfo *zsinfo = NULL;
+ if (index < fResults.size()) {
+ zsinfo = (ZoneStringInfo*)fResults.elementAt(index);
+ matchLength = fMatchLen[getTimeZoneTranslationTypeIndex(zsinfo->fType)];
+ }
+ return zsinfo;
+}
+
+void
+ZoneStringSearchResultHandler::clear(void) {
+ fResults.removeAllElements();
+ for (int32_t i = 0; i < (int32_t)(sizeof(fMatchLen)/sizeof(fMatchLen[0])); i++) {
+ fMatchLen[i] = 0;
+ }
+}
+// ----------------------------------------------------------------------------
+ZoneStringFormat::ZoneStringFormat(const UnicodeString* const* strings,
+ int32_t rowCount, int32_t columnCount, UErrorCode &status)
+: UMemory(),
+ fLocale(""),
+ fTzidToStrings(uhash_compareUnicodeString, NULL, status),
+ fMzidToStrings(uhash_compareUnicodeString, NULL, status),
+ fZoneStringsTrie(TRUE, deleteZoneStringInfo)
+{
+ if (U_FAILURE(status)) {
+ return;
+ }
+ fLocale.setToBogus();
+ if (strings == NULL || columnCount <= 0 || rowCount <= 0) {
+ status = U_ILLEGAL_ARGUMENT_ERROR;
+ return;
+ }
+
+ fTzidToStrings.setValueDeleter(deleteZoneStrings);
+
+ for (int32_t row = 0; row < rowCount; row++) {
+ if (strings[row][0].isEmpty()) {
+ continue;
+ }
+ UnicodeString *names = new UnicodeString[ZSIDX_COUNT];
+ for (int32_t col = 1; col < columnCount; col++) {
+ if (!strings[row][col].isEmpty()) {
+ int32_t typeIdx = -1;
+ switch (col) {
+ case 1:
+ typeIdx = ZSIDX_LONG_STANDARD;
+ break;
+ case 2:
+ typeIdx = ZSIDX_SHORT_STANDARD;
+ break;
+ case 3:
+ typeIdx = ZSIDX_LONG_DAYLIGHT;
+ break;
+ case 4:
+ typeIdx = ZSIDX_SHORT_DAYLIGHT;
+ break;
+ case 5:
+ typeIdx = ZSIDX_LOCATION;
+ break;
+ case 6:
+ typeIdx = ZSIDX_LONG_GENERIC;
+ break;
+ case 7:
+ typeIdx = ZSIDX_SHORT_GENERIC;
+ break;
+ }
+ if (typeIdx != -1) {
+ names[typeIdx].setTo(strings[row][col]);
+
+ // Put the name into the trie
+ int32_t type = getTimeZoneTranslationType((TimeZoneTranslationTypeIndex)typeIdx);
+ ZoneStringInfo *zsinf = new ZoneStringInfo(strings[row][0], strings[row][col], (TimeZoneTranslationType)type);
+ fZoneStringsTrie.put(strings[row][col], zsinf, status);
+ if (U_FAILURE(status)) {
+ delete zsinf;
+ goto error_cleanup;
+ }
+ }
+ }
+ }
+ ZoneStrings *zstrings = new ZoneStrings(names, ZSIDX_COUNT, TRUE, NULL, 0, 0);
+ fTzidToStrings.put(strings[row][0], zstrings, status);
+ if (U_FAILURE(status)) {
+ delete zstrings;
+ goto error_cleanup;
+ }
+ }
+ return;
+
+error_cleanup:
+ return;
+}
+
+ZoneStringFormat::ZoneStringFormat(const Locale &locale, UErrorCode &status)
+: UMemory(),
+ fLocale(locale),
+ fTzidToStrings(uhash_compareUnicodeString, NULL, status),
+ fMzidToStrings(uhash_compareUnicodeString, NULL, status),
+ fZoneStringsTrie(TRUE, deleteZoneStringInfo)
+{
+ if (U_FAILURE(status)) {
+ return;
+ }
+ fTzidToStrings.setValueDeleter(deleteZoneStrings);
+ fMzidToStrings.setValueDeleter(deleteZoneStrings);
+
+ UResourceBundle *zoneStringsArray = ures_open(NULL, locale.getName(), &status);
+ zoneStringsArray = ures_getByKeyWithFallback(zoneStringsArray, gZoneStringsTag, zoneStringsArray, &status);
+ if (U_FAILURE(status)) {
+ // If no locale bundles are available, zoneStrings will be null.
+ // We still want to go through the rest of zone strings initialization,
+ // because generic location format is generated from tzid for the case.
+ // The rest of code should work even zoneStrings is null.
+ status = U_ZERO_ERROR;
+ ures_close(zoneStringsArray);
+ zoneStringsArray = NULL;
+ }
+
+ StringEnumeration *tzids = NULL;
+ MessageFormat *fallbackFmt = NULL;
+ MessageFormat *regionFmt = NULL;
+
+ UResourceBundle *zoneItem = NULL;
+ UResourceBundle *metazoneItem = NULL;
+
+ char zidkey[ZID_KEY_MAX];
+ const UChar *zstrarray[ZSIDX_COUNT];
+ const UChar *mzstrarray[ZSIDX_COUNT];
+ UnicodeString mzPartialLoc[MAX_METAZONES_PER_ZONE][4];
+
+ UnicodeString region;
+ getRegion(region);
+
+ fallbackFmt = getFallbackFormat(locale, status);
+ if (U_FAILURE(status)) {
+ goto error_cleanup;
+ }
+ regionFmt = getRegionFormat(locale, status);
+ if (U_FAILURE(status)) {
+ goto error_cleanup;
+ }
+
+ tzids = TimeZone::createEnumeration();
+ const char *tzid;
+ while ((tzid = tzids->next(NULL, status))) {
+ if (U_FAILURE(status)) {
+ goto error_cleanup;
+ }
+ // Skip non-canonical IDs
+ UnicodeString utzid(tzid, -1, US_INV);
+ UnicodeString canonicalID;
+ ZoneMeta::getCanonicalID(utzid, canonicalID);
+ if (utzid != canonicalID) {
+ continue;
+ }
+
+ uprv_strcpy(zidkey, tzid);
+
+ // Replace '/' with ':'
+ char *pCity = NULL;
+ char *p = zidkey;
+ while (*p) {
+ if (*p == '/') {
+ *p = ':';
+ pCity = p + 1;
+ }
+ p++;
+ }
+
+ if (zoneStringsArray != NULL) {
+ zoneItem = ures_getByKeyWithFallback(zoneStringsArray, zidkey, zoneItem, &status);
+ if (U_FAILURE(status)) {
+ // If failed to open the zone item, create only location string
+ ures_close(zoneItem);
+ zoneItem = NULL;
+ status = U_ZERO_ERROR;
+ }
+ }
+ zstrarray[ZSIDX_LONG_STANDARD] = getZoneStringFromBundle(zoneItem, gLongStandardTag);
+ zstrarray[ZSIDX_SHORT_STANDARD] = getZoneStringFromBundle(zoneItem, gShortStandardTag);
+ zstrarray[ZSIDX_LONG_DAYLIGHT] = getZoneStringFromBundle(zoneItem, gLongDaylightTag);
+ zstrarray[ZSIDX_SHORT_DAYLIGHT] = getZoneStringFromBundle(zoneItem, gShortDaylightTag);
+ zstrarray[ZSIDX_LONG_GENERIC] = getZoneStringFromBundle(zoneItem, gLongGenericTag);
+ zstrarray[ZSIDX_SHORT_GENERIC] = getZoneStringFromBundle(zoneItem, gShortGenericTag);
+
+ // Compose location format string
+ UnicodeString location;
+ UnicodeString country;
+ UnicodeString city;
+ UnicodeString countryCode;
+ ZoneMeta::getCanonicalCountry(utzid, countryCode);
+ if (countryCode.isEmpty()) {
+ zstrarray[ZSIDX_LOCATION] = NULL;
+ } else {
+ const UChar* tmpCity = getZoneStringFromBundle(zoneItem, gExemplarCityTag);
+ if (tmpCity != NULL) {
+ city.setTo(TRUE, tmpCity, -1);
+ } else {
+ city.setTo(UnicodeString(pCity, -1, US_INV));
+ // Replace '_' with ' '
+ for (int32_t i = 0; i < city.length(); i++) {
+ if (city.charAt(i) == (UChar)0x5F /*'_'*/) {
+ city.setCharAt(i, (UChar)0x20 /*' '*/);
+ }
+ }
+ }
+ getLocalizedCountry(countryCode, locale, country);
+ UnicodeString singleCountry;
+ ZoneMeta::getSingleCountry(utzid, singleCountry);
+ FieldPosition fpos;
+ if (singleCountry.isEmpty()) {
+ Formattable params [] = {
+ Formattable(city),
+ Formattable(country)
+ };
+ fallbackFmt->format(params, 2, location, fpos, status);
+ } else {
+ // If the zone is only one zone in the country, do not add city
+ Formattable params [] = {
+ Formattable(country)
+ };
+ regionFmt->format(params, 1, location, fpos, status);
+ }
+ if (U_FAILURE(status)) {
+ goto error_cleanup;
+ }
+
+ // Workaround for reducing UMR warning in Purify.
+ // Append NULL before calling getTerminatedBuffer()
+ int32_t locLen = location.length();
+ location.append((UChar)0).truncate(locLen);
+
+ zstrarray[ZSIDX_LOCATION] = location.getTerminatedBuffer();
+ }
+
+ UBool commonlyUsed = isCommonlyUsed(zoneItem);
+
+ // Resolve metazones used by this zone
+ int32_t mzPartialLocIdx = 0;
+ const UVector *metazoneMappings = ZoneMeta::getMetazoneMappings(utzid);
+ if (metazoneMappings != NULL) {
+ for (int32_t i = 0; i < metazoneMappings->size(); i++) {
+ const OlsonToMetaMappingEntry *mzmap = (const OlsonToMetaMappingEntry*)metazoneMappings->elementAt(i);
+ UnicodeString mzid(mzmap->mzid);
+ const ZoneStrings *mzStrings = (const ZoneStrings*)fMzidToStrings.get(mzid);
+ if (mzStrings == NULL) {
+ // If the metazone strings are not yet processed, do it now.
+ char mzidkey[ZID_KEY_MAX];
+ uprv_strcpy(mzidkey, gMetazoneIdPrefix);
+ u_UCharsToChars(mzmap->mzid, mzidkey + MZID_PREFIX_LEN, u_strlen(mzmap->mzid) + 1);
+ metazoneItem = ures_getByKeyWithFallback(zoneStringsArray, mzidkey, metazoneItem, &status);
+ if (U_FAILURE(status)) {
+ // No resources available for this metazone
+ // Resource bundle will be cleaned up after end of the loop.
+ status = U_ZERO_ERROR;
+ continue;
+ }
+ UBool mzCommonlyUsed = isCommonlyUsed(metazoneItem);
+ mzstrarray[ZSIDX_LONG_STANDARD] = getZoneStringFromBundle(metazoneItem, gLongStandardTag);
+ mzstrarray[ZSIDX_SHORT_STANDARD] = getZoneStringFromBundle(metazoneItem, gShortStandardTag);
+ mzstrarray[ZSIDX_LONG_DAYLIGHT] = getZoneStringFromBundle(metazoneItem, gLongDaylightTag);
+ mzstrarray[ZSIDX_SHORT_DAYLIGHT] = getZoneStringFromBundle(metazoneItem, gShortDaylightTag);
+ mzstrarray[ZSIDX_LONG_GENERIC] = getZoneStringFromBundle(metazoneItem, gLongGenericTag);
+ mzstrarray[ZSIDX_SHORT_GENERIC] = getZoneStringFromBundle(metazoneItem, gShortGenericTag);
+ mzstrarray[ZSIDX_LOCATION] = NULL;
+
+ int32_t lastNonNullIdx = ZSIDX_COUNT - 1;
+ while (lastNonNullIdx >= 0) {
+ if (mzstrarray[lastNonNullIdx] != NULL) {
+ break;
+ }
+ lastNonNullIdx--;
+ }
+ UnicodeString *strings_mz = NULL;
+ ZoneStrings *tmp_mzStrings = NULL;
+ if (lastNonNullIdx >= 0) {
+ // Create UnicodeString array and put strings to the zone string trie
+ strings_mz = new UnicodeString[lastNonNullIdx + 1];
+
+ UnicodeString preferredIdForLocale;
+ ZoneMeta::getZoneIdByMetazone(mzid, region, preferredIdForLocale);
+
+ for (int32_t typeidx = 0; typeidx <= lastNonNullIdx; typeidx++) {
+ if (mzstrarray[typeidx] != NULL) {
+ strings_mz[typeidx].setTo(TRUE, mzstrarray[typeidx], -1);
+
+ // Add a metazone string to the zone string trie
+ int32_t type = getTimeZoneTranslationType((TimeZoneTranslationTypeIndex)typeidx);
+ ZoneStringInfo *zsinfo = new ZoneStringInfo(preferredIdForLocale, strings_mz[typeidx], (TimeZoneTranslationType)type);
+ fZoneStringsTrie.put(strings_mz[typeidx], zsinfo, status);
+ if (U_FAILURE(status)) {
+ delete zsinfo;
+ delete strings_mz;
+ goto error_cleanup;
+ }
+ }
+ }
+ tmp_mzStrings = new ZoneStrings(strings_mz, lastNonNullIdx + 1, mzCommonlyUsed, NULL, 0, 0);
+ } else {
+ // Create ZoneStrings with empty contents
+ tmp_mzStrings = new ZoneStrings(NULL, 0, FALSE, NULL, 0, 0);
+ }
+
+ fMzidToStrings.put(mzid, tmp_mzStrings, status);
+ if (U_FAILURE(status)) {
+ delete tmp_mzStrings;
+ goto error_cleanup;
+ }
+
+ mzStrings = tmp_mzStrings;
+ }
+
+ // Compose generic partial location format
+ UnicodeString lg;
+ UnicodeString sg;
+
+ mzStrings->getString(ZSIDX_LONG_GENERIC, lg);
+ mzStrings->getString(ZSIDX_SHORT_GENERIC, sg);
+
+ if (!lg.isEmpty() || !sg.isEmpty()) {
+ UBool addMzPartialLocationNames = TRUE;
+ for (int32_t j = 0; j < mzPartialLocIdx; j++) {
+ if (mzPartialLoc[j][0] == mzid) {
+ // already processed
+ addMzPartialLocationNames = FALSE;
+ break;
+ }
+ }
+ if (addMzPartialLocationNames) {
+ UnicodeString *locationPart = NULL;
+ // Check if the zone is the preferred zone for the territory associated with the zone
+ UnicodeString preferredID;
+ ZoneMeta::getZoneIdByMetazone(mzid, countryCode, preferredID);
+ if (utzid == preferredID) {
+ // Use country for the location
+ locationPart = &country;
+ } else {
+ // Use city for the location
+ locationPart = &city;
+ }
+ // Reset the partial location string array
+ mzPartialLoc[mzPartialLocIdx][0].setTo(mzid);
+ mzPartialLoc[mzPartialLocIdx][1].remove();
+ mzPartialLoc[mzPartialLocIdx][2].remove();
+ mzPartialLoc[mzPartialLocIdx][3].remove();
+
+ if (locationPart != NULL) {
+ FieldPosition fpos;
+ if (!lg.isEmpty()) {
+ Formattable params [] = {
+ Formattable(*locationPart),
+ Formattable(lg)
+ };
+ fallbackFmt->format(params, 2, mzPartialLoc[mzPartialLocIdx][1], fpos, status);
+ }
+ if (!sg.isEmpty()) {
+ Formattable params [] = {
+ Formattable(*locationPart),
+ Formattable(sg)
+ };
+ fallbackFmt->format(params, 2, mzPartialLoc[mzPartialLocIdx][2], fpos, status);
+ if (mzStrings->isShortFormatCommonlyUsed()) {
+ mzPartialLoc[mzPartialLocIdx][3].setTo(TRUE, gCommonlyUsedTrue, -1);
+ }
+ }
+ if (U_FAILURE(status)) {
+ goto error_cleanup;
+ }
+ }
+ mzPartialLocIdx++;
+ }
+ }
+ }
+ }
+ // Collected names for a zone
+
+ // Create UnicodeString array for localized zone strings
+ int32_t lastIdx = ZSIDX_COUNT - 1;
+ while (lastIdx >= 0) {
+ if (zstrarray[lastIdx] != NULL) {
+ break;
+ }
+ lastIdx--;
+ }
+ UnicodeString *strings = NULL;
+ int32_t stringsCount = lastIdx + 1;
+
+ if (stringsCount > 0) {
+ strings = new UnicodeString[stringsCount];
+ for (int32_t i = 0; i < stringsCount; i++) {
+ if (zstrarray[i] != NULL) {
+ strings[i].setTo(zstrarray[i], -1);
+
+ // Add names to the trie
+ int32_t type = getTimeZoneTranslationType((TimeZoneTranslationTypeIndex)i);
+ ZoneStringInfo *zsinfo = new ZoneStringInfo(utzid, strings[i], (TimeZoneTranslationType)type);
+ fZoneStringsTrie.put(strings[i], zsinfo, status);
+ if (U_FAILURE(status)) {
+ delete zsinfo;
+ delete[] strings;
+ goto error_cleanup;
+ }
+ }
+ }
+ }
+
+ // Create UnicodeString array for generic partial location strings
+ UnicodeString **genericPartialLocationNames = NULL;
+ int32_t genericPartialRowCount = mzPartialLocIdx;
+ int32_t genericPartialColCount = 4;
+
+ if (genericPartialRowCount != 0) {
+ genericPartialLocationNames = (UnicodeString**)uprv_malloc(genericPartialRowCount * sizeof(UnicodeString*));
+ if (genericPartialLocationNames == NULL) {
+ status = U_MEMORY_ALLOCATION_ERROR;
+ delete[] strings;
+ goto error_cleanup;
+ }
+ for (int32_t i = 0; i < genericPartialRowCount; i++) {
+ genericPartialLocationNames[i] = new UnicodeString[genericPartialColCount];
+ for (int32_t j = 0; j < genericPartialColCount; j++) {
+ genericPartialLocationNames[i][j].setTo(mzPartialLoc[i][j]);
+ // Add names to the trie
+ if ((j == 1 || j == 2) &&!genericPartialLocationNames[i][j].isEmpty()) {
+ ZoneStringInfo *zsinfo;
+ TimeZoneTranslationType type = (j == 1) ? GENERIC_LONG : GENERIC_SHORT;
+ zsinfo = new ZoneStringInfo(utzid, genericPartialLocationNames[i][j], type);
+ fZoneStringsTrie.put(genericPartialLocationNames[i][j], zsinfo, status);
+ if (U_FAILURE(status)) {
+ delete[] genericPartialLocationNames[i];
+ uprv_free(genericPartialLocationNames);
+ delete[] strings;
+ goto error_cleanup;
+ }
+ }
+ }
+ }
+ }
+
+ // Finally, create ZoneStrings instance and put it into the tzidToStinrgs map
+ ZoneStrings *zstrings = new ZoneStrings(strings, stringsCount, commonlyUsed,
+ genericPartialLocationNames, genericPartialRowCount, genericPartialColCount);
+
+ fTzidToStrings.put(utzid, zstrings, status);
+ if (U_FAILURE(status)) {
+ delete zstrings;
+ goto error_cleanup;
+ }
+ }
+
+error_cleanup:
+ if (fallbackFmt != NULL) {
+ delete fallbackFmt;
+ }
+ if (regionFmt != NULL) {
+ delete regionFmt;
+ }
+ if (tzids != NULL) {
+ delete tzids;
+ }
+ ures_close(zoneItem);
+ ures_close(metazoneItem);
+ ures_close(zoneStringsArray);
+}
+
+ZoneStringFormat::~ZoneStringFormat() {
+}
+
+SafeZoneStringFormatPtr*
+ZoneStringFormat::getZoneStringFormat(const Locale& locale, UErrorCode &status) {
+ umtx_lock(&gZSFCacheLock);
+ if (gZoneStringFormatCache == NULL) {
+ gZoneStringFormatCache = new ZSFCache(10 /* capacity */);
+ ucln_i18n_registerCleanup(UCLN_I18N_ZSFORMAT, zoneStringFormat_cleanup);
+ }
+ umtx_unlock(&gZSFCacheLock);
+
+ return gZoneStringFormatCache->get(locale, status);
+}
+
+
+UnicodeString**
+ZoneStringFormat::createZoneStringsArray(UDate date, int32_t &rowCount, int32_t &colCount, UErrorCode &status) const {
+ if (U_FAILURE(status)) {
+ return NULL;
+ }
+ UnicodeString **result = NULL;
+ rowCount = 0;
+ colCount = 0;
+
+ // Collect canonical time zone IDs
+ UVector canonicalIDs(uhash_deleteUnicodeString, uhash_compareUnicodeString, status);
+ if (U_FAILURE(status)) {
+ return NULL;
+ }
+ StringEnumeration *tzids = TimeZone::createEnumeration();
+ const UChar *tzid;
+ while ((tzid = tzids->unext(NULL, status))) {
+ if (U_FAILURE(status)) {
+ delete tzids;
+ return NULL;
+ }
+ UnicodeString utzid(tzid);
+ UnicodeString canonicalID;
+ ZoneMeta::getCanonicalID(UnicodeString(tzid), canonicalID);
+ if (utzid == canonicalID) {
+ canonicalIDs.addElement(new UnicodeString(utzid), status);
+ if (U_FAILURE(status)) {
+ delete tzids;
+ return NULL;
+ }
+ }
+ }
+ delete tzids;
+
+ // Allocate array
+ result = (UnicodeString**)uprv_malloc(canonicalIDs.size() * sizeof(UnicodeString*));
+ if (result == NULL) {
+ status = U_MEMORY_ALLOCATION_ERROR;
+ return NULL;
+ }
+ for (int32_t i = 0; i < canonicalIDs.size(); i++) {
+ result[i] = new UnicodeString[8];
+ UnicodeString *id = (UnicodeString*)canonicalIDs.elementAt(i);
+ result[i][0].setTo(*id);
+ getLongStandard(*id, date, result[i][1]);
+ getShortStandard(*id, date, FALSE, result[i][2]);
+ getLongDaylight(*id, date, result[i][3]);
+ getShortDaylight(*id, date, FALSE, result[i][4]);
+ getGenericLocation(*id, result[i][5]);
+ getLongGenericNonLocation(*id, date, result[i][6]);
+ getShortGenericNonLocation(*id, date, FALSE, result[i][7]);
+ }
+
+ rowCount = canonicalIDs.size();
+ colCount = 8;
+ return result;
+}
+
+UnicodeString&
+ZoneStringFormat::getSpecificLongString(const Calendar &cal, UnicodeString &result,
+ UErrorCode &status) const {
+ result.remove();
+ if (U_FAILURE(status)) {
+ return result;
+ }
+ UnicodeString tzid;
+ cal.getTimeZone().getID(tzid);
+ UDate date = cal.getTime(status);
+ if (cal.get(UCAL_DST_OFFSET, status) == 0) {
+ return getString(tzid, ZSIDX_LONG_STANDARD, date, FALSE /*not used*/, result);
+ } else {
+ return getString(tzid, ZSIDX_LONG_DAYLIGHT, date, FALSE /*not used*/, result);
+ }
+}
+
+UnicodeString&
+ZoneStringFormat::getSpecificShortString(const Calendar &cal, UBool commonlyUsedOnly,
+ UnicodeString &result, UErrorCode &status) const {
+ result.remove();
+ if (U_FAILURE(status)) {
+ return result;
+ }
+ UnicodeString tzid;
+ cal.getTimeZone().getID(tzid);
+ UDate date = cal.getTime(status);
+ if (cal.get(UCAL_DST_OFFSET, status) == 0) {
+ return getString(tzid, ZSIDX_SHORT_STANDARD, date, commonlyUsedOnly, result);
+ } else {
+ return getString(tzid, ZSIDX_SHORT_DAYLIGHT, date, commonlyUsedOnly, result);
+ }
+}
+
+UnicodeString&
+ZoneStringFormat::getGenericLongString(const Calendar &cal, UnicodeString &result,
+ UErrorCode &status) const {
+ return getGenericString(cal, FALSE /*long*/, FALSE /* not used */, result, status);
+}
+
+UnicodeString&
+ZoneStringFormat::getGenericShortString(const Calendar &cal, UBool commonlyUsedOnly,
+ UnicodeString &result, UErrorCode &status) const {
+ return getGenericString(cal, TRUE /*short*/, commonlyUsedOnly, result, status);
+}
+
+UnicodeString&
+ZoneStringFormat::getGenericLocationString(const Calendar &cal, UnicodeString &result,
+ UErrorCode &status) const {
+ UnicodeString tzid;
+ cal.getTimeZone().getID(tzid);
+ UDate date = cal.getTime(status);
+ return getString(tzid, ZSIDX_LOCATION, date, FALSE /*not used*/, result);
+}
+
+const ZoneStringInfo*
+ZoneStringFormat::findSpecificLong(const UnicodeString &text, int32_t start,
+ int32_t &matchLength, UErrorCode &status) const {
+ return find(text, start, STANDARD_LONG | DAYLIGHT_LONG, matchLength, status);
+}
+
+const ZoneStringInfo*
+ZoneStringFormat::findSpecificShort(const UnicodeString &text, int32_t start,
+ int32_t &matchLength, UErrorCode &status) const {
+ return find(text, start, STANDARD_SHORT | DAYLIGHT_SHORT, matchLength, status);
+}
+
+const ZoneStringInfo*
+ZoneStringFormat::findGenericLong(const UnicodeString &text, int32_t start,
+ int32_t &matchLength, UErrorCode &status) const {
+ return find(text, start, GENERIC_LONG | STANDARD_LONG | LOCATION, matchLength, status);
+}
+
+const ZoneStringInfo*
+ZoneStringFormat::findGenericShort(const UnicodeString &text, int32_t start,
+ int32_t &matchLength, UErrorCode &status) const {
+ return find(text, start, GENERIC_SHORT | STANDARD_SHORT | LOCATION, matchLength, status);
+}
+
+const ZoneStringInfo*
+ZoneStringFormat::findGenericLocation(const UnicodeString &text, int32_t start,
+ int32_t &matchLength, UErrorCode &status) const {
+ return find(text, start, LOCATION, matchLength, status);
+}
+
+UnicodeString&
+ZoneStringFormat::getString(const UnicodeString &tzid, TimeZoneTranslationTypeIndex typeIdx, UDate date,
+ UBool commonlyUsedOnly, UnicodeString& result) const {
+ result.remove();
+
+ // ICU's own array does not have entries for aliases
+ UnicodeString canonicalID;
+ ZoneMeta::getCanonicalID(tzid, canonicalID);
+
+ if (fTzidToStrings.count() > 0) {
+ ZoneStrings *zstrings = (ZoneStrings*)fTzidToStrings.get(canonicalID);
+ if (zstrings != NULL) {
+ switch (typeIdx) {
+ case ZSIDX_LONG_STANDARD:
+ case ZSIDX_LONG_DAYLIGHT:
+ case ZSIDX_LONG_GENERIC:
+ case ZSIDX_LOCATION:
+ zstrings->getString(typeIdx, result);
+ break;
+ case ZSIDX_SHORT_STANDARD:
+ case ZSIDX_SHORT_DAYLIGHT:
+ case ZSIDX_SHORT_GENERIC:
+ if (!commonlyUsedOnly || zstrings->isShortFormatCommonlyUsed()) {
+ zstrings->getString(typeIdx, result);
+ }
+ break;
+ }
+ }
+ }
+ if (result.isEmpty() && fMzidToStrings.count() > 0 && typeIdx != ZSIDX_LOCATION) {
+ // Try metazone
+ UnicodeString mzid;
+ ZoneMeta::getMetazoneID(canonicalID, date, mzid);
+ if (!mzid.isEmpty()) {
+ ZoneStrings *mzstrings = (ZoneStrings*)fMzidToStrings.get(mzid);
+ if (mzstrings != NULL) {
+ switch (typeIdx) {
+ case ZSIDX_LONG_STANDARD:
+ case ZSIDX_LONG_DAYLIGHT:
+ case ZSIDX_LONG_GENERIC:
+ case ZSIDX_LOCATION:
+ mzstrings->getString(typeIdx, result);
+ break;
+ case ZSIDX_SHORT_STANDARD:
+ case ZSIDX_SHORT_DAYLIGHT:
+ case ZSIDX_SHORT_GENERIC:
+ if (!commonlyUsedOnly || mzstrings->isShortFormatCommonlyUsed()) {
+ mzstrings->getString(typeIdx, result);
+ }
+ break;
+ }
+ }
+ }
+ }
+ return result;
+}
+
+UnicodeString&
+ZoneStringFormat::getGenericString(const Calendar &cal, UBool isShort, UBool commonlyUsedOnly,
+ UnicodeString &result, UErrorCode &status) const {
+ result.remove();
+ UDate time = cal.getTime(status);
+ if (U_FAILURE(status)) {
+ return result;
+ }
+ const TimeZone &tz = cal.getTimeZone();
+ UnicodeString tzid;
+ tz.getID(tzid);
+
+ // ICU's own array does not have entries for aliases
+ UnicodeString canonicalID;
+ ZoneMeta::getCanonicalID(tzid, canonicalID);
+
+ ZoneStrings *zstrings;
+ if (fTzidToStrings.count() > 0) {
+ zstrings = (ZoneStrings*)fTzidToStrings.get(canonicalID);
+ if (zstrings != NULL) {
+ if (isShort) {
+ if (!commonlyUsedOnly || zstrings->isShortFormatCommonlyUsed()) {
+ zstrings->getString(ZSIDX_SHORT_GENERIC, result);
+ }
+ } else {
+ zstrings->getString(ZSIDX_LONG_GENERIC, result);
+ }
+ }
+ }
+ if (result.isEmpty() && fMzidToStrings.count() > 0) {
+ // try metazone
+ int32_t raw, sav;
+ UnicodeString mzid;
+ ZoneMeta::getMetazoneID(canonicalID, time, mzid);
+ if (!mzid.isEmpty()) {
+ UBool useStandard = FALSE;
+ sav = cal.get(UCAL_DST_OFFSET, status);
+ if (U_FAILURE(status)) {
+ return result;
+ }
+ if (sav == 0) {
+ useStandard = TRUE;
+ // Check if the zone actually uses daylight saving time around the time
+ TimeZone *tmptz = tz.clone();
+ BasicTimeZone *btz = NULL;
+ if (tmptz->getDynamicClassID() == OlsonTimeZone::getStaticClassID()
+ || tmptz->getDynamicClassID() == SimpleTimeZone::getStaticClassID()
+ || tmptz->getDynamicClassID() == RuleBasedTimeZone::getStaticClassID()
+ || tmptz->getDynamicClassID() == VTimeZone::getStaticClassID()) {
+ btz = (BasicTimeZone*)tmptz;
+ }
+
+ if (btz != NULL) {
+ TimeZoneTransition before;
+ UBool beforTrs = btz->getPreviousTransition(time, TRUE, before);
+ if (beforTrs
+ && (time - before.getTime() < kDstCheckRange)
+ && before.getFrom()->getDSTSavings() != 0) {
+ useStandard = FALSE;
+ } else {
+ TimeZoneTransition after;
+ UBool afterTrs = btz->getNextTransition(time, FALSE, after);
+ if (afterTrs
+ && (after.getTime() - time < 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(time - kDstCheckRange, FALSE, raw, sav, status);
+ if (sav != 0) {
+ useStandard = FALSE;
+ } else {
+ tmptz->getOffset(time + kDstCheckRange, FALSE, raw, sav, status);
+ if (sav != 0){
+ useStandard = FALSE;
+ }
+ }
+ if (U_FAILURE(status)) {
+ delete tmptz;
+ result.remove();
+ return result;
+ }
+ }
+ delete tmptz;
+ }
+ if (useStandard) {
+ getString(canonicalID, (isShort ? ZSIDX_SHORT_STANDARD : ZSIDX_LONG_STANDARD),
+ time, commonlyUsedOnly, result);
+
+ // Note:
+ // In CLDR 1.5.1, a same localization is used for both generic and standard
+ // for some metazones in some locales. This is actually data bugs and should
+ // be resolved in later versions of CLDR. For now, we check if the standard
+ // name is different from its generic name below.
+ if (!result.isEmpty()) {
+ UnicodeString genericNonLocation;
+ getString(canonicalID, (isShort ? ZSIDX_SHORT_GENERIC : ZSIDX_LONG_GENERIC),
+ time, commonlyUsedOnly, genericNonLocation);
+ if (!genericNonLocation.isEmpty() && result == genericNonLocation) {
+ result.remove();
+ }
+ }
+ }
+ if (result.isEmpty()) {
+ ZoneStrings *mzstrings = (ZoneStrings*)fMzidToStrings.get(mzid);
+ if (mzstrings != NULL) {
+ if (isShort) {
+ if (!commonlyUsedOnly || mzstrings->isShortFormatCommonlyUsed()) {
+ mzstrings->getString(ZSIDX_SHORT_GENERIC, result);
+ }
+ } else {
+ mzstrings->getString(ZSIDX_LONG_GENERIC, result);
+ }
+ }
+ if (!result.isEmpty()) {
+ // Check if the offsets at the given time matches the preferred zone's offsets
+ UnicodeString preferredId;
+ UnicodeString region;
+ ZoneMeta::getZoneIdByMetazone(mzid, getRegion(region), preferredId);
+ if (canonicalID != preferredId) {
+ // Check if the offsets at the given time are identical with the preferred zone
+ raw = cal.get(UCAL_ZONE_OFFSET, status);
+ if (U_FAILURE(status)) {
+ result.remove();
+ return result;
+ }
+ TimeZone *preferredZone = TimeZone::createTimeZone(preferredId);
+ int32_t prfRaw, prfSav;
+ // Check offset in preferred time zone with wall time.
+ // With getOffset(time, false, preferredOffsets),
+ // you may get incorrect results because of time overlap at DST->STD
+ // transition.
+ preferredZone->getOffset(time + raw + sav, TRUE, prfRaw, prfSav, status);
+ delete preferredZone;
+
+ if (U_FAILURE(status)) {
+ result.remove();
+ return result;
+ }
+ if ((raw != prfRaw || sav != prfSav) && zstrings != NULL) {
+ // Use generic partial location string as fallback
+ zstrings->getGenericPartialLocationString(mzid, isShort, commonlyUsedOnly, result);
+ }
+ }
+ }
+ }
+ }
+ }
+ if (result.isEmpty()) {
+ // Use location format as the final fallback
+ getString(canonicalID, ZSIDX_LOCATION, time, FALSE /*not used*/, result);
+ }
+
+ return result;
+}
+
+UnicodeString&
+ZoneStringFormat::getGenericPartialLocationString(const UnicodeString &tzid, UBool isShort,
+ UDate date, UBool commonlyUsedOnly, UnicodeString &result) const {
+ result.remove();
+ if (fTzidToStrings.count() <= 0) {
+ return result;
+ }
+
+ UnicodeString canonicalID;
+ ZoneMeta::getCanonicalID(tzid, canonicalID);
+
+ UnicodeString mzid;
+ ZoneMeta::getMetazoneID(canonicalID, date, mzid);
+
+ if (!mzid.isEmpty()) {
+ ZoneStrings *zstrings = (ZoneStrings*)fTzidToStrings.get(canonicalID);
+ if (zstrings != NULL) {
+ zstrings->getGenericPartialLocationString(mzid, isShort, commonlyUsedOnly, result);
+ }
+ }
+ return result;
+}
+
+const ZoneStringInfo*
+ZoneStringFormat::find(const UnicodeString &text, int32_t start, int32_t types,
+ int32_t &matchLength, UErrorCode &status) const {
+ matchLength = 0;
+ if (U_FAILURE(status)) {
+ return NULL;
+ }
+ if (fZoneStringsTrie.isEmpty()) {
+ return NULL;
+ }
+ const ZoneStringInfo *result = NULL;
+ const ZoneStringInfo *fallback = NULL;
+ int32_t fallbackMatchLen = 0;
+
+ ZoneStringSearchResultHandler handler(status);
+ fZoneStringsTrie.search(text, start, (TextTrieMapSearchResultHandler*)&handler, status);
+ if (U_SUCCESS(status)) {
+ int32_t numMatches = handler.countMatches();
+ for (int32_t i = 0; i < numMatches; i++) {
+ int32_t tmpMatchLen;
+ const ZoneStringInfo *tmp = handler.getMatch(i, tmpMatchLen);
+ if ((types & tmp->fType) != 0) {
+ if (result == NULL || matchLength < tmpMatchLen) {
+ result = tmp;
+ matchLength = tmpMatchLen;
+ } else if (matchLength == tmpMatchLen) {
+ // Tie breaker - there are some examples that a
+ // long standard name is identical with a location
+ // name - for example, "Uruguay Time". In this case,
+ // we interpret it as generic, not specific.
+ if (tmp->isGeneric() && !result->isGeneric()) {
+ result = tmp;
+ }
+ }
+ } else if (result == NULL) {
+ if (fallback == NULL || fallbackMatchLen < tmpMatchLen) {
+ fallback = tmp;
+ fallbackMatchLen = tmpMatchLen;
+ } else if (fallbackMatchLen == tmpMatchLen) {
+ if (tmp->isGeneric() && !fallback->isGeneric()) {
+ fallback = tmp;
+ }
+ }
+ }
+ }
+ if (result == NULL && fallback != NULL) {
+ result = fallback;
+ matchLength = fallbackMatchLen;
+ }
+ }
+ return result;
+}
+
+
+UnicodeString&
+ZoneStringFormat::getRegion(UnicodeString &region) const {
+ const char* country = fLocale.getCountry();
+ // TODO: Utilize addLikelySubtag in Locale to resolve default region
+ // when the implementation is ready.
+ region.setTo(UnicodeString(country, -1, US_INV));
+ return region;
+}
+
+MessageFormat*
+ZoneStringFormat::getFallbackFormat(const Locale &locale, UErrorCode &status) {
+ if (U_FAILURE(status)) {
+ return NULL;
+ }
+ UnicodeString pattern(TRUE, gDefFallbackPattern, -1);
+ UResourceBundle *zoneStringsArray = ures_open(NULL, locale.getName(), &status);
+ zoneStringsArray = ures_getByKeyWithFallback(zoneStringsArray, gZoneStringsTag, zoneStringsArray, &status);
+ int32_t len;
+ const UChar *flbkfmt = ures_getStringByKeyWithFallback(zoneStringsArray, gFallbackFormatTag, &len, &status);
+ if (U_SUCCESS(status)) {
+ pattern.setTo(flbkfmt);
+ } else {
+ status = U_ZERO_ERROR;
+ }
+ ures_close(zoneStringsArray);
+
+ MessageFormat *fallbackFmt = new MessageFormat(pattern, status);
+ return fallbackFmt;
+}
+
+MessageFormat*
+ZoneStringFormat::getRegionFormat(const Locale& locale, UErrorCode &status) {
+ if (U_FAILURE(status)) {
+ return NULL;
+ }
+ UnicodeString pattern(TRUE, gDefRegionPattern, -1);
+ UResourceBundle *zoneStringsArray = ures_open(NULL, locale.getName(), &status);
+ zoneStringsArray = ures_getByKeyWithFallback(zoneStringsArray, gZoneStringsTag, zoneStringsArray, &status);
+ int32_t len;
+ const UChar *regionfmt = ures_getStringByKeyWithFallback(zoneStringsArray, gRegionFormatTag, &len, &status);
+ if (U_SUCCESS(status)) {
+ pattern.setTo(regionfmt);
+ } else {
+ status = U_ZERO_ERROR;
+ }
+ ures_close(zoneStringsArray);
+
+ MessageFormat *regionFmt = new MessageFormat(pattern, status);
+ return regionFmt;
+}
+
+const UChar*
+ZoneStringFormat::getZoneStringFromBundle(const UResourceBundle *zoneitem, const char *key) {
+ const UChar *str = NULL;
+ if (zoneitem != NULL) {
+ UErrorCode status = U_ZERO_ERROR;
+ int32_t len;
+ str = ures_getStringByKeyWithFallback(zoneitem, key, &len, &status);
+ if (U_FAILURE(status)) {
+ str = NULL;
+ }
+ }
+ return str;
+}
+
+UBool
+ZoneStringFormat::isCommonlyUsed(const UResourceBundle *zoneitem) {
+ if (zoneitem == NULL) {
+ return TRUE;
+ }
+
+ UBool commonlyUsed = FALSE;
+ UErrorCode status = U_ZERO_ERROR;
+ UResourceBundle *cuRes = ures_getByKey(zoneitem, gCommonlyUsedTag, NULL, &status);
+ int32_t cuValue = ures_getInt(cuRes, &status);
+ if (U_SUCCESS(status)) {
+ if (cuValue == 1) {
+ commonlyUsed = TRUE;
+ }
+ }
+ ures_close(cuRes);
+ return commonlyUsed;
+}
+
+UnicodeString&
+ZoneStringFormat::getLocalizedCountry(const UnicodeString &countryCode, const Locale &locale, UnicodeString &displayCountry) {
+ // We do not want to use display country names only from the target language bundle
+ // Note: we should do this in better way.
+ displayCountry.remove();
+ int32_t ccLen = countryCode.length();
+ if (ccLen > 0 && ccLen < ULOC_COUNTRY_CAPACITY) {
+ UErrorCode status = U_ZERO_ERROR;
+ UResourceBundle *localeBundle = ures_open(NULL, locale.getName(), &status);
+ if (U_SUCCESS(status)) {
+ const char *bundleLocStr = ures_getLocale(localeBundle, &status);
+ if (U_SUCCESS(status) && uprv_strlen(bundleLocStr) > 0) {
+ Locale bundleLoc(bundleLocStr);
+ if (uprv_strcmp(bundleLocStr, "root") != 0 && uprv_strcmp(bundleLoc.getLanguage(), locale.getLanguage()) == 0) {
+ // Create a fake locale strings
+ char tmpLocStr[ULOC_COUNTRY_CAPACITY + 3];
+ uprv_strcpy(tmpLocStr, "xx_");
+ u_UCharsToChars(countryCode.getBuffer(), &tmpLocStr[3], ccLen);
+ tmpLocStr[3 + ccLen] = 0;
+
+ Locale tmpLoc(tmpLocStr);
+ tmpLoc.getDisplayCountry(locale, displayCountry);
+ }
+ }
+ }
+ ures_close(localeBundle);
+ }
+ if (displayCountry.isEmpty()) {
+ // Use the country code as the fallback
+ displayCountry.setTo(countryCode);
+ }
+ return displayCountry;
+}
+
+// ----------------------------------------------------------------------------
+/*
+ * This constructor adopts the input UnicodeString arrays.
+ */
+ZoneStrings::ZoneStrings(UnicodeString *strings, int32_t stringsCount, UBool commonlyUsed,
+ UnicodeString **genericPartialLocationStrings, int32_t genericRowCount, int32_t genericColCount)
+: UMemory(), fStrings(strings), fStringsCount(stringsCount), fIsCommonlyUsed(commonlyUsed),
+ fGenericPartialLocationStrings(genericPartialLocationStrings),
+ fGenericPartialLocationRowCount(genericRowCount), fGenericPartialLocationColCount(genericColCount) {
+}
+
+ZoneStrings::~ZoneStrings() {
+ if (fStrings != NULL) {
+ delete[] fStrings;
+ }
+ if (fGenericPartialLocationStrings != NULL) {
+ for (int32_t i = 0; i < fGenericPartialLocationRowCount; i++) {
+ delete[] fGenericPartialLocationStrings[i];
+ }
+ uprv_free(fGenericPartialLocationStrings);
+ }
+}
+
+
+UnicodeString&
+ZoneStrings::getString(int32_t typeIdx, UnicodeString &result) const {
+ if (typeIdx >= 0 && typeIdx < fStringsCount) {
+ result.setTo(fStrings[typeIdx]);
+ } else {
+ result.remove();
+ }
+ return result;
+}
+
+UnicodeString&
+ZoneStrings::getGenericPartialLocationString(const UnicodeString &mzid, UBool isShort,
+ UBool commonlyUsedOnly, UnicodeString &result) const {
+ UBool isSet = FALSE;
+ if (fGenericPartialLocationColCount >= 2) {
+ for (int32_t i = 0; i < fGenericPartialLocationRowCount; i++) {
+ if (fGenericPartialLocationStrings[i][0] == mzid) {
+ if (isShort) {
+ if (fGenericPartialLocationColCount >= 3) {
+ if (!commonlyUsedOnly ||
+ fGenericPartialLocationColCount == 3 || fGenericPartialLocationStrings[i][3].length() != 0) {
+ result.setTo(fGenericPartialLocationStrings[i][2]);
+ isSet = TRUE;
+ }
+ }
+ } else {
+ result.setTo(fGenericPartialLocationStrings[i][1]);
+ isSet = TRUE;
+ }
+ break;
+ }
+ }
+ }
+ if (!isSet) {
+ result.remove();
+ }
+ return result;
+}
+
+// --------------------------------------------------------------
+SafeZoneStringFormatPtr::SafeZoneStringFormatPtr(ZSFCacheEntry *cacheEntry)
+: UMemory(), fCacheEntry(cacheEntry) {
+}
+
+SafeZoneStringFormatPtr::~SafeZoneStringFormatPtr() {
+ fCacheEntry->delRef();
+}
+
+const ZoneStringFormat*
+SafeZoneStringFormatPtr::get() const {
+ return fCacheEntry->getZoneStringFormat();
+}
+
+ZSFCacheEntry::ZSFCacheEntry(const Locale &locale, ZoneStringFormat *zsf, ZSFCacheEntry *next)
+: UMemory(), fLocale(locale), fZoneStringFormat(zsf),
+ fNext(next), fRefCount(1)
+{
+}
+
+ZSFCacheEntry::~ZSFCacheEntry () {
+ delete fZoneStringFormat;
+}
+
+const ZoneStringFormat*
+ZSFCacheEntry::getZoneStringFormat(void) {
+ return (const ZoneStringFormat*)fZoneStringFormat;
+}
+
+void
+ZSFCacheEntry::delRef(void) {
+ umtx_lock(&gZSFCacheLock);
+ --fRefCount;
+ umtx_unlock(&gZSFCacheLock);
+}
+
+ZSFCache::ZSFCache(int32_t capacity)
+: UMemory(), fCapacity(capacity), fFirst(NULL) {
+}
+
+ZSFCache::~ZSFCache() {
+ ZSFCacheEntry *entry = fFirst;
+ while (entry) {
+ ZSFCacheEntry *next = entry->fNext;
+ delete entry;
+ entry = next;
+ }
+}
+
+SafeZoneStringFormatPtr*
+ZSFCache::get(const Locale &locale, UErrorCode &status) {
+ SafeZoneStringFormatPtr *result = NULL;
+
+ // Search the cache entry list
+ ZSFCacheEntry *entry = NULL;
+ ZSFCacheEntry *next, *prev;
+
+ umtx_lock(&gZSFCacheLock);
+ entry = fFirst;
+ prev = NULL;
+ while (entry) {
+ next = entry->fNext;
+ if (entry->fLocale == locale) {
+ // Add reference count
+ entry->fRefCount++;
+
+ // move the entry to the top
+ if (entry != fFirst) {
+ prev->fNext = next;
+ entry->fNext = fFirst;
+ fFirst = entry;
+ }
+ break;
+ }
+ prev = entry;
+ entry = next;
+ }
+ umtx_unlock(&gZSFCacheLock);
+
+ // Create a new ZoneStringFormat
+ if (entry == NULL) {
+ ZoneStringFormat *zsf = new ZoneStringFormat(locale, status);
+ if (U_FAILURE(status)) {
+ return NULL;
+ }
+ // Now add the new entry
+ umtx_lock(&gZSFCacheLock);
+ // Make sure no other threads already created the one for the same locale
+ entry = fFirst;
+ prev = NULL;
+ while (entry) {
+ next = entry->fNext;
+ if (entry->fLocale == locale) {
+ // Add reference count
+ entry->fRefCount++;
+
+ // move the entry to the top
+ if (entry != fFirst) {
+ prev->fNext = next;
+ entry->fNext = fFirst;
+ fFirst = entry;
+ }
+ break;
+ }
+ prev = entry;
+ entry = next;
+ }
+ if (entry == NULL) {
+ // Add the new one to the top
+ next = fFirst;
+ entry = new ZSFCacheEntry(locale, zsf, next);
+ fFirst = entry;
+ } else {
+ delete zsf;
+ }
+ umtx_unlock(&gZSFCacheLock);
+ }
+
+ result = new SafeZoneStringFormatPtr(entry);
+
+ // Now, delete unused cache entries beyond the capacity
+ umtx_lock(&gZSFCacheLock);
+ entry = fFirst;
+ prev = NULL;
+ int32_t idx = 1;
+ while (entry) {
+ next = entry->fNext;
+ if (idx >= fCapacity && entry->fRefCount == 0) {
+ if (entry == fFirst) {
+ fFirst = next;
+ } else {
+ prev->fNext = next;
+ }
+ delete entry;
+ } else {
+ prev = entry;
+ }
+ entry = next;
+ idx++;
+ }
+ umtx_unlock(&gZSFCacheLock);
+
+ return result;
+}
+
+U_NAMESPACE_END
+
+#endif /* #if !UCONFIG_NO_FORMATTING */
diff --git a/i18n/zstrfmt.h b/i18n/zstrfmt.h
new file mode 100644
index 00000000..6765d6dd
--- /dev/null
+++ b/i18n/zstrfmt.h
@@ -0,0 +1,442 @@
+/*
+*******************************************************************************
+* Copyright (C) 2007, International Business Machines Corporation and *
+* others. All Rights Reserved. *
+*******************************************************************************
+*/
+#ifndef ZSTRFMT_H
+#define ZSTRFMT_H
+
+#include "unicode/utypes.h"
+
+#if !UCONFIG_NO_FORMATTING
+
+#include "unicode/unistr.h"
+#include "unicode/calendar.h"
+#include "hash.h"
+#include "uvector.h"
+
+U_NAMESPACE_BEGIN
+
+/*
+ * Character node used by TextTrieMap
+ */
+class CharacterNode : public UMemory {
+public:
+ CharacterNode(UChar32 c, UObjectDeleter *fn, UErrorCode &status);
+ virtual ~CharacterNode();
+
+ inline UChar32 getCharacter(void) const;
+ inline const UVector* getValues(void) const;
+ inline const UVector* getChildNodes(void) const;
+
+ void addValue(void *value, UErrorCode &status);
+ CharacterNode* addChildNode(UChar32 c, UErrorCode &status);
+ CharacterNode* getChildNode(UChar32 c) const;
+
+private:
+ UVector fChildren;
+ UVector fValues;
+ UObjectDeleter *fValueDeleter;
+ UChar32 fCharacter;
+};
+
+inline UChar32 CharacterNode::getCharacter(void) const {
+ return fCharacter;
+}
+
+inline const UVector* CharacterNode::getValues(void) const {
+ return &fValues;
+}
+
+inline const UVector* CharacterNode::getChildNodes(void) const {
+ return &fChildren;
+}
+
+/*
+ * Search result handler callback interface used by TextTrieMap search.
+ */
+class TextTrieMapSearchResultHandler {
+public:
+ virtual UBool handleMatch(int32_t matchLength,
+ const UVector *values, UErrorCode& status) = 0;
+};
+
+/**
+ * TextTrieMap is a trie implementation for supporting
+ * fast prefix match for the string key.
+ */
+class TextTrieMap : public UMemory {
+public:
+ TextTrieMap(UBool ignoreCase, UObjectDeleter *valueDeleterFunc);
+ virtual ~TextTrieMap();
+
+ void put(const UnicodeString &key, void *value, UErrorCode &status);
+ void search(const UnicodeString &text, int32_t start,
+ TextTrieMapSearchResultHandler *handler, UErrorCode& status) const;
+ inline int32_t isEmpty() const;
+
+private:
+ UBool fIgnoreCase;
+ UObjectDeleter *fValueDeleter;
+ CharacterNode *fRoot;
+
+ void search(CharacterNode *node, const UnicodeString &text, int32_t start,
+ int32_t index, TextTrieMapSearchResultHandler *handler, UErrorCode &status) const;
+};
+
+inline UChar32 TextTrieMap::isEmpty(void) const {
+ return fRoot == NULL;
+}
+
+// Name types, these bit flag are used for zone string lookup
+enum TimeZoneTranslationType {
+ LOCATION = 0x0001,
+ GENERIC_LONG = 0x0002,
+ GENERIC_SHORT = 0x0004,
+ STANDARD_LONG = 0x0008,
+ STANDARD_SHORT = 0x0010,
+ DAYLIGHT_LONG = 0x0020,
+ DAYLIGHT_SHORT = 0x0040
+};
+
+// Name type index, these constants are used for index in the zone strings array.
+enum TimeZoneTranslationTypeIndex {
+ ZSIDX_LOCATION = 0,
+ ZSIDX_LONG_STANDARD,
+ ZSIDX_SHORT_STANDARD,
+ ZSIDX_LONG_DAYLIGHT,
+ ZSIDX_SHORT_DAYLIGHT,
+ ZSIDX_LONG_GENERIC,
+ ZSIDX_SHORT_GENERIC,
+
+ ZSIDX_COUNT
+};
+
+class MessageFormat;
+
+/*
+ * ZoneStringInfo is a class holding a localized zone string
+ * information.
+ */
+class ZoneStringInfo : public UMemory {
+public:
+ virtual ~ZoneStringInfo();
+
+ inline UnicodeString& getID(UnicodeString &result) const;
+ inline UnicodeString& getString(UnicodeString &result) const;
+ inline UBool isStandard(void) const;
+ inline UBool isDaylight(void) const;
+ inline UBool isGeneric(void) const;
+
+private:
+ friend class ZoneStringFormat;
+ friend class ZoneStringSearchResultHandler;
+
+ ZoneStringInfo(const UnicodeString &id, const UnicodeString &str, TimeZoneTranslationType type);
+
+ UnicodeString fId;
+ UnicodeString fStr;
+ TimeZoneTranslationType fType;
+};
+
+inline UnicodeString& ZoneStringInfo::getID(UnicodeString &result) const {
+ return result.setTo(fId);
+}
+
+inline UnicodeString& ZoneStringInfo::getString(UnicodeString &result) const {
+ return result.setTo(fStr);
+}
+
+inline UBool ZoneStringInfo::isStandard(void) const {
+ return (fType == STANDARD_LONG || fType == STANDARD_SHORT);
+}
+
+inline UBool ZoneStringInfo::isDaylight(void) const {
+ return (fType == DAYLIGHT_LONG || fType == DAYLIGHT_SHORT);
+}
+
+inline UBool ZoneStringInfo::isGeneric(void) const {
+ return (fType == LOCATION || fType == GENERIC_LONG || fType == GENERIC_SHORT);
+}
+
+class SafeZoneStringFormatPtr;
+
+class ZoneStringFormat : public UMemory {
+public:
+ ZoneStringFormat(const UnicodeString* const* strings, int32_t rowCount, int32_t columnCount, UErrorCode &status);
+ ZoneStringFormat(const Locale& locale, UErrorCode &status);
+ virtual ~ZoneStringFormat();
+
+ static SafeZoneStringFormatPtr* getZoneStringFormat(const Locale& locale, UErrorCode &status);
+
+ /*
+ * Create a snapshot of old zone strings array for the given date
+ */
+ UnicodeString** createZoneStringsArray(UDate date, int32_t &rowCount, int32_t &colCount, UErrorCode &status) const;
+
+ const UnicodeString** getZoneStrings(int32_t &rowCount, int32_t &columnCount) const;
+
+ UnicodeString& getSpecificLongString(const Calendar &cal,
+ UnicodeString &result, UErrorCode &status) const;
+
+ UnicodeString& getSpecificShortString(const Calendar &cal,
+ UBool commonlyUsedOnly, UnicodeString &result, UErrorCode &status) const;
+
+ UnicodeString& getGenericLongString(const Calendar &cal,
+ UnicodeString &result, UErrorCode &status) const;
+
+ UnicodeString& getGenericShortString(const Calendar &cal,
+ UBool commonlyUsedOnly, UnicodeString &result, UErrorCode &status) const;
+
+ UnicodeString& getGenericLocationString(const Calendar &cal,
+ UnicodeString &result, UErrorCode &status) const;
+
+ const ZoneStringInfo* findSpecificLong(const UnicodeString &text, int32_t start,
+ int32_t &matchLength, UErrorCode &status) const;
+ const ZoneStringInfo* findSpecificShort(const UnicodeString &text, int32_t start,
+ int32_t &matchLength, UErrorCode &status) const;
+ const ZoneStringInfo* findGenericLong(const UnicodeString &text, int32_t start,
+ int32_t &matchLength, UErrorCode &status) const;
+ const ZoneStringInfo* findGenericShort(const UnicodeString &text, int32_t start,
+ int32_t &matchLength, UErrorCode &status) const;
+ const ZoneStringInfo* findGenericLocation(const UnicodeString &text, int32_t start,
+ int32_t &matchLength, UErrorCode &status) const;
+
+ // Following APIs are not used by SimpleDateFormat, but public for testing purpose
+ inline UnicodeString& getLongStandard(const UnicodeString &tzid, UDate date,
+ UnicodeString &result) const;
+ inline UnicodeString& getLongDaylight(const UnicodeString &tzid, UDate date,
+ UnicodeString &result) const;
+ inline UnicodeString& getLongGenericNonLocation(const UnicodeString &tzid, UDate date,
+ UnicodeString &result) const;
+ inline UnicodeString& getLongGenericPartialLocation(const UnicodeString &tzid, UDate date,
+ UnicodeString &result) const;
+ inline UnicodeString& getShortStandard(const UnicodeString &tzid, UDate date, UBool commonlyUsedOnly,
+ UnicodeString &result) const;
+ inline UnicodeString& getShortDaylight(const UnicodeString &tzid, UDate date, UBool commonlyUsedOnly,
+ UnicodeString &result) const;
+ inline UnicodeString& getShortGenericNonLocation(const UnicodeString &tzid, UDate date, UBool commonlyUsedOnly,
+ UnicodeString &result) const;
+ inline UnicodeString& getShortGenericPartialLocation(const UnicodeString &tzid, UDate date, UBool commonlyUsedOnly,
+ UnicodeString &result) const;
+ inline UnicodeString& getGenericLocation(const UnicodeString &tzid, UnicodeString &result) const;
+
+private:
+ Locale fLocale;
+ Hashtable fTzidToStrings;
+ Hashtable fMzidToStrings;
+ TextTrieMap fZoneStringsTrie;
+
+ /*
+ * Private method to get a zone string except generic partial location types.
+ */
+ UnicodeString& getString(const UnicodeString &tzid, TimeZoneTranslationTypeIndex typeIdx, UDate date,
+ UBool commonlyUsedOnly, UnicodeString& result) const;
+
+ /*
+ * Private method to get a generic string, with fallback logic involved,
+ * that is,
+ *
+ * 1. If a generic non-location string is avaiable for the zone, return it.
+ * 2. If a generic non-location string is associated with a metazone and
+ * the zone never use daylight time around the given date, use the standard
+ * string (if available).
+ *
+ * Note: In CLDR1.5.1, the same localization is used for generic and standard.
+ * In this case, we do not use the standard string and do the rest.
+ *
+ * 3. If a generic non-location string is associated with a metazone and
+ * the offset at the given time is different from the preferred zone for the
+ * current locale, then return the generic partial location string (if avaiable)
+ * 4. If a generic non-location string is not available, use generic location
+ * string.
+ */
+ UnicodeString& getGenericString(const Calendar &cal, UBool isShort, UBool commonlyUsedOnly,
+ UnicodeString &result, UErrorCode &status) const;
+
+ /*
+ * Private method to get a generic partial location string
+ */
+ UnicodeString& getGenericPartialLocationString(const UnicodeString &tzid, UBool isShort,
+ UDate date, UBool commonlyUsedOnly, UnicodeString &result) const;
+
+ /*
+ * Find a prefix matching time zone for the given zone string types.
+ * @param text The text contains a time zone string
+ * @param start The start index within the text
+ * @param types The bit mask representing a set of requested types
+ * @param matchLength Receives the match length
+ * @param status
+ * @return If any zone string matched for the requested types, returns a
+ * ZoneStringInfo for the longest match. If no matches are found for
+ * the requested types, returns a ZoneStringInfo for the longest match
+ * for any other types. If nothing matches at all, returns null.
+ */
+ const ZoneStringInfo* find(const UnicodeString &text, int32_t start, int32_t types,
+ int32_t &matchLength, UErrorCode &status) const;
+
+ UnicodeString& getRegion(UnicodeString &region) const;
+
+ static MessageFormat* getFallbackFormat(const Locale &locale, UErrorCode &status);
+ static MessageFormat* getRegionFormat(const Locale &locale, UErrorCode &status);
+ static const UChar* getZoneStringFromBundle(const UResourceBundle *zoneitem, const char *key);
+ static UBool isCommonlyUsed(const UResourceBundle *zoneitem);
+ static UnicodeString& getLocalizedCountry(const UnicodeString &countryCode, const Locale &locale,
+ UnicodeString &displayCountry);
+};
+
+inline UnicodeString&
+ZoneStringFormat::getLongStandard(const UnicodeString &tzid, UDate date,
+ UnicodeString &result) const {
+ return getString(tzid, ZSIDX_LONG_STANDARD, date, FALSE /* not used */, result);
+}
+
+inline UnicodeString&
+ZoneStringFormat::getLongDaylight(const UnicodeString &tzid, UDate date,
+ UnicodeString &result) const {
+ return getString(tzid, ZSIDX_LONG_DAYLIGHT, date, FALSE /* not used */, result);
+}
+
+inline UnicodeString&
+ZoneStringFormat::getLongGenericNonLocation(const UnicodeString &tzid, UDate date,
+ UnicodeString &result) const {
+ return getString(tzid, ZSIDX_LONG_GENERIC, date, FALSE /* not used */, result);
+}
+
+inline UnicodeString&
+ZoneStringFormat::getLongGenericPartialLocation(const UnicodeString &tzid, UDate date,
+ UnicodeString &result) const {
+ return getGenericPartialLocationString(tzid, FALSE, date, FALSE /* not used */, result);
+}
+
+inline UnicodeString&
+ZoneStringFormat::getShortStandard(const UnicodeString &tzid, UDate date, UBool commonlyUsedOnly,
+ UnicodeString &result) const {
+ return getString(tzid, ZSIDX_SHORT_STANDARD, date, commonlyUsedOnly, result);
+}
+
+inline UnicodeString&
+ZoneStringFormat::getShortDaylight(const UnicodeString &tzid, UDate date, UBool commonlyUsedOnly,
+ UnicodeString &result) const {
+ return getString(tzid, ZSIDX_SHORT_DAYLIGHT, date, commonlyUsedOnly, result);
+}
+
+inline UnicodeString&
+ZoneStringFormat::getShortGenericNonLocation(const UnicodeString &tzid, UDate date, UBool commonlyUsedOnly,
+ UnicodeString &result) const {
+ return getString(tzid, ZSIDX_SHORT_GENERIC, date, commonlyUsedOnly, result);
+}
+
+inline UnicodeString&
+ZoneStringFormat::getShortGenericPartialLocation(const UnicodeString &tzid, UDate date, UBool commonlyUsedOnly,
+ UnicodeString &result) const {
+ return getGenericPartialLocationString(tzid, TRUE, date, commonlyUsedOnly, result);
+}
+
+inline UnicodeString&
+ZoneStringFormat::getGenericLocation(const UnicodeString &tzid, UnicodeString &result) const {
+ return getString(tzid, ZSIDX_LOCATION, 0 /*not used*/, FALSE /*not used*/, result);
+}
+
+
+/*
+ * ZooneStrings is a container of localized zone strings used by ZoneStringFormat
+ */
+class ZoneStrings : public UMemory {
+public:
+ ZoneStrings(UnicodeString *strings, int32_t stringsCount, UBool commonlyUsed,
+ UnicodeString **genericPartialLocationStrings, int32_t genericRowCount, int32_t genericColCount);
+ virtual ~ZoneStrings();
+
+ UnicodeString& getString(int32_t typeIdx, UnicodeString &result) const;
+ inline UBool isShortFormatCommonlyUsed(void) const;
+ UnicodeString& getGenericPartialLocationString(const UnicodeString &mzid, UBool isShort,
+ UBool commonlyUsedOnly, UnicodeString &result) const;
+
+private:
+ UnicodeString *fStrings;
+ int32_t fStringsCount;
+ UBool fIsCommonlyUsed;
+ UnicodeString **fGenericPartialLocationStrings;
+ int32_t fGenericPartialLocationRowCount;
+ int32_t fGenericPartialLocationColCount;
+};
+
+inline UBool
+ZoneStrings::isShortFormatCommonlyUsed(void) const {
+ return fIsCommonlyUsed;
+}
+
+/*
+ * ZoneStringSearchResultHandler is an implementation of
+ * TextTrieMapSearchHandler. This class is used by ZoneStringFormat
+ * for collecting search results for localized zone strings.
+ */
+class ZoneStringSearchResultHandler : public UMemory, TextTrieMapSearchResultHandler {
+public:
+ ZoneStringSearchResultHandler(UErrorCode &status);
+ virtual ~ZoneStringSearchResultHandler();
+
+ virtual UBool handleMatch(int32_t matchLength, const UVector *values, UErrorCode &status);
+ int32_t countMatches(void);
+ const ZoneStringInfo* getMatch(int32_t index, int32_t &matchLength);
+ void clear(void);
+
+private:
+ UVector fResults;
+ int32_t fMatchLen[ZSIDX_COUNT];
+};
+
+
+/*
+ * ZoneStringFormat cache implementation
+ */
+class ZSFCacheEntry : public UMemory {
+public:
+ ~ZSFCacheEntry();
+
+ void delRef(void);
+ const ZoneStringFormat* getZoneStringFormat(void);
+
+private:
+ friend class ZSFCache;
+
+ ZSFCacheEntry(const Locale &locale, ZoneStringFormat *zsf, ZSFCacheEntry *next);
+
+ Locale fLocale;
+ ZoneStringFormat *fZoneStringFormat;
+ ZSFCacheEntry *fNext;
+ int32_t fRefCount;
+};
+
+class SafeZoneStringFormatPtr : public UMemory {
+public:
+ ~SafeZoneStringFormatPtr();
+ const ZoneStringFormat* get() const;
+
+private:
+ friend class ZSFCache;
+
+ SafeZoneStringFormatPtr(ZSFCacheEntry *cacheEntry);
+
+ ZSFCacheEntry *fCacheEntry;
+};
+
+class ZSFCache : public UMemory {
+public:
+ ZSFCache(int32_t capacity);
+ ~ZSFCache();
+
+ SafeZoneStringFormatPtr* get(const Locale &locale, UErrorCode &status);
+
+private:
+ int32_t fCapacity;
+ ZSFCacheEntry *fFirst;
+};
+
+U_NAMESPACE_END
+
+#endif /* #if !UCONFIG_NO_FORMATTING */
+
+#endif // ZSTRFMT_H