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