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