]> git.saurik.com Git - apple/icu.git/blob - icuSources/test/intltest/tzbdtest.cpp
ICU-6.2.6.tar.gz
[apple/icu.git] / icuSources / test / intltest / tzbdtest.cpp
1 /***********************************************************************
2 * COPYRIGHT:
3 * Copyright (c) 1997-2004, International Business Machines Corporation
4 * and others. All Rights Reserved.
5 ***********************************************************************/
6
7 #include "unicode/utypes.h"
8
9 #if !UCONFIG_NO_FORMATTING
10
11 #include "tzbdtest.h"
12 #include "unicode/timezone.h"
13 #include "unicode/simpletz.h"
14 #include "unicode/gregocal.h"
15 #include "putilimp.h"
16
17 void TimeZoneBoundaryTest::runIndexedTest( int32_t index, UBool exec, const char* &name, char* /*par*/ )
18 {
19 if (exec) logln("TestSuite TestTimeZoneBoundary");
20 switch (index) {
21 case 0:
22 name = "TestBoundaries";
23 if (exec) {
24 logln("TestBoundaries---"); logln("");
25 TestBoundaries();
26 }
27 break;
28 case 1:
29 name = "TestNewRules";
30 if (exec) {
31 logln("TestNewRules---"); logln("");
32 TestNewRules();
33 }
34 break;
35 case 2:
36 name = "TestStepwise";
37 if (exec) {
38 logln("TestStepwise---"); logln("");
39 TestStepwise();
40 }
41 break;
42 default: name = ""; break;
43 }
44 }
45
46 // *****************************************************************************
47 // class TimeZoneBoundaryTest
48 // *****************************************************************************
49
50 TimeZoneBoundaryTest::TimeZoneBoundaryTest()
51 :
52 ONE_SECOND(1000),
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)
58 {
59 }
60
61 const int32_t TimeZoneBoundaryTest::MONTH_LENGTH[] = {
62 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
63 };
64
65 const UDate TimeZoneBoundaryTest::PST_1997_BEG = 860320800000.0;
66
67 const UDate TimeZoneBoundaryTest::PST_1997_END = 877856400000.0;
68
69 const UDate TimeZoneBoundaryTest::INTERVAL = 10;
70
71 // -------------------------------------
72
73 void
74 TimeZoneBoundaryTest::findDaylightBoundaryUsingDate(UDate d, const char* startMode, UDate expectedBoundary)
75 {
76 UnicodeString str;
77 if (dateToString(d, str).indexOf(startMode) == - 1) {
78 logln(UnicodeString("Error: ") + startMode + " not present in " + str);
79 }
80 UDate min = d;
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) {
86 min = mid;
87 }
88 else {
89 max = mid;
90 }
91 }
92 logln("Date Before: " + showDate(min));
93 logln("Date After: " + showDate(max));
94 UDate mindelta = expectedBoundary - min;
95 UDate maxdelta = max - expectedBoundary;
96 if (mindelta >= 0 &&
97 mindelta <= INTERVAL &&
98 maxdelta >= 0 &&
99 maxdelta <= INTERVAL) logln(UnicodeString("PASS: Expected boundary at ") + expectedBoundary);
100 else errln(UnicodeString("FAIL: Expected boundary at ") + expectedBoundary);
101 }
102
103 // -------------------------------------
104
105 void
106 TimeZoneBoundaryTest::findDaylightBoundaryUsingTimeZone(UDate d, UBool startsInDST, UDate expectedBoundary)
107 {
108 TimeZone *zone = TimeZone::createDefault();
109 findDaylightBoundaryUsingTimeZone(d, startsInDST, expectedBoundary, zone);
110 delete zone;
111 }
112
113 // -------------------------------------
114
115 void
116 TimeZoneBoundaryTest::findDaylightBoundaryUsingTimeZone(UDate d, UBool startsInDST, UDate expectedBoundary, TimeZone* tz)
117 {
118 UErrorCode status = U_ZERO_ERROR;
119 UnicodeString str;
120 UDate min = d;
121 UDate max = min + SIX_MONTHS;
122 if (tz->inDaylightTime(d, status) != startsInDST) {
123 errln("FAIL: " + tz->getID(str) + " inDaylightTime(" + dateToString(d) + ") != " + (startsInDST ? "true" : "false"));
124 startsInDST = !startsInDST;
125 }
126 if (failure(status, "TimeZone::inDaylightTime")) return;
127 if (tz->inDaylightTime(max, status) == startsInDST) {
128 errln("FAIL: " + tz->getID(str) + " inDaylightTime(" + dateToString(max) + ") != " + (startsInDST ? "false" : "true"));
129 return;
130 }
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) {
137 min = mid;
138 }
139 else {
140 max = mid;
141 }
142 }
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;
147 if (mindelta >= 0 &&
148 mindelta <= INTERVAL &&
149 maxdelta >= 0 &&
150 maxdelta <= INTERVAL) logln(UnicodeString("PASS: Expected boundary at ") + expectedBoundary);
151 else errln(UnicodeString("FAIL: Expected boundary at ") + expectedBoundary);
152 }
153
154 // -------------------------------------
155 /*
156 UnicodeString*
157 TimeZoneBoundaryTest::showDate(int32_t l)
158 {
159 return showDate(new Date(l));
160 }
161 */
162 // -------------------------------------
163
164 UnicodeString
165 TimeZoneBoundaryTest::showDate(UDate d)
166 {
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);
172 }
173
174 // -------------------------------------
175
176 UnicodeString
177 TimeZoneBoundaryTest::showNN(int32_t n)
178 {
179 UnicodeString nStr;
180 if (n < 10) {
181 nStr += UnicodeString("0", "");
182 }
183 return nStr + n;
184 }
185
186 // -------------------------------------
187
188 void
189 TimeZoneBoundaryTest::verifyDST(UDate d, TimeZone* time_zone, UBool expUseDaylightTime, UBool expInDaylightTime, UDate expZoneOffset, UDate expDSTOffset)
190 {
191 UnicodeString str;
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"));
196 else errln(UnicodeString("FAIL: inDaylightTime = ") + (time_zone->inDaylightTime(d, status)?"true":"false"));
197 if (failure(status, "TimeZone::inDaylightTime")) return;
198 if (time_zone->useDaylightTime() == expUseDaylightTime)
199 logln(UnicodeString("PASS: useDaylightTime = ") + (time_zone->useDaylightTime()?"true":"false"));
200 else errln(UnicodeString("FAIL: useDaylightTime = ") + (time_zone->useDaylightTime()?"true":"false"));
201 if (time_zone->getRawOffset() == expZoneOffset) logln(UnicodeString("PASS: getRawOffset() = ") + (expZoneOffset / ONE_HOUR));
202 else errln(UnicodeString("FAIL: getRawOffset() = ") + (time_zone->getRawOffset() / ONE_HOUR) + "; expected " + (expZoneOffset / ONE_HOUR));
203 GregorianCalendar *gc = new GregorianCalendar(time_zone->clone(), status);
204 gc->setTime(d, status);
205 if (failure(status, "GregorianCalendar::setTime")) return;
206 int32_t offset = time_zone->getOffset((uint8_t)gc->get(UCAL_ERA, status),
207 gc->get(UCAL_YEAR, status), gc->get(UCAL_MONTH, status),
208 gc->get(UCAL_DATE, status), (uint8_t)gc->get(UCAL_DAY_OF_WEEK, status),
209 ((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),
210 status);
211 if (failure(status, "GregorianCalendar::get")) return;
212 if (offset == expDSTOffset) logln(UnicodeString("PASS: getOffset() = ") + (offset / ONE_HOUR));
213 else errln(UnicodeString("FAIL: getOffset() = ") + (offset / ONE_HOUR) + "; expected " + (expDSTOffset / ONE_HOUR));
214 delete gc;
215 }
216
217 // -------------------------------------
218
219 /**
220 * Test the behavior of SimpleTimeZone at the transition into and out of DST.
221 * Use a binary search to find boundaries.
222 */
223 void
224 TimeZoneBoundaryTest::TestBoundaries()
225 {
226 #if 1
227 {
228 logln("--- Test a ---");
229 UDate d = date(97, UCAL_APRIL, 6);
230 TimeZone *z = TimeZone::createTimeZone("PST");
231 for (int32_t i = 60; i <= 180; i += 15) {
232 UBool inDST = (i >= 120);
233 UDate e = d + i * 60 * 1000;
234 verifyDST(e, z, TRUE, inDST, - 8 * ONE_HOUR, inDST ? - 7 * ONE_HOUR: - 8 * ONE_HOUR);
235 }
236 delete z;
237 }
238 #endif
239 #if 1
240 {
241 logln("--- Test b ---");
242 TimeZone *tz;
243 TimeZone::setDefault(*(tz = TimeZone::createTimeZone("PST")));
244 delete tz;
245 logln("========================================");
246 findDaylightBoundaryUsingDate(date(97, 0, 1), "PST", PST_1997_BEG);
247 logln("========================================");
248 findDaylightBoundaryUsingDate(date(97, 6, 1), "PDT", PST_1997_END);
249 }
250 #endif
251 #if 1
252 {
253 logln("--- Test c ---");
254 logln("========================================");
255 TimeZone* z = TimeZone::createTimeZone("Australia/Adelaide");
256 findDaylightBoundaryUsingTimeZone(date(97, 0, 1), TRUE, 859653000000.0, z);
257 logln("========================================");
258 findDaylightBoundaryUsingTimeZone(date(97, 6, 1), FALSE, 877797000000.0, z);
259 delete z;
260 }
261 #endif
262 #if 1
263 {
264 logln("--- Test d ---");
265 logln("========================================");
266 findDaylightBoundaryUsingTimeZone(date(97, 0, 1), FALSE, PST_1997_BEG);
267 logln("========================================");
268 findDaylightBoundaryUsingTimeZone(date(97, 6, 1), TRUE, PST_1997_END);
269 }
270 #endif
271 #if 0
272 {
273 logln("--- Test e ---");
274 TimeZone *z = TimeZone::createDefault();
275 logln(UnicodeString("") + z->getOffset(1, 97, 3, 4, 6, 0) + " " + date(97, 3, 4));
276 logln(UnicodeString("") + z->getOffset(1, 97, 3, 5, 7, 0) + " " + date(97, 3, 5));
277 logln(UnicodeString("") + z->getOffset(1, 97, 3, 6, 1, 0) + " " + date(97, 3, 6));
278 logln(UnicodeString("") + z->getOffset(1, 97, 3, 7, 2, 0) + " " + date(97, 3, 7));
279 delete z;
280 }
281 #endif
282 }
283
284 // -------------------------------------
285
286 void
287 TimeZoneBoundaryTest::testUsingBinarySearch(SimpleTimeZone* tz, UDate d, UDate expectedBoundary)
288 {
289 UErrorCode status = U_ZERO_ERROR;
290 UDate min = d;
291 UDate max = min + SIX_MONTHS;
292 UBool startsInDST = tz->inDaylightTime(d, status);
293 if (failure(status, "SimpleTimeZone::inDaylightTime")) return;
294 if (tz->inDaylightTime(max, status) == startsInDST) {
295 logln("Error: inDaylightTime(" + dateToString(max) + ") != " + ((!startsInDST)?"true":"false"));
296 }
297 if (failure(status, "SimpleTimeZone::inDaylightTime")) return;
298 while ((max - min) > INTERVAL) {
299 UDate mid = (min + max) / 2;
300 if (tz->inDaylightTime(mid, status) == startsInDST) {
301 min = mid;
302 }
303 else {
304 max = mid;
305 }
306 if (failure(status, "SimpleTimeZone::inDaylightTime")) return;
307 }
308 logln("Binary Search Before: " + showDate(min));
309 logln("Binary Search After: " + showDate(max));
310 UDate mindelta = expectedBoundary - min;
311 UDate maxdelta = max - expectedBoundary;
312 if (mindelta >= 0 &&
313 mindelta <= INTERVAL &&
314 maxdelta >= 0 &&
315 maxdelta <= INTERVAL) logln(UnicodeString("PASS: Expected boundary at ") + expectedBoundary);
316 else errln(UnicodeString("FAIL: Expected boundary at ") + expectedBoundary);
317 }
318
319 // -------------------------------------
320
321 /**
322 * Test the handling of the "new" rules; that is, rules other than nth Day of week.
323 */
324 void
325 TimeZoneBoundaryTest::TestNewRules()
326 {
327 #if 1
328 {
329 UErrorCode status = U_ZERO_ERROR;
330 SimpleTimeZone *tz;
331 logln("-----------------------------------------------------------------");
332 logln("Aug 2ndTues .. Mar 15");
333 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);
334 logln("========================================");
335 testUsingBinarySearch(tz, date(97, 0, 1), 858416400000.0);
336 logln("========================================");
337 testUsingBinarySearch(tz, date(97, 6, 1), 871380000000.0);
338 delete tz;
339 logln("-----------------------------------------------------------------");
340 logln("Apr Wed>=14 .. Sep Sun<=20");
341 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);
342 logln("========================================");
343 testUsingBinarySearch(tz, date(97, 0, 1), 861184800000.0);
344 logln("========================================");
345 testUsingBinarySearch(tz, date(97, 6, 1), 874227600000.0);
346 delete tz;
347 }
348 #endif
349 }
350
351 // -------------------------------------
352
353 void
354 TimeZoneBoundaryTest::findBoundariesStepwise(int32_t year, UDate interval, TimeZone* z, int32_t expectedChanges)
355 {
356 UErrorCode status = U_ZERO_ERROR;
357 UnicodeString str;
358 UDate d = date(year - 1900, UCAL_JANUARY, 1);
359 UDate time = d;
360 UDate limit = time + ONE_YEAR + ONE_DAY;
361 UBool lastState = z->inDaylightTime(d, status);
362 if (failure(status, "TimeZone::inDaylightTime")) return;
363 int32_t changes = 0;
364 logln(UnicodeString("-- Zone ") + z->getID(str) + " starts in " + year + " with DST = " + (lastState?"true":"false"));
365 logln(UnicodeString("useDaylightTime = ") + (z->useDaylightTime()?"true":"false"));
366 while (time < limit) {
367 d = time;
368 UBool state = z->inDaylightTime(d, status);
369 if (failure(status, "TimeZone::inDaylightTime")) return;
370 if (state != lastState) {
371 logln(UnicodeString(state ? "Entry ": "Exit ") + "at " + d);
372 lastState = state;++changes;
373 }
374 time += interval;
375 }
376 if (changes == 0) {
377 if (!lastState &&
378 !z->useDaylightTime()) logln("No DST");
379 else errln("FAIL: DST all year, or no DST with true useDaylightTime");
380 }
381 else if (changes != 2) {
382 errln(UnicodeString("FAIL: ") + changes + " changes seen; should see 0 or 2");
383 }
384 else if (!z->useDaylightTime()) {
385 errln("FAIL: useDaylightTime false but 2 changes seen");
386 }
387 if (changes != expectedChanges) {
388 errln(UnicodeString("FAIL: ") + changes + " changes seen; expected " + expectedChanges);
389 }
390 }
391
392 // -------------------------------------
393
394 /**
395 * This test is problematic. It makes assumptions about the behavior
396 * of specific zones. Since ICU's zone table is based on the Olson
397 * zones (the UNIX zones), and those change from time to time, this
398 * test can fail after a zone table update. If that happens, the
399 * selected zones need to be updated to have the behavior
400 * expected. That is, they should have DST, not have DST, and have DST
401 * -- other than that this test isn't picky. 12/3/99 aliu
402 *
403 * Test the behavior of SimpleTimeZone at the transition into and out of DST.
404 * Use a stepwise march to find boundaries.
405 */
406 void
407 TimeZoneBoundaryTest::TestStepwise()
408 {
409 TimeZone *zone = TimeZone::createTimeZone("America/New_York");
410 findBoundariesStepwise(1997, ONE_DAY, zone, 2);
411 delete zone;
412 zone = TimeZone::createTimeZone("UTC"); // updated 12/3/99 aliu
413 findBoundariesStepwise(1997, ONE_DAY, zone, 0);
414 delete zone;
415 zone = TimeZone::createTimeZone("Australia/Adelaide");
416 findBoundariesStepwise(1997, ONE_DAY, zone, 2);
417 delete zone;
418 }
419
420 #endif /* #if !UCONFIG_NO_FORMATTING */