• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2 *******************************************************************************
3 * Copyright (C) 2007, International Business Machines Corporation and         *
4 * others. All Rights Reserved.                                                *
5 *******************************************************************************
6 */
7 
8 #include "unicode/utypes.h"
9 
10 #if !UCONFIG_NO_FORMATTING
11 
12 #include "unicode/dtrule.h"
13 #include "unicode/tzrule.h"
14 #include "unicode/rbtz.h"
15 #include "unicode/simpletz.h"
16 #include "unicode/tzrule.h"
17 #include "unicode/calendar.h"
18 #include "unicode/gregocal.h"
19 #include "unicode/ucal.h"
20 #include "unicode/unistr.h"
21 #include "unicode/tztrans.h"
22 #include "unicode/vtzone.h"
23 #include "tzrulets.h"
24 
25 #define CASE(id,test) case id: name = #test; if (exec) { logln(#test "---"); logln((UnicodeString)""); test(); } break
26 #define HOUR (60*60*1000)
27 
28 static const char *const TESTZIDS[] = {
29         "AGT",
30         "America/New_York",
31         "America/Los_Angeles",
32         "America/Indiana/Indianapolis",
33         "America/Havana",
34         "Europe/Lisbon",
35         "Europe/Paris",
36         "Asia/Tokyo",
37         "Asia/Sakhalin",
38         "Africa/Cairo",
39         "Africa/Windhoek",
40         "Australia/Sydney",
41         "Etc/GMT+8"
42 };
43 
44 class TestZIDEnumeration : public StringEnumeration {
45 public:
46     TestZIDEnumeration(UBool all = FALSE);
47     ~TestZIDEnumeration();
48 
count(UErrorCode &) const49     virtual int32_t count(UErrorCode& /*status*/) const {
50         return len;
51     }
52     virtual const UnicodeString *snext(UErrorCode& status);
53     virtual void reset(UErrorCode& status);
getStaticClassID()54     static inline UClassID getStaticClassID() {
55         return (UClassID)&fgClassID;
56     }
getDynamicClassID() const57     virtual UClassID getDynamicClassID() const {
58         return getStaticClassID();
59     }
60 private:
61     static const char fgClassID;
62     int32_t idx;
63     int32_t len;
64     StringEnumeration   *tzenum;
65 };
66 
67 const char TestZIDEnumeration::fgClassID = 0;
68 
TestZIDEnumeration(UBool all)69 TestZIDEnumeration::TestZIDEnumeration(UBool all)
70 : idx(0) {
71     UErrorCode status = U_ZERO_ERROR;
72     if (all) {
73         tzenum = TimeZone::createEnumeration();
74         len = tzenum->count(status);
75     } else {
76         tzenum = NULL;
77         len = (int32_t)sizeof(TESTZIDS)/sizeof(TESTZIDS[0]);
78     }
79 }
80 
~TestZIDEnumeration()81 TestZIDEnumeration::~TestZIDEnumeration() {
82     if (tzenum != NULL) {
83         delete tzenum;
84     }
85 }
86 
87 const UnicodeString*
snext(UErrorCode & status)88 TestZIDEnumeration::snext(UErrorCode& status) {
89     if (tzenum != NULL) {
90         return tzenum->snext(status);
91     } else if (U_SUCCESS(status) && idx < len) {
92         unistr = UnicodeString(TESTZIDS[idx++], "");
93         return &unistr;
94     }
95     return NULL;
96 }
97 
98 void
reset(UErrorCode & status)99 TestZIDEnumeration::reset(UErrorCode& status) {
100     if (tzenum != NULL) {
101         tzenum->reset(status);
102     } else {
103         idx = 0;
104     }
105 }
106 
107 
runIndexedTest(int32_t index,UBool exec,const char * & name,char *)108 void TimeZoneRuleTest::runIndexedTest( int32_t index, UBool exec, const char* &name, char* /*par*/ )
109 {
110     if (exec) {
111         logln("TestSuite TestTimeZoneRule");
112     }
113     switch (index) {
114         CASE(0, TestSimpleRuleBasedTimeZone);
115         CASE(1, TestHistoricalRuleBasedTimeZone);
116         CASE(2, TestOlsonTransition);
117         CASE(3, TestRBTZTransition);
118         CASE(4, TestHasEquivalentTransitions);
119         CASE(5, TestVTimeZoneRoundTrip);
120         CASE(6, TestVTimeZoneRoundTripPartial);
121         CASE(7, TestVTimeZoneSimpleWrite);
122         CASE(8, TestVTimeZoneHeaderProps);
123         CASE(9, TestGetSimpleRules);
124         CASE(10, TestTimeZoneRuleCoverage);
125         CASE(11, TestSimpleTimeZoneCoverage);
126         CASE(12, TestVTimeZoneCoverage);
127         CASE(13, TestVTimeZoneParse);
128         default: name = ""; break;
129     }
130 }
131 
132 /*
133  * Compare SimpleTimeZone with equivalent RBTZ
134  */
135 void
TestSimpleRuleBasedTimeZone(void)136 TimeZoneRuleTest::TestSimpleRuleBasedTimeZone(void) {
137     UErrorCode status = U_ZERO_ERROR;
138     SimpleTimeZone stz(-1*HOUR, "TestSTZ",
139         UCAL_SEPTEMBER, -30, -UCAL_SATURDAY, 1*HOUR, SimpleTimeZone::WALL_TIME,
140         UCAL_FEBRUARY, 2, UCAL_SUNDAY, 1*HOUR, SimpleTimeZone::WALL_TIME,
141         1*HOUR, status);
142     if (U_FAILURE(status)) {
143         errln("FAIL: Couldn't create SimpleTimezone.");
144     }
145 
146     DateTimeRule *dtr;
147     AnnualTimeZoneRule *atzr;
148     int32_t STARTYEAR = 2000;
149 
150     InitialTimeZoneRule *ir = new InitialTimeZoneRule(
151         "RBTZ_Initial", // Initial time Name
152         -1*HOUR,        // Raw offset
153         1*HOUR);        // DST saving amount
154 
155     // Original rules
156     RuleBasedTimeZone *rbtz1 = new RuleBasedTimeZone("RBTZ1", ir->clone());
157     dtr = new DateTimeRule(UCAL_SEPTEMBER, 30, UCAL_SATURDAY, FALSE,
158         1*HOUR, DateTimeRule::WALL_TIME); // SUN<=30 in September, at 1AM wall time
159     atzr = new AnnualTimeZoneRule("RBTZ_DST1",
160         -1*HOUR /*rawOffset*/, 1*HOUR /*dstSavings*/, dtr,
161         STARTYEAR, AnnualTimeZoneRule::MAX_YEAR);
162     rbtz1->addTransitionRule(atzr, status);
163     if (U_FAILURE(status)) {
164         errln("FAIL: couldn't add AnnualTimeZoneRule 1-1.");
165     }
166     dtr = new DateTimeRule(UCAL_FEBRUARY, 2, UCAL_SUNDAY,
167         1*HOUR, DateTimeRule::WALL_TIME);  // 2nd Sunday in February, at 1AM wall time
168     atzr = new AnnualTimeZoneRule("RBTZ_STD1",
169         -1*HOUR /*rawOffset*/, 0 /*dstSavings*/, dtr,
170         STARTYEAR, AnnualTimeZoneRule::MAX_YEAR);
171     rbtz1->addTransitionRule(atzr, status);
172     if (U_FAILURE(status)) {
173         errln("FAIL: couldn't add AnnualTimeZoneRule 1-2.");
174     }
175     rbtz1->complete(status);
176     if (U_FAILURE(status)) {
177         errln("FAIL: couldn't complete RBTZ 1.");
178     }
179 
180     // Equivalent, but different date rule type
181     RuleBasedTimeZone *rbtz2 = new RuleBasedTimeZone("RBTZ2", ir->clone());
182     dtr = new DateTimeRule(UCAL_SEPTEMBER, -1, UCAL_SATURDAY,
183         1*HOUR, DateTimeRule::WALL_TIME); // Last Sunday in September at 1AM wall time
184     atzr = new AnnualTimeZoneRule("RBTZ_DST2", -1*HOUR, 1*HOUR, dtr, STARTYEAR, AnnualTimeZoneRule::MAX_YEAR);
185     rbtz2->addTransitionRule(atzr, status);
186     if (U_FAILURE(status)) {
187         errln("FAIL: couldn't add AnnualTimeZoneRule 2-1.");
188     }
189     dtr = new DateTimeRule(UCAL_FEBRUARY, 8, UCAL_SUNDAY, true,
190         1*HOUR, DateTimeRule::WALL_TIME); // SUN>=8 in February, at 1AM wall time
191     atzr = new AnnualTimeZoneRule("RBTZ_STD2", -1*HOUR, 0, dtr, STARTYEAR, AnnualTimeZoneRule::MAX_YEAR);
192     rbtz2->addTransitionRule(atzr, status);
193     if (U_FAILURE(status)) {
194         errln("FAIL: couldn't add AnnualTimeZoneRule 2-2.");
195     }
196     rbtz2->complete(status);
197     if (U_FAILURE(status)) {
198         errln("FAIL: couldn't complete RBTZ 2");
199     }
200 
201     // Equivalent, but different time rule type
202     RuleBasedTimeZone *rbtz3 = new RuleBasedTimeZone("RBTZ3", ir->clone());
203     dtr = new DateTimeRule(UCAL_SEPTEMBER, 30, UCAL_SATURDAY, false,
204         2*HOUR, DateTimeRule::UTC_TIME);
205     atzr = new AnnualTimeZoneRule("RBTZ_DST3", -1*HOUR, 1*HOUR, dtr, STARTYEAR, AnnualTimeZoneRule::MAX_YEAR);
206     rbtz3->addTransitionRule(atzr, status);
207     if (U_FAILURE(status)) {
208         errln("FAIL: couldn't add AnnualTimeZoneRule 3-1.");
209     }
210     dtr = new DateTimeRule(UCAL_FEBRUARY, 2, UCAL_SUNDAY,
211         0*HOUR, DateTimeRule::STANDARD_TIME);
212     atzr = new AnnualTimeZoneRule("RBTZ_STD3", -1*HOUR, 0, dtr, STARTYEAR, AnnualTimeZoneRule::MAX_YEAR);
213     rbtz3->addTransitionRule(atzr, status);
214     if (U_FAILURE(status)) {
215         errln("FAIL: couldn't add AnnualTimeZoneRule 3-2.");
216     }
217     rbtz3->complete(status);
218     if (U_FAILURE(status)) {
219         errln("FAIL: couldn't complete RBTZ 3");
220     }
221 
222     // Check equivalency for 10 years
223     UDate start = getUTCMillis(STARTYEAR, UCAL_JANUARY, 1);
224     UDate until = getUTCMillis(STARTYEAR + 10, UCAL_JANUARY, 1);
225 
226     if (!(stz.hasEquivalentTransitions(*rbtz1, start, until, TRUE, status))) {
227         errln("FAIL: rbtz1 must be equivalent to the SimpleTimeZone in the time range.");
228     }
229     if (U_FAILURE(status)) {
230         errln("FAIL: error returned from hasEquivalentTransitions");
231     }
232     if (!(stz.hasEquivalentTransitions(*rbtz2, start, until, TRUE, status))) {
233         errln("FAIL: rbtz2 must be equivalent to the SimpleTimeZone in the time range.");
234     }
235     if (U_FAILURE(status)) {
236         errln("FAIL: error returned from hasEquivalentTransitions");
237     }
238     if (!(stz.hasEquivalentTransitions(*rbtz3, start, until, TRUE, status))) {
239         errln("FAIL: rbtz3 must be equivalent to the SimpleTimeZone in the time range.");
240     }
241     if (U_FAILURE(status)) {
242         errln("FAIL: error returned from hasEquivalentTransitions");
243     }
244 
245     // hasSameRules
246     if (rbtz1->hasSameRules(*rbtz2)) {
247         errln("FAIL: rbtz1 and rbtz2 have different rules, but returned true.");
248     }
249     if (rbtz1->hasSameRules(*rbtz3)) {
250         errln("FAIL: rbtz1 and rbtz3 have different rules, but returned true.");
251     }
252     RuleBasedTimeZone *rbtz1c = (RuleBasedTimeZone*)rbtz1->clone();
253     if (!rbtz1->hasSameRules(*rbtz1c)) {
254         errln("FAIL: Cloned RuleBasedTimeZone must have the same rules with the original.");
255     }
256 
257     // getOffset
258     int32_t era, year, month, dayOfMonth, dayOfWeek, millisInDay;
259     UDate time;
260     int32_t offset, dstSavings;
261     UBool dst;
262 
263     GregorianCalendar *cal = new GregorianCalendar(status);
264     if (U_FAILURE(status)) {
265         errln("FAIL: Could not create a Gregorian calendar instance.");
266     }
267     cal->setTimeZone(*rbtz1);
268     cal->clear();
269 
270     // Jan 1, 1000 BC
271     cal->set(UCAL_ERA, GregorianCalendar::BC);
272     cal->set(1000, UCAL_JANUARY, 1);
273 
274     era = cal->get(UCAL_ERA, status);
275     year = cal->get(UCAL_YEAR, status);
276     month = cal->get(UCAL_MONTH, status);
277     dayOfMonth = cal->get(UCAL_DAY_OF_MONTH, status);
278     dayOfWeek = cal->get(UCAL_DAY_OF_WEEK, status);
279     millisInDay = cal->get(UCAL_MILLISECONDS_IN_DAY, status);
280     time = cal->getTime(status);
281     if (U_FAILURE(status)) {
282         errln("FAIL: Could not get calendar field values.");
283     }
284     offset = rbtz1->getOffset(era, year, month, dayOfMonth, dayOfWeek, millisInDay, status);
285     if (U_FAILURE(status)) {
286         errln("FAIL: getOffset(7 args) failed.");
287     }
288     if (offset != 0) {
289         errln(UnicodeString("FAIL: Invalid time zone offset: ") + offset + " /expected: 0");
290     }
291     dst = rbtz1->inDaylightTime(time, status);
292     if (U_FAILURE(status)) {
293         errln("FAIL: inDaylightTime failed.");
294     }
295     if (!dst) {
296         errln("FAIL: Invalid daylight saving time");
297     }
298     rbtz1->getOffset(time, TRUE, offset, dstSavings, status);
299     if (U_FAILURE(status)) {
300         errln("FAIL: getOffset(5 args) failed.");
301     }
302     if (offset != -3600000) {
303         errln(UnicodeString("FAIL: Invalid time zone raw offset: ") + offset + " /expected: -3600000");
304     }
305     if (dstSavings != 3600000) {
306         errln(UnicodeString("FAIL: Invalid DST amount: ") + dstSavings + " /expected: 3600000");
307     }
308 
309     // July 1, 2000, AD
310     cal->set(UCAL_ERA, GregorianCalendar::AD);
311     cal->set(2000, UCAL_JULY, 1);
312 
313     era = cal->get(UCAL_ERA, status);
314     year = cal->get(UCAL_YEAR, status);
315     month = cal->get(UCAL_MONTH, status);
316     dayOfMonth = cal->get(UCAL_DAY_OF_MONTH, status);
317     dayOfWeek = cal->get(UCAL_DAY_OF_WEEK, status);
318     millisInDay = cal->get(UCAL_MILLISECONDS_IN_DAY, status);
319     time = cal->getTime(status);
320     if (U_FAILURE(status)) {
321         errln("FAIL: Could not get calendar field values.");
322     }
323     offset = rbtz1->getOffset(era, year, month, dayOfMonth, dayOfWeek, millisInDay, status);
324     if (U_FAILURE(status)) {
325         errln("FAIL: getOffset(7 args) failed.");
326     }
327     if (offset != -3600000) {
328         errln((UnicodeString)"FAIL: Invalid time zone offset: " + offset + " /expected: -3600000");
329     }
330     dst = rbtz1->inDaylightTime(time, status);
331     if (U_FAILURE(status)) {
332         errln("FAIL: inDaylightTime failed.");
333     }
334     if (dst) {
335         errln("FAIL: Invalid daylight saving time");
336     }
337     rbtz1->getOffset(time, TRUE, offset, dstSavings, status);
338     if (U_FAILURE(status)) {
339         errln("FAIL: getOffset(5 args) failed.");
340     }
341     if (offset != -3600000) {
342         errln((UnicodeString)"FAIL: Invalid time zone raw offset: " + offset + " /expected: -3600000");
343     }
344     if (dstSavings != 0) {
345         errln((UnicodeString)"FAIL: Invalid DST amount: " + dstSavings + " /expected: 0");
346     }
347 
348     // getRawOffset
349     offset = rbtz1->getRawOffset();
350     if (offset != -1*HOUR) {
351         errln((UnicodeString)"FAIL: Invalid time zone raw offset returned by getRawOffset: "
352             + offset + " /expected: -3600000");
353     }
354 
355     // operator=/==/!=
356     RuleBasedTimeZone rbtz0("RBTZ1", ir->clone());
357     if (rbtz0 == *rbtz1 || !(rbtz0 != *rbtz1)) {
358         errln("FAIL: RuleBasedTimeZone rbtz0 is not equal to rbtz1, but got wrong result");
359     }
360     rbtz0 = *rbtz1;
361     if (rbtz0 != *rbtz1 || !(rbtz0 == *rbtz1)) {
362         errln("FAIL: RuleBasedTimeZone rbtz0 is equal to rbtz1, but got wrong result");
363     }
364 
365     // setRawOffset
366     const int32_t RAW = -10*HOUR;
367     rbtz0.setRawOffset(RAW);
368     if (rbtz0.getRawOffset() != RAW) {
369         logln("setRawOffset is implemented in RuleBasedTimeZone");
370     }
371 
372     // useDaylightTime
373     if (!rbtz1->useDaylightTime()) {
374         errln("FAIL: useDaylightTime returned FALSE");
375     }
376 
377     // Try to add 3rd final rule
378     dtr = new DateTimeRule(UCAL_OCTOBER, 15, 1*HOUR, DateTimeRule::WALL_TIME);
379     atzr = new AnnualTimeZoneRule("3RD_ATZ", -1*HOUR, 2*HOUR, dtr, STARTYEAR, AnnualTimeZoneRule::MAX_YEAR);
380     rbtz1->addTransitionRule(atzr, status);
381     if (U_SUCCESS(status)) {
382         errln("FAIL: 3rd final rule must be rejected");
383     } else {
384         delete atzr;
385     }
386 
387     // Try to add an initial rule
388     InitialTimeZoneRule *ir1 = new InitialTimeZoneRule("Test Initial", 2*HOUR, 0);
389     rbtz1->addTransitionRule(ir1, status);
390     if (U_SUCCESS(status)) {
391         errln("FAIL: InitialTimeZoneRule must be rejected");
392     } else {
393         delete ir1;
394     }
395 
396     delete ir;
397     delete rbtz1;
398     delete rbtz2;
399     delete rbtz3;
400     delete rbtz1c;
401     delete cal;
402 }
403 
404 /*
405  * Test equivalency between OlsonTimeZone and custom RBTZ representing the
406  * equivalent rules in a certain time range
407  */
408 void
TestHistoricalRuleBasedTimeZone(void)409 TimeZoneRuleTest::TestHistoricalRuleBasedTimeZone(void) {
410     UErrorCode status = U_ZERO_ERROR;
411 
412     // Compare to America/New_York with equivalent RBTZ
413     BasicTimeZone *ny = (BasicTimeZone*)TimeZone::createTimeZone("America/New_York");
414 
415     //RBTZ
416     InitialTimeZoneRule *ir = new InitialTimeZoneRule("EST", -5*HOUR, 0);
417     RuleBasedTimeZone *rbtz = new RuleBasedTimeZone("EST5EDT", ir);
418 
419     DateTimeRule *dtr;
420     AnnualTimeZoneRule *tzr;
421 
422     // Standard time
423     dtr = new DateTimeRule(UCAL_OCTOBER, -1, UCAL_SUNDAY,
424         2*HOUR, DateTimeRule::WALL_TIME); // Last Sunday in October, at 2AM wall time
425     tzr = new AnnualTimeZoneRule("EST", -5*HOUR /*rawOffset*/, 0 /*dstSavings*/, dtr, 1967, 2006);
426     rbtz->addTransitionRule(tzr, status);
427     if (U_FAILURE(status)) {
428         errln("FAIL: couldn't add AnnualTimeZoneRule 1.");
429     }
430 
431     dtr = new DateTimeRule(UCAL_NOVEMBER, 1, UCAL_SUNDAY,
432         true, 2*HOUR, DateTimeRule::WALL_TIME); // SUN>=1 in November, at 2AM wall time
433     tzr = new AnnualTimeZoneRule("EST", -5*HOUR, 0, dtr, 2007, AnnualTimeZoneRule::MAX_YEAR);
434     rbtz->addTransitionRule(tzr, status);
435     if (U_FAILURE(status)) {
436         errln("FAIL: couldn't add AnnualTimeZoneRule 2.");
437     }
438 
439     // Daylight saving time
440     dtr = new DateTimeRule(UCAL_APRIL, -1, UCAL_SUNDAY,
441         2*HOUR, DateTimeRule::WALL_TIME); // Last Sunday in April, at 2AM wall time
442     tzr = new AnnualTimeZoneRule("EDT", -5*HOUR, 1*HOUR, dtr, 1967, 1973);
443     rbtz->addTransitionRule(tzr, status);
444     if (U_FAILURE(status)) {
445         errln("FAIL: couldn't add AnnualTimeZoneRule 3.");
446     }
447 
448     dtr = new DateTimeRule(UCAL_JANUARY, 6,
449         2*HOUR, DateTimeRule::WALL_TIME); // January 6, at 2AM wall time
450     tzr = new AnnualTimeZoneRule("EDT", -5*HOUR, 1*HOUR, dtr, 1974, 1974);
451     rbtz->addTransitionRule(tzr, status);
452     if (U_FAILURE(status)) {
453         errln("FAIL: couldn't add AnnualTimeZoneRule 4.");
454     }
455 
456     dtr = new DateTimeRule(UCAL_FEBRUARY, 23,
457         2*HOUR, DateTimeRule::WALL_TIME); // February 23, at 2AM wall time
458     tzr = new AnnualTimeZoneRule("EDT", -5*HOUR, 1*HOUR, dtr, 1975, 1975);
459     rbtz->addTransitionRule(tzr, status);
460     if (U_FAILURE(status)) {
461         errln("FAIL: couldn't add AnnualTimeZoneRule 5.");
462     }
463 
464     dtr = new DateTimeRule(UCAL_APRIL, -1, UCAL_SUNDAY,
465         2*HOUR, DateTimeRule::WALL_TIME); // Last Sunday in April, at 2AM wall time
466     tzr = new AnnualTimeZoneRule("EDT", -5*HOUR, 1*HOUR, dtr, 1976, 1986);
467     rbtz->addTransitionRule(tzr, status);
468     if (U_FAILURE(status)) {
469         errln("FAIL: couldn't add AnnualTimeZoneRule 6.");
470     }
471 
472     dtr = new DateTimeRule(UCAL_APRIL, 1, UCAL_SUNDAY,
473         true, 2*HOUR, DateTimeRule::WALL_TIME); // SUN>=1 in April, at 2AM wall time
474     tzr = new AnnualTimeZoneRule("EDT", -5*HOUR, 1*HOUR, dtr, 1987, 2006);
475     rbtz->addTransitionRule(tzr, status);
476     if (U_FAILURE(status)) {
477         errln("FAIL: couldn't add AnnualTimeZoneRule 7.");
478     }
479 
480     dtr = new DateTimeRule(UCAL_MARCH, 8, UCAL_SUNDAY,
481         true, 2*HOUR, DateTimeRule::WALL_TIME); // SUN>=8 in March, at 2AM wall time
482     tzr = new AnnualTimeZoneRule("EDT", -5*HOUR, 1*HOUR, dtr, 2007, AnnualTimeZoneRule::MAX_YEAR);
483     rbtz->addTransitionRule(tzr, status);
484     if (U_FAILURE(status)) {
485         errln("FAIL: couldn't add AnnualTimeZoneRule 7.");
486     }
487 
488     rbtz->complete(status);
489     if (U_FAILURE(status)) {
490         errln("FAIL: couldn't complete RBTZ.");
491     }
492 
493     // hasEquivalentTransitions
494     UDate jan1_1950 = getUTCMillis(1950, UCAL_JANUARY, 1);
495     UDate jan1_1967 = getUTCMillis(1971, UCAL_JANUARY, 1);
496     UDate jan1_2010 = getUTCMillis(2010, UCAL_JANUARY, 1);
497 
498     if (!ny->hasEquivalentTransitions(*rbtz, jan1_1967, jan1_2010, TRUE, status)) {
499         errln("FAIL: The RBTZ must be equivalent to America/New_York between 1967 and 2010");
500     }
501     if (U_FAILURE(status)) {
502         errln("FAIL: error returned from hasEquivalentTransitions for ny/rbtz 1967-2010");
503     }
504     if (ny->hasEquivalentTransitions(*rbtz, jan1_1950, jan1_2010, TRUE, status)) {
505         errln("FAIL: The RBTZ must not be equivalent to America/New_York between 1950 and 2010");
506     }
507     if (U_FAILURE(status)) {
508         errln("FAIL: error returned from hasEquivalentTransitions for ny/rbtz 1950-2010");
509     }
510 
511     // Same with above, but calling RBTZ#hasEquivalentTransitions against OlsonTimeZone
512     if (!rbtz->hasEquivalentTransitions(*ny, jan1_1967, jan1_2010, TRUE, status)) {
513         errln("FAIL: The RBTZ must be equivalent to America/New_York between 1967 and 2010");
514     }
515     if (U_FAILURE(status)) {
516         errln("FAIL: error returned from hasEquivalentTransitions for rbtz/ny 1967-2010");
517     }
518     if (rbtz->hasEquivalentTransitions(*ny, jan1_1950, jan1_2010, TRUE, status)) {
519         errln("FAIL: The RBTZ must not be equivalent to America/New_York between 1950 and 2010");
520     }
521     if (U_FAILURE(status)) {
522         errln("FAIL: error returned from hasEquivalentTransitions for rbtz/ny 1950-2010");
523     }
524 
525     // TimeZone APIs
526     if (ny->hasSameRules(*rbtz) || rbtz->hasSameRules(*ny)) {
527         errln("FAIL: hasSameRules must return false");
528     }
529     RuleBasedTimeZone *rbtzc = (RuleBasedTimeZone*)rbtz->clone();
530     if (!rbtz->hasSameRules(*rbtzc) || !rbtz->hasEquivalentTransitions(*rbtzc, jan1_1950, jan1_2010, TRUE, status)) {
531         errln("FAIL: hasSameRules/hasEquivalentTransitions must return true for cloned RBTZs");
532     }
533     if (U_FAILURE(status)) {
534         errln("FAIL: error returned from hasEquivalentTransitions for rbtz/rbtzc 1950-2010");
535     }
536 
537     UDate times[] = {
538         getUTCMillis(2006, UCAL_MARCH, 15),
539         getUTCMillis(2006, UCAL_NOVEMBER, 1),
540         getUTCMillis(2007, UCAL_MARCH, 15),
541         getUTCMillis(2007, UCAL_NOVEMBER, 1),
542         getUTCMillis(2008, UCAL_MARCH, 15),
543         getUTCMillis(2008, UCAL_NOVEMBER, 1),
544         0
545     };
546     int32_t offset1, dst1;
547     int32_t offset2, dst2;
548 
549     for (int i = 0; times[i] != 0; i++) {
550         // Check getOffset - must return the same results for these time data
551         rbtz->getOffset(times[i], FALSE, offset1, dst1, status);
552         if (U_FAILURE(status)) {
553             errln("FAIL: rbtz->getOffset failed");
554         }
555         ny->getOffset(times[i], FALSE, offset2, dst2, status);
556         if (U_FAILURE(status)) {
557             errln("FAIL: ny->getOffset failed");
558         }
559         if (offset1 != offset2 || dst1 != dst2) {
560             errln("FAIL: Incompatible time zone offset/dstSavings for ny and rbtz");
561         }
562 
563         // Check inDaylightTime
564         if (rbtz->inDaylightTime(times[i], status) != ny->inDaylightTime(times[i], status)) {
565             errln("FAIL: Incompatible daylight saving time for ny and rbtz");
566         }
567         if (U_FAILURE(status)) {
568             errln("FAIL: inDaylightTime failed");
569         }
570     }
571 
572     delete ny;
573     delete rbtz;
574     delete rbtzc;
575 }
576 
577 /*
578  * Check if transitions returned by getNextTransition/getPreviousTransition
579  * are actual time transitions.
580  */
581 void
TestOlsonTransition(void)582 TimeZoneRuleTest::TestOlsonTransition(void) {
583 
584     const int32_t TESTYEARS[][2] = {
585         {1895, 1905}, // including int32 minimum second
586         {1965, 1975}, // including the epoch
587         {1995, 2015}, // practical year range
588         {0,0}
589     };
590 
591     UErrorCode status = U_ZERO_ERROR;
592     TestZIDEnumeration tzenum(!quick);
593     while (TRUE) {
594         const UnicodeString *tzid = tzenum.snext(status);
595         if (tzid == NULL) {
596             break;
597         }
598         if (U_FAILURE(status)) {
599             errln("FAIL: error returned while enumerating timezone IDs.");
600             break;
601         }
602         BasicTimeZone *tz = (BasicTimeZone*)TimeZone::createTimeZone(*tzid);
603         for (int32_t i = 0; TESTYEARS[i][0] != 0 || TESTYEARS[i][1] != 0; i++) {
604             UDate lo = getUTCMillis(TESTYEARS[i][0], UCAL_JANUARY, 1);
605             UDate hi = getUTCMillis(TESTYEARS[i][1], UCAL_JANUARY, 1);
606             verifyTransitions(*tz, lo, hi);
607         }
608         delete tz;
609     }
610 }
611 
612 /*
613  * Check if an OlsonTimeZone and its equivalent RBTZ have the exact same
614  * transitions.
615  */
616 void
TestRBTZTransition(void)617 TimeZoneRuleTest::TestRBTZTransition(void) {
618     const int32_t STARTYEARS[] = {
619         1900,
620         1960,
621         1990,
622         2010,
623         0
624     };
625 
626     UErrorCode status = U_ZERO_ERROR;
627     TestZIDEnumeration tzenum(!quick);
628     while (TRUE) {
629         const UnicodeString *tzid = tzenum.snext(status);
630         if (tzid == NULL) {
631             break;
632         }
633         if (U_FAILURE(status)) {
634             errln("FAIL: error returned while enumerating timezone IDs.");
635             break;
636         }
637         BasicTimeZone *tz = (BasicTimeZone*)TimeZone::createTimeZone(*tzid);
638         int32_t ruleCount = tz->countTransitionRules(status);
639 
640         const InitialTimeZoneRule *initial;
641         const TimeZoneRule **trsrules = new const TimeZoneRule*[ruleCount];
642         tz->getTimeZoneRules(initial, trsrules, ruleCount, status);
643         if (U_FAILURE(status)) {
644             errln((UnicodeString)"FAIL: failed to get the TimeZoneRules from time zone " + *tzid);
645         }
646         RuleBasedTimeZone *rbtz = new RuleBasedTimeZone(*tzid, initial->clone());
647         if (U_FAILURE(status)) {
648             errln((UnicodeString)"FAIL: failed to get the transition rule count from time zone " + *tzid);
649         }
650         for (int32_t i = 0; i < ruleCount; i++) {
651             rbtz->addTransitionRule(trsrules[i]->clone(), status);
652             if (U_FAILURE(status)) {
653                 errln((UnicodeString)"FAIL: failed to add a transition rule at index " + i + " to the RBTZ for " + *tzid);
654             }
655         }
656         rbtz->complete(status);
657         if (U_FAILURE(status)) {
658             errln((UnicodeString)"FAIL: complete() failed for the RBTZ for " + *tzid);
659         }
660 
661         for (int32_t idx = 0; STARTYEARS[idx] != 0; idx++) {
662             UDate start = getUTCMillis(STARTYEARS[idx], UCAL_JANUARY, 1);
663             UDate until = getUTCMillis(STARTYEARS[idx] + 20, UCAL_JANUARY, 1);
664             // Compare the original OlsonTimeZone with the RBTZ starting the startTime for 20 years
665 
666             // Ascending
667             compareTransitionsAscending(*tz, *rbtz, start, until, FALSE);
668             // Ascending/inclusive
669             compareTransitionsAscending(*tz, *rbtz, start + 1, until, TRUE);
670             // Descending
671             compareTransitionsDescending(*tz, *rbtz, start, until, FALSE);
672             // Descending/inclusive
673             compareTransitionsDescending(*tz, *rbtz, start + 1, until, TRUE);
674         }
675         delete [] trsrules;
676         delete rbtz;
677         delete tz;
678     }
679 }
680 
681 void
TestHasEquivalentTransitions(void)682 TimeZoneRuleTest::TestHasEquivalentTransitions(void) {
683     // America/New_York and America/Indiana/Indianapolis are equivalent
684     // since 2006
685     UErrorCode status = U_ZERO_ERROR;
686     BasicTimeZone *newyork = (BasicTimeZone*)TimeZone::createTimeZone("America/New_York");
687     BasicTimeZone *indianapolis = (BasicTimeZone*)TimeZone::createTimeZone("America/Indiana/Indianapolis");
688     BasicTimeZone *gmt_5 = (BasicTimeZone*)TimeZone::createTimeZone("Etc/GMT+5");
689 
690     UDate jan1_1971 = getUTCMillis(1971, UCAL_JANUARY, 1);
691     UDate jan1_2005 = getUTCMillis(2005, UCAL_JANUARY, 1);
692     UDate jan1_2006 = getUTCMillis(2006, UCAL_JANUARY, 1);
693     UDate jan1_2007 = getUTCMillis(2007, UCAL_JANUARY, 1);
694     UDate jan1_2011 = getUTCMillis(2010, UCAL_JANUARY, 1);
695 
696     if (newyork->hasEquivalentTransitions(*indianapolis, jan1_2005, jan1_2011, TRUE, status)) {
697         errln("FAIL: New_York is not equivalent to Indianapolis between 2005 and 2010");
698     }
699     if (U_FAILURE(status)) {
700         errln("FAIL: error status is returned from hasEquivalentTransition");
701     }
702     if (!newyork->hasEquivalentTransitions(*indianapolis, jan1_2006, jan1_2011, TRUE, status)) {
703         errln("FAIL: New_York is equivalent to Indianapolis between 2006 and 2010");
704     }
705     if (U_FAILURE(status)) {
706         errln("FAIL: error status is returned from hasEquivalentTransition");
707     }
708 
709     if (!indianapolis->hasEquivalentTransitions(*gmt_5, jan1_1971, jan1_2006, TRUE, status)) {
710         errln("FAIL: Indianapolis is equivalent to GMT+5 between 1971 and 2005");
711     }
712     if (U_FAILURE(status)) {
713         errln("FAIL: error status is returned from hasEquivalentTransition");
714     }
715     if (indianapolis->hasEquivalentTransitions(*gmt_5, jan1_1971, jan1_2007, TRUE, status)) {
716         errln("FAIL: Indianapolis is not equivalent to GMT+5 between 1971 and 2006");
717     }
718     if (U_FAILURE(status)) {
719         errln("FAIL: error status is returned from hasEquivalentTransition");
720     }
721 
722     // Cloned TimeZone
723     BasicTimeZone *newyork2 = (BasicTimeZone*)newyork->clone();
724     if (!newyork->hasEquivalentTransitions(*newyork2, jan1_1971, jan1_2011, FALSE, status)) {
725         errln("FAIL: Cloned TimeZone must have the same transitions");
726     }
727     if (U_FAILURE(status)) {
728         errln("FAIL: error status is returned from hasEquivalentTransition for newyork/newyork2");
729     }
730     if (!newyork->hasEquivalentTransitions(*newyork2, jan1_1971, jan1_2011, TRUE, status)) {
731         errln("FAIL: Cloned TimeZone must have the same transitions");
732     }
733     if (U_FAILURE(status)) {
734         errln("FAIL: error status is returned from hasEquivalentTransition for newyork/newyork2");
735     }
736 
737     // America/New_York and America/Los_Angeles has same DST start rules, but
738     // raw offsets are different
739     BasicTimeZone *losangeles = (BasicTimeZone*)TimeZone::createTimeZone("America/Los_Angeles");
740     if (newyork->hasEquivalentTransitions(*losangeles, jan1_2006, jan1_2011, TRUE, status)) {
741         errln("FAIL: New_York is not equivalent to Los Angeles, but returned true");
742     }
743     if (U_FAILURE(status)) {
744         errln("FAIL: error status is returned from hasEquivalentTransition for newyork/losangeles");
745     }
746 
747     delete newyork;
748     delete newyork2;
749     delete indianapolis;
750     delete gmt_5;
751     delete losangeles;
752 }
753 
754 /*
755  * Write out time zone rules of OlsonTimeZone into VTIMEZONE format, create a new
756  * VTimeZone from the VTIMEZONE data, then compare transitions
757  */
758 void
TestVTimeZoneRoundTrip(void)759 TimeZoneRuleTest::TestVTimeZoneRoundTrip(void) {
760     UDate startTime = getUTCMillis(1850, UCAL_JANUARY, 1);
761     UDate endTime = getUTCMillis(2050, UCAL_JANUARY, 1);
762 
763     UErrorCode status = U_ZERO_ERROR;
764     TestZIDEnumeration tzenum(!quick);
765     while (TRUE) {
766         const UnicodeString *tzid = tzenum.snext(status);
767         if (tzid == NULL) {
768             break;
769         }
770         if (U_FAILURE(status)) {
771             errln("FAIL: error returned while enumerating timezone IDs.");
772             break;
773         }
774         BasicTimeZone *tz = (BasicTimeZone*)TimeZone::createTimeZone(*tzid);
775         VTimeZone *vtz_org = VTimeZone::createVTimeZoneByID(*tzid);
776         vtz_org->setTZURL("http://source.icu-project.org/timezone");
777         vtz_org->setLastModified(Calendar::getNow());
778         VTimeZone *vtz_new = NULL;
779         UnicodeString vtzdata;
780         // Write out VTIMEZONE data
781         vtz_org->write(vtzdata, status);
782         if (U_FAILURE(status)) {
783             errln((UnicodeString)"FAIL: error returned while writing time zone rules for " +
784                 *tzid + " into VTIMEZONE format.");
785         } else {
786             // Read VTIMEZONE data
787             vtz_new = VTimeZone::createVTimeZone(vtzdata, status);
788             if (U_FAILURE(status)) {
789                 errln((UnicodeString)"FAIL: error returned while reading VTIMEZONE data for " + *tzid);
790             } else {
791                 // Write out VTIMEZONE one more time
792                 UnicodeString vtzdata1;
793                 vtz_new->write(vtzdata1, status);
794                 if (U_FAILURE(status)) {
795                     errln((UnicodeString)"FAIL: error returned while writing time zone rules for " +
796                         *tzid + "(vtz_new) into VTIMEZONE format.");
797                 } else {
798                     // Make sure VTIMEZONE data is exactly same with the first one
799                     if (vtzdata != vtzdata1) {
800                         errln((UnicodeString)"FAIL: different VTIMEZONE data after round trip for " + *tzid);
801                     }
802                 }
803                 // Check equivalency after the first transition.
804                 // The DST information before the first transition might be lost
805                 // because there is no good way to represent the initial time with
806                 // VTIMEZONE.
807                 int32_t raw1, raw2, dst1, dst2;
808                 tz->getOffset(startTime, FALSE, raw1, dst1, status);
809                 vtz_new->getOffset(startTime, FALSE, raw2, dst2, status);
810                 if (U_FAILURE(status)) {
811                     errln("FAIL: error status is returned from getOffset");
812                 } else {
813                     if (raw1 + dst1 != raw2 + dst2) {
814                         errln("FAIL: VTimeZone for " + *tzid +
815                             " is not equivalent to its OlsonTimeZone corresponding at "
816                             + dateToString(startTime));
817                     }
818                     TimeZoneTransition trans;
819                     UBool avail = tz->getNextTransition(startTime, FALSE, trans);
820                     if (avail) {
821                         if (!vtz_new->hasEquivalentTransitions(*tz, trans.getTime(),
822                                 endTime, TRUE, status)) {
823                             errln("FAIL: VTimeZone for " + *tzid +
824                                 " is not equivalent to its OlsonTimeZone corresponding.");
825                         }
826                         if (U_FAILURE(status)) {
827                             errln("FAIL: error status is returned from hasEquivalentTransition");
828                         }
829                     }
830                 }
831             }
832             if (vtz_new != NULL) {
833                 delete vtz_new;
834                 vtz_new = NULL;
835             }
836         }
837         delete tz;
838         delete vtz_org;
839     }
840 }
841 
842 /*
843  * Write out time zone rules of OlsonTimeZone after a cutover date into VTIMEZONE format,
844  * create a new VTimeZone from the VTIMEZONE data, then compare transitions
845  */
846 void
TestVTimeZoneRoundTripPartial(void)847 TimeZoneRuleTest::TestVTimeZoneRoundTripPartial(void) {
848     const int32_t STARTYEARS[] = {
849         1900,
850         1950,
851         2020,
852         0
853     };
854     UDate endTime = getUTCMillis(2050, UCAL_JANUARY, 1);
855 
856     UErrorCode status = U_ZERO_ERROR;
857     TestZIDEnumeration tzenum(!quick);
858     while (TRUE) {
859         const UnicodeString *tzid = tzenum.snext(status);
860         if (tzid == NULL) {
861             break;
862         }
863         if (U_FAILURE(status)) {
864             errln("FAIL: error returned while enumerating timezone IDs.");
865             break;
866         }
867         BasicTimeZone *tz = (BasicTimeZone*)TimeZone::createTimeZone(*tzid);
868         VTimeZone *vtz_org = VTimeZone::createVTimeZoneByID(*tzid);
869         VTimeZone *vtz_new = NULL;
870         UnicodeString vtzdata;
871 
872         for (int32_t i = 0; STARTYEARS[i] != 0; i++) {
873             // Write out VTIMEZONE
874             UDate startTime = getUTCMillis(STARTYEARS[i], UCAL_JANUARY, 1);
875             vtz_org->write(startTime, vtzdata, status);
876             if (U_FAILURE(status)) {
877                 errln((UnicodeString)"FAIL: error returned while writing time zone rules for " +
878                     *tzid + " into VTIMEZONE format since " + dateToString(startTime));
879             } else {
880                 // Read VTIMEZONE data
881                 vtz_new = VTimeZone::createVTimeZone(vtzdata, status);
882                 if (U_FAILURE(status)) {
883                     errln((UnicodeString)"FAIL: error returned while reading VTIMEZONE data for " + *tzid
884                         + " since " + dateToString(startTime));
885                 } else {
886                     // Check equivalency after the first transition.
887                     // The DST information before the first transition might be lost
888                     // because there is no good way to represent the initial time with
889                     // VTIMEZONE.
890                     int32_t raw1, raw2, dst1, dst2;
891                     tz->getOffset(startTime, FALSE, raw1, dst1, status);
892                     vtz_new->getOffset(startTime, FALSE, raw2, dst2, status);
893                     if (U_FAILURE(status)) {
894                         errln("FAIL: error status is returned from getOffset");
895                     } else {
896                         if (raw1 + dst1 != raw2 + dst2) {
897                             errln("FAIL: VTimeZone for " + *tzid +
898                                 " is not equivalent to its OlsonTimeZone corresponding at "
899                                 + dateToString(startTime));
900                         }
901                         TimeZoneTransition trans;
902                         UBool avail = tz->getNextTransition(startTime, FALSE, trans);
903                         if (avail) {
904                             if (!vtz_new->hasEquivalentTransitions(*tz, trans.getTime(),
905                                     endTime, TRUE, status)) {
906                                 errln("FAIL: VTimeZone for " + *tzid +
907                                     " is not equivalent to its OlsonTimeZone corresponding.");
908                             }
909                             if (U_FAILURE(status)) {
910                                 errln("FAIL: error status is returned from hasEquivalentTransition");
911                             }
912                         }
913                     }
914                 }
915             }
916             if (vtz_new != NULL) {
917                 delete vtz_new;
918                 vtz_new = NULL;
919             }
920         }
921         delete tz;
922         delete vtz_org;
923     }
924 }
925 
926 /*
927  * Write out simple time zone rules from an OlsonTimeZone at various time into VTIMEZONE
928  * format and create a new VTimeZone from the VTIMEZONE data, then make sure the raw offset
929  * and DST savings are same in these two time zones.
930  */
931 void
TestVTimeZoneSimpleWrite(void)932 TimeZoneRuleTest::TestVTimeZoneSimpleWrite(void) {
933     const int32_t TESTDATES[][3] = {
934         {2006,  UCAL_JANUARY,   1},
935         {2006,  UCAL_MARCH,     15},
936         {2006,  UCAL_MARCH,     31},
937         {2006,  UCAL_OCTOBER,   25},
938         {2006,  UCAL_NOVEMBER,  1},
939         {2006,  UCAL_NOVEMBER,  5},
940         {2007,  UCAL_JANUARY,   1},
941         {0,     0,              0}
942     };
943 
944     UErrorCode status = U_ZERO_ERROR;
945     TestZIDEnumeration tzenum(!quick);
946     while (TRUE) {
947         const UnicodeString *tzid = tzenum.snext(status);
948         if (tzid == NULL) {
949             break;
950         }
951         if (U_FAILURE(status)) {
952             errln("FAIL: error returned while enumerating timezone IDs.");
953             break;
954         }
955         VTimeZone *vtz_org = VTimeZone::createVTimeZoneByID(*tzid);
956         VTimeZone *vtz_new = NULL;
957         UnicodeString vtzdata;
958 
959         for (int32_t i = 0; TESTDATES[i][0] != 0; i++) {
960             // Write out VTIMEZONE
961             UDate time = getUTCMillis(TESTDATES[i][0], TESTDATES[i][1], TESTDATES[i][2]);
962             vtz_org->writeSimple(time, vtzdata, status);
963             if (U_FAILURE(status)) {
964                 errln((UnicodeString)"FAIL: error returned while writing simple time zone rules for " +
965                     *tzid + " into VTIMEZONE format at " + dateToString(time));
966             } else {
967                 // Read VTIMEZONE data
968                 vtz_new = VTimeZone::createVTimeZone(vtzdata, status);
969                 if (U_FAILURE(status)) {
970                     errln((UnicodeString)"FAIL: error returned while reading simple VTIMEZONE data for " + *tzid
971                         + " at " + dateToString(time));
972                 } else {
973                     // Check equivalency
974                     int32_t raw0, dst0;
975                     int32_t raw1, dst1;
976                     vtz_org->getOffset(time, FALSE, raw0, dst0, status);
977                     vtz_new->getOffset(time, FALSE, raw1, dst1, status);
978                     if (U_SUCCESS(status)) {
979                         if (raw0 != raw1 || dst0 != dst1) {
980                             errln("FAIL: VTimeZone writeSimple for " + *tzid + " at "
981                                 + dateToString(time) + " failed to the round trip.");
982                         }
983                     } else {
984                         errln("FAIL: getOffset returns error status");
985                     }
986                 }
987             }
988             if (vtz_new != NULL) {
989                 delete vtz_new;
990                 vtz_new = NULL;
991             }
992         }
993         delete vtz_org;
994     }
995 }
996 
997 /*
998  * Write out time zone rules of OlsonTimeZone into VTIMEZONE format with RFC2445 header TZURL and
999  * LAST-MODIFIED, create a new VTimeZone from the VTIMEZONE data to see if the headers are preserved.
1000  */
1001 void
TestVTimeZoneHeaderProps(void)1002 TimeZoneRuleTest::TestVTimeZoneHeaderProps(void) {
1003     const UnicodeString TESTURL1("http://source.icu-project.org");
1004     const UnicodeString TESTURL2("http://www.ibm.com");
1005 
1006     UErrorCode status = U_ZERO_ERROR;
1007     UnicodeString tzurl;
1008     UDate lmod;
1009     UDate lastmod = getUTCMillis(2007, UCAL_JUNE, 1);
1010     VTimeZone *vtz = VTimeZone::createVTimeZoneByID("America/Chicago");
1011     vtz->setTZURL(TESTURL1);
1012     vtz->setLastModified(lastmod);
1013 
1014     // Roundtrip conversion
1015     UnicodeString vtzdata;
1016     vtz->write(vtzdata, status);
1017     VTimeZone *newvtz1 = NULL;
1018     if (U_FAILURE(status)) {
1019         errln("FAIL: error returned while writing VTIMEZONE data 1");
1020         return;
1021     }
1022     // Create a new one
1023     newvtz1 = VTimeZone::createVTimeZone(vtzdata, status);
1024     if (U_FAILURE(status)) {
1025         errln("FAIL: error returned while loading VTIMEZONE data 1");
1026     } else {
1027         // Check if TZURL and LAST-MODIFIED properties are preserved
1028         newvtz1->getTZURL(tzurl);
1029         if (tzurl != TESTURL1) {
1030             errln("FAIL: TZURL 1 was not preserved");
1031         }
1032         vtz->getLastModified(lmod);
1033         if (lastmod != lmod) {
1034             errln("FAIL: LAST-MODIFIED was not preserved");
1035         }
1036     }
1037 
1038     if (U_SUCCESS(status)) {
1039         // Set different tzurl
1040         newvtz1->setTZURL(TESTURL2);
1041 
1042         // Second roundtrip, with a cutover
1043         newvtz1->write(vtzdata, status);
1044         if (U_FAILURE(status)) {
1045             errln("FAIL: error returned while writing VTIMEZONE data 2");
1046         } else {
1047             VTimeZone *newvtz2 = VTimeZone::createVTimeZone(vtzdata, status);
1048             if (U_FAILURE(status)) {
1049                 errln("FAIL: error returned while loading VTIMEZONE data 2");
1050             } else {
1051                 // Check if TZURL and LAST-MODIFIED properties are preserved
1052                 newvtz2->getTZURL(tzurl);
1053                 if (tzurl != TESTURL2) {
1054                     errln("FAIL: TZURL was not preserved in the second roundtrip");
1055                 }
1056                 vtz->getLastModified(lmod);
1057                 if (lastmod != lmod) {
1058                     errln("FAIL: LAST-MODIFIED was not preserved in the second roundtrip");
1059                 }
1060             }
1061             delete newvtz2;
1062         }
1063     }
1064     delete newvtz1;
1065     delete vtz;
1066 }
1067 
1068 /*
1069  * Extract simple rules from an OlsonTimeZone and make sure the rule format matches
1070  * the expected format.
1071  */
1072 void
TestGetSimpleRules(void)1073 TimeZoneRuleTest::TestGetSimpleRules(void) {
1074     UDate testTimes[] = {
1075         getUTCMillis(1970, UCAL_JANUARY, 1),
1076         getUTCMillis(2000, UCAL_MARCH, 31),
1077         getUTCMillis(2005, UCAL_JULY, 1),
1078         getUTCMillis(2010, UCAL_NOVEMBER, 1),
1079     };
1080     int32_t numTimes = sizeof(testTimes)/sizeof(UDate);
1081     UErrorCode status = U_ZERO_ERROR;
1082     TestZIDEnumeration tzenum(!quick);
1083     InitialTimeZoneRule *initial;
1084     AnnualTimeZoneRule *std, *dst;
1085     for (int32_t i = 0; i < numTimes ; i++) {
1086         while (TRUE) {
1087             const UnicodeString *tzid = tzenum.snext(status);
1088             if (tzid == NULL) {
1089                 break;
1090             }
1091             if (U_FAILURE(status)) {
1092                 errln("FAIL: error returned while enumerating timezone IDs.");
1093                 break;
1094             }
1095             BasicTimeZone *tz = (BasicTimeZone*)TimeZone::createTimeZone(*tzid);
1096             initial = NULL;
1097             std = dst = NULL;
1098             tz->getSimpleRulesNear(testTimes[i], initial, std, dst, status);
1099             if (U_FAILURE(status)) {
1100                 errln("FAIL: getSimpleRules failed.");
1101                 break;
1102             }
1103             if (initial == NULL) {
1104                 errln("FAIL: initial rule must not be NULL");
1105                 break;
1106             } else if (!(std == NULL && dst == NULL || std != NULL && dst != NULL)) {
1107                 errln("FAIL: invalid std/dst pair.");
1108                 break;
1109             }
1110             if (std != NULL) {
1111                 const DateTimeRule *dtr = std->getRule();
1112                 if (dtr->getDateRuleType() != DateTimeRule::DOW) {
1113                     errln("FAIL: simple std rull must use DateTimeRule::DOW as date rule.");
1114                     break;
1115                 }
1116                 if (dtr->getTimeRuleType() != DateTimeRule::WALL_TIME) {
1117                     errln("FAIL: simple std rull must use DateTimeRule::WALL_TIME as time rule.");
1118                     break;
1119                 }
1120                 dtr = dst->getRule();
1121                 if (dtr->getDateRuleType() != DateTimeRule::DOW) {
1122                     errln("FAIL: simple dst rull must use DateTimeRule::DOW as date rule.");
1123                     break;
1124                 }
1125                 if (dtr->getTimeRuleType() != DateTimeRule::WALL_TIME) {
1126                     errln("FAIL: simple dst rull must use DateTimeRule::WALL_TIME as time rule.");
1127                     break;
1128                 }
1129             }
1130             // Create an RBTZ from the rules and compare the offsets at the date
1131             RuleBasedTimeZone *rbtz = new RuleBasedTimeZone(*tzid, initial);
1132             if (std != NULL) {
1133                 rbtz->addTransitionRule(std, status);
1134                 if (U_FAILURE(status)) {
1135                     errln("FAIL: couldn't add std rule.");
1136                 }
1137                 rbtz->addTransitionRule(dst, status);
1138                 if (U_FAILURE(status)) {
1139                     errln("FAIL: couldn't add dst rule.");
1140                 }
1141             }
1142             rbtz->complete(status);
1143             if (U_FAILURE(status)) {
1144                 errln("FAIL: couldn't complete rbtz for " + *tzid);
1145             }
1146 
1147             int32_t raw0, dst0, raw1, dst1;
1148             tz->getOffset(testTimes[i], FALSE, raw0, dst0, status);
1149             if (U_FAILURE(status)) {
1150                 errln("FAIL: couldn't get offsets from tz for " + *tzid);
1151             }
1152             rbtz->getOffset(testTimes[i], FALSE, raw1, dst1, status);
1153             if (U_FAILURE(status)) {
1154                 errln("FAIL: couldn't get offsets from rbtz for " + *tzid);
1155             }
1156             if (raw0 != raw1 || dst0 != dst1) {
1157                 errln("FAIL: rbtz created by simple rule does not match the original tz for tzid " + *tzid);
1158             }
1159             delete rbtz;
1160             delete tz;
1161         }
1162     }
1163 }
1164 
1165 /*
1166  * API coverage tests for TimeZoneRule
1167  */
1168 void
TestTimeZoneRuleCoverage(void)1169 TimeZoneRuleTest::TestTimeZoneRuleCoverage(void) {
1170     UDate time1 = getUTCMillis(2005, UCAL_JULY, 4);
1171     UDate time2 = getUTCMillis(2015, UCAL_JULY, 4);
1172     UDate time3 = getUTCMillis(1950, UCAL_JULY, 4);
1173 
1174     DateTimeRule *dtr1 = new DateTimeRule(UCAL_FEBRUARY, 29, UCAL_SUNDAY, FALSE,
1175             3*HOUR, DateTimeRule::WALL_TIME); // Last Sunday on or before Feb 29, at 3 AM, wall time
1176     DateTimeRule *dtr2 = new DateTimeRule(UCAL_MARCH, 11, 2*HOUR,
1177             DateTimeRule::STANDARD_TIME); // Mar 11, at 2 AM, standard time
1178     DateTimeRule *dtr3 = new DateTimeRule(UCAL_OCTOBER, -1, UCAL_SATURDAY,
1179             6*HOUR, DateTimeRule::UTC_TIME); //Last Saturday in Oct, at 6 AM, UTC
1180     DateTimeRule *dtr4 = new DateTimeRule(UCAL_MARCH, 8, UCAL_SUNDAY, TRUE,
1181             2*HOUR, DateTimeRule::WALL_TIME); // First Sunday on or after Mar 8, at 2 AM, wall time
1182 
1183     AnnualTimeZoneRule *a1 = new AnnualTimeZoneRule("a1", -3*HOUR, 1*HOUR, *dtr1,
1184             2000, AnnualTimeZoneRule::MAX_YEAR);
1185     AnnualTimeZoneRule *a2 = new AnnualTimeZoneRule("a2", -3*HOUR, 1*HOUR, *dtr1,
1186             2000, AnnualTimeZoneRule::MAX_YEAR);
1187     AnnualTimeZoneRule *a3 = new AnnualTimeZoneRule("a3", -3*HOUR, 1*HOUR, *dtr1,
1188             2000, 2010);
1189 
1190     InitialTimeZoneRule *i1 = new InitialTimeZoneRule("i1", -3*HOUR, 0);
1191     InitialTimeZoneRule *i2 = new InitialTimeZoneRule("i2", -3*HOUR, 0);
1192     InitialTimeZoneRule *i3 = new InitialTimeZoneRule("i3", -3*HOUR, 1*HOUR);
1193 
1194     UDate trtimes1[] = {0.0};
1195     UDate trtimes2[] = {0.0, 10000000.0};
1196 
1197     TimeArrayTimeZoneRule *t1 = new TimeArrayTimeZoneRule("t1", -3*HOUR, 0, trtimes1, 1, DateTimeRule::UTC_TIME);
1198     TimeArrayTimeZoneRule *t2 = new TimeArrayTimeZoneRule("t2", -3*HOUR, 0, trtimes1, 1, DateTimeRule::UTC_TIME);
1199     TimeArrayTimeZoneRule *t3 = new TimeArrayTimeZoneRule("t3", -3*HOUR, 0, trtimes2, 2, DateTimeRule::UTC_TIME);
1200     TimeArrayTimeZoneRule *t4 = new TimeArrayTimeZoneRule("t4", -3*HOUR, 0, trtimes1, 1, DateTimeRule::STANDARD_TIME);
1201     TimeArrayTimeZoneRule *t5 = new TimeArrayTimeZoneRule("t5", -4*HOUR, 1*HOUR, trtimes1, 1, DateTimeRule::WALL_TIME);
1202 
1203     // DateTimeRule::operator=/clone
1204     DateTimeRule dtr0(UCAL_MAY, 31, 2*HOUR, DateTimeRule::WALL_TIME);
1205     if (dtr0 == *dtr1 || !(dtr0 != *dtr1)) {
1206         errln("FAIL: DateTimeRule dtr0 is not equal to dtr1, but got wrong result");
1207     }
1208     dtr0 = *dtr1;
1209     if (dtr0 != *dtr1 || !(dtr0 == *dtr1)) {
1210         errln("FAIL: DateTimeRule dtr0 is equal to dtr1, but got wrong result");
1211     }
1212     DateTimeRule *dtr0c = dtr0.clone();
1213     if (*dtr0c != *dtr1 || !(*dtr0c == *dtr1)) {
1214         errln("FAIL: DateTimeRule dtr0c is equal to dtr1, but got wrong result");
1215     }
1216     delete dtr0c;
1217 
1218     // AnnualTimeZonerule::operator=/clone
1219     AnnualTimeZoneRule a0("a0", 5*HOUR, 1*HOUR, *dtr1, 1990, AnnualTimeZoneRule::MAX_YEAR);
1220     if (a0 == *a1 || !(a0 != *a1)) {
1221         errln("FAIL: AnnualTimeZoneRule a0 is not equal to a1, but got wrong result");
1222     }
1223     a0 = *a1;
1224     if (a0 != *a1 || !(a0 == *a1)) {
1225         errln("FAIL: AnnualTimeZoneRule a0 is equal to a1, but got wrong result");
1226     }
1227     AnnualTimeZoneRule *a0c = a0.clone();
1228     if (*a0c != *a1 || !(*a0c == *a1)) {
1229         errln("FAIL: AnnualTimeZoneRule a0c is equal to a1, but got wrong result");
1230     }
1231     delete a0c;
1232 
1233     // AnnualTimeZoneRule::getRule
1234     if (*(a1->getRule()) != *(a2->getRule())) {
1235         errln("FAIL: The same DateTimeRule must be returned from AnnualTimeZoneRule a1 and a2");
1236     }
1237 
1238     // AnnualTimeZoneRule::getStartYear
1239     int32_t startYear = a1->getStartYear();
1240     if (startYear != 2000) {
1241         errln((UnicodeString)"FAIL: The start year of AnnualTimeZoneRule a1 must be 2000 - returned: " + startYear);
1242     }
1243 
1244     // AnnualTimeZoneRule::getEndYear
1245     int32_t endYear = a1->getEndYear();
1246     if (endYear != AnnualTimeZoneRule::MAX_YEAR) {
1247         errln((UnicodeString)"FAIL: The start year of AnnualTimeZoneRule a1 must be MAX_YEAR - returned: " + endYear);
1248     }
1249     endYear = a3->getEndYear();
1250     if (endYear != 2010) {
1251         errln((UnicodeString)"FAIL: The start year of AnnualTimeZoneRule a3 must be 2010 - returned: " + endYear);
1252     }
1253 
1254     // AnnualTimeZone::getStartInYear
1255     UBool b1, b2;
1256     UDate d1, d2;
1257     b1 = a1->getStartInYear(2005, -3*HOUR, 0, d1);
1258     b2 = a3->getStartInYear(2005, -3*HOUR, 0, d2);
1259     if (!b1 || !b2 || d1 != d2) {
1260         errln("FAIL: AnnualTimeZoneRule::getStartInYear did not work as expected");
1261     }
1262     b2 = a3->getStartInYear(2015, -3*HOUR, 0, d2);
1263     if (b2) {
1264         errln("FAIL: AnnualTimeZoneRule::getStartInYear returned TRUE for 2015 which is out of rule range");
1265     }
1266 
1267     // AnnualTimeZone::getFirstStart
1268     b1 = a1->getFirstStart(-3*HOUR, 0, d1);
1269     b2 = a1->getFirstStart(-4*HOUR, 1*HOUR, d2);
1270     if (!b1 || !b2 || d1 != d2) {
1271         errln("FAIL: The same start time should be returned by getFirstStart");
1272     }
1273 
1274     // AnnualTimeZone::getFinalStart
1275     b1 = a1->getFinalStart(-3*HOUR, 0, d1);
1276     if (b1) {
1277         errln("FAIL: getFinalStart returned TRUE for a1");
1278     }
1279     b1 = a1->getStartInYear(2010, -3*HOUR, 0, d1);
1280     b2 = a3->getFinalStart(-3*HOUR, 0, d2);
1281     if (!b1 || !b2 || d1 != d2) {
1282         errln("FAIL: Bad date is returned by getFinalStart");
1283     }
1284 
1285     // AnnualTimeZone::getNextStart / getPreviousStart
1286     b1 = a1->getNextStart(time1, -3*HOUR, 0, FALSE, d1);
1287     if (!b1) {
1288         errln("FAIL: getNextStart returned FALSE for ai");
1289     } else {
1290         b2 = a1->getPreviousStart(d1, -3*HOUR, 0, TRUE, d2);
1291         if (!b2 || d1 != d2) {
1292             errln("FAIL: Bad Date is returned by getPreviousStart");
1293         }
1294     }
1295     b1 = a3->getNextStart(time2, -3*HOUR, 0, FALSE, d1);
1296     if (b1) {
1297         errln("FAIL: getNextStart must return FALSE when no start time is available after the base time");
1298     }
1299     b1 = a3->getFinalStart(-3*HOUR, 0, d1);
1300     b2 = a3->getPreviousStart(time2, -3*HOUR, 0, FALSE, d2);
1301     if (!b1 || !b2 || d1 != d2) {
1302         errln("FAIL: getPreviousStart does not match with getFinalStart after the end year");
1303     }
1304 
1305     // AnnualTimeZone::isEquavalentTo
1306     if (!a1->isEquivalentTo(*a2)) {
1307         errln("FAIL: AnnualTimeZoneRule a1 is equivalent to a2, but returned FALSE");
1308     }
1309     if (a1->isEquivalentTo(*a3)) {
1310         errln("FAIL: AnnualTimeZoneRule a1 is not equivalent to a3, but returned TRUE");
1311     }
1312     if (!a1->isEquivalentTo(*a1)) {
1313         errln("FAIL: AnnualTimeZoneRule a1 is equivalent to itself, but returned FALSE");
1314     }
1315     if (a1->isEquivalentTo(*t1)) {
1316         errln("FAIL: AnnualTimeZoneRule is not equivalent to TimeArrayTimeZoneRule, but returned TRUE");
1317     }
1318 
1319     // InitialTimezoneRule::operator=/clone
1320     InitialTimeZoneRule i0("i0", 10*HOUR, 0);
1321     if (i0 == *i1 || !(i0 != *i1)) {
1322         errln("FAIL: InitialTimeZoneRule i0 is not equal to i1, but got wrong result");
1323     }
1324     i0 = *i1;
1325     if (i0 != *i1 || !(i0 == *i1)) {
1326         errln("FAIL: InitialTimeZoneRule i0 is equal to i1, but got wrong result");
1327     }
1328     InitialTimeZoneRule *i0c = i0.clone();
1329     if (*i0c != *i1 || !(*i0c == *i1)) {
1330         errln("FAIL: InitialTimeZoneRule i0c is equal to i1, but got wrong result");
1331     }
1332     delete i0c;
1333 
1334     // InitialTimeZoneRule::isEquivalentRule
1335     if (!i1->isEquivalentTo(*i2)) {
1336         errln("FAIL: InitialTimeZoneRule i1 is equivalent to i2, but returned FALSE");
1337     }
1338     if (i1->isEquivalentTo(*i3)) {
1339         errln("FAIL: InitialTimeZoneRule i1 is not equivalent to i3, but returned TRUE");
1340     }
1341     if (i1->isEquivalentTo(*a1)) {
1342         errln("FAIL: An InitialTimeZoneRule is not equivalent to an AnnualTimeZoneRule, but returned TRUE");
1343     }
1344 
1345     // InitialTimeZoneRule::getFirstStart/getFinalStart/getNextStart/getPreviousStart
1346     b1 = i1->getFirstStart(0, 0, d1);
1347     if (b1) {
1348         errln("FAIL: InitialTimeZone::getFirstStart returned TRUE");
1349     }
1350     b1 = i1->getFinalStart(0, 0, d1);
1351     if (b1) {
1352         errln("FAIL: InitialTimeZone::getFinalStart returned TRUE");
1353     }
1354     b1 = i1->getNextStart(time1, 0, 0, FALSE, d1);
1355     if (b1) {
1356         errln("FAIL: InitialTimeZone::getNextStart returned TRUE");
1357     }
1358     b1 = i1->getPreviousStart(time1, 0, 0, FALSE, d1);
1359     if (b1) {
1360         errln("FAIL: InitialTimeZone::getPreviousStart returned TRUE");
1361     }
1362 
1363     // TimeArrayTimeZoneRule::operator=/clone
1364     TimeArrayTimeZoneRule t0("t0", 4*HOUR, 0, trtimes1, 1, DateTimeRule::UTC_TIME);
1365     if (t0 == *t1 || !(t0 != *t1)) {
1366         errln("FAIL: TimeArrayTimeZoneRule t0 is not equal to t1, but got wrong result");
1367     }
1368     t0 = *t1;
1369     if (t0 != *t1 || !(t0 == *t1)) {
1370         errln("FAIL: TimeArrayTimeZoneRule t0 is equal to t1, but got wrong result");
1371     }
1372     TimeArrayTimeZoneRule *t0c = t0.clone();
1373     if (*t0c != *t1 || !(*t0c == *t1)) {
1374         errln("FAIL: TimeArrayTimeZoneRule t0c is equal to t1, but got wrong result");
1375     }
1376     delete t0c;
1377 
1378     // TimeArrayTimeZoneRule::countStartTimes
1379     if (t1->countStartTimes() != 1) {
1380         errln("FAIL: Bad start time count is returned by TimeArrayTimeZoneRule::countStartTimes");
1381     }
1382 
1383     // TimeArrayTimeZoneRule::getStartTimeAt
1384     b1 = t1->getStartTimeAt(-1, d1);
1385     if (b1) {
1386         errln("FAIL: TimeArrayTimeZoneRule::getStartTimeAt returned TRUE for index -1");
1387     }
1388     b1 = t1->getStartTimeAt(0, d1);
1389     if (!b1 || d1 != trtimes1[0]) {
1390         errln("FAIL: TimeArrayTimeZoneRule::getStartTimeAt returned incorrect result for index 0");
1391     }
1392     b1 = t1->getStartTimeAt(1, d1);
1393     if (b1) {
1394         errln("FAIL: TimeArrayTimeZoneRule::getStartTimeAt returned TRUE for index 1");
1395     }
1396 
1397     // TimeArrayTimeZoneRule::getTimeType
1398     if (t1->getTimeType() != DateTimeRule::UTC_TIME) {
1399         errln("FAIL: TimeArrayTimeZoneRule t1 uses UTC_TIME, but different type is returned");
1400     }
1401     if (t4->getTimeType() != DateTimeRule::STANDARD_TIME) {
1402         errln("FAIL: TimeArrayTimeZoneRule t4 uses STANDARD_TIME, but different type is returned");
1403     }
1404     if (t5->getTimeType() != DateTimeRule::WALL_TIME) {
1405         errln("FAIL: TimeArrayTimeZoneRule t5 uses WALL_TIME, but different type is returned");
1406     }
1407 
1408     // TimeArrayTimeZoneRule::getFirstStart/getFinalStart
1409     b1 = t1->getFirstStart(0, 0, d1);
1410     if (!b1 || d1 != trtimes1[0]) {
1411         errln("FAIL: Bad first start time returned from TimeArrayTimeZoneRule t1");
1412     }
1413     b1 = t1->getFinalStart(0, 0, d1);
1414     if (!b1 || d1 != trtimes1[0]) {
1415         errln("FAIL: Bad final start time returned from TimeArrayTimeZoneRule t1");
1416     }
1417     b1 = t4->getFirstStart(-4*HOUR, 1*HOUR, d1);
1418     if (!b1 || d1 != (trtimes1[0] + 4*HOUR)) {
1419         errln("FAIL: Bad first start time returned from TimeArrayTimeZoneRule t4");
1420     }
1421     b1 = t5->getFirstStart(-4*HOUR, 1*HOUR, d1);
1422     if (!b1 || d1 != (trtimes1[0] + 3*HOUR)) {
1423         errln("FAIL: Bad first start time returned from TimeArrayTimeZoneRule t5");
1424     }
1425 
1426     // TimeArrayTimeZoneRule::getNextStart/getPreviousStart
1427     b1 = t3->getNextStart(time1, -3*HOUR, 1*HOUR, FALSE, d1);
1428     if (b1) {
1429         errln("FAIL: getNextStart returned TRUE after the final transition for t3");
1430     }
1431     b1 = t3->getPreviousStart(time1, -3*HOUR, 1*HOUR, FALSE, d1);
1432     if (!b1 || d1 != trtimes2[1]) {
1433         errln("FAIL: Bad start time returned by getPreviousStart for t3");
1434     } else {
1435         b2 = t3->getPreviousStart(d1, -3*HOUR, 1*HOUR, FALSE, d2);
1436         if (!b2 || d2 != trtimes2[0]) {
1437             errln("FAIL: Bad start time returned by getPreviousStart for t3");
1438         }
1439     }
1440     b1 = t3->getPreviousStart(time3, -3*HOUR, 1*HOUR, FALSE, d1); //time3 - year 1950, no result expected
1441     if (b1) {
1442         errln("FAIL: getPreviousStart returned TRUE before the first transition for t3");
1443     }
1444 
1445     // TimeArrayTimeZoneRule::isEquivalentTo
1446     if (!t1->isEquivalentTo(*t2)) {
1447         errln("FAIL: TimeArrayTimeZoneRule t1 is equivalent to t2, but returned FALSE");
1448     }
1449     if (t1->isEquivalentTo(*t3)) {
1450         errln("FAIL: TimeArrayTimeZoneRule t1 is not equivalent to t3, but returned TRUE");
1451     }
1452     if (t1->isEquivalentTo(*t4)) {
1453         errln("FAIL: TimeArrayTimeZoneRule t1 is not equivalent to t4, but returned TRUE");
1454     }
1455     if (t1->isEquivalentTo(*a1)) {
1456         errln("FAIL: TimeArrayTimeZoneRule is not equivalent to AnnualTimeZoneRule, but returned TRUE");
1457     }
1458 
1459     delete dtr1;
1460     delete dtr2;
1461     delete dtr3;
1462     delete dtr4;
1463     delete a1;
1464     delete a2;
1465     delete a3;
1466     delete i1;
1467     delete i2;
1468     delete i3;
1469     delete t1;
1470     delete t2;
1471     delete t3;
1472     delete t4;
1473     delete t5;
1474 }
1475 
1476 /*
1477  * API coverage test for BasicTimeZone APIs in SimpleTimeZone
1478  */
1479 void
TestSimpleTimeZoneCoverage(void)1480 TimeZoneRuleTest::TestSimpleTimeZoneCoverage(void) {
1481     UDate time1 = getUTCMillis(1990, UCAL_JUNE, 1);
1482     UDate time2 = getUTCMillis(2000, UCAL_JUNE, 1);
1483 
1484     TimeZoneTransition tzt1, tzt2;
1485     UBool avail1, avail2;
1486     UErrorCode status = U_ZERO_ERROR;
1487     const TimeZoneRule *trrules[2];
1488     const InitialTimeZoneRule *ir = NULL;
1489     int32_t numTzRules;
1490 
1491     // BasicTimeZone API implementation in SimpleTimeZone
1492     SimpleTimeZone *stz1 = new SimpleTimeZone(-5*HOUR, "GMT-5");
1493 
1494     avail1 = stz1->getNextTransition(time1, FALSE, tzt1);
1495     if (avail1) {
1496         errln("FAIL: No transition must be returned by getNextTranstion for SimpleTimeZone with no DST rule");
1497     }
1498     avail1 = stz1->getPreviousTransition(time1, FALSE, tzt1);
1499     if (avail1) {
1500         errln("FAIL: No transition must be returned by getPreviousTransition  for SimpleTimeZone with no DST rule");
1501     }
1502 
1503     numTzRules = stz1->countTransitionRules(status);
1504     if (U_FAILURE(status)) {
1505         errln("FAIL: countTransitionRules failed");
1506     }
1507     if (numTzRules != 0) {
1508         errln((UnicodeString)"FAIL: countTransitionRules returned " + numTzRules);
1509     }
1510     numTzRules = 2;
1511     stz1->getTimeZoneRules(ir, trrules, numTzRules, status);
1512     if (U_FAILURE(status)) {
1513         errln("FAIL: getTimeZoneRules failed");
1514     }
1515     if (numTzRules != 0) {
1516         errln("FAIL: Incorrect transition rule count");
1517     }
1518     if (ir == NULL || ir->getRawOffset() != stz1->getRawOffset()) {
1519         errln("FAIL: Bad initial time zone rule");
1520     }
1521 
1522     // Set DST rule
1523     stz1->setStartRule(UCAL_MARCH, 11, 2*HOUR, status); // March 11
1524     stz1->setEndRule(UCAL_NOVEMBER, 1, UCAL_SUNDAY, 2*HOUR, status); // First Sunday in November
1525     if (U_FAILURE(status)) {
1526         errln("FAIL: Failed to set DST rules in a SimpleTimeZone");
1527     }
1528 
1529     avail1 = stz1->getNextTransition(time1, FALSE, tzt1);
1530     if (!avail1) {
1531         errln("FAIL: Non-null transition must be returned by getNextTranstion for SimpleTimeZone with a DST rule");
1532     }
1533     avail1 = stz1->getPreviousTransition(time1, FALSE, tzt1);
1534     if (!avail1) {
1535         errln("FAIL: Non-null transition must be returned by getPreviousTransition  for SimpleTimeZone with a DST rule");
1536     }
1537 
1538     numTzRules = stz1->countTransitionRules(status);
1539     if (U_FAILURE(status)) {
1540         errln("FAIL: countTransitionRules failed");
1541     }
1542     if (numTzRules != 2) {
1543         errln((UnicodeString)"FAIL: countTransitionRules returned " + numTzRules);
1544     }
1545 
1546     numTzRules = 2;
1547     trrules[0] = NULL;
1548     trrules[1] = NULL;
1549     stz1->getTimeZoneRules(ir, trrules, numTzRules, status);
1550     if (U_FAILURE(status)) {
1551         errln("FAIL: getTimeZoneRules failed");
1552     }
1553     if (numTzRules != 2) {
1554         errln("FAIL: Incorrect transition rule count");
1555     }
1556     if (ir == NULL || ir->getRawOffset() != stz1->getRawOffset()) {
1557         errln("FAIL: Bad initial time zone rule");
1558     }
1559     if (trrules[0] == NULL || trrules[0]->getRawOffset() != stz1->getRawOffset()) {
1560         errln("FAIL: Bad transition rule 0");
1561     }
1562     if (trrules[1] == NULL || trrules[1]->getRawOffset() != stz1->getRawOffset()) {
1563         errln("FAIL: Bad transition rule 1");
1564     }
1565 
1566     // Set DST start year
1567     stz1->setStartYear(2007);
1568     avail1 = stz1->getPreviousTransition(time1, FALSE, tzt1);
1569     if (avail1) {
1570         errln("FAIL: No transition must be returned before 1990");
1571     }
1572     avail1 = stz1->getNextTransition(time1, FALSE, tzt1); // transition after 1990-06-01
1573     avail2 = stz1->getNextTransition(time2, FALSE, tzt2); // transition after 2000-06-01
1574     if (!avail1 || !avail2 || tzt1 != tzt2) {
1575         errln("FAIL: Bad transition returned by SimpleTimeZone::getNextTransition");
1576     }
1577     delete stz1;
1578 }
1579 
1580 /*
1581  * API coverage test for VTimeZone
1582  */
1583 void
TestVTimeZoneCoverage(void)1584 TimeZoneRuleTest::TestVTimeZoneCoverage(void) {
1585     UErrorCode status = U_ZERO_ERROR;
1586     UnicodeString TZID("Europe/Moscow");
1587 
1588     BasicTimeZone *otz = (BasicTimeZone*)TimeZone::createTimeZone(TZID);
1589     VTimeZone *vtz = VTimeZone::createVTimeZoneByID(TZID);
1590 
1591     // getOffset(era, year, month, day, dayOfWeek, milliseconds, ec)
1592     int32_t offset1 = otz->getOffset(GregorianCalendar::AD, 2007, UCAL_JULY, 1, UCAL_SUNDAY, 0, status);
1593     if (U_FAILURE(status)) {
1594         errln("FAIL: getOffset(7 args) failed for otz");
1595     }
1596     int32_t offset2 = vtz->getOffset(GregorianCalendar::AD, 2007, UCAL_JULY, 1, UCAL_SUNDAY, 0, status);
1597     if (U_FAILURE(status)) {
1598         errln("FAIL: getOffset(7 args) failed for vtz");
1599     }
1600     if (offset1 != offset2) {
1601         errln("FAIL: getOffset(7 args) returned different results in VTimeZone and OlsonTimeZone");
1602     }
1603 
1604     // getOffset(era, year, month, day, dayOfWeek, milliseconds, monthLength, ec)
1605     offset1 = otz->getOffset(GregorianCalendar::AD, 2007, UCAL_JULY, 1, UCAL_SUNDAY, 0, 31, status);
1606     if (U_FAILURE(status)) {
1607         errln("FAIL: getOffset(8 args) failed for otz");
1608     }
1609     offset2 = vtz->getOffset(GregorianCalendar::AD, 2007, UCAL_JULY, 1, UCAL_SUNDAY, 0, 31, status);
1610     if (U_FAILURE(status)) {
1611         errln("FAIL: getOffset(8 args) failed for vtz");
1612     }
1613     if (offset1 != offset2) {
1614         errln("FAIL: getOffset(8 args) returned different results in VTimeZone and OlsonTimeZone");
1615     }
1616 
1617 
1618     // getOffset(date, local, rawOffset, dstOffset, ec)
1619     UDate t = Calendar::getNow();
1620     int32_t rawOffset1, dstSavings1;
1621     int32_t rawOffset2, dstSavings2;
1622 
1623     otz->getOffset(t, FALSE, rawOffset1, dstSavings1, status);
1624     if (U_FAILURE(status)) {
1625         errln("FAIL: getOffset(5 args) failed for otz");
1626     }
1627     vtz->getOffset(t, FALSE, rawOffset2, dstSavings2, status);
1628     if (U_FAILURE(status)) {
1629         errln("FAIL: getOffset(5 args) failed for vtz");
1630     }
1631     if (rawOffset1 != rawOffset2 || dstSavings1 != dstSavings2) {
1632         errln("FAIL: getOffset(long,boolean,int[]) returned different results in VTimeZone and OlsonTimeZone");
1633     }
1634 
1635     // getRawOffset
1636     if (otz->getRawOffset() != vtz->getRawOffset()) {
1637         errln("FAIL: getRawOffset returned different results in VTimeZone and OlsonTimeZone");
1638     }
1639 
1640     // inDaylightTime
1641     UBool inDst1, inDst2;
1642     inDst1 = otz->inDaylightTime(t, status);
1643     if (U_FAILURE(status)) {
1644         errln("FAIL: inDaylightTime failed for otz");
1645     }
1646     inDst2 = vtz->inDaylightTime(t, status);
1647     if (U_FAILURE(status)) {
1648         errln("FAIL: inDaylightTime failed for vtz");
1649     }
1650     if (inDst1 != inDst2) {
1651         errln("FAIL: inDaylightTime returned different results in VTimeZone and OlsonTimeZone");
1652     }
1653 
1654     // useDaylightTime
1655     if (otz->useDaylightTime() != vtz->useDaylightTime()) {
1656         errln("FAIL: useDaylightTime returned different results in VTimeZone and OlsonTimeZone");
1657     }
1658 
1659     // setRawOffset
1660     const int32_t RAW = -10*HOUR;
1661     VTimeZone *tmpvtz = (VTimeZone*)vtz->clone();
1662     tmpvtz->setRawOffset(RAW);
1663     if (tmpvtz->getRawOffset() != RAW) {
1664         logln("setRawOffset is implemented in VTimeZone");
1665     }
1666 
1667     // hasSameRules
1668     UBool bSame = otz->hasSameRules(*vtz);
1669     logln((UnicodeString)"OlsonTimeZone::hasSameRules(VTimeZone) should return FALSE always for now - actual: " + bSame);
1670 
1671     // getTZURL/setTZURL
1672     UnicodeString TZURL("http://icu-project.org/timezone");
1673     UnicodeString url;
1674     if (vtz->getTZURL(url)) {
1675         errln("FAIL: getTZURL returned TRUE");
1676     }
1677     vtz->setTZURL(TZURL);
1678     if (!vtz->getTZURL(url) || url != TZURL) {
1679         errln("FAIL: URL returned by getTZURL does not match the one set by setTZURL");
1680     }
1681 
1682     // getLastModified/setLastModified
1683     UDate lastmod;
1684     if (vtz->getLastModified(lastmod)) {
1685         errln("FAIL: getLastModified returned TRUE");
1686     }
1687     vtz->setLastModified(t);
1688     if (!vtz->getLastModified(lastmod) || lastmod != t) {
1689         errln("FAIL: Date returned by getLastModified does not match the one set by setLastModified");
1690     }
1691 
1692     // getNextTransition/getPreviousTransition
1693     UDate base = getUTCMillis(2007, UCAL_JULY, 1);
1694     TimeZoneTransition tzt1, tzt2;
1695     UBool btr1 = otz->getNextTransition(base, TRUE, tzt1);
1696     UBool btr2 = vtz->getNextTransition(base, TRUE, tzt2);
1697     if (!btr1 || !btr2 || tzt1 != tzt2) {
1698         errln("FAIL: getNextTransition returned different results in VTimeZone and OlsonTimeZone");
1699     }
1700     btr1 = otz->getPreviousTransition(base, FALSE, tzt1);
1701     btr2 = vtz->getPreviousTransition(base, FALSE, tzt2);
1702     if (!btr1 || !btr2 || tzt1 != tzt2) {
1703         errln("FAIL: getPreviousTransition returned different results in VTimeZone and OlsonTimeZone");
1704     }
1705 
1706     // TimeZoneTransition constructor/clone
1707     TimeZoneTransition *tzt1c = tzt1.clone();
1708     if (*tzt1c != tzt1 || !(*tzt1c == tzt1)) {
1709         errln("FAIL: TimeZoneTransition tzt1c is equal to tzt1, but got wrong result");
1710     }
1711     delete tzt1c;
1712     TimeZoneTransition tzt3(tzt1);
1713     if (tzt3 != tzt1 || !(tzt3 == tzt1)) {
1714         errln("FAIL: TimeZoneTransition tzt3 is equal to tzt1, but got wrong result");
1715     }
1716 
1717     // hasEquivalentTransitions
1718     UDate time1 = getUTCMillis(1950, UCAL_JANUARY, 1);
1719     UDate time2 = getUTCMillis(2020, UCAL_JANUARY, 1);
1720     UBool equiv = vtz->hasEquivalentTransitions(*otz, time1, time2, FALSE, status);
1721     if (U_FAILURE(status)) {
1722         errln("FAIL: hasEquivalentTransitions failed for vtz/otz");
1723     }
1724     if (!equiv) {
1725         errln("FAIL: hasEquivalentTransitons returned false for the same time zone");
1726     }
1727 
1728     // operator=/operator==/operator!=
1729     VTimeZone *vtz1 = VTimeZone::createVTimeZoneByID("America/Los_Angeles");
1730     if (*vtz1 == *vtz || !(*vtz1 != *vtz)) {
1731         errln("FAIL: VTimeZone vtz1 is not equal to vtz, but got wrong result");
1732     }
1733     *vtz1 = *vtz;
1734     if (*vtz1 != *vtz || !(*vtz1 == *vtz)) {
1735         errln("FAIL: VTimeZone vtz1 is equal to vtz, but got wrong result");
1736     }
1737 
1738     delete otz;
1739     delete vtz;
1740     delete tmpvtz;
1741     delete vtz1;
1742 }
1743 
1744 
1745 void
TestVTimeZoneParse(void)1746 TimeZoneRuleTest::TestVTimeZoneParse(void) {
1747     UErrorCode status = U_ZERO_ERROR;
1748 
1749     // Trying to create VTimeZone from empty data
1750     UnicodeString emptyData;
1751     VTimeZone *empty = VTimeZone::createVTimeZone(emptyData, status);
1752     if (U_SUCCESS(status) || empty != NULL) {
1753         delete empty;
1754         errln("FAIL: Non-null VTimeZone is returned for empty VTIMEZONE data");
1755     }
1756     status = U_ZERO_ERROR;
1757 
1758     // Create VTimeZone for Asia/Tokyo
1759     UnicodeString asiaTokyoID("Asia/Tokyo");
1760     static const UChar asiaTokyo[] = {
1761         /* "BEGIN:VTIMEZONE\x0D\x0A" */
1762         0x42,0x45,0x47,0x49,0x4E,0x3A,0x56,0x54,0x49,0x4D,0x45,0x5A,0x4F,0x4E,0x45,0x0D,0x0A,
1763         /* "TZID:Asia\x0D\x0A" */
1764         0x54,0x5A,0x49,0x44,0x3A,0x41,0x73,0x69,0x61,0x0D,0x0A,
1765         /* "\x09/Tokyo\x0D\x0A" */
1766         0x09,0x2F,0x54,0x6F,0x6B,0x79,0x6F,0x0D,0x0A,
1767         /* "BEGIN:STANDARD\x0D\x0A" */
1768         0x42,0x45,0x47,0x49,0x4E,0x3A,0x53,0x54,0x41,0x4E,0x44,0x41,0x52,0x44,0x0D,0x0A,
1769         /* "TZOFFSETFROM:+0900\x0D\x0A" */
1770         0x54,0x5A,0x4F,0x46,0x46,0x53,0x45,0x54,0x46,0x52,0x4F,0x4D,0x3A,0x2B,0x30,0x39,0x30,0x30,0x0D,0x0A,
1771         /* "TZOFFSETTO:+0900\x0D\x0A" */
1772         0x54,0x5A,0x4F,0x46,0x46,0x53,0x45,0x54,0x54,0x4F,0x3A,0x2B,0x30,0x39,0x30,0x30,0x0D,0x0A,
1773         /* "TZNAME:JST\x0D\x0A" */
1774         0x54,0x5A,0x4E,0x41,0x4D,0x45,0x3A,0x4A,0x53,0x54,0x0D,0x0A,
1775         /* "DTSTART:19700101\x0D\x0A" */
1776         0x44,0x54,0x53,0x54,0x41,0x52,0x54,0x3A,0x31,0x39,0x37,0x30,0x30,0x31,0x30,0x31,0x0D,0x0A,
1777         /* " T000000\x0D\x0A" */
1778         0x20,0x54,0x30,0x30,0x30,0x30,0x30,0x30,0x0D,0x0A,
1779         /* "END:STANDARD\x0D\x0A" */
1780         0x45,0x4E,0x44,0x3A,0x53,0x54,0x41,0x4E,0x44,0x41,0x52,0x44,0x0D,0x0A,
1781         /* "END:VTIMEZONE" */
1782         0x45,0x4E,0x44,0x3A,0x56,0x54,0x49,0x4D,0x45,0x5A,0x4F,0x4E,0x45,
1783         0
1784     };
1785     VTimeZone *tokyo = VTimeZone::createVTimeZone(asiaTokyo, status);
1786     if (U_FAILURE(status) || tokyo == NULL) {
1787         errln("FAIL: Failed to create a VTimeZone tokyo");
1788     } else {
1789         // Check ID
1790         UnicodeString tzid;
1791         tokyo->getID(tzid);
1792         if (tzid != asiaTokyoID) {
1793             errln((UnicodeString)"FAIL: Invalid TZID: " + tzid);
1794         }
1795         // Make sure offsets are correct
1796         int32_t rawOffset, dstSavings;
1797         tokyo->getOffset(Calendar::getNow(), FALSE, rawOffset, dstSavings, status);
1798         if (U_FAILURE(status)) {
1799             errln("FAIL: getOffset failed for tokyo");
1800         }
1801         if (rawOffset != 9*HOUR || dstSavings != 0) {
1802             errln("FAIL: Bad offsets returned by a VTimeZone created for Tokyo");
1803         }
1804     }
1805     delete tokyo;
1806 
1807         // Create VTimeZone from VTIMEZONE data
1808     static const UChar fooData[] = {
1809         /* "BEGIN:VCALENDAR\x0D\x0A" */
1810         0x42,0x45,0x47,0x49,0x4E,0x3A,0x56,0x43,0x41,0x4C,0x45,0x4E,0x44,0x41,0x52,0x0D,0x0A,
1811         /* "BEGIN:VTIMEZONE\x0D\x0A" */
1812         0x42,0x45,0x47,0x49,0x4E,0x3A,0x56,0x54,0x49,0x4D,0x45,0x5A,0x4F,0x4E,0x45,0x0D,0x0A,
1813         /* "TZID:FOO\x0D\x0A" */
1814         0x54,0x5A,0x49,0x44,0x3A,0x46,0x4F,0x4F,0x0D,0x0A,
1815         /* "BEGIN:STANDARD\x0D\x0A" */
1816         0x42,0x45,0x47,0x49,0x4E,0x3A,0x53,0x54,0x41,0x4E,0x44,0x41,0x52,0x44,0x0D,0x0A,
1817         /* "TZOFFSETFROM:-0700\x0D\x0A" */
1818         0x54,0x5A,0x4F,0x46,0x46,0x53,0x45,0x54,0x46,0x52,0x4F,0x4D,0x3A,0x2D,0x30,0x37,0x30,0x30,0x0D,0x0A,
1819         /* "TZOFFSETTO:-0800\x0D\x0A" */
1820         0x54,0x5A,0x4F,0x46,0x46,0x53,0x45,0x54,0x54,0x4F,0x3A,0x2D,0x30,0x38,0x30,0x30,0x0D,0x0A,
1821         /* "TZNAME:FST\x0D\x0A" */
1822         0x54,0x5A,0x4E,0x41,0x4D,0x45,0x3A,0x46,0x53,0x54,0x0D,0x0A,
1823         /* "DTSTART:20071010T010000\x0D\x0A" */
1824         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,
1825         /* "RRULE:FREQ=YEARLY;BYDAY=WE;BYMONTHDAY=10,11,12,13,14,15,16;BYMONTH=10\x0D\x0A" */
1826         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,
1827         /* "END:STANDARD\x0D\x0A" */
1828         0x45,0x4E,0x44,0x3A,0x53,0x54,0x41,0x4E,0x44,0x41,0x52,0x44,0x0D,0x0A,
1829         /* "BEGIN:DAYLIGHT\x0D\x0A" */
1830         0x42,0x45,0x47,0x49,0x4E,0x3A,0x44,0x41,0x59,0x4C,0x49,0x47,0x48,0x54,0x0D,0x0A,
1831         /* "TZOFFSETFROM:-0800\x0D\x0A" */
1832         0x54,0x5A,0x4F,0x46,0x46,0x53,0x45,0x54,0x46,0x52,0x4F,0x4D,0x3A,0x2D,0x30,0x38,0x30,0x30,0x0D,0x0A,
1833         /* "TZOFFSETTO:-0700\x0D\x0A" */
1834         0x54,0x5A,0x4F,0x46,0x46,0x53,0x45,0x54,0x54,0x4F,0x3A,0x2D,0x30,0x37,0x30,0x30,0x0D,0x0A,
1835         /* "TZNAME:FDT\x0D\x0A" */
1836         0x54,0x5A,0x4E,0x41,0x4D,0x45,0x3A,0x46,0x44,0x54,0x0D,0x0A,
1837         /* "DTSTART:20070415T010000\x0D\x0A" */
1838         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,
1839         /* "RRULE:FREQ=YEARLY;BYMONTHDAY=15;BYMONTH=4\x0D\x0A" */
1840         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,
1841         /* "END:DAYLIGHT\x0D\x0A" */
1842         0x45,0x4E,0x44,0x3A,0x44,0x41,0x59,0x4C,0x49,0x47,0x48,0x54,0x0D,0x0A,
1843         /* "END:VTIMEZONE\x0D\x0A" */
1844         0x45,0x4E,0x44,0x3A,0x56,0x54,0x49,0x4D,0x45,0x5A,0x4F,0x4E,0x45,0x0D,0x0A,
1845         /* "END:VCALENDAR" */
1846         0x45,0x4E,0x44,0x3A,0x56,0x43,0x41,0x4C,0x45,0x4E,0x44,0x41,0x52,
1847         0
1848     };
1849 
1850     VTimeZone *foo = VTimeZone::createVTimeZone(fooData, status);
1851     if (U_FAILURE(status) || foo == NULL) {
1852         errln("FAIL: Failed to create a VTimeZone foo");
1853     } else {
1854         // Write VTIMEZONE data
1855         UnicodeString fooData2;
1856         foo->write(getUTCMillis(2005, UCAL_JANUARY, 1), fooData2, status);
1857         if (U_FAILURE(status)) {
1858             errln("FAIL: Failed to write VTIMEZONE data for foo");
1859         }
1860         logln(fooData2);
1861     }
1862     delete foo;
1863 }
1864 
1865 //----------- private test helpers -------------------------------------------------
1866 
1867 UDate
getUTCMillis(int32_t y,int32_t m,int32_t d,int32_t hr,int32_t min,int32_t sec,int32_t msec)1868 TimeZoneRuleTest::getUTCMillis(int32_t y, int32_t m, int32_t d,
1869                                int32_t hr, int32_t min, int32_t sec, int32_t msec) {
1870     UErrorCode status = U_ZERO_ERROR;
1871     const TimeZone *tz = TimeZone::getGMT();
1872     Calendar *cal = Calendar::createInstance(*tz, status);
1873     if (U_FAILURE(status)) {
1874         delete cal;
1875         errln("FAIL: Calendar::createInstance failed");
1876         return 0.0;
1877     }
1878     cal->set(y, m, d, hr, min, sec);
1879     cal->set(UCAL_MILLISECOND, msec);
1880     UDate utc = cal->getTime(status);
1881     if (U_FAILURE(status)) {
1882         delete cal;
1883         errln("FAIL: Calendar::getTime failed");
1884         return 0.0;
1885     }
1886     delete cal;
1887     return utc;
1888 }
1889 
1890 /*
1891  * Check if a time shift really happens on each transition returned by getNextTransition or
1892  * getPreviousTransition in the specified time range
1893  */
1894 void
verifyTransitions(BasicTimeZone & icutz,UDate start,UDate end)1895 TimeZoneRuleTest::verifyTransitions(BasicTimeZone& icutz, UDate start, UDate end) {
1896     UErrorCode status = U_ZERO_ERROR;
1897     UDate time;
1898     int32_t raw, dst, raw0, dst0;
1899     TimeZoneTransition tzt, tzt0;
1900     UBool avail;
1901     UBool first = TRUE;
1902     UnicodeString tzid;
1903 
1904     // Ascending
1905     time = start;
1906     while (TRUE) {
1907         avail = icutz.getNextTransition(time, FALSE, tzt);
1908         if (!avail) {
1909             break;
1910         }
1911         time = tzt.getTime();
1912         if (time >= end) {
1913             break;
1914         }
1915         icutz.getOffset(time, FALSE, raw, dst, status);
1916         icutz.getOffset(time - 1, FALSE, raw0, dst0, status);
1917         if (U_FAILURE(status)) {
1918             errln("FAIL: Error in getOffset");
1919             break;
1920         }
1921 
1922         if (raw == raw0 && dst == dst0) {
1923             errln((UnicodeString)"FAIL: False transition returned by getNextTransition for "
1924                 + icutz.getID(tzid) + " at " + dateToString(time));
1925         }
1926         if (!first &&
1927                 (tzt0.getTo()->getRawOffset() != tzt.getFrom()->getRawOffset()
1928                 || tzt0.getTo()->getDSTSavings() != tzt.getFrom()->getDSTSavings())) {
1929             errln((UnicodeString)"FAIL: TO rule of the previous transition does not match FROM rule of this transtion at "
1930                     + dateToString(time) + " for " + icutz.getID(tzid));
1931         }
1932         tzt0 = tzt;
1933         first = FALSE;
1934     }
1935 
1936     // Descending
1937     first = TRUE;
1938     time = end;
1939     while(true) {
1940         avail = icutz.getPreviousTransition(time, FALSE, tzt);
1941         if (!avail) {
1942             break;
1943         }
1944         time = tzt.getTime();
1945         if (time <= start) {
1946             break;
1947         }
1948         icutz.getOffset(time, FALSE, raw, dst, status);
1949         icutz.getOffset(time - 1, FALSE, raw0, dst0, status);
1950         if (U_FAILURE(status)) {
1951             errln("FAIL: Error in getOffset");
1952             break;
1953         }
1954 
1955         if (raw == raw0 && dst == dst0) {
1956             errln((UnicodeString)"FAIL: False transition returned by getPreviousTransition for "
1957                 + icutz.getID(tzid) + " at " + dateToString(time));
1958         }
1959 
1960         if (!first &&
1961                 (tzt0.getFrom()->getRawOffset() != tzt.getTo()->getRawOffset()
1962                 || tzt0.getFrom()->getDSTSavings() != tzt.getTo()->getDSTSavings())) {
1963             errln((UnicodeString)"FAIL: TO rule of the next transition does not match FROM rule in this transtion at "
1964                     + dateToString(time) + " for " + icutz.getID(tzid));
1965         }
1966         tzt0 = tzt;
1967         first = FALSE;
1968     }
1969 }
1970 
1971 /*
1972  * Compare all time transitions in 2 time zones in the specified time range in ascending order
1973  */
1974 void
compareTransitionsAscending(BasicTimeZone & z1,BasicTimeZone & z2,UDate start,UDate end,UBool inclusive)1975 TimeZoneRuleTest::compareTransitionsAscending(BasicTimeZone& z1, BasicTimeZone& z2,
1976                                               UDate start, UDate end, UBool inclusive) {
1977     UnicodeString zid1, zid2;
1978     TimeZoneTransition tzt1, tzt2;
1979     UBool avail1, avail2;
1980     UBool inRange1, inRange2;
1981 
1982     z1.getID(zid1);
1983     z2.getID(zid2);
1984 
1985     UDate time = start;
1986     while (TRUE) {
1987         avail1 = z1.getNextTransition(time, inclusive, tzt1);
1988         avail2 = z2.getNextTransition(time, inclusive, tzt2);
1989 
1990         inRange1 = inRange2 = FALSE;
1991         if (avail1) {
1992             if (tzt1.getTime() < end || (inclusive && tzt1.getTime() == end)) {
1993                 inRange1 = TRUE;
1994             }
1995         }
1996         if (avail2) {
1997             if (tzt2.getTime() < end || (inclusive && tzt2.getTime() == end)) {
1998                 inRange2 = TRUE;
1999             }
2000         }
2001         if (!inRange1 && !inRange2) {
2002             // No more transition in the range
2003             break;
2004         }
2005         if (!inRange1) {
2006             errln((UnicodeString)"FAIL: " + zid1 + " does not have any transitions after "
2007                 + dateToString(time) + " before " + dateToString(end));
2008             break;
2009         }
2010         if (!inRange2) {
2011             errln((UnicodeString)"FAIL: " + zid2 + " does not have any transitions after "
2012                 + dateToString(time) + " before " + dateToString(end));
2013             break;
2014         }
2015         if (tzt1.getTime() != tzt2.getTime()) {
2016             errln((UnicodeString)"FAIL: First transition after " + dateToString(time) + " "
2017                     + zid1 + "[" + dateToString(tzt1.getTime()) + "] "
2018                     + zid2 + "[" + dateToString(tzt2.getTime()) + "]");
2019             break;
2020         }
2021         time = tzt1.getTime();
2022         if (inclusive) {
2023             time += 1;
2024         }
2025     }
2026 }
2027 
2028 /*
2029  * Compare all time transitions in 2 time zones in the specified time range in descending order
2030  */
2031 void
compareTransitionsDescending(BasicTimeZone & z1,BasicTimeZone & z2,UDate start,UDate end,UBool inclusive)2032 TimeZoneRuleTest::compareTransitionsDescending(BasicTimeZone& z1, BasicTimeZone& z2,
2033                                                UDate start, UDate end, UBool inclusive) {
2034     UnicodeString zid1, zid2;
2035     TimeZoneTransition tzt1, tzt2;
2036     UBool avail1, avail2;
2037     UBool inRange1, inRange2;
2038 
2039     z1.getID(zid1);
2040     z2.getID(zid2);
2041 
2042     UDate time = end;
2043     while (TRUE) {
2044         avail1 = z1.getPreviousTransition(time, inclusive, tzt1);
2045         avail2 = z2.getPreviousTransition(time, inclusive, tzt2);
2046 
2047         inRange1 = inRange2 = FALSE;
2048         if (avail1) {
2049             if (tzt1.getTime() > start || (inclusive && tzt1.getTime() == start)) {
2050                 inRange1 = TRUE;
2051             }
2052         }
2053         if (avail2) {
2054             if (tzt2.getTime() > start || (inclusive && tzt2.getTime() == start)) {
2055                 inRange2 = TRUE;
2056             }
2057         }
2058         if (!inRange1 && !inRange2) {
2059             // No more transition in the range
2060             break;
2061         }
2062         if (!inRange1) {
2063             errln((UnicodeString)"FAIL: " + zid1 + " does not have any transitions before "
2064                 + dateToString(time) + " after " + dateToString(start));
2065             break;
2066         }
2067         if (!inRange2) {
2068             errln((UnicodeString)"FAIL: " + zid2 + " does not have any transitions before "
2069                 + dateToString(time) + " after " + dateToString(start));
2070             break;
2071         }
2072         if (tzt1.getTime() != tzt2.getTime()) {
2073             errln((UnicodeString)"FAIL: Last transition before " + dateToString(time) + " "
2074                     + zid1 + "[" + dateToString(tzt1.getTime()) + "] "
2075                     + zid2 + "[" + dateToString(tzt2.getTime()) + "]");
2076             break;
2077         }
2078         time = tzt1.getTime();
2079         if (inclusive) {
2080             time -= 1;
2081         }
2082     }
2083 }
2084 
2085 #endif /* #if !UCONFIG_NO_FORMATTING */
2086 
2087 //eof
2088