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