ICU-449 TimeZone equivalency support
X-SVN-Rev: 2523
This commit is contained in:
parent
741b94d474
commit
0f626e9c1e
@ -565,6 +565,34 @@ DateFormatSymbols::initializeData(const Locale& locale, UErrorCode& status, UBoo
|
||||
* @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
|
||||
{
|
||||
// {sfb} kludge to support case-insensitive comparison
|
||||
UnicodeString lcaseID(ID);
|
||||
@ -573,8 +601,9 @@ int32_t DateFormatSymbols::getZoneIndex(const UnicodeString& ID) const
|
||||
for(int32_t index = 0; index < fZoneStringsRowCount; index++) {
|
||||
UnicodeString lcase(fZoneStrings[index][0]);
|
||||
lcase.toLower();
|
||||
if (lcaseID == lcase)
|
||||
if (lcaseID == lcase) {
|
||||
return index;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
|
@ -218,6 +218,22 @@ TimeZone::createSystemTimeZone(const UnicodeString& name) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const TZEquivalencyGroup *eg = lookupEquivalencyGroup(name);
|
||||
if (eg != 0) {
|
||||
return eg->isDST ?
|
||||
new SimpleTimeZone(eg->u.d.zone, name) :
|
||||
new SimpleTimeZone(eg->u.s.zone, name);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Lookup the given ID in the system time zone equivalency group table.
|
||||
* Return a pointer to the equivalency group, or NULL if not found.
|
||||
* DATA MUST BE INITIALIZED AND NON-NULL.
|
||||
*/
|
||||
const TZEquivalencyGroup*
|
||||
TimeZone::lookupEquivalencyGroup(const UnicodeString& id) {
|
||||
// Perform a binary search. Possible optimization: Unroll the
|
||||
// search. Not worth it given the small number of zones (416 in
|
||||
// 1999j).
|
||||
@ -227,22 +243,15 @@ TimeZone::createSystemTimeZone(const UnicodeString& name) {
|
||||
// Invariant: match, if present, must be in the range [low,
|
||||
// high).
|
||||
uint32_t i = (low + high) / 2;
|
||||
int8_t c = name.compare(ZONE_IDS[i]);
|
||||
int8_t c = id.compare(ZONE_IDS[i]);
|
||||
if (c == 0) {
|
||||
const TZEquivalencyGroup *eg = (TZEquivalencyGroup*)
|
||||
((int8_t*)DATA + INDEX_BY_ID[i]);
|
||||
// NOTE: standard zones must be before DST zones. We test
|
||||
// for this when loading up the data; see loadZoneData().
|
||||
return eg->isDST ?
|
||||
new SimpleTimeZone(eg->u.d.zone, name) :
|
||||
new SimpleTimeZone(eg->u.s.zone, name);
|
||||
return (TZEquivalencyGroup*) ((int8_t*)DATA + INDEX_BY_ID[i]);
|
||||
} else if (c < 0) {
|
||||
high = i;
|
||||
} else {
|
||||
low = i + 1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -473,6 +482,39 @@ TimeZone::createAvailableIDs(int32_t& numIDs)
|
||||
|
||||
// ---------------------------------------
|
||||
|
||||
int32_t
|
||||
TimeZone::countEquivalentIDs(const UnicodeString& id) {
|
||||
if (!DATA_LOADED) {
|
||||
loadZoneData();
|
||||
}
|
||||
if (0 == DATA) {
|
||||
return 0;
|
||||
}
|
||||
const TZEquivalencyGroup *eg = lookupEquivalencyGroup(id);
|
||||
return (eg != 0) ? (eg->isDST ? eg->u.d.count : eg->u.s.count) : 0;
|
||||
}
|
||||
|
||||
// ---------------------------------------
|
||||
|
||||
const UnicodeString
|
||||
TimeZone::getEquivalentID(const UnicodeString& id, int32_t index) {
|
||||
if (!DATA_LOADED) {
|
||||
loadZoneData();
|
||||
}
|
||||
if (0 != DATA) {
|
||||
const TZEquivalencyGroup *eg = lookupEquivalencyGroup(id);
|
||||
if (eg != 0) {
|
||||
const uint16_t *p = eg->isDST ? &eg->u.d.count : &eg->u.s.count;
|
||||
if (index >= 0 && index < *p) {
|
||||
return ZONE_IDS[p[index+1]];
|
||||
}
|
||||
}
|
||||
}
|
||||
return UnicodeString();
|
||||
}
|
||||
|
||||
// ---------------------------------------
|
||||
|
||||
|
||||
UnicodeString&
|
||||
TimeZone::getDisplayName(UnicodeString& result) const
|
||||
|
@ -344,6 +344,9 @@ private:
|
||||
*/
|
||||
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 and reset the fIsOwned flag
|
||||
* to indicate that arrays have been deleted.
|
||||
|
@ -34,6 +34,7 @@
|
||||
class SimpleTimeZone;
|
||||
struct TZHeader;
|
||||
struct OffsetIndex;
|
||||
struct TZEquivalencyGroup;
|
||||
|
||||
/**
|
||||
* <code>TimeZone</code> represents a time zone offset, and also figures out daylight
|
||||
@ -171,6 +172,44 @@ public:
|
||||
*/
|
||||
static const UnicodeString** const createAvailableIDs(int32_t& numIDs);
|
||||
|
||||
/**
|
||||
* Returns the number of IDs in the equivalency group that
|
||||
* includes the given ID. An equivalency group contains zones
|
||||
* that have the same GMT offset and rules.
|
||||
*
|
||||
* <p>The returned count includes the given ID; it is always >= 1.
|
||||
* The given ID must be a system time zone. If it is not, returns
|
||||
* zero.
|
||||
* @param id a system time zone ID
|
||||
* @return the number of zones in the equivalency group containing
|
||||
* 'id', or zero if 'id' is not a valid system ID
|
||||
* @see #getEquivalentID
|
||||
* @draft
|
||||
*/
|
||||
static int32_t countEquivalentIDs(const UnicodeString& id);
|
||||
|
||||
/**
|
||||
* Returns an ID in the equivalency group that
|
||||
* includes the given ID. An equivalency group contains zones
|
||||
* that have the same GMT offset and rules.
|
||||
*
|
||||
* <p>The given index must be in the range 0..n-1, where n is the
|
||||
* value returned by <code>countEquivalentIDs(id)</code>. For
|
||||
* some value of 'index', the returned value will be equal to the
|
||||
* given id. If the given id is not a valid system time zone, or
|
||||
* if 'index' is out of range, then returns an empty string.
|
||||
* @param id a system time zone ID
|
||||
* @param index a value from 0 to n-1, where n is the value
|
||||
* returned by <code>countEquivalentIDs(id)</code>
|
||||
* @return the ID of the index-th zone in the equivalency group
|
||||
* containing 'id', or an empty string if 'id' is not a valid
|
||||
* system ID or 'index' is out of range
|
||||
* @see #countEquivalentIDs
|
||||
* @draft
|
||||
*/
|
||||
static const UnicodeString getEquivalentID(const UnicodeString& id,
|
||||
int32_t index);
|
||||
|
||||
/**
|
||||
* Creates a new copy of the default TimeZone for this host. Unless the default time
|
||||
* zone has already been set using adoptDefault() or setDefault(), the default is
|
||||
@ -544,6 +583,9 @@ private:
|
||||
// See source file for documentation
|
||||
static TimeZone* createSystemTimeZone(const UnicodeString& name);
|
||||
|
||||
// See source file for documentation
|
||||
static const TZEquivalencyGroup* lookupEquivalencyGroup(const UnicodeString& id);
|
||||
|
||||
UnicodeString fID; // this time zone's ID
|
||||
};
|
||||
|
||||
|
@ -17,7 +17,7 @@
|
||||
// class TimeZoneRegressionTest
|
||||
// *****************************************************************************
|
||||
|
||||
#define CASE(id,test) case id: name = #test; if (exec) { logln(#test "---"); logln((UnicodeString)""); test(); } break;
|
||||
#define CASE(id,test) case id: name = #test; if (exec) { logln(#test "---"); logln((UnicodeString)""); test(); } break
|
||||
|
||||
void
|
||||
TimeZoneRegressionTest::runIndexedTest( int32_t index, UBool exec, const char* &name, char* /*par*/ )
|
||||
@ -25,22 +25,23 @@ TimeZoneRegressionTest::runIndexedTest( int32_t index, UBool exec, const char* &
|
||||
// if (exec) logln((UnicodeString)"TestSuite NumberFormatRegressionTest");
|
||||
switch (index) {
|
||||
|
||||
CASE(0, Test4052967)
|
||||
CASE(1, Test4073209)
|
||||
CASE(2, Test4073215)
|
||||
CASE(3, Test4084933)
|
||||
CASE(4, Test4096952)
|
||||
CASE(5, Test4109314)
|
||||
CASE(6, Test4126678)
|
||||
CASE(7, Test4151406)
|
||||
CASE(8, Test4151429)
|
||||
CASE(9, Test4154537)
|
||||
CASE(10, Test4154542)
|
||||
CASE(11, Test4154650)
|
||||
CASE(12, Test4154525)
|
||||
CASE(13, Test4162593)
|
||||
CASE(14, TestJ186)
|
||||
CASE(15, TestJDK12API)
|
||||
CASE(0, Test4052967);
|
||||
CASE(1, Test4073209);
|
||||
CASE(2, Test4073215);
|
||||
CASE(3, Test4084933);
|
||||
CASE(4, Test4096952);
|
||||
CASE(5, Test4109314);
|
||||
CASE(6, Test4126678);
|
||||
CASE(7, Test4151406);
|
||||
CASE(8, Test4151429);
|
||||
CASE(9, Test4154537);
|
||||
CASE(10, Test4154542);
|
||||
CASE(11, Test4154650);
|
||||
CASE(12, Test4154525);
|
||||
CASE(13, Test4162593);
|
||||
CASE(14, TestJ186);
|
||||
CASE(15, TestJ449);
|
||||
CASE(16, TestJDK12API);
|
||||
|
||||
default: name = ""; break;
|
||||
}
|
||||
@ -877,6 +878,63 @@ void TimeZoneRegressionTest::TestJ186() {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test to see if DateFormat understands zone equivalency groups. It
|
||||
* might seem that this should be a DateFormat test, but it's really a
|
||||
* TimeZone test -- the changes to DateFormat are minor.
|
||||
*
|
||||
* We use two known, stable zones that shouldn't change much over time
|
||||
* -- America/Vancouver and America/Los_Angeles. However, they MAY
|
||||
* change at some point -- if that happens, replace them with any two
|
||||
* zones in an equivalency group where one zone has localized name
|
||||
* data, and the other doesn't, in some locale.
|
||||
*/
|
||||
void TimeZoneRegressionTest::TestJ449() {
|
||||
UErrorCode status = U_ZERO_ERROR;
|
||||
UnicodeString str;
|
||||
|
||||
// Modify the following three as necessary. The two IDs must
|
||||
// specify two zones in the same equivalency group. One must have
|
||||
// locale data in 'loc'; the other must not.
|
||||
const char* idWithLocaleData = "America/Los_Angeles";
|
||||
const char* idWithoutLocaleData = "America/Vancouver";
|
||||
const Locale loc("en", "", "");
|
||||
|
||||
TimeZone *zoneWith = TimeZone::createTimeZone(idWithLocaleData);
|
||||
TimeZone *zoneWithout = TimeZone::createTimeZone(idWithoutLocaleData);
|
||||
// Make sure we got valid zones
|
||||
if (zoneWith->getID(str) != UnicodeString(idWithLocaleData) ||
|
||||
zoneWithout->getID(str) != UnicodeString(idWithoutLocaleData)) {
|
||||
errln("Fail: Unable to create zones");
|
||||
} else {
|
||||
GregorianCalendar calWith(*zoneWith, status);
|
||||
GregorianCalendar calWithout(*zoneWithout, status);
|
||||
SimpleDateFormat fmt("MMM d yyyy hh:mm a zzz", loc, status);
|
||||
if (U_FAILURE(status)) {
|
||||
errln("Fail: Unable to create GregorianCalendar/SimpleDateFormat");
|
||||
} else {
|
||||
UDate date = 0;
|
||||
UnicodeString strWith, strWithout;
|
||||
fmt.setCalendar(calWith);
|
||||
fmt.format(date, strWith);
|
||||
fmt.setCalendar(calWithout);
|
||||
fmt.format(date, strWithout);
|
||||
if (strWith == strWithout) {
|
||||
logln((UnicodeString)"Ok: " + idWithLocaleData + " -> " +
|
||||
strWith + "; " + idWithoutLocaleData + " -> " +
|
||||
strWithout);
|
||||
} else {
|
||||
errln((UnicodeString)"FAIL: " + idWithLocaleData + " -> " +
|
||||
strWith + "; " + idWithoutLocaleData + " -> " +
|
||||
strWithout);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
delete zoneWith;
|
||||
delete zoneWithout;
|
||||
}
|
||||
|
||||
// test new API for JDK 1.2 8/31 putback
|
||||
void
|
||||
TimeZoneRegressionTest::TestJDK12API()
|
||||
|
@ -40,6 +40,7 @@ public:
|
||||
void Test4154525(void);
|
||||
void Test4162593(void);
|
||||
void TestJ186(void);
|
||||
void TestJ449(void);
|
||||
void TestJDK12API(void);
|
||||
|
||||
UBool checkCalendar314(GregorianCalendar *testCal, TimeZone *testTZ);
|
||||
|
Loading…
Reference in New Issue
Block a user