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