blob: 0b45476db4f5d3a2da2ecab94243b4523aba25db [file] [log] [blame]
/*
*******************************************************************************
* Copyright (C) 2007, International Business Machines Corporation and *
* others. All Rights Reserved. *
*******************************************************************************
*/
#include "unicode/utypes.h"
#if !UCONFIG_NO_FORMATTING
#include "unicode/dtrule.h"
#include "unicode/tzrule.h"
#include "unicode/rbtz.h"
#include "unicode/simpletz.h"
#include "unicode/tzrule.h"
#include "unicode/calendar.h"
#include "unicode/ucal.h"
#include "unicode/unistr.h"
#include "unicode/tztrans.h"
#include "unicode/vtzone.h"
#include "tzrulets.h"
#define CASE(id,test) case id: name = #test; if (exec) { logln(#test "---"); logln((UnicodeString)""); test(); } break
#define HOUR (60*60*1000)
static const char *const TESTZIDS[] = {
"AGT",
"America/New_York",
"America/Los_Angeles",
"America/Indiana/Indianapolis",
"America/Havana",
"Europe/Lisbon",
"Europe/Paris",
"Asia/Tokyo",
"Asia/Sakhalin",
"Africa/Cairo",
"Africa/Windhoek",
"Australia/Sydney",
"Etc/GMT+8"
};
class TestZIDEnumeration : public StringEnumeration {
public:
TestZIDEnumeration(UBool all = FALSE);
~TestZIDEnumeration();
virtual int32_t count(UErrorCode& /*status*/) const {
return len;
}
virtual const UnicodeString *snext(UErrorCode& status);
virtual void reset(UErrorCode& status);
static inline UClassID getStaticClassID() {
return (UClassID)&fgClassID;
}
virtual UClassID getDynamicClassID() const {
return getStaticClassID();
}
private:
static const char fgClassID;
int32_t idx;
int32_t len;
StringEnumeration *tzenum;
};
const char TestZIDEnumeration::fgClassID = 0;
TestZIDEnumeration::TestZIDEnumeration(UBool all)
: idx(0) {
UErrorCode status = U_ZERO_ERROR;
if (all) {
tzenum = TimeZone::createEnumeration();
len = tzenum->count(status);
} else {
tzenum = NULL;
len = (int32_t)sizeof(TESTZIDS)/sizeof(TESTZIDS[0]);
}
}
TestZIDEnumeration::~TestZIDEnumeration() {
if (tzenum != NULL) {
delete tzenum;
}
}
const UnicodeString*
TestZIDEnumeration::snext(UErrorCode& status) {
if (tzenum != NULL) {
return tzenum->snext(status);
} else if (U_SUCCESS(status) && idx < len) {
unistr = UnicodeString(TESTZIDS[idx++], "");
return &unistr;
}
return NULL;
}
void
TestZIDEnumeration::reset(UErrorCode& status) {
if (tzenum != NULL) {
tzenum->reset(status);
} else {
idx = 0;
}
}
void TimeZoneRuleTest::runIndexedTest( int32_t index, UBool exec, const char* &name, char* /*par*/ )
{
if (exec) {
logln("TestSuite TestTimeZoneRule");
}
switch (index) {
CASE(0, TestSimpleRuleBasedTimeZone);
CASE(1, TestHistoricalRuleBasedTimeZone);
CASE(2, TestOlsonTransition);
CASE(3, TestRBTZTransition);
CASE(4, TestHasEquivalentTransitions);
CASE(5, TestVTimeZoneRoundTrip);
CASE(6, TestVTimeZoneRoundTripPartial);
CASE(7, TestVTimeZoneSimpleWrite);
CASE(8, TestVTimeZoneHeaderProps);
CASE(9, TestGetSimpleRules);
default: name = ""; break;
}
}
/*
* Compare SimpleTimeZone with equivalent RBTZ
*/
void
TimeZoneRuleTest::TestSimpleRuleBasedTimeZone(void) {
UErrorCode status = U_ZERO_ERROR;
SimpleTimeZone stz(-1*HOUR, "TestSTZ",
UCAL_SEPTEMBER, -30, -UCAL_SATURDAY, 1*HOUR, SimpleTimeZone::WALL_TIME,
UCAL_FEBRUARY, 2, UCAL_SUNDAY, 1*HOUR, SimpleTimeZone::WALL_TIME,
1*HOUR, status);
if (U_FAILURE(status)) {
errln("FAIL: Couldn't create SimpleTimezone.");
}
DateTimeRule *dtr;
AnnualTimeZoneRule *atzr;
int32_t STARTYEAR = 2000;
InitialTimeZoneRule *ir = new InitialTimeZoneRule(
"RBTZ_Initial", // Initial time Name
-1*HOUR, // Raw offset
1*HOUR); // DST saving amount
// Original rules
RuleBasedTimeZone *rbtz1 = new RuleBasedTimeZone("RBTZ1", ir->clone());
dtr = new DateTimeRule(UCAL_SEPTEMBER, 30, UCAL_SATURDAY, FALSE,
1*HOUR, DateTimeRule::WALL_TIME); // SUN<=30 in September, at 1AM wall time
atzr = new AnnualTimeZoneRule("RBTZ_DST1",
-1*HOUR /*rawOffset*/, 1*HOUR /*dstSavings*/, dtr,
STARTYEAR, AnnualTimeZoneRule::MAX_YEAR);
rbtz1->addTransitionRule(atzr, status);
if (U_FAILURE(status)) {
errln("FAIL: couldn't add AnnualTimeZoneRule 1-1.");
}
dtr = new DateTimeRule(UCAL_FEBRUARY, 2, UCAL_SUNDAY,
1*HOUR, DateTimeRule::WALL_TIME); // 2nd Sunday in February, at 1AM wall time
atzr = new AnnualTimeZoneRule("RBTZ_STD1",
-1*HOUR /*rawOffset*/, 0 /*dstSavings*/, dtr,
STARTYEAR, AnnualTimeZoneRule::MAX_YEAR);
rbtz1->addTransitionRule(atzr, status);
if (U_FAILURE(status)) {
errln("FAIL: couldn't add AnnualTimeZoneRule 1-2.");
}
rbtz1->complete(status);
if (U_FAILURE(status)) {
errln("FAIL: couldn't complete RBTZ 1.");
}
// Equivalent, but different date rule type
RuleBasedTimeZone *rbtz2 = new RuleBasedTimeZone("RBTZ2", ir->clone());
dtr = new DateTimeRule(UCAL_SEPTEMBER, -1, UCAL_SATURDAY,
1*HOUR, DateTimeRule::WALL_TIME); // Last Sunday in September at 1AM wall time
atzr = new AnnualTimeZoneRule("RBTZ_DST2", -1*HOUR, 1*HOUR, dtr, STARTYEAR, AnnualTimeZoneRule::MAX_YEAR);
rbtz2->addTransitionRule(atzr, status);
if (U_FAILURE(status)) {
errln("FAIL: couldn't add AnnualTimeZoneRule 2-1.");
}
dtr = new DateTimeRule(UCAL_FEBRUARY, 8, UCAL_SUNDAY, true,
1*HOUR, DateTimeRule::WALL_TIME); // SUN>=8 in February, at 1AM wall time
atzr = new AnnualTimeZoneRule("RBTZ_STD2", -1*HOUR, 0, dtr, STARTYEAR, AnnualTimeZoneRule::MAX_YEAR);
rbtz2->addTransitionRule(atzr, status);
if (U_FAILURE(status)) {
errln("FAIL: couldn't add AnnualTimeZoneRule 2-2.");
}
rbtz2->complete(status);
if (U_FAILURE(status)) {
errln("FAIL: couldn't complete RBTZ 2");
}
// Equivalent, but different time rule type
RuleBasedTimeZone *rbtz3 = new RuleBasedTimeZone("RBTZ3", ir->clone());
dtr = new DateTimeRule(UCAL_SEPTEMBER, 30, UCAL_SATURDAY, false,
2*HOUR, DateTimeRule::UTC_TIME);
atzr = new AnnualTimeZoneRule("RBTZ_DST3", -1*HOUR, 1*HOUR, dtr, STARTYEAR, AnnualTimeZoneRule::MAX_YEAR);
rbtz3->addTransitionRule(atzr, status);
if (U_FAILURE(status)) {
errln("FAIL: couldn't add AnnualTimeZoneRule 3-1.");
}
dtr = new DateTimeRule(UCAL_FEBRUARY, 2, UCAL_SUNDAY,
0*HOUR, DateTimeRule::STANDARD_TIME);
atzr = new AnnualTimeZoneRule("RBTZ_STD3", -1*HOUR, 0, dtr, STARTYEAR, AnnualTimeZoneRule::MAX_YEAR);
rbtz3->addTransitionRule(atzr, status);
if (U_FAILURE(status)) {
errln("FAIL: couldn't add AnnualTimeZoneRule 3-2.");
}
rbtz3->complete(status);
if (U_FAILURE(status)) {
errln("FAIL: couldn't complete RBTZ 3");
}
// Check equivalency for 10 years
UDate start = getUTCMillis(STARTYEAR, UCAL_JANUARY, 1);
UDate until = getUTCMillis(STARTYEAR + 10, UCAL_JANUARY, 1);
if (!(stz.hasEquivalentTransitions(*rbtz1, start, until, TRUE, status))) {
errln("FAIL: rbtz1 must be equivalent to the SimpleTimeZone in the time range.");
}
if (U_FAILURE(status)) {
errln("FAIL: error returned from hasEquivalentTransitions");
}
if (!(stz.hasEquivalentTransitions(*rbtz2, start, until, TRUE, status))) {
errln("FAIL: rbtz2 must be equivalent to the SimpleTimeZone in the time range.");
}
if (U_FAILURE(status)) {
errln("FAIL: error returned from hasEquivalentTransitions");
}
if (!(stz.hasEquivalentTransitions(*rbtz3, start, until, TRUE, status))) {
errln("FAIL: rbtz3 must be equivalent to the SimpleTimeZone in the time range.");
}
if (U_FAILURE(status)) {
errln("FAIL: error returned from hasEquivalentTransitions");
}
delete ir;
delete rbtz1;
delete rbtz2;
delete rbtz3;
}
/*
* Test equivalency between OlsonTimeZone and custom RBTZ representing the
* equivalent rules in a certain time range
*/
void
TimeZoneRuleTest::TestHistoricalRuleBasedTimeZone(void) {
UErrorCode status = U_ZERO_ERROR;
// Compare to America/New_York with equivalent RBTZ
BasicTimeZone *ny = (BasicTimeZone*)TimeZone::createTimeZone("America/New_York");
//RBTZ
InitialTimeZoneRule *ir = new InitialTimeZoneRule("EST", -5*HOUR, 0);
RuleBasedTimeZone *rbtz = new RuleBasedTimeZone("EST5EDT", ir);
DateTimeRule *dtr;
AnnualTimeZoneRule *tzr;
// Standard time
dtr = new DateTimeRule(UCAL_OCTOBER, -1, UCAL_SUNDAY,
2*HOUR, DateTimeRule::WALL_TIME); // Last Sunday in October, at 2AM wall time
tzr = new AnnualTimeZoneRule("EST", -5*HOUR /*rawOffset*/, 0 /*dstSavings*/, dtr, 1967, 2006);
rbtz->addTransitionRule(tzr, status);
if (U_FAILURE(status)) {
errln("FAIL: couldn't add AnnualTimeZoneRule 1.");
}
dtr = new DateTimeRule(UCAL_NOVEMBER, 1, UCAL_SUNDAY,
true, 2*HOUR, DateTimeRule::WALL_TIME); // SUN>=1 in November, at 2AM wall time
tzr = new AnnualTimeZoneRule("EST", -5*HOUR, 0, dtr, 2007, AnnualTimeZoneRule::MAX_YEAR);
rbtz->addTransitionRule(tzr, status);
if (U_FAILURE(status)) {
errln("FAIL: couldn't add AnnualTimeZoneRule 2.");
}
// Daylight saving time
dtr = new DateTimeRule(UCAL_APRIL, -1, UCAL_SUNDAY,
2*HOUR, DateTimeRule::WALL_TIME); // Last Sunday in April, at 2AM wall time
tzr = new AnnualTimeZoneRule("EDT", -5*HOUR, 1*HOUR, dtr, 1967, 1973);
rbtz->addTransitionRule(tzr, status);
if (U_FAILURE(status)) {
errln("FAIL: couldn't add AnnualTimeZoneRule 3.");
}
dtr = new DateTimeRule(UCAL_JANUARY, 6,
2*HOUR, DateTimeRule::WALL_TIME); // January 6, at 2AM wall time
tzr = new AnnualTimeZoneRule("EDT", -5*HOUR, 1*HOUR, dtr, 1974, 1974);
rbtz->addTransitionRule(tzr, status);
if (U_FAILURE(status)) {
errln("FAIL: couldn't add AnnualTimeZoneRule 4.");
}
dtr = new DateTimeRule(UCAL_FEBRUARY, 23,
2*HOUR, DateTimeRule::WALL_TIME); // February 23, at 2AM wall time
tzr = new AnnualTimeZoneRule("EDT", -5*HOUR, 1*HOUR, dtr, 1975, 1975);
rbtz->addTransitionRule(tzr, status);
if (U_FAILURE(status)) {
errln("FAIL: couldn't add AnnualTimeZoneRule 5.");
}
dtr = new DateTimeRule(UCAL_APRIL, -1, UCAL_SUNDAY,
2*HOUR, DateTimeRule::WALL_TIME); // Last Sunday in April, at 2AM wall time
tzr = new AnnualTimeZoneRule("EDT", -5*HOUR, 1*HOUR, dtr, 1976, 1986);
rbtz->addTransitionRule(tzr, status);
if (U_FAILURE(status)) {
errln("FAIL: couldn't add AnnualTimeZoneRule 6.");
}
dtr = new DateTimeRule(UCAL_APRIL, 1, UCAL_SUNDAY,
true, 2*HOUR, DateTimeRule::WALL_TIME); // SUN>=1 in April, at 2AM wall time
tzr = new AnnualTimeZoneRule("EDT", -5*HOUR, 1*HOUR, dtr, 1987, 2006);
rbtz->addTransitionRule(tzr, status);
if (U_FAILURE(status)) {
errln("FAIL: couldn't add AnnualTimeZoneRule 7.");
}
dtr = new DateTimeRule(UCAL_MARCH, 8, UCAL_SUNDAY,
true, 2*HOUR, DateTimeRule::WALL_TIME); // SUN>=8 in March, at 2AM wall time
tzr = new AnnualTimeZoneRule("EDT", -5*HOUR, 1*HOUR, dtr, 2007, AnnualTimeZoneRule::MAX_YEAR);
rbtz->addTransitionRule(tzr, status);
if (U_FAILURE(status)) {
errln("FAIL: couldn't add AnnualTimeZoneRule 7.");
}
rbtz->complete(status);
if (U_FAILURE(status)) {
errln("FAIL: couldn't complete RBTZ.");
}
// hasEquivalentTransitions
UDate jan1_1950 = getUTCMillis(1950, UCAL_JANUARY, 1);
UDate jan1_1967 = getUTCMillis(1971, UCAL_JANUARY, 1);
UDate jan1_2010 = getUTCMillis(2010, UCAL_JANUARY, 1);
if (!ny->hasEquivalentTransitions(*rbtz, jan1_1967, jan1_2010, TRUE, status)) {
errln("FAIL: The RBTZ must be equivalent to America/New_York between 1967 and 2010");
}
if (U_FAILURE(status)) {
errln("FAIL: error returned from hasEquivalentTransitions");
}
if (ny->hasEquivalentTransitions(*rbtz, jan1_1950, jan1_2010, TRUE, status)) {
errln("FAIL: The RBTZ must not be equivalent to America/New_York between 1950 and 2010");
}
if (U_FAILURE(status)) {
errln("FAIL: error returned from hasEquivalentTransitions");
}
// Same with above, but calling RBTZ#hasEquivalentTransitions against OlsonTimeZone
if (!rbtz->hasEquivalentTransitions(*ny, jan1_1967, jan1_2010, TRUE, status)) {
errln("FAIL: The RBTZ must be equivalent to America/New_York between 1967 and 2010");
}
if (U_FAILURE(status)) {
errln("FAIL: error returned from hasEquivalentTransitions");
}
if (rbtz->hasEquivalentTransitions(*ny, jan1_1950, jan1_2010, TRUE, status)) {
errln("FAIL: The RBTZ must not be equivalent to America/New_York between 1950 and 2010");
}
if (U_FAILURE(status)) {
errln("FAIL: error returned from hasEquivalentTransitions");
}
delete ny;
delete rbtz;
}
/*
* Check if transitions returned by getNextTransition/getPreviousTransition
* are actual time transitions.
*/
void
TimeZoneRuleTest::TestOlsonTransition(void) {
const int32_t TESTYEARS[][2] = {
{1895, 1905}, // including int32 minimum second
{1965, 1975}, // including the epoch
{1995, 2015}, // practical year range
{0,0}
};
UErrorCode status = U_ZERO_ERROR;
TestZIDEnumeration tzenum(!quick);
while (TRUE) {
const UnicodeString *tzid = tzenum.snext(status);
if (tzid == NULL) {
break;
}
if (U_FAILURE(status)) {
errln("FAIL: error returned while enumerating timezone IDs.");
break;
}
BasicTimeZone *tz = (BasicTimeZone*)TimeZone::createTimeZone(*tzid);
for (int32_t i = 0; TESTYEARS[i][0] != 0 || TESTYEARS[i][1] != 0; i++) {
UDate lo = getUTCMillis(TESTYEARS[i][0], UCAL_JANUARY, 1);
UDate hi = getUTCMillis(TESTYEARS[i][1], UCAL_JANUARY, 1);
verifyTransitions(*tz, lo, hi);
}
delete tz;
}
}
/*
* Check if an OlsonTimeZone and its equivalent RBTZ have the exact same
* transitions.
*/
void
TimeZoneRuleTest::TestRBTZTransition(void) {
const int32_t STARTYEARS[] = {
1900,
1960,
1990,
2010,
0
};
UErrorCode status = U_ZERO_ERROR;
TestZIDEnumeration tzenum(!quick);
while (TRUE) {
const UnicodeString *tzid = tzenum.snext(status);
if (tzid == NULL) {
break;
}
if (U_FAILURE(status)) {
errln("FAIL: error returned while enumerating timezone IDs.");
break;
}
BasicTimeZone *tz = (BasicTimeZone*)TimeZone::createTimeZone(*tzid);
int32_t ruleCount = tz->countTransitionRules(status);
const InitialTimeZoneRule *initial;
const TimeZoneRule **trsrules = new const TimeZoneRule*[ruleCount];
tz->getTimeZoneRules(initial, trsrules, ruleCount, status);
if (U_FAILURE(status)) {
errln((UnicodeString)"FAIL: failed to get the TimeZoneRules from time zone " + *tzid);
}
RuleBasedTimeZone *rbtz = new RuleBasedTimeZone(*tzid, initial->clone());
if (U_FAILURE(status)) {
errln((UnicodeString)"FAIL: failed to get the transition rule count from time zone " + *tzid);
}
for (int32_t i = 0; i < ruleCount; i++) {
rbtz->addTransitionRule(trsrules[i]->clone(), status);
if (U_FAILURE(status)) {
errln((UnicodeString)"FAIL: failed to add a transition rule at index " + i + " to the RBTZ for " + *tzid);
}
}
rbtz->complete(status);
if (U_FAILURE(status)) {
errln((UnicodeString)"FAIL: complete() failed for the RBTZ for " + *tzid);
}
for (int32_t idx = 0; STARTYEARS[idx] != 0; idx++) {
UDate start = getUTCMillis(STARTYEARS[idx], UCAL_JANUARY, 1);
UDate until = getUTCMillis(STARTYEARS[idx] + 20, UCAL_JANUARY, 1);
// Compare the original OlsonTimeZone with the RBTZ starting the startTime for 20 years
// Ascending
compareTransitionsAscending(*tz, *rbtz, start, until, FALSE);
// Ascending/inclusive
compareTransitionsAscending(*tz, *rbtz, start + 1, until, TRUE);
// Descending
compareTransitionsDescending(*tz, *rbtz, start, until, FALSE);
// Descending/inclusive
compareTransitionsDescending(*tz, *rbtz, start + 1, until, TRUE);
}
delete [] trsrules;
delete rbtz;
delete tz;
}
}
void
TimeZoneRuleTest::TestHasEquivalentTransitions(void) {
// America/New_York and America/Indiana/Indianapolis are equivalent
// since 2006
UErrorCode status = U_ZERO_ERROR;
BasicTimeZone *newyork = (BasicTimeZone*)TimeZone::createTimeZone("America/New_York");
BasicTimeZone *indianapolis = (BasicTimeZone*)TimeZone::createTimeZone("America/Indiana/Indianapolis");
BasicTimeZone *gmt_5 = (BasicTimeZone*)TimeZone::createTimeZone("Etc/GMT+5");
UDate jan1_1971 = getUTCMillis(1971, UCAL_JANUARY, 1);
UDate jan1_2005 = getUTCMillis(2005, UCAL_JANUARY, 1);
UDate jan1_2006 = getUTCMillis(2006, UCAL_JANUARY, 1);
UDate jan1_2007 = getUTCMillis(2007, UCAL_JANUARY, 1);
UDate jan1_2011 = getUTCMillis(2010, UCAL_JANUARY, 1);
if (newyork->hasEquivalentTransitions(*indianapolis, jan1_2005, jan1_2011, TRUE, status)) {
errln("FAIL: New_York is not equivalent to Indianapolis between 2005 and 2010");
}
if (U_FAILURE(status)) {
errln("FAIL: error status is returned from hasEquivalentTransition");
}
if (!newyork->hasEquivalentTransitions(*indianapolis, jan1_2006, jan1_2011, TRUE, status)) {
errln("FAIL: New_York is equivalent to Indianapolis between 2006 and 2010");
}
if (U_FAILURE(status)) {
errln("FAIL: error status is returned from hasEquivalentTransition");
}
if (!indianapolis->hasEquivalentTransitions(*gmt_5, jan1_1971, jan1_2006, TRUE, status)) {
errln("FAIL: Indianapolis is equivalent to GMT+5 between 1971 and 2005");
}
if (U_FAILURE(status)) {
errln("FAIL: error status is returned from hasEquivalentTransition");
}
if (indianapolis->hasEquivalentTransitions(*gmt_5, jan1_1971, jan1_2007, TRUE, status)) {
errln("FAIL: Indianapolis is not equivalent to GMT+5 between 1971 and 2006");
}
if (U_FAILURE(status)) {
errln("FAIL: error status is returned from hasEquivalentTransition");
}
delete newyork;
delete indianapolis;
delete gmt_5;
}
/*
* Write out time zone rules of OlsonTimeZone into VTIMEZONE format, create a new
* VTimeZone from the VTIMEZONE data, then compare transitions
*/
void
TimeZoneRuleTest::TestVTimeZoneRoundTrip(void) {
UDate startTime = getUTCMillis(1850, UCAL_JANUARY, 1);
UDate endTime = getUTCMillis(2050, UCAL_JANUARY, 1);
UErrorCode status = U_ZERO_ERROR;
TestZIDEnumeration tzenum(!quick);
while (TRUE) {
const UnicodeString *tzid = tzenum.snext(status);
if (tzid == NULL) {
break;
}
if (U_FAILURE(status)) {
errln("FAIL: error returned while enumerating timezone IDs.");
break;
}
BasicTimeZone *tz = (BasicTimeZone*)TimeZone::createTimeZone(*tzid);
VTimeZone *vtz_org = VTimeZone::createVTimeZoneByID(*tzid);
VTimeZone *vtz_new = NULL;
UnicodeString vtzdata;
// Write out VTIMEZONE data
vtz_org->write(vtzdata, status);
if (U_FAILURE(status)) {
errln((UnicodeString)"FAIL: error returned while writing time zone rules for " +
*tzid + " into VTIMEZONE format.");
} else {
// Read VTIMEZONE data
vtz_new = VTimeZone::createVTimeZone(vtzdata, status);
if (U_FAILURE(status)) {
errln((UnicodeString)"FAIL: error returned while reading VTIMEZONE data for " + *tzid);
} else {
// Check equivalency after the first transition.
// The DST information before the first transition might be lost
// because there is no good way to represent the initial time with
// VTIMEZONE.
int32_t raw1, raw2, dst1, dst2;
tz->getOffset(startTime, FALSE, raw1, dst1, status);
vtz_new->getOffset(startTime, FALSE, raw2, dst2, status);
if (U_FAILURE(status)) {
errln("FAIL: error status is returned from getOffset");
} else {
if (raw1 + dst1 != raw2 + dst2) {
errln("FAIL: VTimeZone for " + *tzid +
" is not equivalent to its OlsonTimeZone corresponding at "
+ dateToString(startTime));
}
TimeZoneTransition trans;
UBool avail = tz->getNextTransition(startTime, FALSE, trans);
if (avail) {
if (!vtz_new->hasEquivalentTransitions(*tz, trans.getTime(),
endTime, TRUE, status)) {
errln("FAIL: VTimeZone for " + *tzid +
" is not equivalent to its OlsonTimeZone corresponding.");
}
if (U_FAILURE(status)) {
errln("FAIL: error status is returned from hasEquivalentTransition");
}
}
}
}
delete vtz_new;
}
delete tz;
delete vtz_org;
}
}
/*
* Write out time zone rules of OlsonTimeZone after a cutover date into VTIMEZONE format,
* create a new VTimeZone from the VTIMEZONE data, then compare transitions
*/
void
TimeZoneRuleTest::TestVTimeZoneRoundTripPartial(void) {
const int32_t CUTOVERYEARS[] = {
1900,
1950,
2020,
0
};
UDate endTime = getUTCMillis(2050, UCAL_JANUARY, 1);
UErrorCode status = U_ZERO_ERROR;
TestZIDEnumeration tzenum(!quick);
while (TRUE) {
const UnicodeString *tzid = tzenum.snext(status);
if (tzid == NULL) {
break;
}
if (U_FAILURE(status)) {
errln("FAIL: error returned while enumerating timezone IDs.");
break;
}
BasicTimeZone *tz = (BasicTimeZone*)TimeZone::createTimeZone(*tzid);
VTimeZone *vtz_org = VTimeZone::createVTimeZoneByID(*tzid);
VTimeZone *vtz_new = NULL;
UnicodeString vtzdata;
for (int32_t i = 0; CUTOVERYEARS[i] != 0; i++) {
UDate startTime = getUTCMillis(CUTOVERYEARS[i], UCAL_JANUARY, 1);
vtz_org->write(startTime, vtzdata, status);
if (U_FAILURE(status)) {
errln((UnicodeString)"FAIL: error returned while writing time zone rules for " +
*tzid + " into VTIMEZONE format since " + dateToString(startTime));
} else {
// Read VTIMEZONE data
vtz_new = VTimeZone::createVTimeZone(vtzdata, status);
if (U_FAILURE(status)) {
errln((UnicodeString)"FAIL: error returned while reading VTIMEZONE data for " + *tzid
+ " since " + dateToString(startTime));
} else {
// Check equivalency after the first transition.
// The DST information before the first transition might be lost
// because there is no good way to represent the initial time with
// VTIMEZONE.
int32_t raw1, raw2, dst1, dst2;
tz->getOffset(startTime, FALSE, raw1, dst1, status);
vtz_new->getOffset(startTime, FALSE, raw2, dst2, status);
if (U_FAILURE(status)) {
errln("FAIL: error status is returned from getOffset");
} else {
if (raw1 + dst1 != raw2 + dst2) {
errln("FAIL: VTimeZone for " + *tzid +
" is not equivalent to its OlsonTimeZone corresponding at "
+ dateToString(startTime));
}
TimeZoneTransition trans;
UBool avail = tz->getNextTransition(startTime, FALSE, trans);
if (avail) {
if (!vtz_new->hasEquivalentTransitions(*tz, trans.getTime(),
endTime, TRUE, status)) {
errln("FAIL: VTimeZone for " + *tzid +
" is not equivalent to its OlsonTimeZone corresponding.");
}
if (U_FAILURE(status)) {
errln("FAIL: error status is returned from hasEquivalentTransition");
}
}
}
}
}
if (vtz_new != NULL) {
delete vtz_new;
vtz_new = NULL;
}
}
delete tz;
delete vtz_org;
}
}
/*
* Write out simple time zone rules from an OlsonTimeZone at various time into VTIMEZONE
* format and create a new VTimeZone from the VTIMEZONE data, then make sure the raw offset
* and DST savings are same in these two time zones.
*/
void
TimeZoneRuleTest::TestVTimeZoneSimpleWrite(void) {
const int32_t TESTDATES[][3] = {
{2006, UCAL_JANUARY, 1},
{2006, UCAL_MARCH, 15},
{2006, UCAL_MARCH, 31},
{2006, UCAL_OCTOBER, 25},
{2006, UCAL_NOVEMBER, 1},
{2006, UCAL_NOVEMBER, 5},
{2007, UCAL_JANUARY, 1},
{0, 0, 0}
};
UErrorCode status = U_ZERO_ERROR;
TestZIDEnumeration tzenum(!quick);
while (TRUE) {
const UnicodeString *tzid = tzenum.snext(status);
if (tzid == NULL) {
break;
}
if (U_FAILURE(status)) {
errln("FAIL: error returned while enumerating timezone IDs.");
break;
}
VTimeZone *vtz_org = VTimeZone::createVTimeZoneByID(*tzid);
VTimeZone *vtz_new = NULL;
UnicodeString vtzdata;
for (int32_t i = 0; TESTDATES[i][0] != 0; i++) {
UDate time = getUTCMillis(TESTDATES[i][0], TESTDATES[i][1], TESTDATES[i][2]);
vtz_org->writeSimple(time, vtzdata, status);
if (U_FAILURE(status)) {
errln((UnicodeString)"FAIL: error returned while writing simple time zone rules for " +
*tzid + " into VTIMEZONE format at " + dateToString(time));
} else {
// Read VTIMEZONE data
vtz_new = VTimeZone::createVTimeZone(vtzdata, status);
if (U_FAILURE(status)) {
errln((UnicodeString)"FAIL: error returned while reading simple VTIMEZONE data for " + *tzid
+ " at " + dateToString(time));
} else {
int32_t raw0, dst0;
int32_t raw1, dst1;
vtz_org->getOffset(time, FALSE, raw0, dst0, status);
vtz_new->getOffset(time, FALSE, raw1, dst1, status);
if (U_SUCCESS(status)) {
if (raw0 != raw1 || dst0 != dst1) {
errln("FAIL: VTimeZone writeSimple for " + *tzid + " at "
+ dateToString(time) + " failed to the round trip.");
}
} else {
errln("FAIL: getOffset returns error status");
}
}
}
if (vtz_new != NULL) {
delete vtz_new;
vtz_new = NULL;
}
}
delete vtz_org;
}
}
/*
* Write out time zone rules of OlsonTimeZone into VTIMEZONE format with RFC2445 header TZURL and
* LAST-MODIFIED, create a new VTimeZone from the VTIMEZONE data to see if the headers are preserved.
*/
void
TimeZoneRuleTest::TestVTimeZoneHeaderProps(void) {
const UnicodeString TESTURL1("http://source.icu-project.org");
const UnicodeString TESTURL2("http://www.ibm.com");
UErrorCode status = U_ZERO_ERROR;
UnicodeString tzurl;
UDate lmod;
UDate lastmod = getUTCMillis(2007, UCAL_JUNE, 1);
VTimeZone *vtz = VTimeZone::createVTimeZoneByID("America/Chicago");
vtz->setTZURL(TESTURL1);
vtz->setLastModified(lastmod);
// Roundtrip conversion
UnicodeString vtzdata;
vtz->write(vtzdata, status);
VTimeZone *newvtz1 = NULL;
if (U_FAILURE(status)) {
errln("FAIL: error returned while writing VTIMEZONE data 1");
} else {
// Create a new one
newvtz1 = VTimeZone::createVTimeZone(vtzdata, status);
if (U_FAILURE(status)) {
errln("FAIL: error returned while loading VTIMEZONE data 1");
} else {
// Check if TZURL and LAST-MODIFIED properties are preserved
newvtz1->getTZURL(tzurl);
if (tzurl.compare(TESTURL1) != 0) {
errln("FAIL: TZURL 1 was not preserved");
}
vtz->getLastModified(lmod);
if (lastmod != lmod) {
errln("FAIL: LAST-MODIFIED was not preserved");
}
}
}
if (U_SUCCESS(status)) {
// Set different tzurl
newvtz1->setTZURL(TESTURL2);
// Second roundtrip, with a cutover
newvtz1->write(vtzdata, status);
if (U_FAILURE(status)) {
errln("FAIL: error returned while writing VTIMEZONE data 2");
} else {
VTimeZone *newvtz2 = VTimeZone::createVTimeZone(vtzdata, status);
if (U_FAILURE(status)) {
errln("FAIL: error returned while loading VTIMEZONE data 2");
} else {
// Check if TZURL and LAST-MODIFIED properties are preserved
newvtz2->getTZURL(tzurl);
if (tzurl.compare(TESTURL2) != 0) {
errln("FAIL: TZURL was not preserved in the second roundtrip");
}
vtz->getLastModified(lmod);
if (lastmod != lmod) {
errln("FAIL: LAST-MODIFIED was not preserved in the second roundtrip");
}
}
delete newvtz2;
}
}
delete newvtz1;
delete vtz;
}
void
TimeZoneRuleTest::TestGetSimpleRules(void) {
UDate testTimes[] = {
getUTCMillis(1970, UCAL_JANUARY, 1),
getUTCMillis(2000, UCAL_MARCH, 31),
getUTCMillis(2005, UCAL_JULY, 1),
getUTCMillis(2010, UCAL_NOVEMBER, 1),
};
int32_t numTimes = sizeof(testTimes)/sizeof(UDate);
UErrorCode status = U_ZERO_ERROR;
TestZIDEnumeration tzenum(!quick);
InitialTimeZoneRule *initial;
AnnualTimeZoneRule *std, *dst;
for (int32_t i = 0; i < numTimes ; i++) {
while (TRUE) {
const UnicodeString *tzid = tzenum.snext(status);
if (tzid == NULL) {
break;
}
if (U_FAILURE(status)) {
errln("FAIL: error returned while enumerating timezone IDs.");
break;
}
BasicTimeZone *tz = (BasicTimeZone*)TimeZone::createTimeZone(*tzid);
initial = NULL;
std = dst = NULL;
tz->getSimpleRulesNear(testTimes[i], initial, std, dst, status);
if (U_FAILURE(status)) {
errln("FAIL: getSimpleRules failed.");
break;
}
if (initial == NULL) {
errln("FAIL: initial rule must not be NULL");
break;
} else if (!(std == NULL && dst == NULL || std != NULL && dst != NULL)) {
errln("FAIL: invalid std/dst pair.");
break;
}
if (std != NULL) {
const DateTimeRule *dtr = std->getRule();
if (dtr->getDateRuleType() != DateTimeRule::DOW) {
errln("FAIL: simple std rull must use DateTimeRule::DOW as date rule.");
break;
}
if (dtr->getTimeRuleType() != DateTimeRule::WALL_TIME) {
errln("FAIL: simple std rull must use DateTimeRule::WALL_TIME as time rule.");
break;
}
dtr = dst->getRule();
if (dtr->getDateRuleType() != DateTimeRule::DOW) {
errln("FAIL: simple dst rull must use DateTimeRule::DOW as date rule.");
break;
}
if (dtr->getTimeRuleType() != DateTimeRule::WALL_TIME) {
errln("FAIL: simple dst rull must use DateTimeRule::WALL_TIME as time rule.");
break;
}
}
// Create an RBTZ from the rules and compare the offsets at the date
RuleBasedTimeZone *rbtz = new RuleBasedTimeZone(*tzid, initial);
if (std != NULL) {
rbtz->addTransitionRule(std, status);
if (U_FAILURE(status)) {
errln("FAIL: couldn't add std rule.");
}
rbtz->addTransitionRule(dst, status);
if (U_FAILURE(status)) {
errln("FAIL: couldn't add dst rule.");
}
}
rbtz->complete(status);
if (U_FAILURE(status)) {
errln("FAIL: couldn't complete rbtz for " + *tzid);
}
int32_t raw0, dst0, raw1, dst1;
tz->getOffset(testTimes[i], FALSE, raw0, dst0, status);
if (U_FAILURE(status)) {
errln("FAIL: couldn't get offsets from tz for " + *tzid);
}
rbtz->getOffset(testTimes[i], FALSE, raw1, dst1, status);
if (U_FAILURE(status)) {
errln("FAIL: couldn't get offsets from rbtz for " + *tzid);
}
if (raw0 != raw1 || dst0 != dst1) {
errln("FAIL: rbtz created by simple rule does not match the original tz for tzid " + *tzid);
}
delete rbtz;
delete tz;
}
}
}
UDate
TimeZoneRuleTest::getUTCMillis(int32_t y, int32_t m, int32_t d,
int32_t hr, int32_t min, int32_t sec, int32_t msec) {
UErrorCode status = U_ZERO_ERROR;
const TimeZone *tz = TimeZone::getGMT();
Calendar *cal = Calendar::createInstance(*tz, status);
if (U_FAILURE(status)) {
delete cal;
errln("FAIL: Calendar::createInstance failed");
return 0.0;
}
cal->set(y, m, d, hr, min, sec);
cal->set(UCAL_MILLISECOND, msec);
UDate utc = cal->getTime(status);
if (U_FAILURE(status)) {
delete cal;
errln("FAIL: Calendar::getTime failed");
return 0.0;
}
delete cal;
return utc;
}
/*
* Check if a time shift really happens on each transition returned by getNextTransition or
* getPreviousTransition in the specified time range
*/
void
TimeZoneRuleTest::verifyTransitions(BasicTimeZone& icutz, UDate start, UDate end) {
UErrorCode status = U_ZERO_ERROR;
UDate time;
int32_t raw, dst, raw0, dst0;
TimeZoneTransition tzt, tzt0;
UBool avail;
UBool first = TRUE;
UnicodeString tzid;
// Ascending
time = start;
while (TRUE) {
avail = icutz.getNextTransition(time, FALSE, tzt);
if (!avail) {
break;
}
time = tzt.getTime();
if (time >= end) {
break;
}
icutz.getOffset(time, FALSE, raw, dst, status);
icutz.getOffset(time - 1, FALSE, raw0, dst0, status);
if (U_FAILURE(status)) {
errln("FAIL: Error in getOffset");
break;
}
if (raw == raw0 && dst == dst0) {
errln((UnicodeString)"FAIL: False transition returned by getNextTransition for "
+ icutz.getID(tzid) + " at " + dateToString(time));
}
if (!first &&
(tzt0.getTo()->getRawOffset() != tzt.getFrom()->getRawOffset()
|| tzt0.getTo()->getDSTSavings() != tzt.getFrom()->getDSTSavings())) {
errln((UnicodeString)"FAIL: TO rule of the previous transition does not match FROM rule of this transtion at "
+ dateToString(time) + " for " + icutz.getID(tzid));
}
tzt0 = tzt;
first = FALSE;
}
// Descending
first = TRUE;
time = end;
while(true) {
avail = icutz.getPreviousTransition(time, FALSE, tzt);
if (!avail) {
break;
}
time = tzt.getTime();
if (time <= start) {
break;
}
icutz.getOffset(time, FALSE, raw, dst, status);
icutz.getOffset(time - 1, FALSE, raw0, dst0, status);
if (U_FAILURE(status)) {
errln("FAIL: Error in getOffset");
break;
}
if (raw == raw0 && dst == dst0) {
errln((UnicodeString)"FAIL: False transition returned by getPreviousTransition for "
+ icutz.getID(tzid) + " at " + dateToString(time));
}
if (!first &&
(tzt0.getFrom()->getRawOffset() != tzt.getTo()->getRawOffset()
|| tzt0.getFrom()->getDSTSavings() != tzt.getTo()->getDSTSavings())) {
errln((UnicodeString)"FAIL: TO rule of the next transition does not match FROM rule in this transtion at "
+ dateToString(time) + " for " + icutz.getID(tzid));
}
tzt0 = tzt;
first = FALSE;
}
}
/*
* Compare all time transitions in 2 time zones in the specified time range in ascending order
*/
void
TimeZoneRuleTest::compareTransitionsAscending(BasicTimeZone& z1, BasicTimeZone& z2,
UDate start, UDate end, UBool inclusive) {
UnicodeString zid1, zid2;
TimeZoneTransition tzt1, tzt2;
UBool avail1, avail2;
UBool inRange1, inRange2;
z1.getID(zid1);
z2.getID(zid2);
UDate time = start;
while (TRUE) {
avail1 = z1.getNextTransition(time, inclusive, tzt1);
avail2 = z2.getNextTransition(time, inclusive, tzt2);
inRange1 = inRange2 = FALSE;
if (avail1) {
if (tzt1.getTime() < end || (inclusive && tzt1.getTime() == end)) {
inRange1 = TRUE;
}
}
if (avail2) {
if (tzt2.getTime() < end || (inclusive && tzt2.getTime() == end)) {
inRange2 = TRUE;
}
}
if (!inRange1 && !inRange2) {
// No more transition in the range
break;
}
if (!inRange1) {
errln((UnicodeString)"FAIL: " + zid1 + " does not have any transitions after "
+ dateToString(time) + " before " + dateToString(end));
break;
}
if (!inRange2) {
errln((UnicodeString)"FAIL: " + zid2 + " does not have any transitions after "
+ dateToString(time) + " before " + dateToString(end));
break;
}
if (tzt1.getTime() != tzt2.getTime()) {
errln((UnicodeString)"FAIL: First transition after " + dateToString(time) + " "
+ zid1 + "[" + dateToString(tzt1.getTime()) + "] "
+ zid2 + "[" + dateToString(tzt2.getTime()) + "]");
break;
}
time = tzt1.getTime();
if (inclusive) {
time += 1;
}
}
}
/*
* Compare all time transitions in 2 time zones in the specified time range in descending order
*/
void
TimeZoneRuleTest::compareTransitionsDescending(BasicTimeZone& z1, BasicTimeZone& z2,
UDate start, UDate end, UBool inclusive) {
UnicodeString zid1, zid2;
TimeZoneTransition tzt1, tzt2;
UBool avail1, avail2;
UBool inRange1, inRange2;
z1.getID(zid1);
z2.getID(zid2);
UDate time = end;
while (TRUE) {
avail1 = z1.getPreviousTransition(time, inclusive, tzt1);
avail2 = z2.getPreviousTransition(time, inclusive, tzt2);
inRange1 = inRange2 = FALSE;
if (avail1) {
if (tzt1.getTime() > start || (inclusive && tzt1.getTime() == start)) {
inRange1 = TRUE;
}
}
if (avail2) {
if (tzt2.getTime() > start || (inclusive && tzt2.getTime() == start)) {
inRange2 = TRUE;
}
}
if (!inRange1 && !inRange2) {
// No more transition in the range
break;
}
if (!inRange1) {
errln((UnicodeString)"FAIL: " + zid1 + " does not have any transitions before "
+ dateToString(time) + " after " + dateToString(start));
break;
}
if (!inRange2) {
errln((UnicodeString)"FAIL: " + zid2 + " does not have any transitions before "
+ dateToString(time) + " after " + dateToString(start));
break;
}
if (tzt1.getTime() != tzt2.getTime()) {
errln((UnicodeString)"FAIL: Last transition before " + dateToString(time) + " "
+ zid1 + "[" + dateToString(tzt1.getTime()) + "] "
+ zid2 + "[" + dateToString(tzt2.getTime()) + "]");
break;
}
time = tzt1.getTime();
if (inclusive) {
time -= 1;
}
}
}
#endif /* #if !UCONFIG_NO_FORMATTING */
//eof