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