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