1/* GENERATED SOURCE. DO NOT MODIFY. */ 2// © 2016 and later: Unicode, Inc. and others. 3// License & terms of use: http://www.unicode.org/copyright.html#License 4/** 5 ******************************************************************************* 6 * Copyright (C) 2000-2015, International Business Machines Corporation and 7 * others. All Rights Reserved. 8 ******************************************************************************* 9 */ 10package android.icu.dev.test.timezone; 11import java.util.Date; 12 13import org.junit.Test; 14import org.junit.runner.RunWith; 15import org.junit.runners.JUnit4; 16 17import android.icu.dev.test.TestFmwk; 18import android.icu.dev.test.TestUtil; 19import android.icu.dev.test.TestUtil.JavaVendor; 20import android.icu.text.DateFormat; 21import android.icu.util.Calendar; 22import android.icu.util.SimpleTimeZone; 23import android.icu.util.TimeZone; 24import android.icu.testsharding.MainTestShard; 25 26/** 27 * A test which discovers the boundaries of DST programmatically and verifies 28 * that they are correct. 29 */ 30@MainTestShard 31@RunWith(JUnit4.class) 32public class TimeZoneBoundaryTest extends TestFmwk 33{ 34 static final int ONE_SECOND = 1000; 35 static final int ONE_MINUTE = 60*ONE_SECOND; 36 static final int ONE_HOUR = 60*ONE_MINUTE; 37 static final long ONE_DAY = 24*ONE_HOUR; 38 static final long ONE_YEAR = (long)(365.25 * ONE_DAY); 39 static final long SIX_MONTHS = ONE_YEAR / 2; 40 41 static final int MONTH_LENGTH[] = {31,29,31,30,31,30,31,31,30,31,30,31}; 42 43 // These values are empirically determined to be correct 44 static final long PST_1997_BEG = 860320800000L; 45 static final long PST_1997_END = 877856400000L; 46 47 // Minimum interval for binary searches in ms; should be no larger 48 // than 1000. 49 static final long INTERVAL = 10; // Milliseconds 50 51 // [3Jan01 Liu] Updated for 2000f data 52 static final String AUSTRALIA = "Australia/Adelaide"; 53 static final long AUSTRALIA_1997_BEG = 877797000000L; 54 static final long AUSTRALIA_1997_END = 859653000000L; 55 56 /** 57 * Date.toString().substring() Boundary Test 58 * Look for a DST changeover to occur within 6 months of the given Date. 59 * The initial Date.toString() should yield a string containing the 60 * startMode as a SUBSTRING. The boundary will be tested to be 61 * at the expectedBoundary value. 62 */ 63 void findDaylightBoundaryUsingDate(Date d, String startMode, long expectedBoundary) 64 { 65 // Given a date with a year start, find the Daylight onset 66 // and end. The given date should be 1/1/xx in some year. 67 68 if (d.toString().indexOf(startMode) == -1) 69 { 70 logln("Error: " + startMode + " not present in " + d); 71 } 72 73 // Use a binary search, assuming that we have a Standard 74 // time at the midpoint. 75 long min = d.getTime(); 76 long max = min + SIX_MONTHS; 77 78 while ((max - min) > INTERVAL) 79 { 80 long mid = (min + max) >> 1; 81 String s = new Date(mid).toString(); 82 // logln(s); 83 if (s.indexOf(startMode) != -1) 84 { 85 min = mid; 86 } 87 else 88 { 89 max = mid; 90 } 91 } 92 93 logln("Date Before: " + showDate(min)); 94 logln("Date After: " + showDate(max)); 95 long mindelta = expectedBoundary - min; 96 // not used long maxdelta = max - expectedBoundary; 97 if (mindelta >= 0 && mindelta <= INTERVAL && 98 mindelta >= 0 && mindelta <= INTERVAL) 99 logln("PASS: Expected boundary at " + expectedBoundary); 100 else 101 errln("FAIL: Expected boundary at " + expectedBoundary); 102 } 103 104 // This test cannot be compiled until the inDaylightTime() method of GregorianCalendar 105 // becomes public. 106 // static void findDaylightBoundaryUsingCalendar(Date d, boolean startsInDST) 107 // { 108 // // Given a date with a year start, find the Daylight onset 109 // // and end. The given date should be 1/1/xx in some year. 110 // 111 // GregorianCalendar cal = new GregorianCalendar(); 112 // cal.setTime(d); 113 // if (cal.inDaylightTime() != startsInDST) 114 // { 115 // logln("Error: inDaylightTime(" + d + ") != " + startsInDST); 116 // } 117 // 118 // // Use a binary search, assuming that we have a Standard 119 // // time at the midpoint. 120 // long min = d.getTime(); 121 // long max = min + (long)(365.25 / 2 * 24*60*60*1000); 122 // 123 // while ((max - min) > INTERVAL) 124 // { 125 // long mid = (min + max) >> 1; 126 // cal.setTime(new Date(mid)); 127 // if (cal.inDaylightTime() == startsInDST) 128 // { 129 // min = mid; 130 // } 131 // else 132 // { 133 // max = mid; 134 // } 135 // } 136 // 137 // logln("Calendar Before: " + showDate(min)); 138 // logln("Calendar After: " + showDate(max)); 139 // } 140 141 void findDaylightBoundaryUsingTimeZone(Date d, boolean startsInDST, long expectedBoundary) 142 { 143 findDaylightBoundaryUsingTimeZone(d, startsInDST, expectedBoundary, 144 TimeZone.getDefault()); 145 } 146 147 void findDaylightBoundaryUsingTimeZone(Date d, boolean startsInDST, 148 long expectedBoundary, TimeZone tz) 149 { 150 // Given a date with a year start, find the Daylight onset 151 // and end. The given date should be 1/1/xx in some year. 152 153 // Use a binary search, assuming that we have a Standard 154 // time at the midpoint. 155 long min = d.getTime(); 156 long max = min + SIX_MONTHS; 157 158 if (tz.inDaylightTime(d) != startsInDST) 159 { 160 errln("FAIL: " + tz.getID() + " inDaylightTime(" + 161 d + ") != " + startsInDST); 162 startsInDST = !startsInDST; // Flip over; find the apparent value 163 } 164 165 if (tz.inDaylightTime(new Date(max)) == startsInDST) 166 { 167 errln("FAIL: " + tz.getID() + " inDaylightTime(" + 168 (new Date(max)) + ") != " + (!startsInDST)); 169 return; 170 } 171 172 while ((max - min) > INTERVAL) 173 { 174 long mid = (min + max) >> 1; 175 boolean isIn = tz.inDaylightTime(new Date(mid)); 176 if (isIn == startsInDST) 177 { 178 min = mid; 179 } 180 else 181 { 182 max = mid; 183 } 184 } 185 186 logln(tz.getID() + " Before: " + showDate(min, tz)); 187 logln(tz.getID() + " After: " + showDate(max, tz)); 188 189 long mindelta = expectedBoundary - min; 190 // not used long maxdelta = max - expectedBoundary; 191 DateFormat fmt = DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.LONG); 192 fmt.setTimeZone(tz); 193 if (mindelta >= 0 && mindelta <= INTERVAL && 194 mindelta >= 0 && mindelta <= INTERVAL) 195 logln("PASS: Expected boundary at " + expectedBoundary + " = " + fmt.format(new Date(expectedBoundary))); 196 else 197 errln("FAIL: Expected boundary at " + expectedBoundary + " = " + fmt.format(new Date(expectedBoundary))); 198 } 199 200 private static String showDate(long l) 201 { 202 return showDate(new Date(l)); 203 } 204 205 private static String showDate(Date d) 206 { 207 java.util.Calendar cal = java.util.Calendar.getInstance(); 208 cal.setTime(d); 209 return "" + (cal.get(Calendar.YEAR) - 1900) + "/" + 210 showNN(cal.get(Calendar.MONTH) + 1) + "/" + 211 showNN(cal.get(Calendar.DAY_OF_MONTH)) + " " + 212 showNN(cal.get(Calendar.HOUR_OF_DAY)) + ":" 213 + showNN(cal.get(Calendar.MINUTE)) + " \"" + d + "\" = " + 214 d.getTime(); 215 } 216 217 private static String showDate(long l, TimeZone z) 218 { 219 return showDate(new Date(l), z); 220 } 221 222 private static String showDate(Date d, TimeZone zone) 223 { 224 DateFormat fmt = DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.LONG); 225 fmt.setTimeZone(zone); 226 java.util.Calendar cal = java.util.Calendar.getInstance(); 227 cal.setTime(d); 228 return "" + (cal.get(Calendar.YEAR) - 1900) + "/" + 229 showNN(cal.get(Calendar.MONTH) + 1) + "/" + 230 showNN(cal.get(Calendar.DAY_OF_MONTH)) + " " + 231 showNN(cal.get(Calendar.HOUR_OF_DAY)) + ":" + 232 showNN(cal.get(Calendar.MINUTE)) + " \"" + d + "\" = " + 233 fmt.format(d) + " = " + d.getTime(); 234 } 235 236 private static String showNN(int n) 237 { 238 return ((n < 10) ? "0" : "") + n; 239 } 240 241 /** 242 * Given a date, a TimeZone, and expected values for inDaylightTime, 243 * useDaylightTime, zone and DST offset, verify that this is the case. 244 */ 245 void verifyDST(String tag, Calendar cal, TimeZone time_zone, 246 boolean expUseDaylightTime, boolean expInDaylightTime, 247 int expRawOffset, int expOffset) 248 { 249 Date d = cal.getTime(); 250 251 logln("-- " + tag + ": " + d + 252 " in zone " + time_zone.getID() + " (" + 253 d.getTime()/3600000.0 + ")"); 254 255 if (time_zone.inDaylightTime(d) == expInDaylightTime) 256 logln("PASS: inDaylightTime = " + time_zone.inDaylightTime(d)); 257 else 258 errln("FAIL: inDaylightTime = " + time_zone.inDaylightTime(d)); 259 260 if (time_zone.useDaylightTime() == expUseDaylightTime) 261 logln("PASS: useDaylightTime = " + time_zone.useDaylightTime()); 262 else 263 errln("FAIL: useDaylightTime = " + time_zone.useDaylightTime()); 264 265 if (time_zone.getRawOffset() == expRawOffset) 266 logln("PASS: getRawOffset() = " + expRawOffset/(double)ONE_HOUR); 267 else 268 errln("FAIL: getRawOffset() = " + time_zone.getRawOffset()/(double)ONE_HOUR + 269 "; expected " + expRawOffset/(double)ONE_HOUR); 270 271 //GregorianCalendar gc = new GregorianCalendar(time_zone); 272 //gc.setTime(d); 273 int offset = time_zone.getOffset(cal.get(Calendar.ERA), cal.get(Calendar.YEAR), cal.get(Calendar.MONTH), 274 cal.get(Calendar.DAY_OF_MONTH), cal.get(Calendar.DAY_OF_WEEK), 275 ((cal.get(Calendar.HOUR_OF_DAY) * 60 + 276 cal.get(Calendar.MINUTE)) * 60 + 277 cal.get(Calendar.SECOND)) * 1000 + 278 cal.get(Calendar.MILLISECOND)); 279 if (offset == expOffset) 280 logln("PASS: getOffset() = " + offset/(double)ONE_HOUR); 281 else { 282 logln("era=" + cal.get(Calendar.ERA) + 283 ", year=" + cal.get(Calendar.YEAR) + 284 ", month=" + cal.get(Calendar.MONTH) + 285 ", dom=" + cal.get(Calendar.DAY_OF_MONTH) + 286 ", dow=" + cal.get(Calendar.DAY_OF_WEEK) + 287 ", time-of-day=" + (((cal.get(Calendar.HOUR_OF_DAY) * 60 + 288 cal.get(Calendar.MINUTE)) * 60 + 289 cal.get(Calendar.SECOND)) * 1000 + 290 cal.get(Calendar.MILLISECOND)) / 3600000.0 + 291 " hours"); 292 errln("FAIL: getOffset() = " + offset/(double)ONE_HOUR + 293 "; expected " + expOffset/(double)ONE_HOUR); 294 } 295 } 296 297 /** 298 * Check that the given year/month/dom/hour maps to and from the 299 * given epochHours. This verifies the functioning of the 300 * calendar and time zone in conjunction with one another, 301 * including the calendar time->fields and fields->time and 302 * the time zone getOffset method. 303 * 304 * @param epochHours hours after Jan 1 1970 0:00 GMT. 305 */ 306 void verifyMapping(Calendar cal, int year, int month, int dom, int hour, 307 double epochHours) { 308 double H = 3600000.0; 309 cal.clear(); 310 cal.set(year, month, dom, hour, 0, 0); 311 Date d = cal.getTime(); 312 double e = d.getTime() / H; 313 Date ed = new Date((long)(epochHours * H)); 314 if (e == epochHours) { 315 logln("Ok: " + year + "/" + (month+1) + "/" + dom + " " + hour + ":00 => " + 316 e + " (" + ed + ")"); 317 } else { 318 errln("FAIL: " + year + "/" + (month+1) + "/" + dom + " " + hour + ":00 => " + 319 e + " (" + new Date((long)(e * H)) + ")" + 320 ", expected " + epochHours + " (" + ed + ")"); 321 } 322 cal.setTime(ed); 323 if (cal.get(Calendar.YEAR) == year && 324 cal.get(Calendar.MONTH) == month && 325 cal.get(Calendar.DATE) == dom && 326 cal.get(Calendar.MILLISECONDS_IN_DAY) == hour * 3600000) { 327 logln("Ok: " + epochHours + " (" + ed + ") => " + 328 cal.get(Calendar.YEAR) + "/" + 329 (cal.get(Calendar.MONTH)+1) + "/" + 330 cal.get(Calendar.DATE) + " " + 331 cal.get(Calendar.MILLISECONDS_IN_DAY)/H); 332 } else { 333 errln("FAIL: " + epochHours + " (" + ed + ") => " + 334 cal.get(Calendar.YEAR) + "/" + 335 (cal.get(Calendar.MONTH)+1) + "/" + 336 cal.get(Calendar.DATE) + " " + 337 cal.get(Calendar.MILLISECONDS_IN_DAY)/H + 338 ", expected " + year + "/" + (month+1) + "/" + dom + 339 " " + hour); 340 } 341 } 342 343// NOTE: Enable this code to check the behavior of the underlying JDK, 344// using a JDK Calendar object. 345// 346// int millisInDay(java.util.Calendar cal) { 347// return ((cal.get(Calendar.HOUR_OF_DAY) * 60 + 348// cal.get(Calendar.MINUTE)) * 60 + 349// cal.get(Calendar.SECOND)) * 1000 + 350// cal.get(Calendar.MILLISECOND); 351// } 352// 353// void verifyMapping(java.util.Calendar cal, int year, int month, int dom, int hour, 354// double epochHours) { 355// cal.clear(); 356// cal.set(year, month, dom, hour, 0, 0); 357// Date d = cal.getTime(); 358// double e = d.getTime() / 3600000.0; 359// Date ed = new Date((long)(epochHours * 3600000)); 360// if (e == epochHours) { 361// logln("Ok: " + year + "/" + (month+1) + "/" + dom + " " + hour + ":00 => " + 362// e + " (" + ed + ")"); 363// } else { 364// errln("FAIL: " + year + "/" + (month+1) + "/" + dom + " " + hour + ":00 => " + 365// e + " (" + new Date((long)(e * 3600000)) + ")" + 366// ", expected " + epochHours + " (" + ed + ")"); 367// } 368// cal.setTime(ed); 369// if (cal.get(Calendar.YEAR) == year && 370// cal.get(Calendar.MONTH) == month && 371// cal.get(Calendar.DATE) == dom && 372// millisInDay(cal) == hour * 3600000) { 373// logln("Ok: " + epochHours + " (" + ed + ") => " + 374// cal.get(Calendar.YEAR) + "/" + 375// (cal.get(Calendar.MONTH)+1) + "/" + 376// cal.get(Calendar.DATE) + " " + 377// millisInDay(cal)/3600000.0); 378// } else { 379// errln("FAIL: " + epochHours + " (" + ed + ") => " + 380// cal.get(Calendar.YEAR) + "/" + 381// (cal.get(Calendar.MONTH)+1) + "/" + 382// cal.get(Calendar.DATE) + " " + 383// millisInDay(cal)/3600000.0 + 384// ", expected " + year + "/" + (month+1) + "/" + dom + 385// " " + hour); 386// } 387// } 388 389 @Test 390 public void TestBoundaries() 391 { 392 TimeZone save = TimeZone.getDefault(); 393 394 // Check basic mappings. We had a problem with this for ICU 395 // 2.8 after migrating to using pass-through time zones. The 396 // problem appeared only on JDK 1.3. 397 TimeZone pst = safeGetTimeZone("PST"); 398 Calendar tempcal = Calendar.getInstance(pst); 399 verifyMapping(tempcal, 1997, Calendar.APRIL, 3, 0, 238904.0); 400 verifyMapping(tempcal, 1997, Calendar.APRIL, 4, 0, 238928.0); 401 verifyMapping(tempcal, 1997, Calendar.APRIL, 5, 0, 238952.0); 402 verifyMapping(tempcal, 1997, Calendar.APRIL, 5, 23, 238975.0); 403 verifyMapping(tempcal, 1997, Calendar.APRIL, 6, 0, 238976.0); 404 verifyMapping(tempcal, 1997, Calendar.APRIL, 6, 1, 238977.0); 405 verifyMapping(tempcal, 1997, Calendar.APRIL, 6, 3, 238978.0); 406 407 TimeZone utc = safeGetTimeZone("UTC"); 408 Calendar utccal = Calendar.getInstance(utc); 409 verifyMapping(utccal, 1997, Calendar.APRIL, 6, 0, 238968.0); 410 411// NOTE: Enable this code to check the behavior of the underlying JDK, 412// using a JDK Calendar object. 413// 414// java.util.TimeZone jdkpst = java.util.TimeZone.getTimeZone("PST"); 415// java.util.Calendar jdkcal = java.util.Calendar.getInstance(jdkpst); 416// verifyMapping(jdkcal, 1997, Calendar.APRIL, 5, 0, 238952.0); 417// verifyMapping(jdkcal, 1997, Calendar.APRIL, 5, 23, 238975.0); 418// verifyMapping(jdkcal, 1997, Calendar.APRIL, 6, 0, 238976.0); 419// verifyMapping(jdkcal, 1997, Calendar.APRIL, 6, 1, 238977.0); 420// verifyMapping(jdkcal, 1997, Calendar.APRIL, 6, 3, 238978.0); 421 422 tempcal.clear(); 423 tempcal.set(1997, Calendar.APRIL, 6); 424 Date d = tempcal.getTime(); 425 426 try { 427 TimeZone.setDefault(pst); 428 429 // DST changeover for PST is 4/6/1997 at 2 hours past midnight 430 // at 238978.0 epoch hours. 431 432 // i is minutes past midnight standard time 433 for (int i=-120; i<=180; i+=60) 434 { 435 boolean inDST = (i >= 120); 436 tempcal.setTimeInMillis(d.getTime() + i*60*1000); 437 verifyDST("hour=" + i/60, 438 tempcal, pst, true, inDST, -8*ONE_HOUR, 439 inDST ? -7*ONE_HOUR : -8*ONE_HOUR); 440 } 441 } finally { 442 TimeZone.setDefault(save); 443 } 444 445 // We no longer use ICU TimeZone implementation for Java 446 // default TimeZone. Java 1.3 or older version do not 447 // support historic transitions, therefore, the test below 448 // will fail on such environment (with the latest TimeZone 449 // patch for US 2007+ rule). 450 if (TestUtil.getJavaVendor() == JavaVendor.Android || TestUtil.getJavaVersion() > 3) { 451 // This only works in PST/PDT 452 TimeZone.setDefault(safeGetTimeZone("PST")); 453 logln("========================================"); 454 tempcal.set(1997, 0, 1); 455 findDaylightBoundaryUsingDate(tempcal.getTime(), "PST", PST_1997_BEG); 456 logln("========================================"); 457 tempcal.set(1997, 6, 1); 458 findDaylightBoundaryUsingDate(tempcal.getTime(), "PDT", PST_1997_END); 459 } 460 461 // if (true) 462 // { 463 // logln("========================================"); 464 // findDaylightBoundaryUsingCalendar(new Date(97,0,1), false); 465 // logln("========================================"); 466 // findDaylightBoundaryUsingCalendar(new Date(97,6,1), true); 467 // } 468 469 if (true) 470 { 471 // Southern hemisphere test 472 logln("========================================"); 473 TimeZone z = safeGetTimeZone(AUSTRALIA); 474 tempcal.set(1997, 0, 1); 475 findDaylightBoundaryUsingTimeZone(tempcal.getTime(), true, AUSTRALIA_1997_END, z); 476 logln("========================================"); 477 tempcal.set(1997, 6, 1); 478 findDaylightBoundaryUsingTimeZone(tempcal.getTime(), false, AUSTRALIA_1997_BEG, z); 479 } 480 481 if (true) 482 { 483 logln("========================================"); 484 tempcal.set(1997, 0, 1); 485 findDaylightBoundaryUsingTimeZone(tempcal.getTime(), false, PST_1997_BEG); 486 logln("========================================"); 487 tempcal.set(1997, 6, 1); 488 findDaylightBoundaryUsingTimeZone(tempcal.getTime(), true, PST_1997_END); 489 } 490 491 // This just shows the offset for April 4-7 in 1997. This is redundant 492 // with a test above, so we disable it. 493 if (false) 494 { 495 TimeZone z = TimeZone.getDefault(); 496 tempcal.set(1997, 3, 4); 497 logln(z.getOffset(1, 97, 3, 4, 6, 0) + " " + tempcal.getTime()); 498 tempcal.set(1997, 3, 5); 499 logln(z.getOffset(1, 97, 3, 5, 7, 0) + " " + tempcal.getTime()); 500 tempcal.set(1997, 3, 6); 501 logln(z.getOffset(1, 97, 3, 6, 1, 0) + " " + tempcal.getTime()); 502 tempcal.set(1997, 3, 7); 503 logln(z.getOffset(1, 97, 3, 7, 2, 0) + " " + tempcal.getTime()); 504 } 505 } 506 507 508 //---------------------------------------------------------------------- 509 // Can't do any of these without a public inDaylightTime in GC 510 //---------------------------------------------------------------------- 511 512 513 // static GregorianCalendar cal = new GregorianCalendar(); 514 // 515 // static void _testUsingBinarySearch(Date d, boolean startsInDST) 516 // { 517 // // Given a date with a year start, find the Daylight onset 518 // // and end. The given date should be 1/1/xx in some year. 519 // 520 // // Use a binary search, assuming that we have a Standard 521 // // time at the midpoint. 522 // long min = d.getTime(); 523 // long max = min + (long)(365.25 / 2 * ONE_DAY); 524 // 525 // // First check the max 526 // cal.setTime(new Date(max)); 527 // if (cal.inDaylightTime() == startsInDST) 528 // { 529 // logln("Error: inDaylightTime(" + (new Date(max)) + ") != " + (!startsInDST)); 530 // } 531 // 532 // cal.setTime(d); 533 // if (cal.inDaylightTime() != startsInDST) 534 // { 535 // logln("Error: inDaylightTime(" + d + ") != " + startsInDST); 536 // } 537 // 538 // while ((max - min) > INTERVAL) 539 // { 540 // long mid = (min + max) >> 1; 541 // cal.setTime(new Date(mid)); 542 // if (cal.inDaylightTime() == startsInDST) 543 // { 544 // min = mid; 545 // } 546 // else 547 // { 548 // max = mid; 549 // } 550 // } 551 // 552 // logln("Binary Search Before: " + showDate(min)); 553 // logln("Binary Search After: " + showDate(max)); 554 // } 555 // 556 // static void _testUsingMillis(Date d, boolean startsInDST) 557 // { 558 // long millis = d.getTime(); 559 // long max = millis + (long)(370 * ONE_DAY); // A year plus extra 560 // 561 // boolean lastDST = startsInDST; 562 // while (millis < max) 563 // { 564 // cal.setTime(new Date(millis)); 565 // boolean inDaylight = cal.inDaylightTime(); 566 // 567 // if (inDaylight != lastDST) 568 // { 569 // logln("Switch " + (inDaylight ? "into" : "out of") 570 // + " DST at " + (new Date(millis))); 571 // lastDST = inDaylight; 572 // } 573 // 574 // millis += 15*ONE_MINUTE; 575 // } 576 // } 577 // 578 // static void _testUsingFields(int y, boolean startsInDST) 579 // { 580 // boolean lastDST = startsInDST; 581 // for (int m = 0; m < 12; ++m) 582 // { 583 // for (int d = 1; d <= MONTH_LENGTH[m]; ++d) 584 // { 585 // for (int h = 0; h < 24; ++h) 586 // { 587 // for (int min = 0; min < 60; min += 15) 588 // { 589 // cal.clear(); 590 // cal.set(y, m, d, h, min); 591 // boolean inDaylight = cal.inDaylightTime(); 592 // if (inDaylight != lastDST) 593 // { 594 // lastDST = inDaylight; 595 // log("Switch " + (lastDST ? "into" : "out of") 596 // + " DST at " + y + "/" + (m+1) + "/" + d 597 // + " " + showNN(h) + ":" + showNN(min)); 598 // logln(" " + cal.getTime()); 599 // 600 // cal.set(y, m, d, h-1, 45); 601 // log("Before = " 602 //+ y + "/" + (m+1) + "/" + d 603 //+ " " + showNN(h-1) + ":" + showNN(45)); 604 // logln(" " + cal.getTime()); 605 // } 606 // } 607 // } 608 // } 609 // } 610 // } 611 // 612 // public void Test1() 613 // { 614 // logln(Locale.getDefault().getDisplayName()); 615 // logln(TimeZone.getDefault().getID()); 616 // logln(new Date(0)); 617 // 618 // if (true) 619 // { 620 // logln("========================================"); 621 // _testUsingBinarySearch(new Date(97,0,1), false); 622 // logln("========================================"); 623 // _testUsingBinarySearch(new Date(97,6,1), true); 624 // } 625 // 626 // if (true) 627 // { 628 // logln("========================================"); 629 // logln("Stepping using millis"); 630 // _testUsingMillis(new Date(97,0,1), false); 631 // } 632 // 633 // if (true) 634 // { 635 // logln("========================================"); 636 // logln("Stepping using fields"); 637 // _testUsingFields(1997, false); 638 // } 639 // 640 // if (false) 641 // { 642 // cal.clear(); 643 // cal.set(1997, 3, 5, 10, 0); 644 // // cal.inDaylightTime(); 645 // logln("Date = " + cal.getTime()); 646 // logln("Millis = " + cal.getTime().getTime()/3600000); 647 // } 648 // } 649 650 //---------------------------------------------------------------------- 651 //---------------------------------------------------------------------- 652 //---------------------------------------------------------------------- 653 654 void _testUsingBinarySearch(SimpleTimeZone tz, Date d, long expectedBoundary) 655 { 656 // Given a date with a year start, find the Daylight onset 657 // and end. The given date should be 1/1/xx in some year. 658 659 // Use a binary search, assuming that we have a Standard 660 // time at the midpoint. 661 long min = d.getTime(); 662 long max = min + (long)(365.25 / 2 * ONE_DAY); 663 664 // First check the boundaries 665 boolean startsInDST = tz.inDaylightTime(d); 666 667 if (tz.inDaylightTime(new Date(max)) == startsInDST) 668 { 669 errln("Error: inDaylightTime(" + (new Date(max)) + ") != " + (!startsInDST)); 670 } 671 672 while ((max - min) > INTERVAL) 673 { 674 long mid = (min + max) >> 1; 675 if (tz.inDaylightTime(new Date(mid)) == startsInDST) 676 { 677 min = mid; 678 } 679 else 680 { 681 max = mid; 682 } 683 } 684 685 logln("Binary Search Before: " + showDate(min)); 686 logln("Binary Search After: " + showDate(max)); 687 688 long mindelta = expectedBoundary - min; 689 // not used long maxdelta = max - expectedBoundary; 690 if (mindelta >= 0 && mindelta <= INTERVAL && 691 mindelta >= 0 && mindelta <= INTERVAL) 692 logln("PASS: Expected boundary at " + expectedBoundary); 693 else 694 errln("FAIL: Expected boundary at " + expectedBoundary); 695 } 696 697 /* 698 static void _testUsingMillis(Date d, boolean startsInDST) 699 { 700 long millis = d.getTime(); 701 long max = millis + (long)(370 * ONE_DAY); // A year plus extra 702 703 boolean lastDST = startsInDST; 704 while (millis < max) 705 { 706 cal.setTime(new Date(millis)); 707 boolean inDaylight = cal.inDaylightTime(); 708 709 if (inDaylight != lastDST) 710 { 711 logln("Switch " + (inDaylight ? "into" : "out of") 712 + " DST at " + (new Date(millis))); 713 lastDST = inDaylight; 714 } 715 716 millis += 15*ONE_MINUTE; 717 } 718 } 719 */ 720 721 /** 722 * Test new rule formats. 723 */ 724 @Test 725 public void TestNewRules() 726 { 727 //logln(Locale.getDefault().getDisplayName()); 728 //logln(TimeZone.getDefault().getID()); 729 //logln(new Date(0)); 730 731 if (true) 732 { 733 // Doesn't matter what the default TimeZone is here, since we 734 // are creating our own TimeZone objects. 735 736 SimpleTimeZone tz; 737 java.util.Calendar tempcal = java.util.Calendar.getInstance(); 738 tempcal.clear(); 739 740 logln("-----------------------------------------------------------------"); 741 logln("Aug 2ndTues .. Mar 15"); 742 tz = new SimpleTimeZone(-8*ONE_HOUR, "Test_1", 743 Calendar.AUGUST, 2, Calendar.TUESDAY, 2*ONE_HOUR, 744 Calendar.MARCH, 15, 0, 2*ONE_HOUR); 745 //logln(tz.toString()); 746 logln("========================================"); 747 tempcal.set(1997, 0, 1); 748 _testUsingBinarySearch(tz, tempcal.getTime(), 858416400000L); 749 logln("========================================"); 750 tempcal.set(1997, 6, 1); 751 _testUsingBinarySearch(tz, tempcal.getTime(), 871380000000L); 752 753 logln("-----------------------------------------------------------------"); 754 logln("Apr Wed>=14 .. Sep Sun<=20"); 755 tz = new SimpleTimeZone(-8*ONE_HOUR, "Test_2", 756 Calendar.APRIL, 14, -Calendar.WEDNESDAY, 2*ONE_HOUR, 757 Calendar.SEPTEMBER, -20, -Calendar.SUNDAY, 2*ONE_HOUR); 758 //logln(tz.toString()); 759 logln("========================================"); 760 tempcal.set(1997, 0, 1); 761 _testUsingBinarySearch(tz, tempcal.getTime(), 861184800000L); 762 logln("========================================"); 763 tempcal.set(1997, 6, 1); 764 _testUsingBinarySearch(tz, tempcal.getTime(), 874227600000L); 765 } 766 767 /* 768 if (true) 769 { 770 logln("========================================"); 771 logln("Stepping using millis"); 772 _testUsingMillis(new Date(97,0,1), false); 773 } 774 775 if (true) 776 { 777 logln("========================================"); 778 logln("Stepping using fields"); 779 _testUsingFields(1997, false); 780 } 781 782 if (false) 783 { 784 cal.clear(); 785 cal.set(1997, 3, 5, 10, 0); 786 // cal.inDaylightTime(); 787 logln("Date = " + cal.getTime()); 788 logln("Millis = " + cal.getTime().getTime()/3600000); 789 } 790 */ 791 } 792 793 //---------------------------------------------------------------------- 794 //---------------------------------------------------------------------- 795 //---------------------------------------------------------------------- 796 // Long Bug 797 //---------------------------------------------------------------------- 798 //---------------------------------------------------------------------- 799 //---------------------------------------------------------------------- 800 801 //public void Test3() 802 //{ 803 // findDaylightBoundaryUsingTimeZone(new Date(97,6,1), true); 804 //} 805 806 /** 807 * Find boundaries by stepping. 808 */ 809 void findBoundariesStepwise(int year, long interval, TimeZone z, int expectedChanges) 810 { 811 java.util.Calendar tempcal = java.util.Calendar.getInstance(); 812 tempcal.clear(); 813 tempcal.set(year, Calendar.JANUARY, 1); 814 Date d = tempcal.getTime(); 815 long time = d.getTime(); // ms 816 long limit = time + ONE_YEAR + ONE_DAY; 817 boolean lastState = z.inDaylightTime(d); 818 int changes = 0; 819 logln("-- Zone " + z.getID() + " starts in " + year + " with DST = " + lastState); 820 logln("useDaylightTime = " + z.useDaylightTime()); 821 while (time < limit) 822 { 823 d.setTime(time); 824 boolean state = z.inDaylightTime(d); 825 if (state != lastState) 826 { 827 logln((state ? "Entry " : "Exit ") + 828 "at " + d); 829 lastState = state; 830 ++changes; 831 } 832 time += interval; 833 } 834 if (changes == 0) 835 { 836 if (!lastState && !z.useDaylightTime()) logln("No DST"); 837 else errln("FAIL: DST all year, or no DST with true useDaylightTime"); 838 } 839 else if (changes != 2) 840 { 841 errln("FAIL: " + changes + " changes seen; should see 0 or 2"); 842 } 843 else if (!z.useDaylightTime()) 844 { 845 errln("FAIL: useDaylightTime false but 2 changes seen"); 846 } 847 if (changes != expectedChanges) 848 { 849 errln("FAIL: " + changes + " changes seen; expected " + expectedChanges); 850 } 851 } 852 853 @Test 854 public void TestStepwise() 855 { 856 findBoundariesStepwise(1997, ONE_DAY, safeGetTimeZone("America/New_York"), 2); 857 // disabled Oct 2003 aliu; ACT could mean anything, depending on the underlying JDK, as of 2.8 858 // findBoundariesStepwise(1997, ONE_DAY, safeGetTimeZone("ACT"), 2); 859 findBoundariesStepwise(1997, ONE_DAY, safeGetTimeZone("America/Phoenix"), 0); // Added 3Jan01 860 findBoundariesStepwise(1997, ONE_DAY, safeGetTimeZone(AUSTRALIA), 2); 861 } 862} 863