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