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