]>
git.saurik.com Git - apple/icu.git/blob - icuSources/test/intltest/tzbdtest.cpp
1 // © 2016 and later: Unicode, Inc. and others.
2 // License & terms of use: http://www.unicode.org/copyright.html
3 /***********************************************************************
5 * Copyright (c) 1997-2010, International Business Machines Corporation
6 * and others. All Rights Reserved.
7 ***********************************************************************/
9 #include "unicode/utypes.h"
11 #if !UCONFIG_NO_FORMATTING
14 #include "unicode/timezone.h"
15 #include "unicode/simpletz.h"
16 #include "unicode/gregocal.h"
19 void TimeZoneBoundaryTest::runIndexedTest( int32_t index
, UBool exec
, const char* &name
, char* /*par*/ )
21 if (exec
) logln("TestSuite TestTimeZoneBoundary");
24 name
= "TestBoundaries";
26 logln("TestBoundaries---"); logln("");
31 name
= "TestNewRules";
33 logln("TestNewRules---"); logln("");
38 name
= "TestStepwise";
40 logln("TestStepwise---"); logln("");
44 default: name
= ""; break;
48 // *****************************************************************************
49 // class TimeZoneBoundaryTest
50 // *****************************************************************************
52 TimeZoneBoundaryTest::TimeZoneBoundaryTest()
55 ONE_MINUTE(60 * ONE_SECOND
),
56 ONE_HOUR(60 * ONE_MINUTE
),
57 ONE_DAY(24 * ONE_HOUR
),
58 ONE_YEAR(uprv_floor(365.25 * ONE_DAY
)),
59 SIX_MONTHS(ONE_YEAR
/ 2)
63 const int32_t TimeZoneBoundaryTest::MONTH_LENGTH
[] = {
64 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
67 const UDate
TimeZoneBoundaryTest::PST_1997_BEG
= 860320800000.0;
69 const UDate
TimeZoneBoundaryTest::PST_1997_END
= 877856400000.0;
71 const UDate
TimeZoneBoundaryTest::INTERVAL
= 10;
73 // -------------------------------------
76 TimeZoneBoundaryTest::findDaylightBoundaryUsingDate(UDate d
, const char* startMode
, UDate expectedBoundary
)
79 if (dateToString(d
, str
).indexOf(startMode
) == - 1) {
80 logln(UnicodeString("Error: ") + startMode
+ " not present in " + str
);
83 UDate max
= min
+ SIX_MONTHS
;
84 while ((max
- min
) > INTERVAL
) {
85 UDate mid
= (min
+ max
) / 2;
86 UnicodeString
* s
= &dateToString(mid
, str
);
87 if (s
->indexOf(startMode
) != - 1) {
94 logln("Date Before: " + showDate(min
));
95 logln("Date After: " + showDate(max
));
96 UDate mindelta
= expectedBoundary
- min
;
97 UDate maxdelta
= max
- expectedBoundary
;
99 mindelta
<= INTERVAL
&&
101 maxdelta
<= INTERVAL
) logln(UnicodeString("PASS: Expected boundary at ") + expectedBoundary
);
102 else dataerrln(UnicodeString("FAIL: Expected boundary at ") + expectedBoundary
);
105 // -------------------------------------
108 TimeZoneBoundaryTest::findDaylightBoundaryUsingTimeZone(UDate d
, UBool startsInDST
, UDate expectedBoundary
)
110 TimeZone
*zone
= TimeZone::createDefault();
111 findDaylightBoundaryUsingTimeZone(d
, startsInDST
, expectedBoundary
, zone
);
115 // -------------------------------------
118 TimeZoneBoundaryTest::findDaylightBoundaryUsingTimeZone(UDate d
, UBool startsInDST
, UDate expectedBoundary
, TimeZone
* tz
)
120 UErrorCode status
= U_ZERO_ERROR
;
123 UDate max
= min
+ SIX_MONTHS
;
124 if (tz
->inDaylightTime(d
, status
) != startsInDST
) {
125 dataerrln("FAIL: " + tz
->getID(str
) + " inDaylightTime(" + dateToString(d
) + ") != " + (startsInDST
? "true" : "false"));
126 startsInDST
= !startsInDST
;
128 if (failure(status
, "TimeZone::inDaylightTime", TRUE
)) return;
129 if (tz
->inDaylightTime(max
, status
) == startsInDST
) {
130 dataerrln("FAIL: " + tz
->getID(str
) + " inDaylightTime(" + dateToString(max
) + ") != " + (startsInDST
? "false" : "true"));
133 if (failure(status
, "TimeZone::inDaylightTime")) return;
134 while ((max
- min
) > INTERVAL
) {
135 UDate mid
= (min
+ max
) / 2;
136 UBool isIn
= tz
->inDaylightTime(mid
, status
);
137 if (failure(status
, "TimeZone::inDaylightTime")) return;
138 if (isIn
== startsInDST
) {
145 logln(tz
->getID(str
) + " Before: " + showDate(min
));
146 logln(tz
->getID(str
) + " After: " + showDate(max
));
147 UDate mindelta
= expectedBoundary
- min
;
148 UDate maxdelta
= max
- expectedBoundary
;
150 mindelta
<= INTERVAL
&&
152 maxdelta
<= INTERVAL
) logln(UnicodeString("PASS: Expected boundary at ") + expectedBoundary
);
153 else errln(UnicodeString("FAIL: Expected boundary at ") + expectedBoundary
);
156 // -------------------------------------
159 TimeZoneBoundaryTest::showDate(int32_t l)
161 return showDate(new Date(l));
164 // -------------------------------------
167 TimeZoneBoundaryTest::showDate(UDate d
)
169 int32_t y
, m
, day
, h
, min
, sec
;
170 dateToFields(d
, y
, m
, day
, h
, min
, sec
);
171 return UnicodeString("") + y
+ "/" + showNN(m
+ 1) + "/" +
172 showNN(day
) + " " + showNN(h
) + ":" + showNN(min
) +
173 " \"" + dateToString(d
) + "\" = " + uprv_floor(d
+0.5);
176 // -------------------------------------
179 TimeZoneBoundaryTest::showNN(int32_t n
)
183 nStr
+= UnicodeString("0", "");
188 // -------------------------------------
191 TimeZoneBoundaryTest::verifyDST(UDate d
, TimeZone
* time_zone
, UBool expUseDaylightTime
, UBool expInDaylightTime
, UDate expZoneOffset
, UDate expDSTOffset
)
194 UErrorCode status
= U_ZERO_ERROR
;
195 logln("-- Verifying time " + dateToString(d
) + " in zone " + time_zone
->getID(str
));
196 if (time_zone
->inDaylightTime(d
, status
) == expInDaylightTime
)
197 logln(UnicodeString("PASS: inDaylightTime = ") + (time_zone
->inDaylightTime(d
, status
)?"true":"false"));
199 dataerrln(UnicodeString("FAIL: inDaylightTime = ") + (time_zone
->inDaylightTime(d
, status
)?"true":"false"));
200 if (failure(status
, "TimeZone::inDaylightTime", TRUE
))
202 if (time_zone
->useDaylightTime() == expUseDaylightTime
)
203 logln(UnicodeString("PASS: useDaylightTime = ") + (time_zone
->useDaylightTime()?"true":"false"));
205 dataerrln(UnicodeString("FAIL: useDaylightTime = ") + (time_zone
->useDaylightTime()?"true":"false"));
206 if (time_zone
->getRawOffset() == expZoneOffset
)
207 logln(UnicodeString("PASS: getRawOffset() = ") + (expZoneOffset
/ ONE_HOUR
));
209 dataerrln(UnicodeString("FAIL: getRawOffset() = ") + (time_zone
->getRawOffset() / ONE_HOUR
) + "; expected " + (expZoneOffset
/ ONE_HOUR
));
211 GregorianCalendar
*gc
= new GregorianCalendar(time_zone
->clone(), status
);
212 gc
->setTime(d
, status
);
213 if (failure(status
, "GregorianCalendar::setTime")) return;
214 int32_t offset
= time_zone
->getOffset((uint8_t)gc
->get(UCAL_ERA
, status
),
215 gc
->get(UCAL_YEAR
, status
), gc
->get(UCAL_MONTH
, status
),
216 gc
->get(UCAL_DATE
, status
), (uint8_t)gc
->get(UCAL_DAY_OF_WEEK
, status
),
217 ((gc
->get(UCAL_HOUR_OF_DAY
, status
) * 60 + gc
->get(UCAL_MINUTE
, status
)) * 60 + gc
->get(UCAL_SECOND
, status
)) * 1000 + gc
->get(UCAL_MILLISECOND
, status
),
219 if (failure(status
, "GregorianCalendar::get")) return;
220 if (offset
== expDSTOffset
) logln(UnicodeString("PASS: getOffset() = ") + (offset
/ ONE_HOUR
));
221 else dataerrln(UnicodeString("FAIL: getOffset() = ") + (offset
/ ONE_HOUR
) + "; expected " + (expDSTOffset
/ ONE_HOUR
));
225 // -------------------------------------
227 * Check that the given year/month/dom/hour maps to and from the
228 * given epochHours. This verifies the functioning of the
229 * calendar and time zone in conjunction with one another,
230 * including the calendar time->fields and fields->time and
231 * the time zone getOffset method.
233 * @param epochHours hours after Jan 1 1970 0:00 GMT.
235 void TimeZoneBoundaryTest::verifyMapping(Calendar
& cal
, int year
, int month
, int dom
, int hour
,
237 double H
= 3600000.0;
238 UErrorCode status
= U_ZERO_ERROR
;
240 cal
.set(year
, month
, dom
, hour
, 0, 0);
241 UDate e
= cal
.getTime(status
)/ H
;
242 UDate ed
= (epochHours
* H
);
243 if (e
== epochHours
) {
244 logln(UnicodeString("Ok: ") + year
+ "/" + (month
+1) + "/" + dom
+ " " + hour
+ ":00 => " +
245 e
+ " (" + ed
+ ")");
247 dataerrln(UnicodeString("FAIL: ") + year
+ "/" + (month
+1) + "/" + dom
+ " " + hour
+ ":00 => " +
248 e
+ " (" + (e
* H
) + ")" +
249 ", expected " + epochHours
+ " (" + ed
+ ")");
251 cal
.setTime(ed
, status
);
252 if (cal
.get(UCAL_YEAR
, status
) == year
&&
253 cal
.get(UCAL_MONTH
, status
) == month
&&
254 cal
.get(UCAL_DATE
, status
) == dom
&&
255 cal
.get(UCAL_MILLISECONDS_IN_DAY
, status
) == hour
* 3600000) {
256 logln(UnicodeString("Ok: ") + epochHours
+ " (" + ed
+ ") => " +
257 cal
.get(UCAL_YEAR
, status
) + "/" +
258 (cal
.get(UCAL_MONTH
, status
)+1) + "/" +
259 cal
.get(UCAL_DATE
, status
) + " " +
260 cal
.get(UCAL_MILLISECOND
, status
)/H
);
262 dataerrln(UnicodeString("FAIL: ") + epochHours
+ " (" + ed
+ ") => " +
263 cal
.get(UCAL_YEAR
, status
) + "/" +
264 (cal
.get(UCAL_MONTH
, status
)+1) + "/" +
265 cal
.get(UCAL_DATE
, status
) + " " +
266 cal
.get(UCAL_MILLISECOND
, status
)/H
+
267 ", expected " + year
+ "/" + (month
+1) + "/" + dom
+
273 * Test the behavior of SimpleTimeZone at the transition into and out of DST.
274 * Use a binary search to find boundaries.
277 TimeZoneBoundaryTest::TestBoundaries()
279 UErrorCode status
= U_ZERO_ERROR
;
280 TimeZone
* pst
= TimeZone::createTimeZone("PST");
281 Calendar
* tempcal
= Calendar::createInstance(pst
, status
);
282 if(U_SUCCESS(status
)){
283 verifyMapping(*tempcal
, 1997, Calendar::APRIL
, 3, 0, 238904.0);
284 verifyMapping(*tempcal
, 1997, Calendar::APRIL
, 4, 0, 238928.0);
285 verifyMapping(*tempcal
, 1997, Calendar::APRIL
, 5, 0, 238952.0);
286 verifyMapping(*tempcal
, 1997, Calendar::APRIL
, 5, 23, 238975.0);
287 verifyMapping(*tempcal
, 1997, Calendar::APRIL
, 6, 0, 238976.0);
288 verifyMapping(*tempcal
, 1997, Calendar::APRIL
, 6, 1, 238977.0);
289 verifyMapping(*tempcal
, 1997, Calendar::APRIL
, 6, 3, 238978.0);
291 dataerrln("Could not create calendar. Error: %s", u_errorName(status
));
293 TimeZone
* utc
= TimeZone::createTimeZone("UTC");
294 Calendar
* utccal
= Calendar::createInstance(utc
, status
);
295 if(U_SUCCESS(status
)){
296 verifyMapping(*utccal
, 1997, Calendar::APRIL
, 6, 0, 238968.0);
298 dataerrln("Could not create calendar. Error: %s", u_errorName(status
));
300 TimeZone
* save
= TimeZone::createDefault();
301 TimeZone::setDefault(*pst
);
303 if (tempcal
!= NULL
) {
304 // DST changeover for PST is 4/6/1997 at 2 hours past midnight
305 // at 238978.0 epoch hours.
307 tempcal
->set(1997, Calendar::APRIL
, 6);
308 UDate d
= tempcal
->getTime(status
);
310 // i is minutes past midnight standard time
311 for (int i
=-120; i
<=180; i
+=60)
313 UBool inDST
= (i
>= 120);
314 tempcal
->setTime(d
+ i
*60*1000, status
);
315 verifyDST(tempcal
->getTime(status
),pst
, TRUE
, inDST
, -8*ONE_HOUR
,inDST
? -7*ONE_HOUR
: -8*ONE_HOUR
);
318 TimeZone::setDefault(*save
);
325 logln("--- Test a ---");
326 UDate d
= date(97, UCAL_APRIL
, 6);
327 TimeZone
*z
= TimeZone::createTimeZone("PST");
328 for (int32_t i
= 60; i
<= 180; i
+= 15) {
329 UBool inDST
= (i
>= 120);
330 UDate e
= d
+ i
* 60 * 1000;
331 verifyDST(e
, z
, TRUE
, inDST
, - 8 * ONE_HOUR
, inDST
? - 7 * ONE_HOUR
: - 8 * ONE_HOUR
);
338 logln("--- Test b ---");
340 TimeZone::setDefault(*(tz
= TimeZone::createTimeZone("PST")));
342 logln("========================================");
343 findDaylightBoundaryUsingDate(date(97, 0, 1), "PST", PST_1997_BEG
);
344 logln("========================================");
345 findDaylightBoundaryUsingDate(date(97, 6, 1), "PDT", PST_1997_END
);
350 logln("--- Test c ---");
351 logln("========================================");
352 TimeZone
* z
= TimeZone::createTimeZone("Australia/Adelaide");
353 findDaylightBoundaryUsingTimeZone(date(97, 0, 1), TRUE
, 859653000000.0, z
);
354 logln("========================================");
355 findDaylightBoundaryUsingTimeZone(date(97, 6, 1), FALSE
, 877797000000.0, z
);
361 logln("--- Test d ---");
362 logln("========================================");
363 findDaylightBoundaryUsingTimeZone(date(97, 0, 1), FALSE
, PST_1997_BEG
);
364 logln("========================================");
365 findDaylightBoundaryUsingTimeZone(date(97, 6, 1), TRUE
, PST_1997_END
);
370 logln("--- Test e ---");
371 TimeZone
*z
= TimeZone::createDefault();
372 logln(UnicodeString("") + z
->getOffset(1, 97, 3, 4, 6, 0) + " " + date(97, 3, 4));
373 logln(UnicodeString("") + z
->getOffset(1, 97, 3, 5, 7, 0) + " " + date(97, 3, 5));
374 logln(UnicodeString("") + z
->getOffset(1, 97, 3, 6, 1, 0) + " " + date(97, 3, 6));
375 logln(UnicodeString("") + z
->getOffset(1, 97, 3, 7, 2, 0) + " " + date(97, 3, 7));
381 // -------------------------------------
384 TimeZoneBoundaryTest::testUsingBinarySearch(SimpleTimeZone
* tz
, UDate d
, UDate expectedBoundary
)
386 UErrorCode status
= U_ZERO_ERROR
;
388 UDate max
= min
+ SIX_MONTHS
;
389 UBool startsInDST
= tz
->inDaylightTime(d
, status
);
390 if (failure(status
, "SimpleTimeZone::inDaylightTime", TRUE
)) return;
391 if (tz
->inDaylightTime(max
, status
) == startsInDST
) {
392 errln("Error: inDaylightTime(" + dateToString(max
) + ") != " + ((!startsInDST
)?"true":"false"));
394 if (failure(status
, "SimpleTimeZone::inDaylightTime")) return;
395 while ((max
- min
) > INTERVAL
) {
396 UDate mid
= (min
+ max
) / 2;
397 if (tz
->inDaylightTime(mid
, status
) == startsInDST
) {
403 if (failure(status
, "SimpleTimeZone::inDaylightTime")) return;
405 logln("Binary Search Before: " + showDate(min
));
406 logln("Binary Search After: " + showDate(max
));
407 UDate mindelta
= expectedBoundary
- min
;
408 UDate maxdelta
= max
- expectedBoundary
;
410 mindelta
<= INTERVAL
&&
412 maxdelta
<= INTERVAL
) logln(UnicodeString("PASS: Expected boundary at ") + expectedBoundary
);
413 else errln(UnicodeString("FAIL: Expected boundary at ") + expectedBoundary
);
416 // -------------------------------------
419 * Test the handling of the "new" rules; that is, rules other than nth Day of week.
422 TimeZoneBoundaryTest::TestNewRules()
426 UErrorCode status
= U_ZERO_ERROR
;
428 logln("-----------------------------------------------------------------");
429 logln("Aug 2ndTues .. Mar 15");
430 tz
= new SimpleTimeZone(- 8 * (int32_t)ONE_HOUR
, "Test_1", UCAL_AUGUST
, 2, UCAL_TUESDAY
, 2 * (int32_t)ONE_HOUR
, UCAL_MARCH
, 15, 0, 2 * (int32_t)ONE_HOUR
, status
);
431 logln("========================================");
432 testUsingBinarySearch(tz
, date(97, 0, 1), 858416400000.0);
433 logln("========================================");
434 testUsingBinarySearch(tz
, date(97, 6, 1), 871380000000.0);
436 logln("-----------------------------------------------------------------");
437 logln("Apr Wed>=14 .. Sep Sun<=20");
438 tz
= new SimpleTimeZone(- 8 * (int32_t)ONE_HOUR
, "Test_2", UCAL_APRIL
, 14, - UCAL_WEDNESDAY
, 2 *(int32_t)ONE_HOUR
, UCAL_SEPTEMBER
, - 20, - UCAL_SUNDAY
, 2 * (int32_t)ONE_HOUR
, status
);
439 logln("========================================");
440 testUsingBinarySearch(tz
, date(97, 0, 1), 861184800000.0);
441 logln("========================================");
442 testUsingBinarySearch(tz
, date(97, 6, 1), 874227600000.0);
448 // -------------------------------------
451 TimeZoneBoundaryTest::findBoundariesStepwise(int32_t year
, UDate interval
, TimeZone
* z
, int32_t expectedChanges
)
453 UErrorCode status
= U_ZERO_ERROR
;
455 UDate d
= date(year
- 1900, UCAL_JANUARY
, 1);
457 UDate limit
= time
+ ONE_YEAR
+ ONE_DAY
;
458 UBool lastState
= z
->inDaylightTime(d
, status
);
459 if (failure(status
, "TimeZone::inDaylightTime", TRUE
)) return;
461 logln(UnicodeString("-- Zone ") + z
->getID(str
) + " starts in " + year
+ " with DST = " + (lastState
?"true":"false"));
462 logln(UnicodeString("useDaylightTime = ") + (z
->useDaylightTime()?"true":"false"));
463 while (time
< limit
) {
465 UBool state
= z
->inDaylightTime(d
, status
);
466 if (failure(status
, "TimeZone::inDaylightTime")) return;
467 if (state
!= lastState
) {
468 logln(UnicodeString(state
? "Entry ": "Exit ") + "at " + d
);
469 lastState
= state
;++changes
;
475 !z
->useDaylightTime()) logln("No DST");
476 else errln("FAIL: DST all year, or no DST with true useDaylightTime");
478 else if (changes
!= 2) {
479 errln(UnicodeString("FAIL: ") + changes
+ " changes seen; should see 0 or 2");
481 else if (!z
->useDaylightTime()) {
482 errln("FAIL: useDaylightTime false but 2 changes seen");
484 if (changes
!= expectedChanges
) {
485 dataerrln(UnicodeString("FAIL: ") + changes
+ " changes seen; expected " + expectedChanges
);
489 // -------------------------------------
492 * This test is problematic. It makes assumptions about the behavior
493 * of specific zones. Since ICU's zone table is based on the Olson
494 * zones (the UNIX zones), and those change from time to time, this
495 * test can fail after a zone table update. If that happens, the
496 * selected zones need to be updated to have the behavior
497 * expected. That is, they should have DST, not have DST, and have DST
498 * -- other than that this test isn't picky. 12/3/99 aliu
500 * Test the behavior of SimpleTimeZone at the transition into and out of DST.
501 * Use a stepwise march to find boundaries.
504 TimeZoneBoundaryTest::TestStepwise()
506 TimeZone
*zone
= TimeZone::createTimeZone("America/New_York");
507 findBoundariesStepwise(1997, ONE_DAY
, zone
, 2);
509 zone
= TimeZone::createTimeZone("UTC"); // updated 12/3/99 aliu
510 findBoundariesStepwise(1997, ONE_DAY
, zone
, 0);
512 zone
= TimeZone::createTimeZone("Australia/Adelaide");
513 findBoundariesStepwise(1997, ONE_DAY
, zone
, 2);
517 #endif /* #if !UCONFIG_NO_FORMATTING */