Home | History | Annotate | Download | only in intltest
      1 /*
      2 *******************************************************************************
      3 * Copyright (C) 2007-2009, International Business Machines Corporation and    *
      4 * others. All Rights Reserved.                                                *
      5 *******************************************************************************
      6 */
      7 
      8 #include "unicode/utypes.h"
      9 
     10 #if !UCONFIG_NO_FORMATTING
     11 
     12 #include "unicode/dtrule.h"
     13 #include "unicode/tzrule.h"
     14 #include "unicode/rbtz.h"
     15 #include "unicode/simpletz.h"
     16 #include "unicode/tzrule.h"
     17 #include "unicode/calendar.h"
     18 #include "unicode/gregocal.h"
     19 #include "unicode/ucal.h"
     20 #include "unicode/unistr.h"
     21 #include "unicode/tztrans.h"
     22 #include "unicode/vtzone.h"
     23 #include "tzrulets.h"
     24 
     25 #define CASE(id,test) case id: name = #test; if (exec) { logln(#test "---"); logln((UnicodeString)""); test(); } break
     26 #define HOUR (60*60*1000)
     27 
     28 static const UVersionInfo ICU_432 = {4,3,2,0};
     29 
     30 static const char *const TESTZIDS[] = {
     31         "AGT",
     32         "America/New_York",
     33         "America/Los_Angeles",
     34         "America/Indiana/Indianapolis",
     35         "America/Havana",
     36         "Europe/Lisbon",
     37         "Europe/Paris",
     38         "Asia/Tokyo",
     39         "Asia/Sakhalin",
     40         "Africa/Cairo",
     41         "Africa/Windhoek",
     42         "Australia/Sydney",
     43         "Etc/GMT+8"
     44 };
     45 
     46 class TestZIDEnumeration : public StringEnumeration {
     47 public:
     48     TestZIDEnumeration(UBool all = FALSE);
     49     ~TestZIDEnumeration();
     50 
     51     virtual int32_t count(UErrorCode& /*status*/) const {
     52         return len;
     53     }
     54     virtual const UnicodeString *snext(UErrorCode& status);
     55     virtual void reset(UErrorCode& status);
     56     static inline UClassID getStaticClassID() {
     57         return (UClassID)&fgClassID;
     58     }
     59     virtual UClassID getDynamicClassID() const {
     60         return getStaticClassID();
     61     }
     62 private:
     63     static const char fgClassID;
     64     int32_t idx;
     65     int32_t len;
     66     StringEnumeration   *tzenum;
     67 };
     68 
     69 const char TestZIDEnumeration::fgClassID = 0;
     70 
     71 TestZIDEnumeration::TestZIDEnumeration(UBool all)
     72 : idx(0) {
     73     UErrorCode status = U_ZERO_ERROR;
     74     if (all) {
     75         tzenum = TimeZone::createEnumeration();
     76         len = tzenum->count(status);
     77     } else {
     78         tzenum = NULL;
     79         len = (int32_t)sizeof(TESTZIDS)/sizeof(TESTZIDS[0]);
     80     }
     81 }
     82 
     83 TestZIDEnumeration::~TestZIDEnumeration() {
     84     if (tzenum != NULL) {
     85         delete tzenum;
     86     }
     87 }
     88 
     89 const UnicodeString*
     90 TestZIDEnumeration::snext(UErrorCode& status) {
     91     if (tzenum != NULL) {
     92         return tzenum->snext(status);
     93     } else if (U_SUCCESS(status) && idx < len) {
     94         unistr = UnicodeString(TESTZIDS[idx++], "");
     95         return &unistr;
     96     }
     97     return NULL;
     98 }
     99 
    100 void
    101 TestZIDEnumeration::reset(UErrorCode& status) {
    102     if (tzenum != NULL) {
    103         tzenum->reset(status);
    104     } else {
    105         idx = 0;
    106     }
    107 }
    108 
    109 
    110 void TimeZoneRuleTest::runIndexedTest( int32_t index, UBool exec, const char* &name, char* /*par*/ )
    111 {
    112     if (exec) {
    113         logln("TestSuite TestTimeZoneRule");
    114     }
    115     switch (index) {
    116         CASE(0, TestSimpleRuleBasedTimeZone);
    117         CASE(1, TestHistoricalRuleBasedTimeZone);
    118         CASE(2, TestOlsonTransition);
    119         CASE(3, TestRBTZTransition);
    120         CASE(4, TestHasEquivalentTransitions);
    121         CASE(5, TestVTimeZoneRoundTrip);
    122         CASE(6, TestVTimeZoneRoundTripPartial);
    123         CASE(7, TestVTimeZoneSimpleWrite);
    124         CASE(8, TestVTimeZoneHeaderProps);
    125         CASE(9, TestGetSimpleRules);
    126         CASE(10, TestTimeZoneRuleCoverage);
    127         CASE(11, TestSimpleTimeZoneCoverage);
    128         CASE(12, TestVTimeZoneCoverage);
    129         CASE(13, TestVTimeZoneParse);
    130         CASE(14, TestT6216);
    131         CASE(15, TestT6669);
    132         default: name = ""; break;
    133     }
    134 }
    135 
    136 /*
    137  * Compare SimpleTimeZone with equivalent RBTZ
    138  */
    139 void
    140 TimeZoneRuleTest::TestSimpleRuleBasedTimeZone(void) {
    141     UErrorCode status = U_ZERO_ERROR;
    142     SimpleTimeZone stz(-1*HOUR, "TestSTZ",
    143         UCAL_SEPTEMBER, -30, -UCAL_SATURDAY, 1*HOUR, SimpleTimeZone::WALL_TIME,
    144         UCAL_FEBRUARY, 2, UCAL_SUNDAY, 1*HOUR, SimpleTimeZone::WALL_TIME,
    145         1*HOUR, status);
    146     if (U_FAILURE(status)) {
    147         errln("FAIL: Couldn't create SimpleTimezone.");
    148     }
    149 
    150     DateTimeRule *dtr;
    151     AnnualTimeZoneRule *atzr;
    152     int32_t STARTYEAR = 2000;
    153 
    154     InitialTimeZoneRule *ir = new InitialTimeZoneRule(
    155         "RBTZ_Initial", // Initial time Name
    156         -1*HOUR,        // Raw offset
    157         1*HOUR);        // DST saving amount
    158 
    159     // Original rules
    160     RuleBasedTimeZone *rbtz1 = new RuleBasedTimeZone("RBTZ1", ir->clone());
    161     dtr = new DateTimeRule(UCAL_SEPTEMBER, 30, UCAL_SATURDAY, FALSE,
    162         1*HOUR, DateTimeRule::WALL_TIME); // SUN<=30 in September, at 1AM wall time
    163     atzr = new AnnualTimeZoneRule("RBTZ_DST1",
    164         -1*HOUR /*rawOffset*/, 1*HOUR /*dstSavings*/, dtr,
    165         STARTYEAR, AnnualTimeZoneRule::MAX_YEAR);
    166     rbtz1->addTransitionRule(atzr, status);
    167     if (U_FAILURE(status)) {
    168         errln("FAIL: couldn't add AnnualTimeZoneRule 1-1.");
    169     }
    170     dtr = new DateTimeRule(UCAL_FEBRUARY, 2, UCAL_SUNDAY,
    171         1*HOUR, DateTimeRule::WALL_TIME);  // 2nd Sunday in February, at 1AM wall time
    172     atzr = new AnnualTimeZoneRule("RBTZ_STD1",
    173         -1*HOUR /*rawOffset*/, 0 /*dstSavings*/, dtr,
    174         STARTYEAR, AnnualTimeZoneRule::MAX_YEAR);
    175     rbtz1->addTransitionRule(atzr, status);
    176     if (U_FAILURE(status)) {
    177         errln("FAIL: couldn't add AnnualTimeZoneRule 1-2.");
    178     }
    179     rbtz1->complete(status);
    180     if (U_FAILURE(status)) {
    181         errln("FAIL: couldn't complete RBTZ 1.");
    182     }
    183 
    184     // Equivalent, but different date rule type
    185     RuleBasedTimeZone *rbtz2 = new RuleBasedTimeZone("RBTZ2", ir->clone());
    186     dtr = new DateTimeRule(UCAL_SEPTEMBER, -1, UCAL_SATURDAY,
    187         1*HOUR, DateTimeRule::WALL_TIME); // Last Sunday in September at 1AM wall time
    188     atzr = new AnnualTimeZoneRule("RBTZ_DST2", -1*HOUR, 1*HOUR, dtr, STARTYEAR, AnnualTimeZoneRule::MAX_YEAR);
    189     rbtz2->addTransitionRule(atzr, status);
    190     if (U_FAILURE(status)) {
    191         errln("FAIL: couldn't add AnnualTimeZoneRule 2-1.");
    192     }
    193     dtr = new DateTimeRule(UCAL_FEBRUARY, 8, UCAL_SUNDAY, true,
    194         1*HOUR, DateTimeRule::WALL_TIME); // SUN>=8 in February, at 1AM wall time
    195     atzr = new AnnualTimeZoneRule("RBTZ_STD2", -1*HOUR, 0, dtr, STARTYEAR, AnnualTimeZoneRule::MAX_YEAR);
    196     rbtz2->addTransitionRule(atzr, status);
    197     if (U_FAILURE(status)) {
    198         errln("FAIL: couldn't add AnnualTimeZoneRule 2-2.");
    199     }
    200     rbtz2->complete(status);
    201     if (U_FAILURE(status)) {
    202         errln("FAIL: couldn't complete RBTZ 2");
    203     }
    204 
    205     // Equivalent, but different time rule type
    206     RuleBasedTimeZone *rbtz3 = new RuleBasedTimeZone("RBTZ3", ir->clone());
    207     dtr = new DateTimeRule(UCAL_SEPTEMBER, 30, UCAL_SATURDAY, false,
    208         2*HOUR, DateTimeRule::UTC_TIME);
    209     atzr = new AnnualTimeZoneRule("RBTZ_DST3", -1*HOUR, 1*HOUR, dtr, STARTYEAR, AnnualTimeZoneRule::MAX_YEAR);
    210     rbtz3->addTransitionRule(atzr, status);
    211     if (U_FAILURE(status)) {
    212         errln("FAIL: couldn't add AnnualTimeZoneRule 3-1.");
    213     }
    214     dtr = new DateTimeRule(UCAL_FEBRUARY, 2, UCAL_SUNDAY,
    215         0*HOUR, DateTimeRule::STANDARD_TIME);
    216     atzr = new AnnualTimeZoneRule("RBTZ_STD3", -1*HOUR, 0, dtr, STARTYEAR, AnnualTimeZoneRule::MAX_YEAR);
    217     rbtz3->addTransitionRule(atzr, status);
    218     if (U_FAILURE(status)) {
    219         errln("FAIL: couldn't add AnnualTimeZoneRule 3-2.");
    220     }
    221     rbtz3->complete(status);
    222     if (U_FAILURE(status)) {
    223         errln("FAIL: couldn't complete RBTZ 3");
    224     }
    225 
    226     // Check equivalency for 10 years
    227     UDate start = getUTCMillis(STARTYEAR, UCAL_JANUARY, 1);
    228     UDate until = getUTCMillis(STARTYEAR + 10, UCAL_JANUARY, 1);
    229 
    230     if (!(stz.hasEquivalentTransitions(*rbtz1, start, until, TRUE, status))) {
    231         errln("FAIL: rbtz1 must be equivalent to the SimpleTimeZone in the time range.");
    232     }
    233     if (U_FAILURE(status)) {
    234         errln("FAIL: error returned from hasEquivalentTransitions");
    235     }
    236     if (!(stz.hasEquivalentTransitions(*rbtz2, start, until, TRUE, status))) {
    237         errln("FAIL: rbtz2 must be equivalent to the SimpleTimeZone in the time range.");
    238     }
    239     if (U_FAILURE(status)) {
    240         errln("FAIL: error returned from hasEquivalentTransitions");
    241     }
    242     if (!(stz.hasEquivalentTransitions(*rbtz3, start, until, TRUE, status))) {
    243         errln("FAIL: rbtz3 must be equivalent to the SimpleTimeZone in the time range.");
    244     }
    245     if (U_FAILURE(status)) {
    246         errln("FAIL: error returned from hasEquivalentTransitions");
    247     }
    248 
    249     // hasSameRules
    250     if (rbtz1->hasSameRules(*rbtz2)) {
    251         errln("FAIL: rbtz1 and rbtz2 have different rules, but returned true.");
    252     }
    253     if (rbtz1->hasSameRules(*rbtz3)) {
    254         errln("FAIL: rbtz1 and rbtz3 have different rules, but returned true.");
    255     }
    256     RuleBasedTimeZone *rbtz1c = (RuleBasedTimeZone*)rbtz1->clone();
    257     if (!rbtz1->hasSameRules(*rbtz1c)) {
    258         errln("FAIL: Cloned RuleBasedTimeZone must have the same rules with the original.");
    259     }
    260 
    261     // getOffset
    262     int32_t era, year, month, dayOfMonth, dayOfWeek, millisInDay;
    263     UDate time;
    264     int32_t offset, dstSavings;
    265     UBool dst;
    266 
    267     GregorianCalendar *cal = new GregorianCalendar(status);
    268     if (U_FAILURE(status)) {
    269         errln("FAIL: Could not create a Gregorian calendar instance.");
    270     }
    271     cal->setTimeZone(*rbtz1);
    272     cal->clear();
    273 
    274     // Jan 1, 1000 BC
    275     cal->set(UCAL_ERA, GregorianCalendar::BC);
    276     cal->set(1000, UCAL_JANUARY, 1);
    277 
    278     era = cal->get(UCAL_ERA, status);
    279     year = cal->get(UCAL_YEAR, status);
    280     month = cal->get(UCAL_MONTH, status);
    281     dayOfMonth = cal->get(UCAL_DAY_OF_MONTH, status);
    282     dayOfWeek = cal->get(UCAL_DAY_OF_WEEK, status);
    283     millisInDay = cal->get(UCAL_MILLISECONDS_IN_DAY, status);
    284     time = cal->getTime(status);
    285     if (U_FAILURE(status)) {
    286         errln("FAIL: Could not get calendar field values.");
    287     }
    288     offset = rbtz1->getOffset(era, year, month, dayOfMonth, dayOfWeek, millisInDay, status);
    289     if (U_FAILURE(status)) {
    290         errln("FAIL: getOffset(7 args) failed.");
    291     }
    292     if (offset != 0) {
    293         errln(UnicodeString("FAIL: Invalid time zone offset: ") + offset + " /expected: 0");
    294     }
    295     dst = rbtz1->inDaylightTime(time, status);
    296     if (U_FAILURE(status)) {
    297         errln("FAIL: inDaylightTime failed.");
    298     }
    299     if (!dst) {
    300         errln("FAIL: Invalid daylight saving time");
    301     }
    302     rbtz1->getOffset(time, TRUE, offset, dstSavings, status);
    303     if (U_FAILURE(status)) {
    304         errln("FAIL: getOffset(5 args) failed.");
    305     }
    306     if (offset != -3600000) {
    307         errln(UnicodeString("FAIL: Invalid time zone raw offset: ") + offset + " /expected: -3600000");
    308     }
    309     if (dstSavings != 3600000) {
    310         errln(UnicodeString("FAIL: Invalid DST amount: ") + dstSavings + " /expected: 3600000");
    311     }
    312 
    313     // July 1, 2000, AD
    314     cal->set(UCAL_ERA, GregorianCalendar::AD);
    315     cal->set(2000, UCAL_JULY, 1);
    316 
    317     era = cal->get(UCAL_ERA, status);
    318     year = cal->get(UCAL_YEAR, status);
    319     month = cal->get(UCAL_MONTH, status);
    320     dayOfMonth = cal->get(UCAL_DAY_OF_MONTH, status);
    321     dayOfWeek = cal->get(UCAL_DAY_OF_WEEK, status);
    322     millisInDay = cal->get(UCAL_MILLISECONDS_IN_DAY, status);
    323     time = cal->getTime(status);
    324     if (U_FAILURE(status)) {
    325         errln("FAIL: Could not get calendar field values.");
    326     }
    327     offset = rbtz1->getOffset(era, year, month, dayOfMonth, dayOfWeek, millisInDay, status);
    328     if (U_FAILURE(status)) {
    329         errln("FAIL: getOffset(7 args) failed.");
    330     }
    331     if (offset != -3600000) {
    332         errln((UnicodeString)"FAIL: Invalid time zone offset: " + offset + " /expected: -3600000");
    333     }
    334     dst = rbtz1->inDaylightTime(time, status);
    335     if (U_FAILURE(status)) {
    336         errln("FAIL: inDaylightTime failed.");
    337     }
    338     if (dst) {
    339         errln("FAIL: Invalid daylight saving time");
    340     }
    341     rbtz1->getOffset(time, TRUE, offset, dstSavings, status);
    342     if (U_FAILURE(status)) {
    343         errln("FAIL: getOffset(5 args) failed.");
    344     }
    345     if (offset != -3600000) {
    346         errln((UnicodeString)"FAIL: Invalid time zone raw offset: " + offset + " /expected: -3600000");
    347     }
    348     if (dstSavings != 0) {
    349         errln((UnicodeString)"FAIL: Invalid DST amount: " + dstSavings + " /expected: 0");
    350     }
    351 
    352     // getRawOffset
    353     offset = rbtz1->getRawOffset();
    354     if (offset != -1*HOUR) {
    355         errln((UnicodeString)"FAIL: Invalid time zone raw offset returned by getRawOffset: "
    356             + offset + " /expected: -3600000");
    357     }
    358 
    359     // operator=/==/!=
    360     RuleBasedTimeZone rbtz0("RBTZ1", ir->clone());
    361     if (rbtz0 == *rbtz1 || !(rbtz0 != *rbtz1)) {
    362         errln("FAIL: RuleBasedTimeZone rbtz0 is not equal to rbtz1, but got wrong result");
    363     }
    364     rbtz0 = *rbtz1;
    365     if (rbtz0 != *rbtz1 || !(rbtz0 == *rbtz1)) {
    366         errln("FAIL: RuleBasedTimeZone rbtz0 is equal to rbtz1, but got wrong result");
    367     }
    368 
    369     // setRawOffset
    370     const int32_t RAW = -10*HOUR;
    371     rbtz0.setRawOffset(RAW);
    372     if (rbtz0.getRawOffset() != RAW) {
    373         logln("setRawOffset is implemented in RuleBasedTimeZone");
    374     }
    375 
    376     // useDaylightTime
    377     if (!rbtz1->useDaylightTime()) {
    378         errln("FAIL: useDaylightTime returned FALSE");
    379     }
    380 
    381     // Try to add 3rd final rule
    382     dtr = new DateTimeRule(UCAL_OCTOBER, 15, 1*HOUR, DateTimeRule::WALL_TIME);
    383     atzr = new AnnualTimeZoneRule("3RD_ATZ", -1*HOUR, 2*HOUR, dtr, STARTYEAR, AnnualTimeZoneRule::MAX_YEAR);
    384     rbtz1->addTransitionRule(atzr, status);
    385     if (U_SUCCESS(status)) {
    386         errln("FAIL: 3rd final rule must be rejected");
    387     } else {
    388         delete atzr;
    389     }
    390 
    391     // Try to add an initial rule
    392     InitialTimeZoneRule *ir1 = new InitialTimeZoneRule("Test Initial", 2*HOUR, 0);
    393     rbtz1->addTransitionRule(ir1, status);
    394     if (U_SUCCESS(status)) {
    395         errln("FAIL: InitialTimeZoneRule must be rejected");
    396     } else {
    397         delete ir1;
    398     }
    399 
    400     delete ir;
    401     delete rbtz1;
    402     delete rbtz2;
    403     delete rbtz3;
    404     delete rbtz1c;
    405     delete cal;
    406 }
    407 
    408 /*
    409  * Test equivalency between OlsonTimeZone and custom RBTZ representing the
    410  * equivalent rules in a certain time range
    411  */
    412 void
    413 TimeZoneRuleTest::TestHistoricalRuleBasedTimeZone(void) {
    414     UErrorCode status = U_ZERO_ERROR;
    415 
    416     // Compare to America/New_York with equivalent RBTZ
    417     BasicTimeZone *ny = (BasicTimeZone*)TimeZone::createTimeZone("America/New_York");
    418 
    419     //RBTZ
    420     InitialTimeZoneRule *ir = new InitialTimeZoneRule("EST", -5*HOUR, 0);
    421     RuleBasedTimeZone *rbtz = new RuleBasedTimeZone("EST5EDT", ir);
    422 
    423     DateTimeRule *dtr;
    424     AnnualTimeZoneRule *tzr;
    425 
    426     // Standard time
    427     dtr = new DateTimeRule(UCAL_OCTOBER, -1, UCAL_SUNDAY,
    428         2*HOUR, DateTimeRule::WALL_TIME); // Last Sunday in October, at 2AM wall time
    429     tzr = new AnnualTimeZoneRule("EST", -5*HOUR /*rawOffset*/, 0 /*dstSavings*/, dtr, 1967, 2006);
    430     rbtz->addTransitionRule(tzr, status);
    431     if (U_FAILURE(status)) {
    432         errln("FAIL: couldn't add AnnualTimeZoneRule 1.");
    433     }
    434 
    435     dtr = new DateTimeRule(UCAL_NOVEMBER, 1, UCAL_SUNDAY,
    436         true, 2*HOUR, DateTimeRule::WALL_TIME); // SUN>=1 in November, at 2AM wall time
    437     tzr = new AnnualTimeZoneRule("EST", -5*HOUR, 0, dtr, 2007, AnnualTimeZoneRule::MAX_YEAR);
    438     rbtz->addTransitionRule(tzr, status);
    439     if (U_FAILURE(status)) {
    440         errln("FAIL: couldn't add AnnualTimeZoneRule 2.");
    441     }
    442 
    443     // Daylight saving time
    444     dtr = new DateTimeRule(UCAL_APRIL, -1, UCAL_SUNDAY,
    445         2*HOUR, DateTimeRule::WALL_TIME); // Last Sunday in April, at 2AM wall time
    446     tzr = new AnnualTimeZoneRule("EDT", -5*HOUR, 1*HOUR, dtr, 1967, 1973);
    447     rbtz->addTransitionRule(tzr, status);
    448     if (U_FAILURE(status)) {
    449         errln("FAIL: couldn't add AnnualTimeZoneRule 3.");
    450     }
    451 
    452     dtr = new DateTimeRule(UCAL_JANUARY, 6,
    453         2*HOUR, DateTimeRule::WALL_TIME); // January 6, at 2AM wall time
    454     tzr = new AnnualTimeZoneRule("EDT", -5*HOUR, 1*HOUR, dtr, 1974, 1974);
    455     rbtz->addTransitionRule(tzr, status);
    456     if (U_FAILURE(status)) {
    457         errln("FAIL: couldn't add AnnualTimeZoneRule 4.");
    458     }
    459 
    460     dtr = new DateTimeRule(UCAL_FEBRUARY, 23,
    461         2*HOUR, DateTimeRule::WALL_TIME); // February 23, at 2AM wall time
    462     tzr = new AnnualTimeZoneRule("EDT", -5*HOUR, 1*HOUR, dtr, 1975, 1975);
    463     rbtz->addTransitionRule(tzr, status);
    464     if (U_FAILURE(status)) {
    465         errln("FAIL: couldn't add AnnualTimeZoneRule 5.");
    466     }
    467 
    468     dtr = new DateTimeRule(UCAL_APRIL, -1, UCAL_SUNDAY,
    469         2*HOUR, DateTimeRule::WALL_TIME); // Last Sunday in April, at 2AM wall time
    470     tzr = new AnnualTimeZoneRule("EDT", -5*HOUR, 1*HOUR, dtr, 1976, 1986);
    471     rbtz->addTransitionRule(tzr, status);
    472     if (U_FAILURE(status)) {
    473         errln("FAIL: couldn't add AnnualTimeZoneRule 6.");
    474     }
    475 
    476     dtr = new DateTimeRule(UCAL_APRIL, 1, UCAL_SUNDAY,
    477         true, 2*HOUR, DateTimeRule::WALL_TIME); // SUN>=1 in April, at 2AM wall time
    478     tzr = new AnnualTimeZoneRule("EDT", -5*HOUR, 1*HOUR, dtr, 1987, 2006);
    479     rbtz->addTransitionRule(tzr, status);
    480     if (U_FAILURE(status)) {
    481         errln("FAIL: couldn't add AnnualTimeZoneRule 7.");
    482     }
    483 
    484     dtr = new DateTimeRule(UCAL_MARCH, 8, UCAL_SUNDAY,
    485         true, 2*HOUR, DateTimeRule::WALL_TIME); // SUN>=8 in March, at 2AM wall time
    486     tzr = new AnnualTimeZoneRule("EDT", -5*HOUR, 1*HOUR, dtr, 2007, AnnualTimeZoneRule::MAX_YEAR);
    487     rbtz->addTransitionRule(tzr, status);
    488     if (U_FAILURE(status)) {
    489         errln("FAIL: couldn't add AnnualTimeZoneRule 7.");
    490     }
    491 
    492     rbtz->complete(status);
    493     if (U_FAILURE(status)) {
    494         errln("FAIL: couldn't complete RBTZ.");
    495     }
    496 
    497     // hasEquivalentTransitions
    498     UDate jan1_1950 = getUTCMillis(1950, UCAL_JANUARY, 1);
    499     UDate jan1_1967 = getUTCMillis(1971, UCAL_JANUARY, 1);
    500     UDate jan1_2010 = getUTCMillis(2010, UCAL_JANUARY, 1);
    501 
    502     if (!ny->hasEquivalentTransitions(*rbtz, jan1_1967, jan1_2010, TRUE, status)) {
    503         dataerrln("FAIL: The RBTZ must be equivalent to America/New_York between 1967 and 2010");
    504     }
    505     if (U_FAILURE(status)) {
    506         errln("FAIL: error returned from hasEquivalentTransitions for ny/rbtz 1967-2010");
    507     }
    508     if (ny->hasEquivalentTransitions(*rbtz, jan1_1950, jan1_2010, TRUE, status)) {
    509         errln("FAIL: The RBTZ must not be equivalent to America/New_York between 1950 and 2010");
    510     }
    511     if (U_FAILURE(status)) {
    512         errln("FAIL: error returned from hasEquivalentTransitions for ny/rbtz 1950-2010");
    513     }
    514 
    515     // Same with above, but calling RBTZ#hasEquivalentTransitions against OlsonTimeZone
    516     if (!rbtz->hasEquivalentTransitions(*ny, jan1_1967, jan1_2010, TRUE, status)) {
    517         dataerrln("FAIL: The RBTZ must be equivalent to America/New_York between 1967 and 2010 ");
    518     }
    519     if (U_FAILURE(status)) {
    520         errln("FAIL: error returned from hasEquivalentTransitions for rbtz/ny 1967-2010");
    521     }
    522     if (rbtz->hasEquivalentTransitions(*ny, jan1_1950, jan1_2010, TRUE, status)) {
    523         errln("FAIL: The RBTZ must not be equivalent to America/New_York between 1950 and 2010");
    524     }
    525     if (U_FAILURE(status)) {
    526         errln("FAIL: error returned from hasEquivalentTransitions for rbtz/ny 1950-2010");
    527     }
    528 
    529     // TimeZone APIs
    530     if (ny->hasSameRules(*rbtz) || rbtz->hasSameRules(*ny)) {
    531         errln("FAIL: hasSameRules must return false");
    532     }
    533     RuleBasedTimeZone *rbtzc = (RuleBasedTimeZone*)rbtz->clone();
    534     if (!rbtz->hasSameRules(*rbtzc) || !rbtz->hasEquivalentTransitions(*rbtzc, jan1_1950, jan1_2010, TRUE, status)) {
    535         errln("FAIL: hasSameRules/hasEquivalentTransitions must return true for cloned RBTZs");
    536     }
    537     if (U_FAILURE(status)) {
    538         errln("FAIL: error returned from hasEquivalentTransitions for rbtz/rbtzc 1950-2010");
    539     }
    540 
    541     UDate times[] = {
    542         getUTCMillis(2006, UCAL_MARCH, 15),
    543         getUTCMillis(2006, UCAL_NOVEMBER, 1),
    544         getUTCMillis(2007, UCAL_MARCH, 15),
    545         getUTCMillis(2007, UCAL_NOVEMBER, 1),
    546         getUTCMillis(2008, UCAL_MARCH, 15),
    547         getUTCMillis(2008, UCAL_NOVEMBER, 1),
    548         0
    549     };
    550     int32_t offset1, dst1;
    551     int32_t offset2, dst2;
    552 
    553     for (int i = 0; times[i] != 0; i++) {
    554         // Check getOffset - must return the same results for these time data
    555         rbtz->getOffset(times[i], FALSE, offset1, dst1, status);
    556         if (U_FAILURE(status)) {
    557             errln("FAIL: rbtz->getOffset failed");
    558         }
    559         ny->getOffset(times[i], FALSE, offset2, dst2, status);
    560         if (U_FAILURE(status)) {
    561             errln("FAIL: ny->getOffset failed");
    562         }
    563         if (offset1 != offset2 || dst1 != dst2) {
    564             dataerrln("FAIL: Incompatible time zone offset/dstSavings for ny and rbtz");
    565         }
    566 
    567         // Check inDaylightTime
    568         if (rbtz->inDaylightTime(times[i], status) != ny->inDaylightTime(times[i], status)) {
    569             dataerrln("FAIL: Incompatible daylight saving time for ny and rbtz");
    570         }
    571         if (U_FAILURE(status)) {
    572             errln("FAIL: inDaylightTime failed");
    573         }
    574     }
    575 
    576     delete ny;
    577     delete rbtz;
    578     delete rbtzc;
    579 }
    580 
    581 /*
    582  * Check if transitions returned by getNextTransition/getPreviousTransition
    583  * are actual time transitions.
    584  */
    585 void
    586 TimeZoneRuleTest::TestOlsonTransition(void) {
    587 
    588     const int32_t TESTYEARS[][2] = {
    589         {1895, 1905}, // including int32 minimum second
    590         {1965, 1975}, // including the epoch
    591         {1995, 2015}, // practical year range
    592         {0,0}
    593     };
    594 
    595     UErrorCode status = U_ZERO_ERROR;
    596     TestZIDEnumeration tzenum(!quick);
    597     while (TRUE) {
    598         const UnicodeString *tzid = tzenum.snext(status);
    599         if (tzid == NULL) {
    600             break;
    601         }
    602         if (U_FAILURE(status)) {
    603             errln("FAIL: error returned while enumerating timezone IDs.");
    604             break;
    605         }
    606         BasicTimeZone *tz = (BasicTimeZone*)TimeZone::createTimeZone(*tzid);
    607         for (int32_t i = 0; TESTYEARS[i][0] != 0 || TESTYEARS[i][1] != 0; i++) {
    608             UDate lo = getUTCMillis(TESTYEARS[i][0], UCAL_JANUARY, 1);
    609             UDate hi = getUTCMillis(TESTYEARS[i][1], UCAL_JANUARY, 1);
    610             verifyTransitions(*tz, lo, hi);
    611         }
    612         delete tz;
    613     }
    614 }
    615 
    616 /*
    617  * Check if an OlsonTimeZone and its equivalent RBTZ have the exact same
    618  * transitions.
    619  */
    620 void
    621 TimeZoneRuleTest::TestRBTZTransition(void) {
    622     const int32_t STARTYEARS[] = {
    623         1900,
    624         1960,
    625         1990,
    626         2010,
    627         0
    628     };
    629 
    630     UErrorCode status = U_ZERO_ERROR;
    631     TestZIDEnumeration tzenum(!quick);
    632     while (TRUE) {
    633         const UnicodeString *tzid = tzenum.snext(status);
    634         if (tzid == NULL) {
    635             break;
    636         }
    637         if (U_FAILURE(status)) {
    638             errln("FAIL: error returned while enumerating timezone IDs.");
    639             break;
    640         }
    641         BasicTimeZone *tz = (BasicTimeZone*)TimeZone::createTimeZone(*tzid);
    642         int32_t ruleCount = tz->countTransitionRules(status);
    643 
    644         const InitialTimeZoneRule *initial;
    645         const TimeZoneRule **trsrules = new const TimeZoneRule*[ruleCount];
    646         tz->getTimeZoneRules(initial, trsrules, ruleCount, status);
    647         if (U_FAILURE(status)) {
    648             errln((UnicodeString)"FAIL: failed to get the TimeZoneRules from time zone " + *tzid);
    649         }
    650         RuleBasedTimeZone *rbtz = new RuleBasedTimeZone(*tzid, initial->clone());
    651         if (U_FAILURE(status)) {
    652             errln((UnicodeString)"FAIL: failed to get the transition rule count from time zone " + *tzid);
    653         }
    654         for (int32_t i = 0; i < ruleCount; i++) {
    655             rbtz->addTransitionRule(trsrules[i]->clone(), status);
    656             if (U_FAILURE(status)) {
    657                 errln((UnicodeString)"FAIL: failed to add a transition rule at index " + i + " to the RBTZ for " + *tzid);
    658             }
    659         }
    660         rbtz->complete(status);
    661         if (U_FAILURE(status)) {
    662             errln((UnicodeString)"FAIL: complete() failed for the RBTZ for " + *tzid);
    663         }
    664 
    665         for (int32_t idx = 0; STARTYEARS[idx] != 0; idx++) {
    666             UDate start = getUTCMillis(STARTYEARS[idx], UCAL_JANUARY, 1);
    667             UDate until = getUTCMillis(STARTYEARS[idx] + 20, UCAL_JANUARY, 1);
    668             // Compare the original OlsonTimeZone with the RBTZ starting the startTime for 20 years
    669 
    670             // Ascending
    671             compareTransitionsAscending(*tz, *rbtz, start, until, FALSE);
    672             // Ascending/inclusive
    673             compareTransitionsAscending(*tz, *rbtz, start + 1, until, TRUE);
    674             // Descending
    675             compareTransitionsDescending(*tz, *rbtz, start, until, FALSE);
    676             // Descending/inclusive
    677             compareTransitionsDescending(*tz, *rbtz, start + 1, until, TRUE);
    678         }
    679         delete [] trsrules;
    680         delete rbtz;
    681         delete tz;
    682     }
    683 }
    684 
    685 void
    686 TimeZoneRuleTest::TestHasEquivalentTransitions(void) {
    687     // America/New_York and America/Indiana/Indianapolis are equivalent
    688     // since 2006
    689     UErrorCode status = U_ZERO_ERROR;
    690     BasicTimeZone *newyork = (BasicTimeZone*)TimeZone::createTimeZone("America/New_York");
    691     BasicTimeZone *indianapolis = (BasicTimeZone*)TimeZone::createTimeZone("America/Indiana/Indianapolis");
    692     BasicTimeZone *gmt_5 = (BasicTimeZone*)TimeZone::createTimeZone("Etc/GMT+5");
    693 
    694     UDate jan1_1971 = getUTCMillis(1971, UCAL_JANUARY, 1);
    695     UDate jan1_2005 = getUTCMillis(2005, UCAL_JANUARY, 1);
    696     UDate jan1_2006 = getUTCMillis(2006, UCAL_JANUARY, 1);
    697     UDate jan1_2007 = getUTCMillis(2007, UCAL_JANUARY, 1);
    698     UDate jan1_2011 = getUTCMillis(2010, UCAL_JANUARY, 1);
    699 
    700     if (newyork->hasEquivalentTransitions(*indianapolis, jan1_2005, jan1_2011, TRUE, status)) {
    701         dataerrln("FAIL: New_York is not equivalent to Indianapolis between 2005 and 2010");
    702     }
    703     if (U_FAILURE(status)) {
    704         errln("FAIL: error status is returned from hasEquivalentTransition");
    705     }
    706     if (!newyork->hasEquivalentTransitions(*indianapolis, jan1_2006, jan1_2011, TRUE, status)) {
    707         errln("FAIL: New_York is equivalent to Indianapolis between 2006 and 2010");
    708     }
    709     if (U_FAILURE(status)) {
    710         errln("FAIL: error status is returned from hasEquivalentTransition");
    711     }
    712 
    713     if (!indianapolis->hasEquivalentTransitions(*gmt_5, jan1_1971, jan1_2006, TRUE, status)) {
    714         errln("FAIL: Indianapolis is equivalent to GMT+5 between 1971 and 2005");
    715     }
    716     if (U_FAILURE(status)) {
    717         errln("FAIL: error status is returned from hasEquivalentTransition");
    718     }
    719     if (indianapolis->hasEquivalentTransitions(*gmt_5, jan1_1971, jan1_2007, TRUE, status)) {
    720         dataerrln("FAIL: Indianapolis is not equivalent to GMT+5 between 1971 and 2006");
    721     }
    722     if (U_FAILURE(status)) {
    723         errln("FAIL: error status is returned from hasEquivalentTransition");
    724     }
    725 
    726     // Cloned TimeZone
    727     BasicTimeZone *newyork2 = (BasicTimeZone*)newyork->clone();
    728     if (!newyork->hasEquivalentTransitions(*newyork2, jan1_1971, jan1_2011, FALSE, status)) {
    729         errln("FAIL: Cloned TimeZone must have the same transitions");
    730     }
    731     if (U_FAILURE(status)) {
    732         errln("FAIL: error status is returned from hasEquivalentTransition for newyork/newyork2");
    733     }
    734     if (!newyork->hasEquivalentTransitions(*newyork2, jan1_1971, jan1_2011, TRUE, status)) {
    735         errln("FAIL: Cloned TimeZone must have the same transitions");
    736     }
    737     if (U_FAILURE(status)) {
    738         errln("FAIL: error status is returned from hasEquivalentTransition for newyork/newyork2");
    739     }
    740 
    741     // America/New_York and America/Los_Angeles has same DST start rules, but
    742     // raw offsets are different
    743     BasicTimeZone *losangeles = (BasicTimeZone*)TimeZone::createTimeZone("America/Los_Angeles");
    744     if (newyork->hasEquivalentTransitions(*losangeles, jan1_2006, jan1_2011, TRUE, status)) {
    745         dataerrln("FAIL: New_York is not equivalent to Los Angeles, but returned true");
    746     }
    747     if (U_FAILURE(status)) {
    748         errln("FAIL: error status is returned from hasEquivalentTransition for newyork/losangeles");
    749     }
    750 
    751     delete newyork;
    752     delete newyork2;
    753     delete indianapolis;
    754     delete gmt_5;
    755     delete losangeles;
    756 }
    757 
    758 /*
    759  * Write out time zone rules of OlsonTimeZone into VTIMEZONE format, create a new
    760  * VTimeZone from the VTIMEZONE data, then compare transitions
    761  */
    762 void
    763 TimeZoneRuleTest::TestVTimeZoneRoundTrip(void) {
    764     UDate startTime = getUTCMillis(1850, UCAL_JANUARY, 1);
    765     UDate endTime = getUTCMillis(2050, UCAL_JANUARY, 1);
    766 
    767     UErrorCode status = U_ZERO_ERROR;
    768     TestZIDEnumeration tzenum(!quick);
    769     while (TRUE) {
    770         const UnicodeString *tzid = tzenum.snext(status);
    771         if (tzid == NULL) {
    772             break;
    773         }
    774         if (U_FAILURE(status)) {
    775             errln("FAIL: error returned while enumerating timezone IDs.");
    776             break;
    777         }
    778         if (!isICUVersionAtLeast(ICU_432)) {
    779             // See ticket#7008
    780             if (*tzid == UnicodeString("Asia/Amman")) {
    781                 continue;
    782             }
    783         }
    784         BasicTimeZone *tz = (BasicTimeZone*)TimeZone::createTimeZone(*tzid);
    785         VTimeZone *vtz_org = VTimeZone::createVTimeZoneByID(*tzid);
    786         vtz_org->setTZURL("http://source.icu-project.org/timezone");
    787         vtz_org->setLastModified(Calendar::getNow());
    788         VTimeZone *vtz_new = NULL;
    789         UnicodeString vtzdata;
    790         // Write out VTIMEZONE data
    791         vtz_org->write(vtzdata, status);
    792         if (U_FAILURE(status)) {
    793             errln((UnicodeString)"FAIL: error returned while writing time zone rules for " +
    794                 *tzid + " into VTIMEZONE format.");
    795         } else {
    796             // Read VTIMEZONE data
    797             vtz_new = VTimeZone::createVTimeZone(vtzdata, status);
    798             if (U_FAILURE(status)) {
    799                 errln((UnicodeString)"FAIL: error returned while reading VTIMEZONE data for " + *tzid);
    800             } else {
    801                 // Write out VTIMEZONE one more time
    802                 UnicodeString vtzdata1;
    803                 vtz_new->write(vtzdata1, status);
    804                 if (U_FAILURE(status)) {
    805                     errln((UnicodeString)"FAIL: error returned while writing time zone rules for " +
    806                         *tzid + "(vtz_new) into VTIMEZONE format.");
    807                 } else {
    808                     // Make sure VTIMEZONE data is exactly same with the first one
    809                     if (vtzdata != vtzdata1) {
    810                         errln((UnicodeString)"FAIL: different VTIMEZONE data after round trip for " + *tzid);
    811                     }
    812                 }
    813                 // Check equivalency after the first transition.
    814                 // The DST information before the first transition might be lost
    815                 // because there is no good way to represent the initial time with
    816                 // VTIMEZONE.
    817                 int32_t raw1, raw2, dst1, dst2;
    818                 tz->getOffset(startTime, FALSE, raw1, dst1, status);
    819                 vtz_new->getOffset(startTime, FALSE, raw2, dst2, status);
    820                 if (U_FAILURE(status)) {
    821                     errln("FAIL: error status is returned from getOffset");
    822                 } else {
    823                     if (raw1 + dst1 != raw2 + dst2) {
    824                         errln("FAIL: VTimeZone for " + *tzid +
    825                             " is not equivalent to its OlsonTimeZone corresponding at "
    826                             + dateToString(startTime));
    827                     }
    828                     TimeZoneTransition trans;
    829                     UBool avail = tz->getNextTransition(startTime, FALSE, trans);
    830                     if (avail) {
    831                         if (!vtz_new->hasEquivalentTransitions(*tz, trans.getTime(),
    832                                 endTime, TRUE, status)) {
    833                             errln("FAIL: VTimeZone for " + *tzid +
    834                                 " is not equivalent to its OlsonTimeZone corresponding.");
    835                         }
    836                         if (U_FAILURE(status)) {
    837                             errln("FAIL: error status is returned from hasEquivalentTransition");
    838                         }
    839                     }
    840                 }
    841             }
    842             if (vtz_new != NULL) {
    843                 delete vtz_new;
    844                 vtz_new = NULL;
    845             }
    846         }
    847         delete tz;
    848         delete vtz_org;
    849     }
    850 }
    851 
    852 /*
    853  * Write out time zone rules of OlsonTimeZone after a cutover date into VTIMEZONE format,
    854  * create a new VTimeZone from the VTIMEZONE data, then compare transitions
    855  */
    856 void
    857 TimeZoneRuleTest::TestVTimeZoneRoundTripPartial(void) {
    858     const int32_t STARTYEARS[] = {
    859         1900,
    860         1950,
    861         2020,
    862         0
    863     };
    864     UDate endTime = getUTCMillis(2050, UCAL_JANUARY, 1);
    865 
    866     UErrorCode status = U_ZERO_ERROR;
    867     TestZIDEnumeration tzenum(!quick);
    868     while (TRUE) {
    869         const UnicodeString *tzid = tzenum.snext(status);
    870         if (tzid == NULL) {
    871             break;
    872         }
    873         if (U_FAILURE(status)) {
    874             errln("FAIL: error returned while enumerating timezone IDs.");
    875             break;
    876         }
    877         if (!isICUVersionAtLeast(ICU_432)) {
    878             // See ticket#7008
    879             if (*tzid == UnicodeString("Asia/Amman")) {
    880                 continue;
    881             }
    882         }
    883         BasicTimeZone *tz = (BasicTimeZone*)TimeZone::createTimeZone(*tzid);
    884         VTimeZone *vtz_org = VTimeZone::createVTimeZoneByID(*tzid);
    885         VTimeZone *vtz_new = NULL;
    886         UnicodeString vtzdata;
    887 
    888         for (int32_t i = 0; STARTYEARS[i] != 0; i++) {
    889             // Write out VTIMEZONE
    890             UDate startTime = getUTCMillis(STARTYEARS[i], UCAL_JANUARY, 1);
    891             vtz_org->write(startTime, vtzdata, status);
    892             if (U_FAILURE(status)) {
    893                 errln((UnicodeString)"FAIL: error returned while writing time zone rules for " +
    894                     *tzid + " into VTIMEZONE format since " + dateToString(startTime));
    895             } else {
    896                 // Read VTIMEZONE data
    897                 vtz_new = VTimeZone::createVTimeZone(vtzdata, status);
    898                 if (U_FAILURE(status)) {
    899                     errln((UnicodeString)"FAIL: error returned while reading VTIMEZONE data for " + *tzid
    900                         + " since " + dateToString(startTime));
    901                 } else {
    902                     // Check equivalency after the first transition.
    903                     // The DST information before the first transition might be lost
    904                     // because there is no good way to represent the initial time with
    905                     // VTIMEZONE.
    906                     int32_t raw1, raw2, dst1, dst2;
    907                     tz->getOffset(startTime, FALSE, raw1, dst1, status);
    908                     vtz_new->getOffset(startTime, FALSE, raw2, dst2, status);
    909                     if (U_FAILURE(status)) {
    910                         errln("FAIL: error status is returned from getOffset");
    911                     } else {
    912                         if (raw1 + dst1 != raw2 + dst2) {
    913                             errln("FAIL: VTimeZone for " + *tzid +
    914                                 " is not equivalent to its OlsonTimeZone corresponding at "
    915                                 + dateToString(startTime));
    916                         }
    917                         TimeZoneTransition trans;
    918                         UBool avail = tz->getNextTransition(startTime, FALSE, trans);
    919                         if (avail) {
    920                             if (!vtz_new->hasEquivalentTransitions(*tz, trans.getTime(),
    921                                     endTime, TRUE, status)) {
    922                                 errln("FAIL: VTimeZone for " + *tzid +
    923                                     " is not equivalent to its OlsonTimeZone corresponding.");
    924                             }
    925                             if (U_FAILURE(status)) {
    926                                 errln("FAIL: error status is returned from hasEquivalentTransition");
    927                             }
    928                         }
    929                     }
    930                 }
    931             }
    932             if (vtz_new != NULL) {
    933                 delete vtz_new;
    934                 vtz_new = NULL;
    935             }
    936         }
    937         delete tz;
    938         delete vtz_org;
    939     }
    940 }
    941 
    942 /*
    943  * Write out simple time zone rules from an OlsonTimeZone at various time into VTIMEZONE
    944  * format and create a new VTimeZone from the VTIMEZONE data, then make sure the raw offset
    945  * and DST savings are same in these two time zones.
    946  */
    947 void
    948 TimeZoneRuleTest::TestVTimeZoneSimpleWrite(void) {
    949     const int32_t TESTDATES[][3] = {
    950         {2006,  UCAL_JANUARY,   1},
    951         {2006,  UCAL_MARCH,     15},
    952         {2006,  UCAL_MARCH,     31},
    953         {2006,  UCAL_OCTOBER,   25},
    954         {2006,  UCAL_NOVEMBER,  1},
    955         {2006,  UCAL_NOVEMBER,  5},
    956         {2007,  UCAL_JANUARY,   1},
    957         {0,     0,              0}
    958     };
    959 
    960     UErrorCode status = U_ZERO_ERROR;
    961     TestZIDEnumeration tzenum(!quick);
    962     while (TRUE) {
    963         const UnicodeString *tzid = tzenum.snext(status);
    964         if (tzid == NULL) {
    965             break;
    966         }
    967         if (U_FAILURE(status)) {
    968             errln("FAIL: error returned while enumerating timezone IDs.");
    969             break;
    970         }
    971         VTimeZone *vtz_org = VTimeZone::createVTimeZoneByID(*tzid);
    972         VTimeZone *vtz_new = NULL;
    973         UnicodeString vtzdata;
    974 
    975         for (int32_t i = 0; TESTDATES[i][0] != 0; i++) {
    976             // Write out VTIMEZONE
    977             UDate time = getUTCMillis(TESTDATES[i][0], TESTDATES[i][1], TESTDATES[i][2]);
    978             vtz_org->writeSimple(time, vtzdata, status);
    979             if (U_FAILURE(status)) {
    980                 errln((UnicodeString)"FAIL: error returned while writing simple time zone rules for " +
    981                     *tzid + " into VTIMEZONE format at " + dateToString(time));
    982             } else {
    983                 // Read VTIMEZONE data
    984                 vtz_new = VTimeZone::createVTimeZone(vtzdata, status);
    985                 if (U_FAILURE(status)) {
    986                     errln((UnicodeString)"FAIL: error returned while reading simple VTIMEZONE data for " + *tzid
    987                         + " at " + dateToString(time));
    988                 } else {
    989                     // Check equivalency
    990                     int32_t raw0, dst0;
    991                     int32_t raw1, dst1;
    992                     vtz_org->getOffset(time, FALSE, raw0, dst0, status);
    993                     vtz_new->getOffset(time, FALSE, raw1, dst1, status);
    994                     if (U_SUCCESS(status)) {
    995                         if (raw0 != raw1 || dst0 != dst1) {
    996                             errln("FAIL: VTimeZone writeSimple for " + *tzid + " at "
    997                                 + dateToString(time) + " failed to the round trip.");
    998                         }
    999                     } else {
   1000                         errln("FAIL: getOffset returns error status");
   1001                     }
   1002                 }
   1003             }
   1004             if (vtz_new != NULL) {
   1005                 delete vtz_new;
   1006                 vtz_new = NULL;
   1007             }
   1008         }
   1009         delete vtz_org;
   1010     }
   1011 }
   1012 
   1013 /*
   1014  * Write out time zone rules of OlsonTimeZone into VTIMEZONE format with RFC2445 header TZURL and
   1015  * LAST-MODIFIED, create a new VTimeZone from the VTIMEZONE data to see if the headers are preserved.
   1016  */
   1017 void
   1018 TimeZoneRuleTest::TestVTimeZoneHeaderProps(void) {
   1019     const UnicodeString TESTURL1("http://source.icu-project.org");
   1020     const UnicodeString TESTURL2("http://www.ibm.com");
   1021 
   1022     UErrorCode status = U_ZERO_ERROR;
   1023     UnicodeString tzurl;
   1024     UDate lmod;
   1025     UDate lastmod = getUTCMillis(2007, UCAL_JUNE, 1);
   1026     VTimeZone *vtz = VTimeZone::createVTimeZoneByID("America/Chicago");
   1027     vtz->setTZURL(TESTURL1);
   1028     vtz->setLastModified(lastmod);
   1029 
   1030     // Roundtrip conversion
   1031     UnicodeString vtzdata;
   1032     vtz->write(vtzdata, status);
   1033     VTimeZone *newvtz1 = NULL;
   1034     if (U_FAILURE(status)) {
   1035         errln("FAIL: error returned while writing VTIMEZONE data 1");
   1036         return;
   1037     }
   1038     // Create a new one
   1039     newvtz1 = VTimeZone::createVTimeZone(vtzdata, status);
   1040     if (U_FAILURE(status)) {
   1041         errln("FAIL: error returned while loading VTIMEZONE data 1");
   1042     } else {
   1043         // Check if TZURL and LAST-MODIFIED properties are preserved
   1044         newvtz1->getTZURL(tzurl);
   1045         if (tzurl != TESTURL1) {
   1046             errln("FAIL: TZURL 1 was not preserved");
   1047         }
   1048         vtz->getLastModified(lmod);
   1049         if (lastmod != lmod) {
   1050             errln("FAIL: LAST-MODIFIED was not preserved");
   1051         }
   1052     }
   1053 
   1054     if (U_SUCCESS(status)) {
   1055         // Set different tzurl
   1056         newvtz1->setTZURL(TESTURL2);
   1057 
   1058         // Second roundtrip, with a cutover
   1059         newvtz1->write(vtzdata, status);
   1060         if (U_FAILURE(status)) {
   1061             errln("FAIL: error returned while writing VTIMEZONE data 2");
   1062         } else {
   1063             VTimeZone *newvtz2 = VTimeZone::createVTimeZone(vtzdata, status);
   1064             if (U_FAILURE(status)) {
   1065                 errln("FAIL: error returned while loading VTIMEZONE data 2");
   1066             } else {
   1067                 // Check if TZURL and LAST-MODIFIED properties are preserved
   1068                 newvtz2->getTZURL(tzurl);
   1069                 if (tzurl != TESTURL2) {
   1070                     errln("FAIL: TZURL was not preserved in the second roundtrip");
   1071                 }
   1072                 vtz->getLastModified(lmod);
   1073                 if (lastmod != lmod) {
   1074                     errln("FAIL: LAST-MODIFIED was not preserved in the second roundtrip");
   1075                 }
   1076             }
   1077             delete newvtz2;
   1078         }
   1079     }
   1080     delete newvtz1;
   1081     delete vtz;
   1082 }
   1083 
   1084 /*
   1085  * Extract simple rules from an OlsonTimeZone and make sure the rule format matches
   1086  * the expected format.
   1087  */
   1088 void
   1089 TimeZoneRuleTest::TestGetSimpleRules(void) {
   1090     UDate testTimes[] = {
   1091         getUTCMillis(1970, UCAL_JANUARY, 1),
   1092         getUTCMillis(2000, UCAL_MARCH, 31),
   1093         getUTCMillis(2005, UCAL_JULY, 1),
   1094         getUTCMillis(2010, UCAL_NOVEMBER, 1),
   1095     };
   1096     int32_t numTimes = sizeof(testTimes)/sizeof(UDate);
   1097     UErrorCode status = U_ZERO_ERROR;
   1098     TestZIDEnumeration tzenum(!quick);
   1099     InitialTimeZoneRule *initial;
   1100     AnnualTimeZoneRule *std, *dst;
   1101     for (int32_t i = 0; i < numTimes ; i++) {
   1102         while (TRUE) {
   1103             const UnicodeString *tzid = tzenum.snext(status);
   1104             if (tzid == NULL) {
   1105                 break;
   1106             }
   1107             if (U_FAILURE(status)) {
   1108                 errln("FAIL: error returned while enumerating timezone IDs.");
   1109                 break;
   1110             }
   1111             BasicTimeZone *tz = (BasicTimeZone*)TimeZone::createTimeZone(*tzid);
   1112             initial = NULL;
   1113             std = dst = NULL;
   1114             tz->getSimpleRulesNear(testTimes[i], initial, std, dst, status);
   1115             if (U_FAILURE(status)) {
   1116                 errln("FAIL: getSimpleRules failed.");
   1117                 break;
   1118             }
   1119             if (initial == NULL) {
   1120                 errln("FAIL: initial rule must not be NULL");
   1121                 break;
   1122             } else if (!(std == NULL && dst == NULL || std != NULL && dst != NULL)) {
   1123                 errln("FAIL: invalid std/dst pair.");
   1124                 break;
   1125             }
   1126             if (std != NULL) {
   1127                 const DateTimeRule *dtr = std->getRule();
   1128                 if (dtr->getDateRuleType() != DateTimeRule::DOW) {
   1129                     errln("FAIL: simple std rull must use DateTimeRule::DOW as date rule.");
   1130                     break;
   1131                 }
   1132                 if (dtr->getTimeRuleType() != DateTimeRule::WALL_TIME) {
   1133                     errln("FAIL: simple std rull must use DateTimeRule::WALL_TIME as time rule.");
   1134                     break;
   1135                 }
   1136                 dtr = dst->getRule();
   1137                 if (dtr->getDateRuleType() != DateTimeRule::DOW) {
   1138                     errln("FAIL: simple dst rull must use DateTimeRule::DOW as date rule.");
   1139                     break;
   1140                 }
   1141                 if (dtr->getTimeRuleType() != DateTimeRule::WALL_TIME) {
   1142                     errln("FAIL: simple dst rull must use DateTimeRule::WALL_TIME as time rule.");
   1143                     break;
   1144                 }
   1145             }
   1146             // Create an RBTZ from the rules and compare the offsets at the date
   1147             RuleBasedTimeZone *rbtz = new RuleBasedTimeZone(*tzid, initial);
   1148             if (std != NULL) {
   1149                 rbtz->addTransitionRule(std, status);
   1150                 if (U_FAILURE(status)) {
   1151                     errln("FAIL: couldn't add std rule.");
   1152                 }
   1153                 rbtz->addTransitionRule(dst, status);
   1154                 if (U_FAILURE(status)) {
   1155                     errln("FAIL: couldn't add dst rule.");
   1156                 }
   1157             }
   1158             rbtz->complete(status);
   1159             if (U_FAILURE(status)) {
   1160                 errln("FAIL: couldn't complete rbtz for " + *tzid);
   1161             }
   1162 
   1163             int32_t raw0, dst0, raw1, dst1;
   1164             tz->getOffset(testTimes[i], FALSE, raw0, dst0, status);
   1165             if (U_FAILURE(status)) {
   1166                 errln("FAIL: couldn't get offsets from tz for " + *tzid);
   1167             }
   1168             rbtz->getOffset(testTimes[i], FALSE, raw1, dst1, status);
   1169             if (U_FAILURE(status)) {
   1170                 errln("FAIL: couldn't get offsets from rbtz for " + *tzid);
   1171             }
   1172             if (raw0 != raw1 || dst0 != dst1) {
   1173                 errln("FAIL: rbtz created by simple rule does not match the original tz for tzid " + *tzid);
   1174             }
   1175             delete rbtz;
   1176             delete tz;
   1177         }
   1178     }
   1179 }
   1180 
   1181 /*
   1182  * API coverage tests for TimeZoneRule
   1183  */
   1184 void
   1185 TimeZoneRuleTest::TestTimeZoneRuleCoverage(void) {
   1186     UDate time1 = getUTCMillis(2005, UCAL_JULY, 4);
   1187     UDate time2 = getUTCMillis(2015, UCAL_JULY, 4);
   1188     UDate time3 = getUTCMillis(1950, UCAL_JULY, 4);
   1189 
   1190     DateTimeRule *dtr1 = new DateTimeRule(UCAL_FEBRUARY, 29, UCAL_SUNDAY, FALSE,
   1191             3*HOUR, DateTimeRule::WALL_TIME); // Last Sunday on or before Feb 29, at 3 AM, wall time
   1192     DateTimeRule *dtr2 = new DateTimeRule(UCAL_MARCH, 11, 2*HOUR,
   1193             DateTimeRule::STANDARD_TIME); // Mar 11, at 2 AM, standard time
   1194     DateTimeRule *dtr3 = new DateTimeRule(UCAL_OCTOBER, -1, UCAL_SATURDAY,
   1195             6*HOUR, DateTimeRule::UTC_TIME); //Last Saturday in Oct, at 6 AM, UTC
   1196     DateTimeRule *dtr4 = new DateTimeRule(UCAL_MARCH, 8, UCAL_SUNDAY, TRUE,
   1197             2*HOUR, DateTimeRule::WALL_TIME); // First Sunday on or after Mar 8, at 2 AM, wall time
   1198 
   1199     AnnualTimeZoneRule *a1 = new AnnualTimeZoneRule("a1", -3*HOUR, 1*HOUR, *dtr1,
   1200             2000, AnnualTimeZoneRule::MAX_YEAR);
   1201     AnnualTimeZoneRule *a2 = new AnnualTimeZoneRule("a2", -3*HOUR, 1*HOUR, *dtr1,
   1202             2000, AnnualTimeZoneRule::MAX_YEAR);
   1203     AnnualTimeZoneRule *a3 = new AnnualTimeZoneRule("a3", -3*HOUR, 1*HOUR, *dtr1,
   1204             2000, 2010);
   1205 
   1206     InitialTimeZoneRule *i1 = new InitialTimeZoneRule("i1", -3*HOUR, 0);
   1207     InitialTimeZoneRule *i2 = new InitialTimeZoneRule("i2", -3*HOUR, 0);
   1208     InitialTimeZoneRule *i3 = new InitialTimeZoneRule("i3", -3*HOUR, 1*HOUR);
   1209 
   1210     UDate trtimes1[] = {0.0};
   1211     UDate trtimes2[] = {0.0, 10000000.0};
   1212 
   1213     TimeArrayTimeZoneRule *t1 = new TimeArrayTimeZoneRule("t1", -3*HOUR, 0, trtimes1, 1, DateTimeRule::UTC_TIME);
   1214     TimeArrayTimeZoneRule *t2 = new TimeArrayTimeZoneRule("t2", -3*HOUR, 0, trtimes1, 1, DateTimeRule::UTC_TIME);
   1215     TimeArrayTimeZoneRule *t3 = new TimeArrayTimeZoneRule("t3", -3*HOUR, 0, trtimes2, 2, DateTimeRule::UTC_TIME);
   1216     TimeArrayTimeZoneRule *t4 = new TimeArrayTimeZoneRule("t4", -3*HOUR, 0, trtimes1, 1, DateTimeRule::STANDARD_TIME);
   1217     TimeArrayTimeZoneRule *t5 = new TimeArrayTimeZoneRule("t5", -4*HOUR, 1*HOUR, trtimes1, 1, DateTimeRule::WALL_TIME);
   1218 
   1219     // DateTimeRule::operator=/clone
   1220     DateTimeRule dtr0(UCAL_MAY, 31, 2*HOUR, DateTimeRule::WALL_TIME);
   1221     if (dtr0 == *dtr1 || !(dtr0 != *dtr1)) {
   1222         errln("FAIL: DateTimeRule dtr0 is not equal to dtr1, but got wrong result");
   1223     }
   1224     dtr0 = *dtr1;
   1225     if (dtr0 != *dtr1 || !(dtr0 == *dtr1)) {
   1226         errln("FAIL: DateTimeRule dtr0 is equal to dtr1, but got wrong result");
   1227     }
   1228     DateTimeRule *dtr0c = dtr0.clone();
   1229     if (*dtr0c != *dtr1 || !(*dtr0c == *dtr1)) {
   1230         errln("FAIL: DateTimeRule dtr0c is equal to dtr1, but got wrong result");
   1231     }
   1232     delete dtr0c;
   1233 
   1234     // AnnualTimeZonerule::operator=/clone
   1235     AnnualTimeZoneRule a0("a0", 5*HOUR, 1*HOUR, *dtr1, 1990, AnnualTimeZoneRule::MAX_YEAR);
   1236     if (a0 == *a1 || !(a0 != *a1)) {
   1237         errln("FAIL: AnnualTimeZoneRule a0 is not equal to a1, but got wrong result");
   1238     }
   1239     a0 = *a1;
   1240     if (a0 != *a1 || !(a0 == *a1)) {
   1241         errln("FAIL: AnnualTimeZoneRule a0 is equal to a1, but got wrong result");
   1242     }
   1243     AnnualTimeZoneRule *a0c = a0.clone();
   1244     if (*a0c != *a1 || !(*a0c == *a1)) {
   1245         errln("FAIL: AnnualTimeZoneRule a0c is equal to a1, but got wrong result");
   1246     }
   1247     delete a0c;
   1248 
   1249     // AnnualTimeZoneRule::getRule
   1250     if (*(a1->getRule()) != *(a2->getRule())) {
   1251         errln("FAIL: The same DateTimeRule must be returned from AnnualTimeZoneRule a1 and a2");
   1252     }
   1253 
   1254     // AnnualTimeZoneRule::getStartYear
   1255     int32_t startYear = a1->getStartYear();
   1256     if (startYear != 2000) {
   1257         errln((UnicodeString)"FAIL: The start year of AnnualTimeZoneRule a1 must be 2000 - returned: " + startYear);
   1258     }
   1259 
   1260     // AnnualTimeZoneRule::getEndYear
   1261     int32_t endYear = a1->getEndYear();
   1262     if (endYear != AnnualTimeZoneRule::MAX_YEAR) {
   1263         errln((UnicodeString)"FAIL: The start year of AnnualTimeZoneRule a1 must be MAX_YEAR - returned: " + endYear);
   1264     }
   1265     endYear = a3->getEndYear();
   1266     if (endYear != 2010) {
   1267         errln((UnicodeString)"FAIL: The start year of AnnualTimeZoneRule a3 must be 2010 - returned: " + endYear);
   1268     }
   1269 
   1270     // AnnualTimeZone::getStartInYear
   1271     UBool b1, b2;
   1272     UDate d1, d2;
   1273     b1 = a1->getStartInYear(2005, -3*HOUR, 0, d1);
   1274     b2 = a3->getStartInYear(2005, -3*HOUR, 0, d2);
   1275     if (!b1 || !b2 || d1 != d2) {
   1276         errln("FAIL: AnnualTimeZoneRule::getStartInYear did not work as expected");
   1277     }
   1278     b2 = a3->getStartInYear(2015, -3*HOUR, 0, d2);
   1279     if (b2) {
   1280         errln("FAIL: AnnualTimeZoneRule::getStartInYear returned TRUE for 2015 which is out of rule range");
   1281     }
   1282 
   1283     // AnnualTimeZone::getFirstStart
   1284     b1 = a1->getFirstStart(-3*HOUR, 0, d1);
   1285     b2 = a1->getFirstStart(-4*HOUR, 1*HOUR, d2);
   1286     if (!b1 || !b2 || d1 != d2) {
   1287         errln("FAIL: The same start time should be returned by getFirstStart");
   1288     }
   1289 
   1290     // AnnualTimeZone::getFinalStart
   1291     b1 = a1->getFinalStart(-3*HOUR, 0, d1);
   1292     if (b1) {
   1293         errln("FAIL: getFinalStart returned TRUE for a1");
   1294     }
   1295     b1 = a1->getStartInYear(2010, -3*HOUR, 0, d1);
   1296     b2 = a3->getFinalStart(-3*HOUR, 0, d2);
   1297     if (!b1 || !b2 || d1 != d2) {
   1298         errln("FAIL: Bad date is returned by getFinalStart");
   1299     }
   1300 
   1301     // AnnualTimeZone::getNextStart / getPreviousStart
   1302     b1 = a1->getNextStart(time1, -3*HOUR, 0, FALSE, d1);
   1303     if (!b1) {
   1304         errln("FAIL: getNextStart returned FALSE for ai");
   1305     } else {
   1306         b2 = a1->getPreviousStart(d1, -3*HOUR, 0, TRUE, d2);
   1307         if (!b2 || d1 != d2) {
   1308             errln("FAIL: Bad Date is returned by getPreviousStart");
   1309         }
   1310     }
   1311     b1 = a3->getNextStart(time2, -3*HOUR, 0, FALSE, d1);
   1312     if (b1) {
   1313         errln("FAIL: getNextStart must return FALSE when no start time is available after the base time");
   1314     }
   1315     b1 = a3->getFinalStart(-3*HOUR, 0, d1);
   1316     b2 = a3->getPreviousStart(time2, -3*HOUR, 0, FALSE, d2);
   1317     if (!b1 || !b2 || d1 != d2) {
   1318         errln("FAIL: getPreviousStart does not match with getFinalStart after the end year");
   1319     }
   1320 
   1321     // AnnualTimeZone::isEquavalentTo
   1322     if (!a1->isEquivalentTo(*a2)) {
   1323         errln("FAIL: AnnualTimeZoneRule a1 is equivalent to a2, but returned FALSE");
   1324     }
   1325     if (a1->isEquivalentTo(*a3)) {
   1326         errln("FAIL: AnnualTimeZoneRule a1 is not equivalent to a3, but returned TRUE");
   1327     }
   1328     if (!a1->isEquivalentTo(*a1)) {
   1329         errln("FAIL: AnnualTimeZoneRule a1 is equivalent to itself, but returned FALSE");
   1330     }
   1331     if (a1->isEquivalentTo(*t1)) {
   1332         errln("FAIL: AnnualTimeZoneRule is not equivalent to TimeArrayTimeZoneRule, but returned TRUE");
   1333     }
   1334 
   1335     // InitialTimezoneRule::operator=/clone
   1336     InitialTimeZoneRule i0("i0", 10*HOUR, 0);
   1337     if (i0 == *i1 || !(i0 != *i1)) {
   1338         errln("FAIL: InitialTimeZoneRule i0 is not equal to i1, but got wrong result");
   1339     }
   1340     i0 = *i1;
   1341     if (i0 != *i1 || !(i0 == *i1)) {
   1342         errln("FAIL: InitialTimeZoneRule i0 is equal to i1, but got wrong result");
   1343     }
   1344     InitialTimeZoneRule *i0c = i0.clone();
   1345     if (*i0c != *i1 || !(*i0c == *i1)) {
   1346         errln("FAIL: InitialTimeZoneRule i0c is equal to i1, but got wrong result");
   1347     }
   1348     delete i0c;
   1349 
   1350     // InitialTimeZoneRule::isEquivalentRule
   1351     if (!i1->isEquivalentTo(*i2)) {
   1352         errln("FAIL: InitialTimeZoneRule i1 is equivalent to i2, but returned FALSE");
   1353     }
   1354     if (i1->isEquivalentTo(*i3)) {
   1355         errln("FAIL: InitialTimeZoneRule i1 is not equivalent to i3, but returned TRUE");
   1356     }
   1357     if (i1->isEquivalentTo(*a1)) {
   1358         errln("FAIL: An InitialTimeZoneRule is not equivalent to an AnnualTimeZoneRule, but returned TRUE");
   1359     }
   1360 
   1361     // InitialTimeZoneRule::getFirstStart/getFinalStart/getNextStart/getPreviousStart
   1362     b1 = i1->getFirstStart(0, 0, d1);
   1363     if (b1) {
   1364         errln("FAIL: InitialTimeZone::getFirstStart returned TRUE");
   1365     }
   1366     b1 = i1->getFinalStart(0, 0, d1);
   1367     if (b1) {
   1368         errln("FAIL: InitialTimeZone::getFinalStart returned TRUE");
   1369     }
   1370     b1 = i1->getNextStart(time1, 0, 0, FALSE, d1);
   1371     if (b1) {
   1372         errln("FAIL: InitialTimeZone::getNextStart returned TRUE");
   1373     }
   1374     b1 = i1->getPreviousStart(time1, 0, 0, FALSE, d1);
   1375     if (b1) {
   1376         errln("FAIL: InitialTimeZone::getPreviousStart returned TRUE");
   1377     }
   1378 
   1379     // TimeArrayTimeZoneRule::operator=/clone
   1380     TimeArrayTimeZoneRule t0("t0", 4*HOUR, 0, trtimes1, 1, DateTimeRule::UTC_TIME);
   1381     if (t0 == *t1 || !(t0 != *t1)) {
   1382         errln("FAIL: TimeArrayTimeZoneRule t0 is not equal to t1, but got wrong result");
   1383     }
   1384     t0 = *t1;
   1385     if (t0 != *t1 || !(t0 == *t1)) {
   1386         errln("FAIL: TimeArrayTimeZoneRule t0 is equal to t1, but got wrong result");
   1387     }
   1388     TimeArrayTimeZoneRule *t0c = t0.clone();
   1389     if (*t0c != *t1 || !(*t0c == *t1)) {
   1390         errln("FAIL: TimeArrayTimeZoneRule t0c is equal to t1, but got wrong result");
   1391     }
   1392     delete t0c;
   1393 
   1394     // TimeArrayTimeZoneRule::countStartTimes
   1395     if (t1->countStartTimes() != 1) {
   1396         errln("FAIL: Bad start time count is returned by TimeArrayTimeZoneRule::countStartTimes");
   1397     }
   1398 
   1399     // TimeArrayTimeZoneRule::getStartTimeAt
   1400     b1 = t1->getStartTimeAt(-1, d1);
   1401     if (b1) {
   1402         errln("FAIL: TimeArrayTimeZoneRule::getStartTimeAt returned TRUE for index -1");
   1403     }
   1404     b1 = t1->getStartTimeAt(0, d1);
   1405     if (!b1 || d1 != trtimes1[0]) {
   1406         errln("FAIL: TimeArrayTimeZoneRule::getStartTimeAt returned incorrect result for index 0");
   1407     }
   1408     b1 = t1->getStartTimeAt(1, d1);
   1409     if (b1) {
   1410         errln("FAIL: TimeArrayTimeZoneRule::getStartTimeAt returned TRUE for index 1");
   1411     }
   1412 
   1413     // TimeArrayTimeZoneRule::getTimeType
   1414     if (t1->getTimeType() != DateTimeRule::UTC_TIME) {
   1415         errln("FAIL: TimeArrayTimeZoneRule t1 uses UTC_TIME, but different type is returned");
   1416     }
   1417     if (t4->getTimeType() != DateTimeRule::STANDARD_TIME) {
   1418         errln("FAIL: TimeArrayTimeZoneRule t4 uses STANDARD_TIME, but different type is returned");
   1419     }
   1420     if (t5->getTimeType() != DateTimeRule::WALL_TIME) {
   1421         errln("FAIL: TimeArrayTimeZoneRule t5 uses WALL_TIME, but different type is returned");
   1422     }
   1423 
   1424     // TimeArrayTimeZoneRule::getFirstStart/getFinalStart
   1425     b1 = t1->getFirstStart(0, 0, d1);
   1426     if (!b1 || d1 != trtimes1[0]) {
   1427         errln("FAIL: Bad first start time returned from TimeArrayTimeZoneRule t1");
   1428     }
   1429     b1 = t1->getFinalStart(0, 0, d1);
   1430     if (!b1 || d1 != trtimes1[0]) {
   1431         errln("FAIL: Bad final start time returned from TimeArrayTimeZoneRule t1");
   1432     }
   1433     b1 = t4->getFirstStart(-4*HOUR, 1*HOUR, d1);
   1434     if (!b1 || d1 != (trtimes1[0] + 4*HOUR)) {
   1435         errln("FAIL: Bad first start time returned from TimeArrayTimeZoneRule t4");
   1436     }
   1437     b1 = t5->getFirstStart(-4*HOUR, 1*HOUR, d1);
   1438     if (!b1 || d1 != (trtimes1[0] + 3*HOUR)) {
   1439         errln("FAIL: Bad first start time returned from TimeArrayTimeZoneRule t5");
   1440     }
   1441 
   1442     // TimeArrayTimeZoneRule::getNextStart/getPreviousStart
   1443     b1 = t3->getNextStart(time1, -3*HOUR, 1*HOUR, FALSE, d1);
   1444     if (b1) {
   1445         errln("FAIL: getNextStart returned TRUE after the final transition for t3");
   1446     }
   1447     b1 = t3->getPreviousStart(time1, -3*HOUR, 1*HOUR, FALSE, d1);
   1448     if (!b1 || d1 != trtimes2[1]) {
   1449         errln("FAIL: Bad start time returned by getPreviousStart for t3");
   1450     } else {
   1451         b2 = t3->getPreviousStart(d1, -3*HOUR, 1*HOUR, FALSE, d2);
   1452         if (!b2 || d2 != trtimes2[0]) {
   1453             errln("FAIL: Bad start time returned by getPreviousStart for t3");
   1454         }
   1455     }
   1456     b1 = t3->getPreviousStart(time3, -3*HOUR, 1*HOUR, FALSE, d1); //time3 - year 1950, no result expected
   1457     if (b1) {
   1458         errln("FAIL: getPreviousStart returned TRUE before the first transition for t3");
   1459     }
   1460 
   1461     // TimeArrayTimeZoneRule::isEquivalentTo
   1462     if (!t1->isEquivalentTo(*t2)) {
   1463         errln("FAIL: TimeArrayTimeZoneRule t1 is equivalent to t2, but returned FALSE");
   1464     }
   1465     if (t1->isEquivalentTo(*t3)) {
   1466         errln("FAIL: TimeArrayTimeZoneRule t1 is not equivalent to t3, but returned TRUE");
   1467     }
   1468     if (t1->isEquivalentTo(*t4)) {
   1469         errln("FAIL: TimeArrayTimeZoneRule t1 is not equivalent to t4, but returned TRUE");
   1470     }
   1471     if (t1->isEquivalentTo(*a1)) {
   1472         errln("FAIL: TimeArrayTimeZoneRule is not equivalent to AnnualTimeZoneRule, but returned TRUE");
   1473     }
   1474 
   1475     delete dtr1;
   1476     delete dtr2;
   1477     delete dtr3;
   1478     delete dtr4;
   1479     delete a1;
   1480     delete a2;
   1481     delete a3;
   1482     delete i1;
   1483     delete i2;
   1484     delete i3;
   1485     delete t1;
   1486     delete t2;
   1487     delete t3;
   1488     delete t4;
   1489     delete t5;
   1490 }
   1491 
   1492 /*
   1493  * API coverage test for BasicTimeZone APIs in SimpleTimeZone
   1494  */
   1495 void
   1496 TimeZoneRuleTest::TestSimpleTimeZoneCoverage(void) {
   1497     UDate time1 = getUTCMillis(1990, UCAL_JUNE, 1);
   1498     UDate time2 = getUTCMillis(2000, UCAL_JUNE, 1);
   1499 
   1500     TimeZoneTransition tzt1, tzt2;
   1501     UBool avail1, avail2;
   1502     UErrorCode status = U_ZERO_ERROR;
   1503     const TimeZoneRule *trrules[2];
   1504     const InitialTimeZoneRule *ir = NULL;
   1505     int32_t numTzRules;
   1506 
   1507     // BasicTimeZone API implementation in SimpleTimeZone
   1508     SimpleTimeZone *stz1 = new SimpleTimeZone(-5*HOUR, "GMT-5");
   1509 
   1510     avail1 = stz1->getNextTransition(time1, FALSE, tzt1);
   1511     if (avail1) {
   1512         errln("FAIL: No transition must be returned by getNextTranstion for SimpleTimeZone with no DST rule");
   1513     }
   1514     avail1 = stz1->getPreviousTransition(time1, FALSE, tzt1);
   1515     if (avail1) {
   1516         errln("FAIL: No transition must be returned by getPreviousTransition  for SimpleTimeZone with no DST rule");
   1517     }
   1518 
   1519     numTzRules = stz1->countTransitionRules(status);
   1520     if (U_FAILURE(status)) {
   1521         errln("FAIL: countTransitionRules failed");
   1522     }
   1523     if (numTzRules != 0) {
   1524         errln((UnicodeString)"FAIL: countTransitionRules returned " + numTzRules);
   1525     }
   1526     numTzRules = 2;
   1527     stz1->getTimeZoneRules(ir, trrules, numTzRules, status);
   1528     if (U_FAILURE(status)) {
   1529         errln("FAIL: getTimeZoneRules failed");
   1530     }
   1531     if (numTzRules != 0) {
   1532         errln("FAIL: Incorrect transition rule count");
   1533     }
   1534     if (ir == NULL || ir->getRawOffset() != stz1->getRawOffset()) {
   1535         errln("FAIL: Bad initial time zone rule");
   1536     }
   1537 
   1538     // Set DST rule
   1539     stz1->setStartRule(UCAL_MARCH, 11, 2*HOUR, status); // March 11
   1540     stz1->setEndRule(UCAL_NOVEMBER, 1, UCAL_SUNDAY, 2*HOUR, status); // First Sunday in November
   1541     if (U_FAILURE(status)) {
   1542         errln("FAIL: Failed to set DST rules in a SimpleTimeZone");
   1543     }
   1544 
   1545     avail1 = stz1->getNextTransition(time1, FALSE, tzt1);
   1546     if (!avail1) {
   1547         errln("FAIL: Non-null transition must be returned by getNextTranstion for SimpleTimeZone with a DST rule");
   1548     }
   1549     avail1 = stz1->getPreviousTransition(time1, FALSE, tzt1);
   1550     if (!avail1) {
   1551         errln("FAIL: Non-null transition must be returned by getPreviousTransition  for SimpleTimeZone with a DST rule");
   1552     }
   1553 
   1554     numTzRules = stz1->countTransitionRules(status);
   1555     if (U_FAILURE(status)) {
   1556         errln("FAIL: countTransitionRules failed");
   1557     }
   1558     if (numTzRules != 2) {
   1559         errln((UnicodeString)"FAIL: countTransitionRules returned " + numTzRules);
   1560     }
   1561 
   1562     numTzRules = 2;
   1563     trrules[0] = NULL;
   1564     trrules[1] = NULL;
   1565     stz1->getTimeZoneRules(ir, trrules, numTzRules, status);
   1566     if (U_FAILURE(status)) {
   1567         errln("FAIL: getTimeZoneRules failed");
   1568     }
   1569     if (numTzRules != 2) {
   1570         errln("FAIL: Incorrect transition rule count");
   1571     }
   1572     if (ir == NULL || ir->getRawOffset() != stz1->getRawOffset()) {
   1573         errln("FAIL: Bad initial time zone rule");
   1574     }
   1575     if (trrules[0] == NULL || trrules[0]->getRawOffset() != stz1->getRawOffset()) {
   1576         errln("FAIL: Bad transition rule 0");
   1577     }
   1578     if (trrules[1] == NULL || trrules[1]->getRawOffset() != stz1->getRawOffset()) {
   1579         errln("FAIL: Bad transition rule 1");
   1580     }
   1581 
   1582     // Set DST start year
   1583     stz1->setStartYear(2007);
   1584     avail1 = stz1->getPreviousTransition(time1, FALSE, tzt1);
   1585     if (avail1) {
   1586         errln("FAIL: No transition must be returned before 1990");
   1587     }
   1588     avail1 = stz1->getNextTransition(time1, FALSE, tzt1); // transition after 1990-06-01
   1589     avail2 = stz1->getNextTransition(time2, FALSE, tzt2); // transition after 2000-06-01
   1590     if (!avail1 || !avail2 || tzt1 != tzt2) {
   1591         errln("FAIL: Bad transition returned by SimpleTimeZone::getNextTransition");
   1592     }
   1593     delete stz1;
   1594 }
   1595 
   1596 /*
   1597  * API coverage test for VTimeZone
   1598  */
   1599 void
   1600 TimeZoneRuleTest::TestVTimeZoneCoverage(void) {
   1601     UErrorCode status = U_ZERO_ERROR;
   1602     UnicodeString TZID("Europe/Moscow");
   1603 
   1604     BasicTimeZone *otz = (BasicTimeZone*)TimeZone::createTimeZone(TZID);
   1605     VTimeZone *vtz = VTimeZone::createVTimeZoneByID(TZID);
   1606 
   1607     // getOffset(era, year, month, day, dayOfWeek, milliseconds, ec)
   1608     int32_t offset1 = otz->getOffset(GregorianCalendar::AD, 2007, UCAL_JULY, 1, UCAL_SUNDAY, 0, status);
   1609     if (U_FAILURE(status)) {
   1610         errln("FAIL: getOffset(7 args) failed for otz");
   1611     }
   1612     int32_t offset2 = vtz->getOffset(GregorianCalendar::AD, 2007, UCAL_JULY, 1, UCAL_SUNDAY, 0, status);
   1613     if (U_FAILURE(status)) {
   1614         errln("FAIL: getOffset(7 args) failed for vtz");
   1615     }
   1616     if (offset1 != offset2) {
   1617         errln("FAIL: getOffset(7 args) returned different results in VTimeZone and OlsonTimeZone");
   1618     }
   1619 
   1620     // getOffset(era, year, month, day, dayOfWeek, milliseconds, monthLength, ec)
   1621     offset1 = otz->getOffset(GregorianCalendar::AD, 2007, UCAL_JULY, 1, UCAL_SUNDAY, 0, 31, status);
   1622     if (U_FAILURE(status)) {
   1623         errln("FAIL: getOffset(8 args) failed for otz");
   1624     }
   1625     offset2 = vtz->getOffset(GregorianCalendar::AD, 2007, UCAL_JULY, 1, UCAL_SUNDAY, 0, 31, status);
   1626     if (U_FAILURE(status)) {
   1627         errln("FAIL: getOffset(8 args) failed for vtz");
   1628     }
   1629     if (offset1 != offset2) {
   1630         errln("FAIL: getOffset(8 args) returned different results in VTimeZone and OlsonTimeZone");
   1631     }
   1632 
   1633 
   1634     // getOffset(date, local, rawOffset, dstOffset, ec)
   1635     UDate t = Calendar::getNow();
   1636     int32_t rawOffset1, dstSavings1;
   1637     int32_t rawOffset2, dstSavings2;
   1638 
   1639     otz->getOffset(t, FALSE, rawOffset1, dstSavings1, status);
   1640     if (U_FAILURE(status)) {
   1641         errln("FAIL: getOffset(5 args) failed for otz");
   1642     }
   1643     vtz->getOffset(t, FALSE, rawOffset2, dstSavings2, status);
   1644     if (U_FAILURE(status)) {
   1645         errln("FAIL: getOffset(5 args) failed for vtz");
   1646     }
   1647     if (rawOffset1 != rawOffset2 || dstSavings1 != dstSavings2) {
   1648         errln("FAIL: getOffset(long,boolean,int[]) returned different results in VTimeZone and OlsonTimeZone");
   1649     }
   1650 
   1651     // getRawOffset
   1652     if (otz->getRawOffset() != vtz->getRawOffset()) {
   1653         errln("FAIL: getRawOffset returned different results in VTimeZone and OlsonTimeZone");
   1654     }
   1655 
   1656     // inDaylightTime
   1657     UBool inDst1, inDst2;
   1658     inDst1 = otz->inDaylightTime(t, status);
   1659     if (U_FAILURE(status)) {
   1660         errln("FAIL: inDaylightTime failed for otz");
   1661     }
   1662     inDst2 = vtz->inDaylightTime(t, status);
   1663     if (U_FAILURE(status)) {
   1664         errln("FAIL: inDaylightTime failed for vtz");
   1665     }
   1666     if (inDst1 != inDst2) {
   1667         errln("FAIL: inDaylightTime returned different results in VTimeZone and OlsonTimeZone");
   1668     }
   1669 
   1670     // useDaylightTime
   1671     if (otz->useDaylightTime() != vtz->useDaylightTime()) {
   1672         errln("FAIL: useDaylightTime returned different results in VTimeZone and OlsonTimeZone");
   1673     }
   1674 
   1675     // setRawOffset
   1676     const int32_t RAW = -10*HOUR;
   1677     VTimeZone *tmpvtz = (VTimeZone*)vtz->clone();
   1678     tmpvtz->setRawOffset(RAW);
   1679     if (tmpvtz->getRawOffset() != RAW) {
   1680         logln("setRawOffset is implemented in VTimeZone");
   1681     }
   1682 
   1683     // hasSameRules
   1684     UBool bSame = otz->hasSameRules(*vtz);
   1685     logln((UnicodeString)"OlsonTimeZone::hasSameRules(VTimeZone) should return FALSE always for now - actual: " + bSame);
   1686 
   1687     // getTZURL/setTZURL
   1688     UnicodeString TZURL("http://icu-project.org/timezone");
   1689     UnicodeString url;
   1690     if (vtz->getTZURL(url)) {
   1691         errln("FAIL: getTZURL returned TRUE");
   1692     }
   1693     vtz->setTZURL(TZURL);
   1694     if (!vtz->getTZURL(url) || url != TZURL) {
   1695         errln("FAIL: URL returned by getTZURL does not match the one set by setTZURL");
   1696     }
   1697 
   1698     // getLastModified/setLastModified
   1699     UDate lastmod;
   1700     if (vtz->getLastModified(lastmod)) {
   1701         errln("FAIL: getLastModified returned TRUE");
   1702     }
   1703     vtz->setLastModified(t);
   1704     if (!vtz->getLastModified(lastmod) || lastmod != t) {
   1705         errln("FAIL: Date returned by getLastModified does not match the one set by setLastModified");
   1706     }
   1707 
   1708     // getNextTransition/getPreviousTransition
   1709     UDate base = getUTCMillis(2007, UCAL_JULY, 1);
   1710     TimeZoneTransition tzt1, tzt2;
   1711     UBool btr1 = otz->getNextTransition(base, TRUE, tzt1);
   1712     UBool btr2 = vtz->getNextTransition(base, TRUE, tzt2);
   1713     if (!btr1 || !btr2 || tzt1 != tzt2) {
   1714         dataerrln("FAIL: getNextTransition returned different results in VTimeZone and OlsonTimeZone");
   1715     }
   1716     btr1 = otz->getPreviousTransition(base, FALSE, tzt1);
   1717     btr2 = vtz->getPreviousTransition(base, FALSE, tzt2);
   1718     if (!btr1 || !btr2 || tzt1 != tzt2) {
   1719         dataerrln("FAIL: getPreviousTransition returned different results in VTimeZone and OlsonTimeZone");
   1720     }
   1721 
   1722     // TimeZoneTransition constructor/clone
   1723     TimeZoneTransition *tzt1c = tzt1.clone();
   1724     if (*tzt1c != tzt1 || !(*tzt1c == tzt1)) {
   1725         errln("FAIL: TimeZoneTransition tzt1c is equal to tzt1, but got wrong result");
   1726     }
   1727     delete tzt1c;
   1728     TimeZoneTransition tzt3(tzt1);
   1729     if (tzt3 != tzt1 || !(tzt3 == tzt1)) {
   1730         errln("FAIL: TimeZoneTransition tzt3 is equal to tzt1, but got wrong result");
   1731     }
   1732 
   1733     // hasEquivalentTransitions
   1734     UDate time1 = getUTCMillis(1950, UCAL_JANUARY, 1);
   1735     UDate time2 = getUTCMillis(2020, UCAL_JANUARY, 1);
   1736     UBool equiv = vtz->hasEquivalentTransitions(*otz, time1, time2, FALSE, status);
   1737     if (U_FAILURE(status)) {
   1738         errln("FAIL: hasEquivalentTransitions failed for vtz/otz");
   1739     }
   1740     if (!equiv) {
   1741         errln("FAIL: hasEquivalentTransitons returned false for the same time zone");
   1742     }
   1743 
   1744     // operator=/operator==/operator!=
   1745     VTimeZone *vtz1 = VTimeZone::createVTimeZoneByID("America/Los_Angeles");
   1746     if (*vtz1 == *vtz || !(*vtz1 != *vtz)) {
   1747         errln("FAIL: VTimeZone vtz1 is not equal to vtz, but got wrong result");
   1748     }
   1749     *vtz1 = *vtz;
   1750     if (*vtz1 != *vtz || !(*vtz1 == *vtz)) {
   1751         errln("FAIL: VTimeZone vtz1 is equal to vtz, but got wrong result");
   1752     }
   1753 
   1754     delete otz;
   1755     delete vtz;
   1756     delete tmpvtz;
   1757     delete vtz1;
   1758 }
   1759 
   1760 
   1761 void
   1762 TimeZoneRuleTest::TestVTimeZoneParse(void) {
   1763     UErrorCode status = U_ZERO_ERROR;
   1764 
   1765     // Trying to create VTimeZone from empty data
   1766     UnicodeString emptyData;
   1767     VTimeZone *empty = VTimeZone::createVTimeZone(emptyData, status);
   1768     if (U_SUCCESS(status) || empty != NULL) {
   1769         delete empty;
   1770         errln("FAIL: Non-null VTimeZone is returned for empty VTIMEZONE data");
   1771     }
   1772     status = U_ZERO_ERROR;
   1773 
   1774     // Create VTimeZone for Asia/Tokyo
   1775     UnicodeString asiaTokyoID("Asia/Tokyo");
   1776     static const UChar asiaTokyo[] = {
   1777         /* "BEGIN:VTIMEZONE\x0D\x0A" */
   1778         0x42,0x45,0x47,0x49,0x4E,0x3A,0x56,0x54,0x49,0x4D,0x45,0x5A,0x4F,0x4E,0x45,0x0D,0x0A,
   1779         /* "TZID:Asia\x0D\x0A" */
   1780         0x54,0x5A,0x49,0x44,0x3A,0x41,0x73,0x69,0x61,0x0D,0x0A,
   1781         /* "\x09/Tokyo\x0D\x0A" */
   1782         0x09,0x2F,0x54,0x6F,0x6B,0x79,0x6F,0x0D,0x0A,
   1783         /* "BEGIN:STANDARD\x0D\x0A" */
   1784         0x42,0x45,0x47,0x49,0x4E,0x3A,0x53,0x54,0x41,0x4E,0x44,0x41,0x52,0x44,0x0D,0x0A,
   1785         /* "TZOFFSETFROM:+0900\x0D\x0A" */
   1786         0x54,0x5A,0x4F,0x46,0x46,0x53,0x45,0x54,0x46,0x52,0x4F,0x4D,0x3A,0x2B,0x30,0x39,0x30,0x30,0x0D,0x0A,
   1787         /* "TZOFFSETTO:+0900\x0D\x0A" */
   1788         0x54,0x5A,0x4F,0x46,0x46,0x53,0x45,0x54,0x54,0x4F,0x3A,0x2B,0x30,0x39,0x30,0x30,0x0D,0x0A,
   1789         /* "TZNAME:JST\x0D\x0A" */
   1790         0x54,0x5A,0x4E,0x41,0x4D,0x45,0x3A,0x4A,0x53,0x54,0x0D,0x0A,
   1791         /* "DTSTART:19700101\x0D\x0A" */
   1792         0x44,0x54,0x53,0x54,0x41,0x52,0x54,0x3A,0x31,0x39,0x37,0x30,0x30,0x31,0x30,0x31,0x0D,0x0A,
   1793         /* " T000000\x0D\x0A" */
   1794         0x20,0x54,0x30,0x30,0x30,0x30,0x30,0x30,0x0D,0x0A,
   1795         /* "END:STANDARD\x0D\x0A" */
   1796         0x45,0x4E,0x44,0x3A,0x53,0x54,0x41,0x4E,0x44,0x41,0x52,0x44,0x0D,0x0A,
   1797         /* "END:VTIMEZONE" */
   1798         0x45,0x4E,0x44,0x3A,0x56,0x54,0x49,0x4D,0x45,0x5A,0x4F,0x4E,0x45,
   1799         0
   1800     };
   1801     VTimeZone *tokyo = VTimeZone::createVTimeZone(asiaTokyo, status);
   1802     if (U_FAILURE(status) || tokyo == NULL) {
   1803         errln("FAIL: Failed to create a VTimeZone tokyo");
   1804     } else {
   1805         // Check ID
   1806         UnicodeString tzid;
   1807         tokyo->getID(tzid);
   1808         if (tzid != asiaTokyoID) {
   1809             errln((UnicodeString)"FAIL: Invalid TZID: " + tzid);
   1810         }
   1811         // Make sure offsets are correct
   1812         int32_t rawOffset, dstSavings;
   1813         tokyo->getOffset(Calendar::getNow(), FALSE, rawOffset, dstSavings, status);
   1814         if (U_FAILURE(status)) {
   1815             errln("FAIL: getOffset failed for tokyo");
   1816         }
   1817         if (rawOffset != 9*HOUR || dstSavings != 0) {
   1818             errln("FAIL: Bad offsets returned by a VTimeZone created for Tokyo");
   1819         }
   1820     }
   1821     delete tokyo;
   1822 
   1823         // Create VTimeZone from VTIMEZONE data
   1824     static const UChar fooData[] = {
   1825         /* "BEGIN:VCALENDAR\x0D\x0A" */
   1826         0x42,0x45,0x47,0x49,0x4E,0x3A,0x56,0x43,0x41,0x4C,0x45,0x4E,0x44,0x41,0x52,0x0D,0x0A,
   1827         /* "BEGIN:VTIMEZONE\x0D\x0A" */
   1828         0x42,0x45,0x47,0x49,0x4E,0x3A,0x56,0x54,0x49,0x4D,0x45,0x5A,0x4F,0x4E,0x45,0x0D,0x0A,
   1829         /* "TZID:FOO\x0D\x0A" */
   1830         0x54,0x5A,0x49,0x44,0x3A,0x46,0x4F,0x4F,0x0D,0x0A,
   1831         /* "BEGIN:STANDARD\x0D\x0A" */
   1832         0x42,0x45,0x47,0x49,0x4E,0x3A,0x53,0x54,0x41,0x4E,0x44,0x41,0x52,0x44,0x0D,0x0A,
   1833         /* "TZOFFSETFROM:-0700\x0D\x0A" */
   1834         0x54,0x5A,0x4F,0x46,0x46,0x53,0x45,0x54,0x46,0x52,0x4F,0x4D,0x3A,0x2D,0x30,0x37,0x30,0x30,0x0D,0x0A,
   1835         /* "TZOFFSETTO:-0800\x0D\x0A" */
   1836         0x54,0x5A,0x4F,0x46,0x46,0x53,0x45,0x54,0x54,0x4F,0x3A,0x2D,0x30,0x38,0x30,0x30,0x0D,0x0A,
   1837         /* "TZNAME:FST\x0D\x0A" */
   1838         0x54,0x5A,0x4E,0x41,0x4D,0x45,0x3A,0x46,0x53,0x54,0x0D,0x0A,
   1839         /* "DTSTART:20071010T010000\x0D\x0A" */
   1840         0x44,0x54,0x53,0x54,0x41,0x52,0x54,0x3A,0x32,0x30,0x30,0x37,0x31,0x30,0x31,0x30,0x54,0x30,0x31,0x30,0x30,0x30,0x30,0x0D,0x0A,
   1841         /* "RRULE:FREQ=YEARLY;BYDAY=WE;BYMONTHDAY=10,11,12,13,14,15,16;BYMONTH=10\x0D\x0A" */
   1842         0x52,0x52,0x55,0x4C,0x45,0x3A,0x46,0x52,0x45,0x51,0x3D,0x59,0x45,0x41,0x52,0x4C,0x59,0x3B,0x42,0x59,0x44,0x41,0x59,0x3D,0x57,0x45,0x3B,0x42,0x59,0x4D,0x4F,0x4E,0x54,0x48,0x44,0x41,0x59,0x3D,0x31,0x30,0x2C,0x31,0x31,0x2C,0x31,0x32,0x2C,0x31,0x33,0x2C,0x31,0x34,0x2C,0x31,0x35,0x2C,0x31,0x36,0x3B,0x42,0x59,0x4D,0x4F,0x4E,0x54,0x48,0x3D,0x31,0x30,0x0D,0x0A,
   1843         /* "END:STANDARD\x0D\x0A" */
   1844         0x45,0x4E,0x44,0x3A,0x53,0x54,0x41,0x4E,0x44,0x41,0x52,0x44,0x0D,0x0A,
   1845         /* "BEGIN:DAYLIGHT\x0D\x0A" */
   1846         0x42,0x45,0x47,0x49,0x4E,0x3A,0x44,0x41,0x59,0x4C,0x49,0x47,0x48,0x54,0x0D,0x0A,
   1847         /* "TZOFFSETFROM:-0800\x0D\x0A" */
   1848         0x54,0x5A,0x4F,0x46,0x46,0x53,0x45,0x54,0x46,0x52,0x4F,0x4D,0x3A,0x2D,0x30,0x38,0x30,0x30,0x0D,0x0A,
   1849         /* "TZOFFSETTO:-0700\x0D\x0A" */
   1850         0x54,0x5A,0x4F,0x46,0x46,0x53,0x45,0x54,0x54,0x4F,0x3A,0x2D,0x30,0x37,0x30,0x30,0x0D,0x0A,
   1851         /* "TZNAME:FDT\x0D\x0A" */
   1852         0x54,0x5A,0x4E,0x41,0x4D,0x45,0x3A,0x46,0x44,0x54,0x0D,0x0A,
   1853         /* "DTSTART:20070415T010000\x0D\x0A" */
   1854         0x44,0x54,0x53,0x54,0x41,0x52,0x54,0x3A,0x32,0x30,0x30,0x37,0x30,0x34,0x31,0x35,0x54,0x30,0x31,0x30,0x30,0x30,0x30,0x0D,0x0A,
   1855         /* "RRULE:FREQ=YEARLY;BYMONTHDAY=15;BYMONTH=4\x0D\x0A" */
   1856         0x52,0x52,0x55,0x4C,0x45,0x3A,0x46,0x52,0x45,0x51,0x3D,0x59,0x45,0x41,0x52,0x4C,0x59,0x3B,0x42,0x59,0x4D,0x4F,0x4E,0x54,0x48,0x44,0x41,0x59,0x3D,0x31,0x35,0x3B,0x42,0x59,0x4D,0x4F,0x4E,0x54,0x48,0x3D,0x34,0x0D,0x0A,
   1857         /* "END:DAYLIGHT\x0D\x0A" */
   1858         0x45,0x4E,0x44,0x3A,0x44,0x41,0x59,0x4C,0x49,0x47,0x48,0x54,0x0D,0x0A,
   1859         /* "END:VTIMEZONE\x0D\x0A" */
   1860         0x45,0x4E,0x44,0x3A,0x56,0x54,0x49,0x4D,0x45,0x5A,0x4F,0x4E,0x45,0x0D,0x0A,
   1861         /* "END:VCALENDAR" */
   1862         0x45,0x4E,0x44,0x3A,0x56,0x43,0x41,0x4C,0x45,0x4E,0x44,0x41,0x52,
   1863         0
   1864     };
   1865 
   1866     VTimeZone *foo = VTimeZone::createVTimeZone(fooData, status);
   1867     if (U_FAILURE(status) || foo == NULL) {
   1868         errln("FAIL: Failed to create a VTimeZone foo");
   1869     } else {
   1870         // Write VTIMEZONE data
   1871         UnicodeString fooData2;
   1872         foo->write(getUTCMillis(2005, UCAL_JANUARY, 1), fooData2, status);
   1873         if (U_FAILURE(status)) {
   1874             errln("FAIL: Failed to write VTIMEZONE data for foo");
   1875         }
   1876         logln(fooData2);
   1877     }
   1878     delete foo;
   1879 }
   1880 
   1881 void
   1882 TimeZoneRuleTest::TestT6216(void) {
   1883     // Test case in #6216
   1884     static const UChar tokyoTZ[] = {
   1885         /* "BEGIN:VCALENDAR\r\n" */
   1886         0x42,0x45,0x47,0x49,0x4e,0x3a,0x56,0x43,0x41,0x4c,0x45,0x4e,0x44,0x41,0x52,0x0d,0x0a,
   1887         /* "VERSION:2.0\r\n" */
   1888         0x56,0x45,0x52,0x53,0x49,0x4f,0x4e,0x3a,0x32,0x2e,0x30,0x0d,0x0a,
   1889         /* "PRODID:-//PYVOBJECT//NONSGML Version 1//EN\r\n" */
   1890         0x50,0x52,0x4f,0x44,0x49,0x44,0x3a,0x2d,0x2f,0x2f,0x50,0x59,0x56,0x4f,0x42,0x4a,0x45,0x43,0x54,0x2f,0x2f,0x4e,0x4f,0x4e,0x53,0x47,0x4d,0x4c,0x20,0x56,0x65,0x72,0x73,0x69,0x6f,0x6e,0x20,0x31,0x2f,0x2f,0x45,0x4e,0x0d,0x0a,
   1891         /* "BEGIN:VTIMEZONE\r\n" */
   1892         0x42,0x45,0x47,0x49,0x4e,0x3a,0x56,0x54,0x49,0x4d,0x45,0x5a,0x4f,0x4e,0x45,0x0d,0x0a,
   1893         /* "TZID:Asia/Tokyo\r\n" */
   1894         0x54,0x5a,0x49,0x44,0x3a,0x41,0x73,0x69,0x61,0x2f,0x54,0x6f,0x6b,0x79,0x6f,0x0d,0x0a,
   1895         /* "BEGIN:STANDARD\r\n" */
   1896         0x42,0x45,0x47,0x49,0x4e,0x3a,0x53,0x54,0x41,0x4e,0x44,0x41,0x52,0x44,0x0d,0x0a,
   1897         /* "DTSTART:20000101T000000\r\n" */
   1898         0x44,0x54,0x53,0x54,0x41,0x52,0x54,0x3a,0x32,0x30,0x30,0x30,0x30,0x31,0x30,0x31,0x54,0x30,0x30,0x30,0x30,0x30,0x30,0x0d,0x0a,
   1899         /* "RRULE:FREQ=YEARLY;BYMONTH=1\r\n" */
   1900         0x52,0x52,0x55,0x4c,0x45,0x3a,0x46,0x52,0x45,0x51,0x3d,0x59,0x45,0x41,0x52,0x4c,0x59,0x3b,0x42,0x59,0x4d,0x4f,0x4e,0x54,0x48,0x3d,0x31,0x0d,0x0a,
   1901         /* "TZNAME:Asia/Tokyo\r\n" */
   1902         0x54,0x5a,0x4e,0x41,0x4d,0x45,0x3a,0x41,0x73,0x69,0x61,0x2f,0x54,0x6f,0x6b,0x79,0x6f,0x0d,0x0a,
   1903         /* "TZOFFSETFROM:+0900\r\n" */
   1904         0x54,0x5a,0x4f,0x46,0x46,0x53,0x45,0x54,0x46,0x52,0x4f,0x4d,0x3a,0x2b,0x30,0x39,0x30,0x30,0x0d,0x0a,
   1905         /* "TZOFFSETTO:+0900\r\n" */
   1906         0x54,0x5a,0x4f,0x46,0x46,0x53,0x45,0x54,0x54,0x4f,0x3a,0x2b,0x30,0x39,0x30,0x30,0x0d,0x0a,
   1907         /* "END:STANDARD\r\n" */
   1908         0x45,0x4e,0x44,0x3a,0x53,0x54,0x41,0x4e,0x44,0x41,0x52,0x44,0x0d,0x0a,
   1909         /* "END:VTIMEZONE\r\n" */
   1910         0x45,0x4e,0x44,0x3a,0x56,0x54,0x49,0x4d,0x45,0x5a,0x4f,0x4e,0x45,0x0d,0x0a,
   1911         /* "END:VCALENDAR" */
   1912         0x45,0x4e,0x44,0x3a,0x56,0x43,0x41,0x4c,0x45,0x4e,0x44,0x41,0x52,0x0d,0x0a,
   1913         0
   1914     };
   1915     // Single final rule, overlapping with another
   1916     static const UChar finalOverlap[] = {
   1917         /* "BEGIN:VCALENDAR\r\n" */
   1918         0x42,0x45,0x47,0x49,0x4e,0x3a,0x56,0x43,0x41,0x4c,0x45,0x4e,0x44,0x41,0x52,0x0d,0x0a,
   1919         /* "BEGIN:VTIMEZONE\r\n" */
   1920         0x42,0x45,0x47,0x49,0x4e,0x3a,0x56,0x54,0x49,0x4d,0x45,0x5a,0x4f,0x4e,0x45,0x0d,0x0a,
   1921         /* "TZID:FinalOverlap\r\n" */
   1922         0x54,0x5a,0x49,0x44,0x3a,0x46,0x69,0x6e,0x61,0x6c,0x4f,0x76,0x65,0x72,0x6c,0x61,0x70,0x0d,0x0a,
   1923         /* "BEGIN:STANDARD\r\n" */
   1924         0x42,0x45,0x47,0x49,0x4e,0x3a,0x53,0x54,0x41,0x4e,0x44,0x41,0x52,0x44,0x0d,0x0a,
   1925         /* "TZOFFSETFROM:-0200\r\n" */
   1926         0x54,0x5a,0x4f,0x46,0x46,0x53,0x45,0x54,0x46,0x52,0x4f,0x4d,0x3a,0x2d,0x30,0x32,0x30,0x30,0x0d,0x0a,
   1927         /* "TZOFFSETTO:-0300\r\n" */
   1928         0x54,0x5a,0x4f,0x46,0x46,0x53,0x45,0x54,0x54,0x4f,0x3a,0x2d,0x30,0x33,0x30,0x30,0x0d,0x0a,
   1929         /* "TZNAME:STD\r\n" */
   1930         0x54,0x5a,0x4e,0x41,0x4d,0x45,0x3a,0x53,0x54,0x44,0x0d,0x0a,
   1931         /* "DTSTART:20001029T020000\r\n" */
   1932         0x44,0x54,0x53,0x54,0x41,0x52,0x54,0x3a,0x32,0x30,0x30,0x30,0x31,0x30,0x32,0x39,0x54,0x30,0x32,0x30,0x30,0x30,0x30,0x0d,0x0a,
   1933         /* "RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10\r\n" */
   1934         0x52,0x52,0x55,0x4c,0x45,0x3a,0x46,0x52,0x45,0x51,0x3d,0x59,0x45,0x41,0x52,0x4c,0x59,0x3b,0x42,0x59,0x44,0x41,0x59,0x3d,0x2d,0x31,0x53,0x55,0x3b,0x42,0x59,0x4d,0x4f,0x4e,0x54,0x48,0x3d,0x31,0x30,0x0d,0x0a,
   1935         /* "END:STANDARD\r\n" */
   1936         0x45,0x4e,0x44,0x3a,0x53,0x54,0x41,0x4e,0x44,0x41,0x52,0x44,0x0d,0x0a,
   1937         /* "BEGIN:DAYLIGHT\r\n" */
   1938         0x42,0x45,0x47,0x49,0x4e,0x3a,0x44,0x41,0x59,0x4c,0x49,0x47,0x48,0x54,0x0d,0x0a,
   1939         /* "TZOFFSETFROM:-0300\r\n" */
   1940         0x54,0x5a,0x4f,0x46,0x46,0x53,0x45,0x54,0x46,0x52,0x4f,0x4d,0x3a,0x2d,0x30,0x33,0x30,0x30,0x0d,0x0a,
   1941         /* "TZOFFSETTO:-0200\r\n" */
   1942         0x54,0x5a,0x4f,0x46,0x46,0x53,0x45,0x54,0x54,0x4f,0x3a,0x2d,0x30,0x32,0x30,0x30,0x0d,0x0a,
   1943         /* "TZNAME:DST\r\n" */
   1944         0x54,0x5a,0x4e,0x41,0x4d,0x45,0x3a,0x44,0x53,0x54,0x0d,0x0a,
   1945         /* "DTSTART:19990404T020000\r\n" */
   1946         0x44,0x54,0x53,0x54,0x41,0x52,0x54,0x3a,0x31,0x39,0x39,0x39,0x30,0x34,0x30,0x34,0x54,0x30,0x32,0x30,0x30,0x30,0x30,0x0d,0x0a,
   1947         /* "RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4;UNTIL=20050403T040000Z\r\n" */
   1948         0x52,0x52,0x55,0x4c,0x45,0x3a,0x46,0x52,0x45,0x51,0x3d,0x59,0x45,0x41,0x52,0x4c,0x59,0x3b,0x42,0x59,0x44,0x41,0x59,0x3d,0x31,0x53,0x55,0x3b,0x42,0x59,0x4d,0x4f,0x4e,0x54,0x48,0x3d,0x34,0x3b,0x55,0x4e,0x54,0x49,0x4c,0x3d,0x32,0x30,0x30,0x35,0x30,0x34,0x30,0x33,0x54,0x30,0x34,0x30,0x30,0x30,0x30,0x5a,0x0d,0x0a,
   1949         /* "END:DAYLIGHT\r\n" */
   1950         0x45,0x4e,0x44,0x3a,0x44,0x41,0x59,0x4c,0x49,0x47,0x48,0x54,0x0d,0x0a,
   1951         /* "END:VTIMEZONE\r\n" */
   1952         0x45,0x4e,0x44,0x3a,0x56,0x54,0x49,0x4d,0x45,0x5a,0x4f,0x4e,0x45,0x0d,0x0a,
   1953         /* "END:VCALENDAR" */
   1954         0x45,0x4e,0x44,0x3a,0x56,0x43,0x41,0x4c,0x45,0x4e,0x44,0x41,0x52,0x0d,0x0a,
   1955         0
   1956     };
   1957     // Single final rule, no overlapping with another
   1958     static const UChar finalNonOverlap[] = {
   1959         /* "BEGIN:VCALENDAR\r\n" */
   1960         0x42,0x45,0x47,0x49,0x4e,0x3a,0x56,0x43,0x41,0x4c,0x45,0x4e,0x44,0x41,0x52,0x0d,0x0a,
   1961         /* "BEGIN:VTIMEZONE\r\n" */
   1962         0x42,0x45,0x47,0x49,0x4e,0x3a,0x56,0x54,0x49,0x4d,0x45,0x5a,0x4f,0x4e,0x45,0x0d,0x0a,
   1963         /* "TZID:FinalNonOverlap\r\n" */
   1964         0x54,0x5a,0x49,0x44,0x3a,0x46,0x69,0x6e,0x61,0x6c,0x4e,0x6f,0x6e,0x4f,0x76,0x65,0x72,0x6c,0x61,0x70,0x0d,0x0a,
   1965         /* "BEGIN:STANDARD\r\n" */
   1966         0x42,0x45,0x47,0x49,0x4e,0x3a,0x53,0x54,0x41,0x4e,0x44,0x41,0x52,0x44,0x0d,0x0a,
   1967         /* "TZOFFSETFROM:-0200\r\n" */
   1968         0x54,0x5a,0x4f,0x46,0x46,0x53,0x45,0x54,0x46,0x52,0x4f,0x4d,0x3a,0x2d,0x30,0x32,0x30,0x30,0x0d,0x0a,
   1969         /* "TZOFFSETTO:-0300\r\n" */
   1970         0x54,0x5a,0x4f,0x46,0x46,0x53,0x45,0x54,0x54,0x4f,0x3a,0x2d,0x30,0x33,0x30,0x30,0x0d,0x0a,
   1971         /* "TZNAME:STD\r\n" */
   1972         0x54,0x5a,0x4e,0x41,0x4d,0x45,0x3a,0x53,0x54,0x44,0x0d,0x0a,
   1973         /* "DTSTART:20001029T020000\r\n" */
   1974         0x44,0x54,0x53,0x54,0x41,0x52,0x54,0x3a,0x32,0x30,0x30,0x30,0x31,0x30,0x32,0x39,0x54,0x30,0x32,0x30,0x30,0x30,0x30,0x0d,0x0a,
   1975         /* "RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10;UNTIL=20041031T040000Z\r\n" */
   1976         0x52,0x52,0x55,0x4c,0x45,0x3a,0x46,0x52,0x45,0x51,0x3d,0x59,0x45,0x41,0x52,0x4c,0x59,0x3b,0x42,0x59,0x44,0x41,0x59,0x3d,0x2d,0x31,0x53,0x55,0x3b,0x42,0x59,0x4d,0x4f,0x4e,0x54,0x48,0x3d,0x31,0x30,0x3b,0x55,0x4e,0x54,0x49,0x4c,0x3d,0x32,0x30,0x30,0x34,0x31,0x30,0x33,0x31,0x54,0x30,0x34,0x30,0x30,0x30,0x30,0x5a,0x0d,0x0a,
   1977         /* "END:STANDARD\r\n" */
   1978         0x45,0x4e,0x44,0x3a,0x53,0x54,0x41,0x4e,0x44,0x41,0x52,0x44,0x0d,0x0a,
   1979         /* "BEGIN:DAYLIGHT\r\n" */
   1980         0x42,0x45,0x47,0x49,0x4e,0x3a,0x44,0x41,0x59,0x4c,0x49,0x47,0x48,0x54,0x0d,0x0a,
   1981         /* "TZOFFSETFROM:-0300\r\n" */
   1982         0x54,0x5a,0x4f,0x46,0x46,0x53,0x45,0x54,0x46,0x52,0x4f,0x4d,0x3a,0x2d,0x30,0x33,0x30,0x30,0x0d,0x0a,
   1983         /* "TZOFFSETTO:-0200\r\n" */
   1984         0x54,0x5a,0x4f,0x46,0x46,0x53,0x45,0x54,0x54,0x4f,0x3a,0x2d,0x30,0x32,0x30,0x30,0x0d,0x0a,
   1985         /* "TZNAME:DST\r\n" */
   1986         0x54,0x5a,0x4e,0x41,0x4d,0x45,0x3a,0x44,0x53,0x54,0x0d,0x0a,
   1987         /* "DTSTART:19990404T020000\r\n" */
   1988         0x44,0x54,0x53,0x54,0x41,0x52,0x54,0x3a,0x31,0x39,0x39,0x39,0x30,0x34,0x30,0x34,0x54,0x30,0x32,0x30,0x30,0x30,0x30,0x0d,0x0a,
   1989         /* "RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4;UNTIL=20050403T040000Z\r\n" */
   1990         0x52,0x52,0x55,0x4c,0x45,0x3a,0x46,0x52,0x45,0x51,0x3d,0x59,0x45,0x41,0x52,0x4c,0x59,0x3b,0x42,0x59,0x44,0x41,0x59,0x3d,0x31,0x53,0x55,0x3b,0x42,0x59,0x4d,0x4f,0x4e,0x54,0x48,0x3d,0x34,0x3b,0x55,0x4e,0x54,0x49,0x4c,0x3d,0x32,0x30,0x30,0x35,0x30,0x34,0x30,0x33,0x54,0x30,0x34,0x30,0x30,0x30,0x30,0x5a,0x0d,0x0a,
   1991         /* "END:DAYLIGHT\r\n" */
   1992         0x45,0x4e,0x44,0x3a,0x44,0x41,0x59,0x4c,0x49,0x47,0x48,0x54,0x0d,0x0a,
   1993         /* "BEGIN:STANDARD\r\n" */
   1994         0x42,0x45,0x47,0x49,0x4e,0x3a,0x53,0x54,0x41,0x4e,0x44,0x41,0x52,0x44,0x0d,0x0a,
   1995         /* "TZOFFSETFROM:-0200\r\n" */
   1996         0x54,0x5a,0x4f,0x46,0x46,0x53,0x45,0x54,0x46,0x52,0x4f,0x4d,0x3a,0x2d,0x30,0x32,0x30,0x30,0x0d,0x0a,
   1997         /* "TZOFFSETTO:-0300\r\n" */
   1998         0x54,0x5a,0x4f,0x46,0x46,0x53,0x45,0x54,0x54,0x4f,0x3a,0x2d,0x30,0x33,0x30,0x30,0x0d,0x0a,
   1999         /* "TZNAME:STDFINAL\r\n" */
   2000         0x54,0x5a,0x4e,0x41,0x4d,0x45,0x3a,0x53,0x54,0x44,0x46,0x49,0x4e,0x41,0x4c,0x0d,0x0a,
   2001         /* "DTSTART:20071028T020000\r\n" */
   2002         0x44,0x54,0x53,0x54,0x41,0x52,0x54,0x3a,0x32,0x30,0x30,0x37,0x31,0x30,0x32,0x38,0x54,0x30,0x32,0x30,0x30,0x30,0x30,0x0d,0x0a,
   2003         /* "RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10\r\n" */
   2004         0x52,0x52,0x55,0x4c,0x45,0x3a,0x46,0x52,0x45,0x51,0x3d,0x59,0x45,0x41,0x52,0x4c,0x59,0x3b,0x42,0x59,0x44,0x41,0x59,0x3d,0x2d,0x31,0x53,0x55,0x3b,0x42,0x59,0x4d,0x4f,0x4e,0x54,0x48,0x3d,0x31,0x30,0x0d,0x0a,
   2005         /* "END:STANDARD\r\n" */
   2006         0x45,0x4e,0x44,0x3a,0x53,0x54,0x41,0x4e,0x44,0x41,0x52,0x44,0x0d,0x0a,
   2007         /* "END:VTIMEZONE\r\n" */
   2008         0x45,0x4e,0x44,0x3a,0x56,0x54,0x49,0x4d,0x45,0x5a,0x4f,0x4e,0x45,0x0d,0x0a,
   2009         /* "END:VCALENDAR" */
   2010         0x45,0x4e,0x44,0x3a,0x56,0x43,0x41,0x4c,0x45,0x4e,0x44,0x41,0x52,0x0d,0x0a,
   2011         0
   2012     };
   2013 
   2014     static const int32_t TestDates[][3] = {
   2015         {1995, UCAL_JANUARY, 1},
   2016         {1995, UCAL_JULY, 1},
   2017         {2000, UCAL_JANUARY, 1},
   2018         {2000, UCAL_JULY, 1},
   2019         {2005, UCAL_JANUARY, 1},
   2020         {2005, UCAL_JULY, 1},
   2021         {2010, UCAL_JANUARY, 1},
   2022         {2010, UCAL_JULY, 1},
   2023         {0, 0, 0}
   2024     };
   2025 
   2026     static const UnicodeString TestZones[] = {
   2027         UnicodeString(tokyoTZ),
   2028         UnicodeString(finalOverlap),
   2029         UnicodeString(finalNonOverlap),
   2030         UnicodeString()
   2031     };
   2032 
   2033     int32_t Expected[][8] = {
   2034       //  JAN90      JUL90      JAN00      JUL00      JAN05      JUL05      JAN10      JUL10
   2035         { 32400000,  32400000,  32400000,  32400000,  32400000,  32400000,  32400000,  32400000},
   2036         {-10800000, -10800000,  -7200000,  -7200000, -10800000,  -7200000, -10800000, -10800000},
   2037         {-10800000, -10800000,  -7200000,  -7200000, -10800000,  -7200000, -10800000, -10800000}
   2038     };
   2039 
   2040     int32_t i, j;
   2041 
   2042     // Get test times
   2043     UDate times[sizeof(TestDates) / (3 * sizeof(int32_t))];
   2044     int32_t numTimes;
   2045 
   2046     UErrorCode status = U_ZERO_ERROR;
   2047     TimeZone *utc = TimeZone::createTimeZone("Etc/GMT");
   2048     GregorianCalendar cal(utc, status);
   2049     if (U_FAILURE(status)) {
   2050         errln("FAIL: Failed to creat a GregorianCalendar");
   2051         return;
   2052     }
   2053     for (i = 0; TestDates[i][2] != 0; i++) {
   2054         cal.clear();
   2055         cal.set(TestDates[i][0], TestDates[i][1], TestDates[i][2]);
   2056         times[i] = cal.getTime(status);
   2057         if (U_FAILURE(status)) {
   2058             errln("FAIL: getTime failed");
   2059             return;
   2060         }
   2061     }
   2062     numTimes = i;
   2063 
   2064     // Test offset
   2065     for (i = 0; !TestZones[i].isEmpty(); i++) {
   2066         VTimeZone *vtz = VTimeZone::createVTimeZone(TestZones[i], status);
   2067         if (U_FAILURE(status)) {
   2068             errln("FAIL: failed to create VTimeZone");
   2069             continue;
   2070         }
   2071         for (j = 0; j < numTimes; j++) {
   2072             int32_t raw, dst;
   2073             status = U_ZERO_ERROR;
   2074             vtz->getOffset(times[j], FALSE, raw, dst, status);
   2075             if (U_FAILURE(status)) {
   2076                 errln((UnicodeString)"FAIL: getOffset failed for time zone " + i + " at " + times[j]);
   2077             }
   2078             int32_t offset = raw + dst;
   2079             if (offset != Expected[i][j]) {
   2080                 errln((UnicodeString)"FAIL: Invalid offset at time(" + times[j] + "):" + offset + " Expected:" + Expected[i][j]);
   2081             }
   2082         }
   2083         delete vtz;
   2084     }
   2085 }
   2086 
   2087 void
   2088 TimeZoneRuleTest::TestT6669(void) {
   2089     UErrorCode status = U_ZERO_ERROR;
   2090     SimpleTimeZone stz(0, "CustomID", UCAL_JANUARY, 1, UCAL_SUNDAY, 0, UCAL_JULY, 1, UCAL_SUNDAY, 0, status);
   2091     if (U_FAILURE(status)) {
   2092         errln("FAIL: Failed to creat a SimpleTimeZone");
   2093         return;
   2094     }
   2095 
   2096     UDate t = 1230681600000.0; //2008-12-31T00:00:00
   2097     UDate expectedNext = 1231027200000.0; //2009-01-04T00:00:00
   2098     UDate expectedPrev = 1215298800000.0; //2008-07-06T00:00:00
   2099 
   2100     TimeZoneTransition tzt;
   2101     UBool avail = stz.getNextTransition(t, FALSE, tzt);
   2102     if (!avail) {
   2103         errln("FAIL: No transition returned by getNextTransition.");
   2104     } else if (tzt.getTime() != expectedNext) {
   2105         errln((UnicodeString)"FAIL: Wrong transition time returned by getNextTransition - "
   2106             + tzt.getTime() + " Expected: " + expectedNext);
   2107     }
   2108 
   2109     avail = stz.getPreviousTransition(t, TRUE, tzt);
   2110     if (!avail) {
   2111         errln("FAIL: No transition returned by getPreviousTransition.");
   2112     } else if (tzt.getTime() != expectedPrev) {
   2113         errln((UnicodeString)"FAIL: Wrong transition time returned by getPreviousTransition - "
   2114             + tzt.getTime() + " Expected: " + expectedPrev);
   2115     }
   2116 }
   2117 
   2118 //----------- private test helpers -------------------------------------------------
   2119 
   2120 UDate
   2121 TimeZoneRuleTest::getUTCMillis(int32_t y, int32_t m, int32_t d,
   2122                                int32_t hr, int32_t min, int32_t sec, int32_t msec) {
   2123     UErrorCode status = U_ZERO_ERROR;
   2124     const TimeZone *tz = TimeZone::getGMT();
   2125     Calendar *cal = Calendar::createInstance(*tz, status);
   2126     if (U_FAILURE(status)) {
   2127         delete cal;
   2128         errln("FAIL: Calendar::createInstance failed");
   2129         return 0.0;
   2130     }
   2131     cal->set(y, m, d, hr, min, sec);
   2132     cal->set(UCAL_MILLISECOND, msec);
   2133     UDate utc = cal->getTime(status);
   2134     if (U_FAILURE(status)) {
   2135         delete cal;
   2136         errln("FAIL: Calendar::getTime failed");
   2137         return 0.0;
   2138     }
   2139     delete cal;
   2140     return utc;
   2141 }
   2142 
   2143 /*
   2144  * Check if a time shift really happens on each transition returned by getNextTransition or
   2145  * getPreviousTransition in the specified time range
   2146  */
   2147 void
   2148 TimeZoneRuleTest::verifyTransitions(BasicTimeZone& icutz, UDate start, UDate end) {
   2149     UErrorCode status = U_ZERO_ERROR;
   2150     UDate time;
   2151     int32_t raw, dst, raw0, dst0;
   2152     TimeZoneTransition tzt, tzt0;
   2153     UBool avail;
   2154     UBool first = TRUE;
   2155     UnicodeString tzid;
   2156 
   2157     // Ascending
   2158     time = start;
   2159     while (TRUE) {
   2160         avail = icutz.getNextTransition(time, FALSE, tzt);
   2161         if (!avail) {
   2162             break;
   2163         }
   2164         time = tzt.getTime();
   2165         if (time >= end) {
   2166             break;
   2167         }
   2168         icutz.getOffset(time, FALSE, raw, dst, status);
   2169         icutz.getOffset(time - 1, FALSE, raw0, dst0, status);
   2170         if (U_FAILURE(status)) {
   2171             errln("FAIL: Error in getOffset");
   2172             break;
   2173         }
   2174 
   2175         if (raw == raw0 && dst == dst0) {
   2176             errln((UnicodeString)"FAIL: False transition returned by getNextTransition for "
   2177                 + icutz.getID(tzid) + " at " + dateToString(time));
   2178         }
   2179         if (!first &&
   2180                 (tzt0.getTo()->getRawOffset() != tzt.getFrom()->getRawOffset()
   2181                 || tzt0.getTo()->getDSTSavings() != tzt.getFrom()->getDSTSavings())) {
   2182             errln((UnicodeString)"FAIL: TO rule of the previous transition does not match FROM rule of this transtion at "
   2183                     + dateToString(time) + " for " + icutz.getID(tzid));
   2184         }
   2185         tzt0 = tzt;
   2186         first = FALSE;
   2187     }
   2188 
   2189     // Descending
   2190     first = TRUE;
   2191     time = end;
   2192     while(true) {
   2193         avail = icutz.getPreviousTransition(time, FALSE, tzt);
   2194         if (!avail) {
   2195             break;
   2196         }
   2197         time = tzt.getTime();
   2198         if (time <= start) {
   2199             break;
   2200         }
   2201         icutz.getOffset(time, FALSE, raw, dst, status);
   2202         icutz.getOffset(time - 1, FALSE, raw0, dst0, status);
   2203         if (U_FAILURE(status)) {
   2204             errln("FAIL: Error in getOffset");
   2205             break;
   2206         }
   2207 
   2208         if (raw == raw0 && dst == dst0) {
   2209             errln((UnicodeString)"FAIL: False transition returned by getPreviousTransition for "
   2210                 + icutz.getID(tzid) + " at " + dateToString(time));
   2211         }
   2212 
   2213         if (!first &&
   2214                 (tzt0.getFrom()->getRawOffset() != tzt.getTo()->getRawOffset()
   2215                 || tzt0.getFrom()->getDSTSavings() != tzt.getTo()->getDSTSavings())) {
   2216             errln((UnicodeString)"FAIL: TO rule of the next transition does not match FROM rule in this transtion at "
   2217                     + dateToString(time) + " for " + icutz.getID(tzid));
   2218         }
   2219         tzt0 = tzt;
   2220         first = FALSE;
   2221     }
   2222 }
   2223 
   2224 /*
   2225  * Compare all time transitions in 2 time zones in the specified time range in ascending order
   2226  */
   2227 void
   2228 TimeZoneRuleTest::compareTransitionsAscending(BasicTimeZone& z1, BasicTimeZone& z2,
   2229                                               UDate start, UDate end, UBool inclusive) {
   2230     UnicodeString zid1, zid2;
   2231     TimeZoneTransition tzt1, tzt2;
   2232     UBool avail1, avail2;
   2233     UBool inRange1, inRange2;
   2234 
   2235     z1.getID(zid1);
   2236     z2.getID(zid2);
   2237 
   2238     UDate time = start;
   2239     while (TRUE) {
   2240         avail1 = z1.getNextTransition(time, inclusive, tzt1);
   2241         avail2 = z2.getNextTransition(time, inclusive, tzt2);
   2242 
   2243         inRange1 = inRange2 = FALSE;
   2244         if (avail1) {
   2245             if (tzt1.getTime() < end || (inclusive && tzt1.getTime() == end)) {
   2246                 inRange1 = TRUE;
   2247             }
   2248         }
   2249         if (avail2) {
   2250             if (tzt2.getTime() < end || (inclusive && tzt2.getTime() == end)) {
   2251                 inRange2 = TRUE;
   2252             }
   2253         }
   2254         if (!inRange1 && !inRange2) {
   2255             // No more transition in the range
   2256             break;
   2257         }
   2258         if (!inRange1) {
   2259             errln((UnicodeString)"FAIL: " + zid1 + " does not have any transitions after "
   2260                 + dateToString(time) + " before " + dateToString(end));
   2261             break;
   2262         }
   2263         if (!inRange2) {
   2264             errln((UnicodeString)"FAIL: " + zid2 + " does not have any transitions after "
   2265                 + dateToString(time) + " before " + dateToString(end));
   2266             break;
   2267         }
   2268         if (tzt1.getTime() != tzt2.getTime()) {
   2269             errln((UnicodeString)"FAIL: First transition after " + dateToString(time) + " "
   2270                     + zid1 + "[" + dateToString(tzt1.getTime()) + "] "
   2271                     + zid2 + "[" + dateToString(tzt2.getTime()) + "]");
   2272             break;
   2273         }
   2274         time = tzt1.getTime();
   2275         if (inclusive) {
   2276             time += 1;
   2277         }
   2278     }
   2279 }
   2280 
   2281 /*
   2282  * Compare all time transitions in 2 time zones in the specified time range in descending order
   2283  */
   2284 void
   2285 TimeZoneRuleTest::compareTransitionsDescending(BasicTimeZone& z1, BasicTimeZone& z2,
   2286                                                UDate start, UDate end, UBool inclusive) {
   2287     UnicodeString zid1, zid2;
   2288     TimeZoneTransition tzt1, tzt2;
   2289     UBool avail1, avail2;
   2290     UBool inRange1, inRange2;
   2291 
   2292     z1.getID(zid1);
   2293     z2.getID(zid2);
   2294 
   2295     UDate time = end;
   2296     while (TRUE) {
   2297         avail1 = z1.getPreviousTransition(time, inclusive, tzt1);
   2298         avail2 = z2.getPreviousTransition(time, inclusive, tzt2);
   2299 
   2300         inRange1 = inRange2 = FALSE;
   2301         if (avail1) {
   2302             if (tzt1.getTime() > start || (inclusive && tzt1.getTime() == start)) {
   2303                 inRange1 = TRUE;
   2304             }
   2305         }
   2306         if (avail2) {
   2307             if (tzt2.getTime() > start || (inclusive && tzt2.getTime() == start)) {
   2308                 inRange2 = TRUE;
   2309             }
   2310         }
   2311         if (!inRange1 && !inRange2) {
   2312             // No more transition in the range
   2313             break;
   2314         }
   2315         if (!inRange1) {
   2316             errln((UnicodeString)"FAIL: " + zid1 + " does not have any transitions before "
   2317                 + dateToString(time) + " after " + dateToString(start));
   2318             break;
   2319         }
   2320         if (!inRange2) {
   2321             errln((UnicodeString)"FAIL: " + zid2 + " does not have any transitions before "
   2322                 + dateToString(time) + " after " + dateToString(start));
   2323             break;
   2324         }
   2325         if (tzt1.getTime() != tzt2.getTime()) {
   2326             errln((UnicodeString)"FAIL: Last transition before " + dateToString(time) + " "
   2327                     + zid1 + "[" + dateToString(tzt1.getTime()) + "] "
   2328                     + zid2 + "[" + dateToString(tzt2.getTime()) + "]");
   2329             break;
   2330         }
   2331         time = tzt1.getTime();
   2332         if (inclusive) {
   2333             time -= 1;
   2334         }
   2335     }
   2336 }
   2337 
   2338 #endif /* #if !UCONFIG_NO_FORMATTING */
   2339 
   2340 //eof
   2341