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