]>
git.saurik.com Git - apple/icu.git/blob - icuSources/test/intltest/tzbdtest.cpp
1 /***********************************************************************
3 * Copyright (c) 1997-2010, International Business Machines Corporation
4 * and others. All Rights Reserved.
5 ***********************************************************************/
7 #include "unicode/utypes.h"
9 #if !UCONFIG_NO_FORMATTING
12 #include "unicode/timezone.h"
13 #include "unicode/simpletz.h"
14 #include "unicode/gregocal.h"
17 void TimeZoneBoundaryTest::runIndexedTest( int32_t index
, UBool exec
, const char* &name
, char* /*par*/ )
19 if (exec
) logln("TestSuite TestTimeZoneBoundary");
22 name
= "TestBoundaries";
24 logln("TestBoundaries---"); logln("");
29 name
= "TestNewRules";
31 logln("TestNewRules---"); logln("");
36 name
= "TestStepwise";
38 logln("TestStepwise---"); logln("");
42 default: name
= ""; break;
46 // *****************************************************************************
47 // class TimeZoneBoundaryTest
48 // *****************************************************************************
50 TimeZoneBoundaryTest::TimeZoneBoundaryTest()
53 ONE_MINUTE(60 * ONE_SECOND
),
54 ONE_HOUR(60 * ONE_MINUTE
),
55 ONE_DAY(24 * ONE_HOUR
),
56 ONE_YEAR(uprv_floor(365.25 * ONE_DAY
)),
57 SIX_MONTHS(ONE_YEAR
/ 2)
61 const int32_t TimeZoneBoundaryTest::MONTH_LENGTH
[] = {
62 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
65 const UDate
TimeZoneBoundaryTest::PST_1997_BEG
= 860320800000.0;
67 const UDate
TimeZoneBoundaryTest::PST_1997_END
= 877856400000.0;
69 const UDate
TimeZoneBoundaryTest::INTERVAL
= 10;
71 // -------------------------------------
74 TimeZoneBoundaryTest::findDaylightBoundaryUsingDate(UDate d
, const char* startMode
, UDate expectedBoundary
)
77 if (dateToString(d
, str
).indexOf(startMode
) == - 1) {
78 logln(UnicodeString("Error: ") + startMode
+ " not present in " + str
);
81 UDate max
= min
+ SIX_MONTHS
;
82 while ((max
- min
) > INTERVAL
) {
83 UDate mid
= (min
+ max
) / 2;
84 UnicodeString
* s
= &dateToString(mid
, str
);
85 if (s
->indexOf(startMode
) != - 1) {
92 logln("Date Before: " + showDate(min
));
93 logln("Date After: " + showDate(max
));
94 UDate mindelta
= expectedBoundary
- min
;
95 UDate maxdelta
= max
- expectedBoundary
;
97 mindelta
<= INTERVAL
&&
99 maxdelta
<= INTERVAL
) logln(UnicodeString("PASS: Expected boundary at ") + expectedBoundary
);
100 else dataerrln(UnicodeString("FAIL: Expected boundary at ") + expectedBoundary
);
103 // -------------------------------------
106 TimeZoneBoundaryTest::findDaylightBoundaryUsingTimeZone(UDate d
, UBool startsInDST
, UDate expectedBoundary
)
108 TimeZone
*zone
= TimeZone::createDefault();
109 findDaylightBoundaryUsingTimeZone(d
, startsInDST
, expectedBoundary
, zone
);
113 // -------------------------------------
116 TimeZoneBoundaryTest::findDaylightBoundaryUsingTimeZone(UDate d
, UBool startsInDST
, UDate expectedBoundary
, TimeZone
* tz
)
118 UErrorCode status
= U_ZERO_ERROR
;
121 UDate max
= min
+ SIX_MONTHS
;
122 if (tz
->inDaylightTime(d
, status
) != startsInDST
) {
123 dataerrln("FAIL: " + tz
->getID(str
) + " inDaylightTime(" + dateToString(d
) + ") != " + (startsInDST
? "true" : "false"));
124 startsInDST
= !startsInDST
;
126 if (failure(status
, "TimeZone::inDaylightTime", TRUE
)) return;
127 if (tz
->inDaylightTime(max
, status
) == startsInDST
) {
128 dataerrln("FAIL: " + tz
->getID(str
) + " inDaylightTime(" + dateToString(max
) + ") != " + (startsInDST
? "false" : "true"));
131 if (failure(status
, "TimeZone::inDaylightTime")) return;
132 while ((max
- min
) > INTERVAL
) {
133 UDate mid
= (min
+ max
) / 2;
134 UBool isIn
= tz
->inDaylightTime(mid
, status
);
135 if (failure(status
, "TimeZone::inDaylightTime")) return;
136 if (isIn
== startsInDST
) {
143 logln(tz
->getID(str
) + " Before: " + showDate(min
));
144 logln(tz
->getID(str
) + " After: " + showDate(max
));
145 UDate mindelta
= expectedBoundary
- min
;
146 UDate maxdelta
= max
- expectedBoundary
;
148 mindelta
<= INTERVAL
&&
150 maxdelta
<= INTERVAL
) logln(UnicodeString("PASS: Expected boundary at ") + expectedBoundary
);
151 else errln(UnicodeString("FAIL: Expected boundary at ") + expectedBoundary
);
154 // -------------------------------------
157 TimeZoneBoundaryTest::showDate(int32_t l)
159 return showDate(new Date(l));
162 // -------------------------------------
165 TimeZoneBoundaryTest::showDate(UDate d
)
167 int32_t y
, m
, day
, h
, min
, sec
;
168 dateToFields(d
, y
, m
, day
, h
, min
, sec
);
169 return UnicodeString("") + y
+ "/" + showNN(m
+ 1) + "/" +
170 showNN(day
) + " " + showNN(h
) + ":" + showNN(min
) +
171 " \"" + dateToString(d
) + "\" = " + uprv_floor(d
+0.5);
174 // -------------------------------------
177 TimeZoneBoundaryTest::showNN(int32_t n
)
181 nStr
+= UnicodeString("0", "");
186 // -------------------------------------
189 TimeZoneBoundaryTest::verifyDST(UDate d
, TimeZone
* time_zone
, UBool expUseDaylightTime
, UBool expInDaylightTime
, UDate expZoneOffset
, UDate expDSTOffset
)
192 UErrorCode status
= U_ZERO_ERROR
;
193 logln("-- Verifying time " + dateToString(d
) + " in zone " + time_zone
->getID(str
));
194 if (time_zone
->inDaylightTime(d
, status
) == expInDaylightTime
)
195 logln(UnicodeString("PASS: inDaylightTime = ") + (time_zone
->inDaylightTime(d
, status
)?"true":"false"));
197 dataerrln(UnicodeString("FAIL: inDaylightTime = ") + (time_zone
->inDaylightTime(d
, status
)?"true":"false"));
198 if (failure(status
, "TimeZone::inDaylightTime", TRUE
))
200 if (time_zone
->useDaylightTime() == expUseDaylightTime
)
201 logln(UnicodeString("PASS: useDaylightTime = ") + (time_zone
->useDaylightTime()?"true":"false"));
203 dataerrln(UnicodeString("FAIL: useDaylightTime = ") + (time_zone
->useDaylightTime()?"true":"false"));
204 if (time_zone
->getRawOffset() == expZoneOffset
)
205 logln(UnicodeString("PASS: getRawOffset() = ") + (expZoneOffset
/ ONE_HOUR
));
207 dataerrln(UnicodeString("FAIL: getRawOffset() = ") + (time_zone
->getRawOffset() / ONE_HOUR
) + "; expected " + (expZoneOffset
/ ONE_HOUR
));
209 GregorianCalendar
*gc
= new GregorianCalendar(time_zone
->clone(), status
);
210 gc
->setTime(d
, status
);
211 if (failure(status
, "GregorianCalendar::setTime")) return;
212 int32_t offset
= time_zone
->getOffset((uint8_t)gc
->get(UCAL_ERA
, status
),
213 gc
->get(UCAL_YEAR
, status
), gc
->get(UCAL_MONTH
, status
),
214 gc
->get(UCAL_DATE
, status
), (uint8_t)gc
->get(UCAL_DAY_OF_WEEK
, status
),
215 ((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
),
217 if (failure(status
, "GregorianCalendar::get")) return;
218 if (offset
== expDSTOffset
) logln(UnicodeString("PASS: getOffset() = ") + (offset
/ ONE_HOUR
));
219 else dataerrln(UnicodeString("FAIL: getOffset() = ") + (offset
/ ONE_HOUR
) + "; expected " + (expDSTOffset
/ ONE_HOUR
));
223 // -------------------------------------
225 * Check that the given year/month/dom/hour maps to and from the
226 * given epochHours. This verifies the functioning of the
227 * calendar and time zone in conjunction with one another,
228 * including the calendar time->fields and fields->time and
229 * the time zone getOffset method.
231 * @param epochHours hours after Jan 1 1970 0:00 GMT.
233 void TimeZoneBoundaryTest::verifyMapping(Calendar
& cal
, int year
, int month
, int dom
, int hour
,
235 double H
= 3600000.0;
236 UErrorCode status
= U_ZERO_ERROR
;
238 cal
.set(year
, month
, dom
, hour
, 0, 0);
239 UDate e
= cal
.getTime(status
)/ H
;
240 UDate ed
= (epochHours
* H
);
241 if (e
== epochHours
) {
242 logln(UnicodeString("Ok: ") + year
+ "/" + (month
+1) + "/" + dom
+ " " + hour
+ ":00 => " +
243 e
+ " (" + ed
+ ")");
245 dataerrln(UnicodeString("FAIL: ") + year
+ "/" + (month
+1) + "/" + dom
+ " " + hour
+ ":00 => " +
246 e
+ " (" + (e
* H
) + ")" +
247 ", expected " + epochHours
+ " (" + ed
+ ")");
249 cal
.setTime(ed
, status
);
250 if (cal
.get(UCAL_YEAR
, status
) == year
&&
251 cal
.get(UCAL_MONTH
, status
) == month
&&
252 cal
.get(UCAL_DATE
, status
) == dom
&&
253 cal
.get(UCAL_MILLISECONDS_IN_DAY
, status
) == hour
* 3600000) {
254 logln(UnicodeString("Ok: ") + epochHours
+ " (" + ed
+ ") => " +
255 cal
.get(UCAL_YEAR
, status
) + "/" +
256 (cal
.get(UCAL_MONTH
, status
)+1) + "/" +
257 cal
.get(UCAL_DATE
, status
) + " " +
258 cal
.get(UCAL_MILLISECOND
, status
)/H
);
260 dataerrln(UnicodeString("FAIL: ") + epochHours
+ " (" + ed
+ ") => " +
261 cal
.get(UCAL_YEAR
, status
) + "/" +
262 (cal
.get(UCAL_MONTH
, status
)+1) + "/" +
263 cal
.get(UCAL_DATE
, status
) + " " +
264 cal
.get(UCAL_MILLISECOND
, status
)/H
+
265 ", expected " + year
+ "/" + (month
+1) + "/" + dom
+
271 * Test the behavior of SimpleTimeZone at the transition into and out of DST.
272 * Use a binary search to find boundaries.
275 TimeZoneBoundaryTest::TestBoundaries()
277 UErrorCode status
= U_ZERO_ERROR
;
278 TimeZone
* pst
= TimeZone::createTimeZone("PST");
279 Calendar
* tempcal
= Calendar::createInstance(pst
, status
);
280 if(U_SUCCESS(status
)){
281 verifyMapping(*tempcal
, 1997, Calendar::APRIL
, 3, 0, 238904.0);
282 verifyMapping(*tempcal
, 1997, Calendar::APRIL
, 4, 0, 238928.0);
283 verifyMapping(*tempcal
, 1997, Calendar::APRIL
, 5, 0, 238952.0);
284 verifyMapping(*tempcal
, 1997, Calendar::APRIL
, 5, 23, 238975.0);
285 verifyMapping(*tempcal
, 1997, Calendar::APRIL
, 6, 0, 238976.0);
286 verifyMapping(*tempcal
, 1997, Calendar::APRIL
, 6, 1, 238977.0);
287 verifyMapping(*tempcal
, 1997, Calendar::APRIL
, 6, 3, 238978.0);
289 dataerrln("Could not create calendar. Error: %s", u_errorName(status
));
291 TimeZone
* utc
= TimeZone::createTimeZone("UTC");
292 Calendar
* utccal
= Calendar::createInstance(utc
, status
);
293 if(U_SUCCESS(status
)){
294 verifyMapping(*utccal
, 1997, Calendar::APRIL
, 6, 0, 238968.0);
296 dataerrln("Could not create calendar. Error: %s", u_errorName(status
));
298 TimeZone
* save
= TimeZone::createDefault();
299 TimeZone::setDefault(*pst
);
301 if (tempcal
!= NULL
) {
302 // DST changeover for PST is 4/6/1997 at 2 hours past midnight
303 // at 238978.0 epoch hours.
305 tempcal
->set(1997, Calendar::APRIL
, 6);
306 UDate d
= tempcal
->getTime(status
);
308 // i is minutes past midnight standard time
309 for (int i
=-120; i
<=180; i
+=60)
311 UBool inDST
= (i
>= 120);
312 tempcal
->setTime(d
+ i
*60*1000, status
);
313 verifyDST(tempcal
->getTime(status
),pst
, TRUE
, inDST
, -8*ONE_HOUR
,inDST
? -7*ONE_HOUR
: -8*ONE_HOUR
);
316 TimeZone::setDefault(*save
);
323 logln("--- Test a ---");
324 UDate d
= date(97, UCAL_APRIL
, 6);
325 TimeZone
*z
= TimeZone::createTimeZone("PST");
326 for (int32_t i
= 60; i
<= 180; i
+= 15) {
327 UBool inDST
= (i
>= 120);
328 UDate e
= d
+ i
* 60 * 1000;
329 verifyDST(e
, z
, TRUE
, inDST
, - 8 * ONE_HOUR
, inDST
? - 7 * ONE_HOUR
: - 8 * ONE_HOUR
);
336 logln("--- Test b ---");
338 TimeZone::setDefault(*(tz
= TimeZone::createTimeZone("PST")));
340 logln("========================================");
341 findDaylightBoundaryUsingDate(date(97, 0, 1), "PST", PST_1997_BEG
);
342 logln("========================================");
343 findDaylightBoundaryUsingDate(date(97, 6, 1), "PDT", PST_1997_END
);
348 logln("--- Test c ---");
349 logln("========================================");
350 TimeZone
* z
= TimeZone::createTimeZone("Australia/Adelaide");
351 findDaylightBoundaryUsingTimeZone(date(97, 0, 1), TRUE
, 859653000000.0, z
);
352 logln("========================================");
353 findDaylightBoundaryUsingTimeZone(date(97, 6, 1), FALSE
, 877797000000.0, z
);
359 logln("--- Test d ---");
360 logln("========================================");
361 findDaylightBoundaryUsingTimeZone(date(97, 0, 1), FALSE
, PST_1997_BEG
);
362 logln("========================================");
363 findDaylightBoundaryUsingTimeZone(date(97, 6, 1), TRUE
, PST_1997_END
);
368 logln("--- Test e ---");
369 TimeZone
*z
= TimeZone::createDefault();
370 logln(UnicodeString("") + z
->getOffset(1, 97, 3, 4, 6, 0) + " " + date(97, 3, 4));
371 logln(UnicodeString("") + z
->getOffset(1, 97, 3, 5, 7, 0) + " " + date(97, 3, 5));
372 logln(UnicodeString("") + z
->getOffset(1, 97, 3, 6, 1, 0) + " " + date(97, 3, 6));
373 logln(UnicodeString("") + z
->getOffset(1, 97, 3, 7, 2, 0) + " " + date(97, 3, 7));
379 // -------------------------------------
382 TimeZoneBoundaryTest::testUsingBinarySearch(SimpleTimeZone
* tz
, UDate d
, UDate expectedBoundary
)
384 UErrorCode status
= U_ZERO_ERROR
;
386 UDate max
= min
+ SIX_MONTHS
;
387 UBool startsInDST
= tz
->inDaylightTime(d
, status
);
388 if (failure(status
, "SimpleTimeZone::inDaylightTime", TRUE
)) return;
389 if (tz
->inDaylightTime(max
, status
) == startsInDST
) {
390 errln("Error: inDaylightTime(" + dateToString(max
) + ") != " + ((!startsInDST
)?"true":"false"));
392 if (failure(status
, "SimpleTimeZone::inDaylightTime")) return;
393 while ((max
- min
) > INTERVAL
) {
394 UDate mid
= (min
+ max
) / 2;
395 if (tz
->inDaylightTime(mid
, status
) == startsInDST
) {
401 if (failure(status
, "SimpleTimeZone::inDaylightTime")) return;
403 logln("Binary Search Before: " + showDate(min
));
404 logln("Binary Search After: " + showDate(max
));
405 UDate mindelta
= expectedBoundary
- min
;
406 UDate maxdelta
= max
- expectedBoundary
;
408 mindelta
<= INTERVAL
&&
410 maxdelta
<= INTERVAL
) logln(UnicodeString("PASS: Expected boundary at ") + expectedBoundary
);
411 else errln(UnicodeString("FAIL: Expected boundary at ") + expectedBoundary
);
414 // -------------------------------------
417 * Test the handling of the "new" rules; that is, rules other than nth Day of week.
420 TimeZoneBoundaryTest::TestNewRules()
424 UErrorCode status
= U_ZERO_ERROR
;
426 logln("-----------------------------------------------------------------");
427 logln("Aug 2ndTues .. Mar 15");
428 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
);
429 logln("========================================");
430 testUsingBinarySearch(tz
, date(97, 0, 1), 858416400000.0);
431 logln("========================================");
432 testUsingBinarySearch(tz
, date(97, 6, 1), 871380000000.0);
434 logln("-----------------------------------------------------------------");
435 logln("Apr Wed>=14 .. Sep Sun<=20");
436 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
);
437 logln("========================================");
438 testUsingBinarySearch(tz
, date(97, 0, 1), 861184800000.0);
439 logln("========================================");
440 testUsingBinarySearch(tz
, date(97, 6, 1), 874227600000.0);
446 // -------------------------------------
449 TimeZoneBoundaryTest::findBoundariesStepwise(int32_t year
, UDate interval
, TimeZone
* z
, int32_t expectedChanges
)
451 UErrorCode status
= U_ZERO_ERROR
;
453 UDate d
= date(year
- 1900, UCAL_JANUARY
, 1);
455 UDate limit
= time
+ ONE_YEAR
+ ONE_DAY
;
456 UBool lastState
= z
->inDaylightTime(d
, status
);
457 if (failure(status
, "TimeZone::inDaylightTime", TRUE
)) return;
459 logln(UnicodeString("-- Zone ") + z
->getID(str
) + " starts in " + year
+ " with DST = " + (lastState
?"true":"false"));
460 logln(UnicodeString("useDaylightTime = ") + (z
->useDaylightTime()?"true":"false"));
461 while (time
< limit
) {
463 UBool state
= z
->inDaylightTime(d
, status
);
464 if (failure(status
, "TimeZone::inDaylightTime")) return;
465 if (state
!= lastState
) {
466 logln(UnicodeString(state
? "Entry ": "Exit ") + "at " + d
);
467 lastState
= state
;++changes
;
473 !z
->useDaylightTime()) logln("No DST");
474 else errln("FAIL: DST all year, or no DST with true useDaylightTime");
476 else if (changes
!= 2) {
477 errln(UnicodeString("FAIL: ") + changes
+ " changes seen; should see 0 or 2");
479 else if (!z
->useDaylightTime()) {
480 errln("FAIL: useDaylightTime false but 2 changes seen");
482 if (changes
!= expectedChanges
) {
483 dataerrln(UnicodeString("FAIL: ") + changes
+ " changes seen; expected " + expectedChanges
);
487 // -------------------------------------
490 * This test is problematic. It makes assumptions about the behavior
491 * of specific zones. Since ICU's zone table is based on the Olson
492 * zones (the UNIX zones), and those change from time to time, this
493 * test can fail after a zone table update. If that happens, the
494 * selected zones need to be updated to have the behavior
495 * expected. That is, they should have DST, not have DST, and have DST
496 * -- other than that this test isn't picky. 12/3/99 aliu
498 * Test the behavior of SimpleTimeZone at the transition into and out of DST.
499 * Use a stepwise march to find boundaries.
502 TimeZoneBoundaryTest::TestStepwise()
504 TimeZone
*zone
= TimeZone::createTimeZone("America/New_York");
505 findBoundariesStepwise(1997, ONE_DAY
, zone
, 2);
507 zone
= TimeZone::createTimeZone("UTC"); // updated 12/3/99 aliu
508 findBoundariesStepwise(1997, ONE_DAY
, zone
, 0);
510 zone
= TimeZone::createTimeZone("Australia/Adelaide");
511 findBoundariesStepwise(1997, ONE_DAY
, zone
, 2);
515 #endif /* #if !UCONFIG_NO_FORMATTING */