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