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