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