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