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