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