1 // 2016 and later: Unicode, Inc. and others. 2 // License & terms of use: http://www.unicode.org/copyright.html#License 3 /* 4 ******************************************************************************* 5 * Copyright (C) 2007-2011, International Business Machines Corporation and * 6 * others. All Rights Reserved. * 7 ******************************************************************************* 8 */ 9 package com.ibm.icu.dev.test.timezone; 10 11 import java.io.ByteArrayInputStream; 12 import java.io.ByteArrayOutputStream; 13 import java.io.IOException; 14 import java.io.InputStreamReader; 15 import java.io.OutputStreamWriter; 16 import java.io.StringReader; 17 import java.io.StringWriter; 18 import java.util.Date; 19 20 import org.junit.Test; 21 import org.junit.runner.RunWith; 22 import org.junit.runners.JUnit4; 23 24 import com.ibm.icu.dev.test.TestFmwk; 25 import com.ibm.icu.util.AnnualTimeZoneRule; 26 import com.ibm.icu.util.BasicTimeZone; 27 import com.ibm.icu.util.Calendar; 28 import com.ibm.icu.util.DateTimeRule; 29 import com.ibm.icu.util.GregorianCalendar; 30 import com.ibm.icu.util.InitialTimeZoneRule; 31 import com.ibm.icu.util.RuleBasedTimeZone; 32 import com.ibm.icu.util.SimpleTimeZone; 33 import com.ibm.icu.util.TimeArrayTimeZoneRule; 34 import com.ibm.icu.util.TimeZone; 35 import com.ibm.icu.util.TimeZoneRule; 36 import com.ibm.icu.util.TimeZoneTransition; 37 import com.ibm.icu.util.ULocale; 38 import com.ibm.icu.util.VTimeZone; 39 40 /** 41 * Test cases for TimeZoneRule and RuleBasedTimeZone 42 */ 43 @RunWith(JUnit4.class) 44 public class TimeZoneRuleTest extends TestFmwk { 45 46 private static final int HOUR = 60 * 60 * 1000; 47 48 /* 49 * RuleBasedTimeZone test cases 50 */ 51 @Test 52 public void TestSimpleRuleBasedTimeZone() { 53 SimpleTimeZone stz = new SimpleTimeZone(-1*HOUR, "TestSTZ", 54 Calendar.SEPTEMBER, -30, -Calendar.SATURDAY, 1*HOUR, SimpleTimeZone.WALL_TIME, 55 Calendar.FEBRUARY, 2, Calendar.SUNDAY, 1*HOUR, SimpleTimeZone.WALL_TIME, 56 1*HOUR); 57 58 59 DateTimeRule dtr; 60 AnnualTimeZoneRule atzr; 61 final int STARTYEAR = 2000; 62 63 InitialTimeZoneRule ir = new InitialTimeZoneRule( 64 "RBTZ_Initial", // Initial time Name 65 -1*HOUR, // Raw offset 66 1*HOUR); // DST saving amount 67 68 // RBTZ 69 RuleBasedTimeZone rbtz1 = new RuleBasedTimeZone("RBTZ1", ir); 70 dtr = new DateTimeRule(Calendar.SEPTEMBER, 30, Calendar.SATURDAY, false, 71 1*HOUR, DateTimeRule.WALL_TIME); // SUN<=30 in September, at 1AM wall time 72 atzr = new AnnualTimeZoneRule("RBTZ_DST1", 73 -1*HOUR /* rawOffset */, 1*HOUR /* dstSavings */, dtr, 74 STARTYEAR, AnnualTimeZoneRule.MAX_YEAR); 75 rbtz1.addTransitionRule(atzr); 76 dtr = new DateTimeRule(Calendar.FEBRUARY, 2, Calendar.SUNDAY, 77 1*HOUR, DateTimeRule.WALL_TIME); // 2nd Sunday in February, at 1AM wall time 78 atzr = new AnnualTimeZoneRule("RBTZ_STD1", 79 -1*HOUR /* rawOffset */, 0 /* dstSavings */, dtr, 80 STARTYEAR, AnnualTimeZoneRule.MAX_YEAR); 81 rbtz1.addTransitionRule(atzr); 82 83 // Equivalent, but different date rule type 84 RuleBasedTimeZone rbtz2 = new RuleBasedTimeZone("RBTZ2", ir); 85 dtr = new DateTimeRule(Calendar.SEPTEMBER, -1, Calendar.SATURDAY, 86 1*HOUR, DateTimeRule.WALL_TIME); // Last Sunday in September at 1AM wall time 87 atzr = new AnnualTimeZoneRule("RBTZ_DST2", -1*HOUR, 1*HOUR, dtr, STARTYEAR, AnnualTimeZoneRule.MAX_YEAR); 88 rbtz2.addTransitionRule(atzr); 89 dtr = new DateTimeRule(Calendar.FEBRUARY, 8, Calendar.SUNDAY, true, 90 1*HOUR, DateTimeRule.WALL_TIME); // SUN>=8 in February, at 1AM wall time 91 atzr = new AnnualTimeZoneRule("RBTZ_STD2", -1*HOUR, 0, dtr, STARTYEAR, AnnualTimeZoneRule.MAX_YEAR); 92 rbtz2.addTransitionRule(atzr); 93 94 // Equivalent, but different time rule type 95 RuleBasedTimeZone rbtz3 = new RuleBasedTimeZone("RBTZ3", ir); 96 dtr = new DateTimeRule(Calendar.SEPTEMBER, 30, Calendar.SATURDAY, false, 97 2*HOUR, DateTimeRule.UTC_TIME); 98 atzr = new AnnualTimeZoneRule("RBTZ_DST3", -1*HOUR, 1*HOUR, dtr, STARTYEAR, AnnualTimeZoneRule.MAX_YEAR); 99 rbtz3.addTransitionRule(atzr); 100 dtr = new DateTimeRule(Calendar.FEBRUARY, 2, Calendar.SUNDAY, 101 0*HOUR, DateTimeRule.STANDARD_TIME); 102 atzr = new AnnualTimeZoneRule("RBTZ_STD3", -1*HOUR, 0, dtr, STARTYEAR, AnnualTimeZoneRule.MAX_YEAR); 103 rbtz3.addTransitionRule(atzr); 104 105 // Check equivalency for 10 years 106 long start = getUTCMillis(STARTYEAR, Calendar.JANUARY, 1); 107 long until = getUTCMillis(STARTYEAR + 10, Calendar.JANUARY, 1); 108 109 if (!(stz.hasEquivalentTransitions(rbtz1, start, until))) { 110 errln("FAIL: rbtz1 must be equivalent to the SimpleTimeZone in the time range."); 111 } 112 if (!(stz.hasEquivalentTransitions(rbtz2, start, until))) { 113 errln("FAIL: rbtz2 must be equivalent to the SimpleTimeZone in the time range."); 114 } 115 if (!(stz.hasEquivalentTransitions(rbtz3, start, until))) { 116 errln("FAIL: rbtz3 must be equivalent to the SimpleTimeZone in the time range."); 117 } 118 119 // hasSameRules 120 if (rbtz1.hasSameRules(rbtz2)) { 121 errln("FAIL: rbtz1 and rbtz2 have different rules, but returned true."); 122 } 123 if (rbtz1.hasSameRules(rbtz3)) { 124 errln("FAIL: rbtz1 and rbtz3 have different rules, but returned true."); 125 } 126 RuleBasedTimeZone rbtz1c = (RuleBasedTimeZone)rbtz1.clone(); 127 if (!rbtz1.hasSameRules(rbtz1c)) { 128 errln("FAIL: Cloned RuleBasedTimeZone must have the same rules with the original."); 129 } 130 131 // getOffset 132 GregorianCalendar cal = new GregorianCalendar(); 133 int[] offsets = new int[2]; 134 int offset; 135 boolean dst; 136 137 cal.setTimeZone(rbtz1); 138 cal.clear(); 139 140 // Jan 1, 1000 BC 141 cal.set(Calendar.ERA, GregorianCalendar.BC); 142 cal.set(1000, Calendar.JANUARY, 1); 143 144 offset = rbtz1.getOffset(cal.get(Calendar.ERA), cal.get(Calendar.YEAR), cal.get(Calendar.MONTH), 145 cal.get(Calendar.DAY_OF_MONTH), cal.get(Calendar.DAY_OF_WEEK), cal.get(Calendar.MILLISECONDS_IN_DAY)); 146 if (offset != 0) { 147 errln("FAIL: Invalid time zone offset: " + offset + " /expected: 0"); 148 } 149 dst = rbtz1.inDaylightTime(cal.getTime()); 150 if (!dst) { 151 errln("FAIL: Invalid daylight saving time"); 152 } 153 rbtz1.getOffset(cal.getTimeInMillis(), true, offsets); 154 if (offsets[0] != -3600000) { 155 errln("FAIL: Invalid time zone raw offset: " + offsets[0] + " /expected: -3600000"); 156 } 157 if (offsets[1] != 3600000) { 158 errln("FAIL: Invalid DST amount: " + offsets[1] + " /expected: 3600000"); 159 } 160 161 // July 1, 2000, AD 162 cal.set(Calendar.ERA, GregorianCalendar.AD); 163 cal.set(2000, Calendar.JULY, 1); 164 165 offset = rbtz1.getOffset(cal.get(Calendar.ERA), cal.get(Calendar.YEAR), cal.get(Calendar.MONTH), 166 cal.get(Calendar.DAY_OF_MONTH), cal.get(Calendar.DAY_OF_WEEK), cal.get(Calendar.MILLISECONDS_IN_DAY)); 167 if (offset != -3600000) { 168 errln("FAIL: Invalid time zone offset: " + offset + " /expected: -3600000"); 169 } 170 dst = rbtz1.inDaylightTime(cal.getTime()); 171 if (dst) { 172 errln("FAIL: Invalid daylight saving time"); 173 } 174 rbtz1.getOffset(cal.getTimeInMillis(), true, offsets); 175 if (offsets[0] != -3600000) { 176 errln("FAIL: Invalid time zone raw offset: " + offsets[0] + " /expected: -3600000"); 177 } 178 if (offsets[1] != 0) { 179 errln("FAIL: Invalid DST amount: " + offsets[1] + " /expected: 0"); 180 } 181 182 // July 1, 2000, AD 183 184 // Try to add 3rd final rule 185 dtr = new DateTimeRule(Calendar.OCTOBER, 15, 1*HOUR, DateTimeRule.WALL_TIME); 186 atzr = new AnnualTimeZoneRule("3RD_ATZ", -1*HOUR, 2*HOUR, dtr, STARTYEAR, AnnualTimeZoneRule.MAX_YEAR); 187 boolean bException = false; 188 try { 189 rbtz1.addTransitionRule(atzr); 190 } catch (IllegalStateException ise) { 191 bException = true; 192 } 193 if (!bException) { 194 errln("FAIL: 3rd final rule must be rejected"); 195 } 196 197 // Try to add an initial rule 198 bException = false; 199 try { 200 rbtz1.addTransitionRule(new InitialTimeZoneRule("Test Initial", 2*HOUR, 0)); 201 } catch (IllegalArgumentException iae) { 202 bException = true; 203 } 204 if (!bException) { 205 errln("FAIL: InitialTimeZoneRule must be rejected"); 206 } 207 } 208 209 /* 210 * Test equivalency between OlsonTimeZone and custom RBTZ representing the 211 * equivalent rules in a certain time range 212 */ 213 @Test 214 public void TestHistoricalRuleBasedTimeZone() { 215 // Compare to America/New_York with equivalent RBTZ 216 TimeZone ny = TimeZone.getTimeZone("America/New_York", TimeZone.TIMEZONE_ICU); 217 218 //RBTZ 219 InitialTimeZoneRule ir = new InitialTimeZoneRule("EST", -5*HOUR, 0); 220 RuleBasedTimeZone rbtz = new RuleBasedTimeZone("EST5EDT", ir); 221 222 DateTimeRule dtr; 223 AnnualTimeZoneRule tzr; 224 225 // Standard time 226 dtr = new DateTimeRule(Calendar.OCTOBER, -1, Calendar.SUNDAY, 227 2*HOUR, DateTimeRule.WALL_TIME); // Last Sunday in October, at 2AM wall time 228 tzr = new AnnualTimeZoneRule("EST", -5*HOUR /* rawOffset */, 0 /* dstSavings */, dtr, 1967, 2006); 229 rbtz.addTransitionRule(tzr); 230 231 dtr = new DateTimeRule(Calendar.NOVEMBER, 1, Calendar.SUNDAY, 232 true, 2*HOUR, DateTimeRule.WALL_TIME); // SUN>=1 in November, at 2AM wall time 233 tzr = new AnnualTimeZoneRule("EST", -5*HOUR, 0, dtr, 2007, AnnualTimeZoneRule.MAX_YEAR); 234 rbtz.addTransitionRule(tzr); 235 236 // Daylight saving time 237 dtr = new DateTimeRule(Calendar.APRIL, -1, Calendar.SUNDAY, 238 2*HOUR, DateTimeRule.WALL_TIME); // Last Sunday in April, at 2AM wall time 239 tzr = new AnnualTimeZoneRule("EDT", -5*HOUR, 1*HOUR, dtr, 1967, 1973); 240 rbtz.addTransitionRule(tzr); 241 242 dtr = new DateTimeRule(Calendar.JANUARY, 6, 243 2*HOUR, DateTimeRule.WALL_TIME); // January 6, at 2AM wall time 244 tzr = new AnnualTimeZoneRule("EDT", -5*HOUR, 1*HOUR, dtr, 1974, 1974); 245 rbtz.addTransitionRule(tzr); 246 247 dtr = new DateTimeRule(Calendar.FEBRUARY, 23, 248 2*HOUR, DateTimeRule.WALL_TIME); // February 23, at 2AM wall time 249 tzr = new AnnualTimeZoneRule("EDT", -5*HOUR, 1*HOUR, dtr, 1975, 1975); 250 rbtz.addTransitionRule(tzr); 251 252 dtr = new DateTimeRule(Calendar.APRIL, -1, Calendar.SUNDAY, 253 2*HOUR, DateTimeRule.WALL_TIME); // Last Sunday in April, at 2AM wall time 254 tzr = new AnnualTimeZoneRule("EDT", -5*HOUR, 1*HOUR, dtr, 1976, 1986); 255 rbtz.addTransitionRule(tzr); 256 257 dtr = new DateTimeRule(Calendar.APRIL, 1, Calendar.SUNDAY, 258 true, 2*HOUR, DateTimeRule.WALL_TIME); // SUN>=1 in April, at 2AM wall time 259 tzr = new AnnualTimeZoneRule("EDT", -5*HOUR, 1*HOUR, dtr, 1987, 2006); 260 rbtz.addTransitionRule(tzr); 261 262 dtr = new DateTimeRule(Calendar.MARCH, 8, Calendar.SUNDAY, 263 true, 2*HOUR, DateTimeRule.WALL_TIME); // SUN>=8 in March, at 2AM wall time 264 tzr = new AnnualTimeZoneRule("EDT", -5*HOUR, 1*HOUR, dtr, 2007, AnnualTimeZoneRule.MAX_YEAR); 265 rbtz.addTransitionRule(tzr); 266 267 // hasEquivalentTransitions 268 long jan1_1950 = getUTCMillis(1950, Calendar.JANUARY, 1); 269 long jan1_1967 = getUTCMillis(1971, Calendar.JANUARY, 1); 270 long jan1_2010 = getUTCMillis(2010, Calendar.JANUARY, 1); 271 272 if (!(((BasicTimeZone)ny).hasEquivalentTransitions(rbtz, jan1_1967, jan1_2010))) { 273 errln("FAIL: The RBTZ must be equivalent to America/New_York between 1967 and 2010"); 274 } 275 if (((BasicTimeZone)ny).hasEquivalentTransitions(rbtz, jan1_1950, jan1_2010)) { 276 errln("FAIL: The RBTZ must not be equivalent to America/New_York between 1950 and 2010"); 277 } 278 279 // Same with above, but calling RBTZ#hasEquivalentTransitions against OlsonTimeZone 280 if (!rbtz.hasEquivalentTransitions(ny, jan1_1967, jan1_2010)) { 281 errln("FAIL: The RBTZ must be equivalent to America/New_York between 1967 and 2010"); 282 } 283 if (rbtz.hasEquivalentTransitions(ny, jan1_1950, jan1_2010)) { 284 errln("FAIL: The RBTZ must not be equivalent to America/New_York between 1950 and 2010"); 285 } 286 287 // TimeZone APIs 288 if (ny.hasSameRules(rbtz) || rbtz.hasSameRules(ny)) { 289 errln("FAIL: hasSameRules must return false"); 290 } 291 RuleBasedTimeZone rbtzc = (RuleBasedTimeZone)rbtz.clone(); 292 if (!rbtz.hasSameRules(rbtzc) || !rbtz.hasEquivalentTransitions(rbtzc, jan1_1950, jan1_2010)) { 293 errln("FAIL: hasSameRules/hasEquivalentTransitions must return true for cloned RBTZs"); 294 } 295 296 long times[] = { 297 getUTCMillis(2006, Calendar.MARCH, 15), 298 getUTCMillis(2006, Calendar.NOVEMBER, 1), 299 getUTCMillis(2007, Calendar.MARCH, 15), 300 getUTCMillis(2007, Calendar.NOVEMBER, 1), 301 getUTCMillis(2008, Calendar.MARCH, 15), 302 getUTCMillis(2008, Calendar.NOVEMBER, 1) 303 }; 304 int[] offsets1 = new int[2]; 305 int[] offsets2 = new int[2]; 306 307 for (int i = 0; i < times.length; i++) { 308 // Check getOffset - must return the same results for these time data 309 rbtz.getOffset(times[i], false, offsets1); 310 ny.getOffset(times[i], false, offsets2); 311 if (offsets1[0] != offsets2[0] || offsets1[1] != offsets2[1]) { 312 errln("FAIL: Incompatible time zone offsets for ny and rbtz"); 313 } 314 // Check inDaylightTime 315 Date d = new Date(times[i]); 316 if (rbtz.inDaylightTime(d) != ny.inDaylightTime(d)) { 317 errln("FAIL: Incompatible daylight saving time for ny and rbtz"); 318 } 319 } 320 } 321 322 /* 323 * Check if transitions returned by getNextTransition/getPreviousTransition 324 * are actual time transitions. 325 */ 326 @Test 327 public void TestOlsonTransition() { 328 String[] zids = getTestZIDs(); 329 for (int i = 0; i < zids.length; i++) { 330 TimeZone tz = TimeZone.getTimeZone(zids[i], TimeZone.TIMEZONE_ICU); 331 if (tz == null) { 332 break; 333 } 334 int j = 0; 335 while (true) { 336 long[] timerange = getTestTimeRange(j++); 337 if (timerange == null) { 338 break; 339 } 340 verifyTransitions(tz, timerange[0], timerange[1]); 341 } 342 } 343 } 344 345 /* 346 * Check if an OlsonTimeZone and its equivalent RBTZ have the exact same 347 * transitions. 348 */ 349 @Test 350 public void TestRBTZTransition() { 351 int[] STARTYEARS = { 352 1950, 353 1975, 354 2000, 355 2010 356 }; 357 358 String[] zids = getTestZIDs(); 359 for (int i = 0; i < zids.length; i++) { 360 TimeZone tz = TimeZone.getTimeZone(zids[i], TimeZone.TIMEZONE_ICU); 361 if (tz == null) { 362 break; 363 } 364 for (int j = 0; j < STARTYEARS.length; j++) { 365 long startTime = getUTCMillis(STARTYEARS[j], Calendar.JANUARY, 1); 366 TimeZoneRule[] rules = ((BasicTimeZone)tz).getTimeZoneRules(startTime); 367 RuleBasedTimeZone rbtz = new RuleBasedTimeZone(tz.getID() + "(RBTZ)", 368 (InitialTimeZoneRule)rules[0]); 369 for (int k = 1; k < rules.length; k++) { 370 rbtz.addTransitionRule(rules[k]); 371 } 372 373 // Compare the original OlsonTimeZone with the RBTZ starting the startTime for 20 years 374 long until = getUTCMillis(STARTYEARS[j] + 20, Calendar.JANUARY, 1); 375 376 // Ascending 377 compareTransitionsAscending(tz, rbtz, startTime, until, false); 378 // Ascending/inclusive 379 compareTransitionsAscending(tz, rbtz, startTime + 1, until, true); 380 // Descending 381 compareTransitionsDescending(tz, rbtz, startTime, until, false); 382 // Descending/inclusive 383 compareTransitionsDescending(tz, rbtz, startTime + 1, until, true); 384 } 385 386 } 387 } 388 389 /* 390 * Test cases for HasTimeZoneRules#hasEquivalentTransitions 391 */ 392 @Test 393 public void TestHasEquivalentTransitions() { 394 // America/New_York and America/Indiana/Indianapolis are equivalent 395 // since 2006 396 TimeZone newyork = TimeZone.getTimeZone("America/New_York", TimeZone.TIMEZONE_ICU); 397 TimeZone indianapolis = TimeZone.getTimeZone("America/Indiana/Indianapolis", TimeZone.TIMEZONE_ICU); 398 TimeZone gmt_5 = TimeZone.getTimeZone("Etc/GMT+5", TimeZone.TIMEZONE_ICU); 399 400 long jan1_1971 = getUTCMillis(1971, Calendar.JANUARY, 1); 401 long jan1_2005 = getUTCMillis(2005, Calendar.JANUARY, 1); 402 long jan1_2006 = getUTCMillis(2006, Calendar.JANUARY, 1); 403 long jan1_2007 = getUTCMillis(2007, Calendar.JANUARY, 1); 404 long jan1_2011 = getUTCMillis(2010, Calendar.JANUARY, 1); 405 406 if (((BasicTimeZone)newyork).hasEquivalentTransitions(indianapolis, jan1_2005, jan1_2011)) { 407 errln("FAIL: New_York is not equivalent to Indianapolis between 2005 and 2010, but returned true"); 408 } 409 if (!((BasicTimeZone)newyork).hasEquivalentTransitions(indianapolis, jan1_2006, jan1_2011)) { 410 errln("FAIL: New_York is equivalent to Indianapolis between 2006 and 2010, but returned false"); 411 } 412 413 if (!((BasicTimeZone)indianapolis).hasEquivalentTransitions(gmt_5, jan1_1971, jan1_2006)) { 414 errln("FAIL: Indianapolis is equivalent to GMT+5 between 1971 and 2005, but returned false"); 415 } 416 if (((BasicTimeZone)indianapolis).hasEquivalentTransitions(gmt_5, jan1_1971, jan1_2007)) { 417 errln("FAIL: Indianapolis is not equivalent to GMT+5 between 1971 and 2006, but returned true"); 418 } 419 420 // Cloned TimeZone 421 TimeZone newyork2 = (TimeZone)newyork.clone(); 422 if (!((BasicTimeZone)newyork).hasEquivalentTransitions(newyork2, jan1_1971, jan1_2011)) { 423 errln("FAIL: Cloned TimeZone must have the same transitions"); 424 } 425 if (!((BasicTimeZone)newyork).hasEquivalentTransitions(newyork2, jan1_1971, jan1_2011, true /*ignoreDstAmount*/)) { 426 errln("FAIL: Cloned TimeZone must have the same transitions"); 427 } 428 429 // America/New_York and America/Los_Angeles has same DST start rules, but 430 // raw offsets are different 431 TimeZone losangeles = TimeZone.getTimeZone("America/Los_Angeles", TimeZone.TIMEZONE_ICU); 432 if (((BasicTimeZone)newyork).hasEquivalentTransitions(losangeles, jan1_2006, jan1_2011)) { 433 errln("FAIL: New_York is not equivalent to Los Angeles, but returned true"); 434 } 435 } 436 437 /* 438 * Write out time zone rules of OlsonTimeZone into VTIMEZONE format, create a new 439 * VTimeZone from the VTIMEZONE data, then compare transitions 440 */ 441 @Test 442 public void TestVTimeZoneRoundTrip() { 443 long startTime = getUTCMillis(1850, Calendar.JANUARY, 1); 444 long endTime = getUTCMillis(2050, Calendar.JANUARY, 1); 445 446 String[] tzids = getTestZIDs(); 447 for (int i = 0; i < tzids.length; i++) { 448 BasicTimeZone olsontz = (BasicTimeZone)TimeZone.getTimeZone(tzids[i], TimeZone.TIMEZONE_ICU); 449 VTimeZone vtz_org = VTimeZone.create(tzids[i]); 450 vtz_org.setTZURL("http://source.icu-project.org/timezone"); 451 vtz_org.setLastModified(new Date()); 452 VTimeZone vtz_new = null; 453 try { 454 // Write out VTIMEZONE 455 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 456 OutputStreamWriter writer = new OutputStreamWriter(baos); 457 vtz_org.write(writer); 458 writer.close(); 459 byte[] vtzdata = baos.toByteArray(); 460 // Read VTIMEZONE 461 ByteArrayInputStream bais = new ByteArrayInputStream(vtzdata); 462 InputStreamReader reader = new InputStreamReader(bais); 463 vtz_new = VTimeZone.create(reader); 464 reader.close(); 465 466 // Write out VTIMEZONE one more time 467 ByteArrayOutputStream baos1 = new ByteArrayOutputStream(); 468 OutputStreamWriter writer1 = new OutputStreamWriter(baos1); 469 vtz_new.write(writer1); 470 writer1.close(); 471 byte[] vtzdata1 = baos1.toByteArray(); 472 473 // Make sure VTIMEZONE data is exactly same with the first one 474 if (vtzdata.length != vtzdata1.length) { 475 errln("FAIL: different VTIMEZONE data length"); 476 } 477 for (int j = 0; j < vtzdata.length; j++) { 478 if (vtzdata[j] != vtzdata1[j]) { 479 errln("FAIL: different VTIMEZONE data"); 480 break; 481 } 482 } 483 } catch (IOException ioe) { 484 errln("FAIL: IO error while writing/reading VTIMEZONE data"); 485 } 486 // Check equivalency after the first transition. 487 // The DST information before the first transition might be lost 488 // because there is no good way to represent the initial time with 489 // VTIMEZONE. 490 if (vtz_new.getOffset(startTime) != olsontz.getOffset(startTime)) { 491 errln("FAIL: VTimeZone for " + tzids[i] 492 + " is not equivalent to its OlsonTimeZone corresponding at " + startTime); 493 } 494 TimeZoneTransition tzt = olsontz.getNextTransition(startTime, false); 495 if (tzt != null) { 496 if (!vtz_new.hasEquivalentTransitions(olsontz, tzt.getTime(), endTime, true)) { 497 int maxDelta = 1000; 498 if (!hasEquivalentTransitions(vtz_new, olsontz, tzt.getTime() + maxDelta, endTime, true, maxDelta)) { 499 errln("FAIL: VTimeZone for " + tzids[i] + " is not equivalent to its OlsonTimeZone corresponding."); 500 } else { 501 logln("VTimeZone for " + tzids[i] + " differs from its OlsonTimeZone corresponding with maximum transition time delta - " + maxDelta); 502 } 503 } 504 if (!vtz_new.hasEquivalentTransitions(olsontz, tzt.getTime(), endTime, false)) { 505 logln("VTimeZone for " + tzids[i] + " is not equivalent to its OlsonTimeZone corresponding in strict comparison mode."); 506 } 507 } 508 } 509 } 510 511 /* 512 * Write out time zone rules of OlsonTimeZone after a cutoff date into VTIMEZONE format, 513 * create a new VTimeZone from the VTIMEZONE data, then compare transitions 514 */ 515 @Test 516 public void TestVTimeZoneRoundTripPartial() { 517 long[] startTimes = new long[] { 518 getUTCMillis(1900, Calendar.JANUARY, 1), 519 getUTCMillis(1950, Calendar.JANUARY, 1), 520 getUTCMillis(2020, Calendar.JANUARY, 1) 521 }; 522 long endTime = getUTCMillis(2050, Calendar.JANUARY, 1); 523 524 String[] tzids = getTestZIDs(); 525 for (int n = 0; n < startTimes.length; n++) { 526 long startTime = startTimes[n]; 527 for (int i = 0; i < tzids.length; i++) { 528 BasicTimeZone olsontz = (BasicTimeZone)TimeZone.getTimeZone(tzids[i], TimeZone.TIMEZONE_ICU); 529 VTimeZone vtz_org = VTimeZone.create(tzids[i]); 530 VTimeZone vtz_new = null; 531 try { 532 // Write out VTIMEZONE 533 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 534 OutputStreamWriter writer = new OutputStreamWriter(baos); 535 vtz_org.write(writer, startTime); 536 writer.close(); 537 byte[] vtzdata = baos.toByteArray(); 538 // Read VTIMEZONE 539 ByteArrayInputStream bais = new ByteArrayInputStream(vtzdata); 540 InputStreamReader reader = new InputStreamReader(bais); 541 vtz_new = VTimeZone.create(reader); 542 reader.close(); 543 544 } catch (IOException ioe) { 545 errln("FAIL: IO error while writing/reading VTIMEZONE data"); 546 } 547 // Check equivalency after the first transition. 548 // The DST information before the first transition might be lost 549 // because there is no good way to represent the initial time with 550 // VTIMEZONE. 551 if (vtz_new.getOffset(startTime) != olsontz.getOffset(startTime)) { 552 errln("FAIL: VTimeZone for " + tzids[i] 553 + " is not equivalent to its OlsonTimeZone corresponding at " + startTime); 554 } 555 TimeZoneTransition tzt = olsontz.getNextTransition(startTime, false); 556 if (tzt != null) { 557 if (!vtz_new.hasEquivalentTransitions(olsontz, tzt.getTime(), endTime, true)) { 558 int maxDelta = 1000; 559 if (!hasEquivalentTransitions(vtz_new, olsontz, tzt.getTime() + maxDelta, endTime, true, maxDelta)) { 560 errln("FAIL: VTimeZone for " + tzids[i] + "(>=" + startTime + ") is not equivalent to its OlsonTimeZone corresponding."); 561 } else { 562 logln("VTimeZone for " + tzids[i] + "(>=" + startTime + ") differs from its OlsonTimeZone corresponding with maximum transition time delta - " + maxDelta); 563 } 564 } 565 } 566 } 567 } 568 } 569 570 /* 571 * Write out simple time zone rules from an OlsonTimeZone at various time into VTIMEZONE 572 * format and create a new VTimeZone from the VTIMEZONE data, then make sure the raw offset 573 * and DST savings are same in these two time zones. 574 */ 575 @Test 576 public void TestVTimeZoneSimpleWrite() { 577 long[] testTimes = new long[] { 578 getUTCMillis(2006, Calendar.JANUARY, 1), 579 getUTCMillis(2006, Calendar.MARCH, 15), 580 getUTCMillis(2006, Calendar.MARCH, 31), 581 getUTCMillis(2006, Calendar.APRIL, 5), 582 getUTCMillis(2006, Calendar.OCTOBER, 25), 583 getUTCMillis(2006, Calendar.NOVEMBER, 1), 584 getUTCMillis(2006, Calendar.NOVEMBER, 5), 585 getUTCMillis(2007, Calendar.JANUARY, 1) 586 }; 587 588 String[] tzids = getTestZIDs(); 589 for (int n = 0; n < testTimes.length; n++) { 590 long time = testTimes[n]; 591 592 int[] offsets1 = new int[2]; 593 int[] offsets2 = new int[2]; 594 595 for (int i = 0; i < tzids.length; i++) { 596 VTimeZone vtz_org = VTimeZone.create(tzids[i]); 597 VTimeZone vtz_new = null; 598 try { 599 // Write out VTIMEZONE 600 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 601 OutputStreamWriter writer = new OutputStreamWriter(baos); 602 vtz_org.writeSimple(writer, time); 603 writer.close(); 604 byte[] vtzdata = baos.toByteArray(); 605 // Read VTIMEZONE 606 ByteArrayInputStream bais = new ByteArrayInputStream(vtzdata); 607 InputStreamReader reader = new InputStreamReader(bais); 608 vtz_new = VTimeZone.create(reader); 609 reader.close(); 610 } catch (IOException ioe) { 611 errln("FAIL: IO error while writing/reading VTIMEZONE data"); 612 } 613 614 // Check equivalency 615 vtz_org.getOffset(time, false, offsets1); 616 vtz_new.getOffset(time, false, offsets2); 617 if (offsets1[0] != offsets2[0] || offsets1[1] != offsets2[1]) { 618 errln("FAIL: VTimeZone writeSimple for " + tzids[i] + " at time " + time + " failed to the round trip."); 619 } 620 } 621 } 622 } 623 624 /* 625 * Write out time zone rules of OlsonTimeZone into VTIMEZONE format with RFC2445 header TZURL and 626 * LAST-MODIFIED, create a new VTimeZone from the VTIMEZONE data to see if the headers are preserved. 627 */ 628 @Test 629 public void TestVTimeZoneHeaderProps() { 630 String tzid = "America/Chicago"; 631 String tzurl = "http://source.icu-project.org"; 632 Date lastmod = new Date(getUTCMillis(2007, Calendar.JUNE, 1)); 633 634 VTimeZone vtz = VTimeZone.create(tzid); 635 vtz.setTZURL(tzurl); 636 vtz.setLastModified(lastmod); 637 638 // Roundtrip conversion 639 VTimeZone newvtz1 = null; 640 try { 641 // Write out VTIMEZONE 642 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 643 OutputStreamWriter writer = new OutputStreamWriter(baos); 644 vtz.write(writer); 645 writer.close(); 646 byte[] vtzdata = baos.toByteArray(); 647 // Read VTIMEZONE 648 ByteArrayInputStream bais = new ByteArrayInputStream(vtzdata); 649 InputStreamReader reader = new InputStreamReader(bais); 650 newvtz1 = VTimeZone.create(reader); 651 reader.close(); 652 653 // Check if TZURL and LAST-MODIFIED headers are preserved 654 if (!(tzurl.equals(newvtz1.getTZURL()))) { 655 errln("FAIL: TZURL property is not preserved during the roundtrip conversion. Before:" 656 + tzurl + "/After:" + newvtz1.getTZURL()); 657 } 658 if (!(lastmod.equals(newvtz1.getLastModified()))) { 659 errln("FAIL: LAST-MODIFIED property is not preserved during the roundtrip conversion. Before:" 660 + lastmod.getTime() + "/After:" + newvtz1.getLastModified().getTime()); 661 } 662 } catch (IOException ioe) { 663 errln("FAIL: IO error while writing/reading VTIMEZONE data"); 664 } 665 666 // Second roundtrip, with a cutoff 667 VTimeZone newvtz2 = null; 668 try { 669 // Set different tzurl 670 String newtzurl = "http://www.ibm.com"; 671 newvtz1.setTZURL(newtzurl); 672 // Write out VTIMEZONE 673 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 674 OutputStreamWriter writer = new OutputStreamWriter(baos); 675 newvtz1.write(writer, getUTCMillis(2000, Calendar.JANUARY, 1)); 676 writer.close(); 677 byte[] vtzdata = baos.toByteArray(); 678 // Read VTIMEZONE 679 ByteArrayInputStream bais = new ByteArrayInputStream(vtzdata); 680 InputStreamReader reader = new InputStreamReader(bais); 681 newvtz2 = VTimeZone.create(reader); 682 reader.close(); 683 684 // Check if TZURL and LAST-MODIFIED headers are preserved 685 if (!(newtzurl.equals(newvtz2.getTZURL()))) { 686 errln("FAIL: TZURL property is not preserved during the second roundtrip conversion. Before:" 687 + newtzurl + "/After:" + newvtz2.getTZURL()); 688 } 689 if (!(lastmod.equals(newvtz2.getLastModified()))) { 690 errln("FAIL: LAST-MODIFIED property is not preserved during the second roundtrip conversion. Before:" 691 + lastmod.getTime() + "/After:" + newvtz2.getLastModified().getTime()); 692 } 693 } catch (IOException ioe) { 694 errln("FAIL: IO error while writing/reading VTIMEZONE data"); 695 } 696 697 } 698 699 /* 700 * Extract simple rules from an OlsonTimeZone and make sure the rule format matches 701 * the expected format. 702 */ 703 @Test 704 public void TestGetSimpleRules() { 705 long[] testTimes = new long[] { 706 getUTCMillis(1970, Calendar.JANUARY, 1), 707 getUTCMillis(2000, Calendar.MARCH, 31), 708 getUTCMillis(2005, Calendar.JULY, 1), 709 getUTCMillis(2010, Calendar.NOVEMBER, 1), 710 }; 711 712 String[] tzids = getTestZIDs(); 713 for (int n = 0; n < testTimes.length; n++) { 714 long time = testTimes[n]; 715 for (int i = 0; i < tzids.length; i++) { 716 BasicTimeZone tz = (BasicTimeZone)TimeZone.getTimeZone(tzids[i], TimeZone.TIMEZONE_ICU); 717 TimeZoneRule[] rules = tz.getSimpleTimeZoneRulesNear(time); 718 if (rules == null) { 719 errln("FAIL: Failed to extract simple rules for " + tzids[i] + " at " + time); 720 } else { 721 if (rules.length == 1) { 722 if (!(rules[0] instanceof InitialTimeZoneRule)) { 723 errln("FAIL: Unexpected rule object type is returned for " + tzids[i] + " at " + time); 724 } 725 } else if (rules.length == 3) { 726 if (!(rules[0] instanceof InitialTimeZoneRule) 727 || !(rules[1] instanceof AnnualTimeZoneRule) 728 || !(rules[2] instanceof AnnualTimeZoneRule)) { 729 errln("FAIL: Unexpected rule object type is returned for " + tzids[i] + " at " + time); 730 } 731 for (int idx = 1; idx <= 2; idx++) { 732 DateTimeRule dtr = ((AnnualTimeZoneRule)rules[idx]).getRule(); 733 if (dtr.getTimeRuleType() != DateTimeRule.WALL_TIME) { 734 errln("FAIL: WALL_TIME is not used as the time rule in the time zone rule(" + idx + ") for " + tzids[i] + " at " + time); 735 } 736 if (dtr.getDateRuleType() != DateTimeRule.DOW) { 737 errln("FAIL: DOW is not used as the date rule in the time zone rule(" + idx + ") for " + tzids[i] + " at " + time); 738 } 739 } 740 } else { 741 errln("FAIL: Unexpected number of rules returned for " + tzids[i] + " at " + time); 742 } 743 } 744 } 745 } 746 } 747 748 /* 749 * API coverage tests for TimeZoneRule 750 */ 751 @Test 752 public void TestTimeZoneRuleCoverage() { 753 long time1 = getUTCMillis(2005, Calendar.JULY, 4); 754 long time2 = getUTCMillis(2015, Calendar.JULY, 4); 755 long time3 = getUTCMillis(1950, Calendar.JULY, 4); 756 757 DateTimeRule dtr1 = new DateTimeRule(Calendar.FEBRUARY, 29, Calendar.SUNDAY, false, 758 3*HOUR, DateTimeRule.WALL_TIME); // Last Sunday on or before Feb 29, at 3 AM, wall time 759 DateTimeRule dtr2 = new DateTimeRule(Calendar.MARCH, 11, 2*HOUR, 760 DateTimeRule.STANDARD_TIME); // Mar 11, at 2 AM, standard time 761 DateTimeRule dtr3 = new DateTimeRule(Calendar.OCTOBER, -1, Calendar.SATURDAY, 762 6*HOUR, DateTimeRule.UTC_TIME); //Last Saturday in Oct, at 6 AM, UTC 763 DateTimeRule dtr4 = new DateTimeRule(Calendar.MARCH, 8, Calendar.SUNDAY, true, 764 2*HOUR, DateTimeRule.WALL_TIME); // First Sunday on or after Mar 8, at 2 AM, wall time 765 766 AnnualTimeZoneRule a1 = new AnnualTimeZoneRule("a1", -3*HOUR, 1*HOUR, dtr1, 767 2000, AnnualTimeZoneRule.MAX_YEAR); 768 AnnualTimeZoneRule a2 = new AnnualTimeZoneRule("a2", -3*HOUR, 1*HOUR, dtr1, 769 2000, AnnualTimeZoneRule.MAX_YEAR); 770 AnnualTimeZoneRule a3 = new AnnualTimeZoneRule("a3", -3*HOUR, 1*HOUR, dtr1, 771 2000, 2010); 772 773 InitialTimeZoneRule i1 = new InitialTimeZoneRule("i1", -3*HOUR, 0); 774 InitialTimeZoneRule i2 = new InitialTimeZoneRule("i2", -3*HOUR, 0); 775 InitialTimeZoneRule i3 = new InitialTimeZoneRule("i3", -3*HOUR, 1*HOUR); 776 777 long[] emptytimes = {}; 778 long[] trtimes1 = {0}; 779 long[] trtimes2 = {0, 10000000}; 780 781 TimeArrayTimeZoneRule t0 = null; 782 try { 783 // Try to construct TimeArrayTimeZoneRule with null transition times 784 t0 = new TimeArrayTimeZoneRule("nulltimes", -3*HOUR, 0, 785 null, DateTimeRule.UTC_TIME); 786 } catch (IllegalArgumentException iae) { 787 logln("TimeArrayTimeZoneRule constructor throws IllegalArgumentException as expected."); 788 t0 = null; 789 } 790 if (t0 != null) { 791 errln("FAIL: TimeArrayTimeZoneRule constructor did not throw IllegalArgumentException for null times"); 792 } 793 794 try { 795 // Try to construct TimeArrayTimeZoneRule with empty transition times 796 t0 = new TimeArrayTimeZoneRule("nulltimes", -3*HOUR, 0, 797 emptytimes, DateTimeRule.UTC_TIME); 798 } catch (IllegalArgumentException iae) { 799 logln("TimeArrayTimeZoneRule constructor throws IllegalArgumentException as expected."); 800 t0 = null; 801 } 802 if (t0 != null) { 803 errln("FAIL: TimeArrayTimeZoneRule constructor did not throw IllegalArgumentException for empty times"); 804 } 805 806 TimeArrayTimeZoneRule t1 = new TimeArrayTimeZoneRule("t1", -3*HOUR, 0, trtimes1, DateTimeRule.UTC_TIME); 807 TimeArrayTimeZoneRule t2 = new TimeArrayTimeZoneRule("t2", -3*HOUR, 0, trtimes1, DateTimeRule.UTC_TIME); 808 TimeArrayTimeZoneRule t3 = new TimeArrayTimeZoneRule("t3", -3*HOUR, 0, trtimes2, DateTimeRule.UTC_TIME); 809 TimeArrayTimeZoneRule t4 = new TimeArrayTimeZoneRule("t4", -3*HOUR, 0, trtimes1, DateTimeRule.STANDARD_TIME); 810 TimeArrayTimeZoneRule t5 = new TimeArrayTimeZoneRule("t5", -4*HOUR, 1*HOUR, trtimes1, DateTimeRule.WALL_TIME); 811 812 // AnnualTimeZoneRule#getRule 813 if (!a1.getRule().equals(a2.getRule())) { 814 errln("FAIL: The same DateTimeRule must be returned from AnnualTimeZoneRule a1 and a2"); 815 } 816 817 // AnnualTimeZoneRule#getStartYear 818 int startYear = a1.getStartYear(); 819 if (startYear != 2000) { 820 errln("FAIL: The start year of AnnualTimeZoneRule a1 must be 2000 - returned: " + startYear); 821 } 822 823 // AnnualTimeZoneRule#getEndYear 824 int endYear = a1.getEndYear(); 825 if (endYear != AnnualTimeZoneRule.MAX_YEAR) { 826 errln("FAIL: The start year of AnnualTimeZoneRule a1 must be MAX_YEAR - returned: " + endYear); 827 } 828 endYear = a3.getEndYear(); 829 if (endYear != 2010) { 830 errln("FAIL: The start year of AnnualTimeZoneRule a3 must be 2010 - returned: " + endYear); 831 } 832 833 // AnnualTimeZone#getStartInYear 834 Date d1 = a1.getStartInYear(2005, -3*HOUR, 0); 835 Date d2 = a3.getStartInYear(2005, -3*HOUR, 0); 836 if (d1 == null || d2 == null || !d1.equals(d2)) { 837 errln("FAIL: AnnualTimeZoneRule#getStartInYear did not work as expected"); 838 } 839 d2 = a3.getStartInYear(2015, -3*HOUR, 0); 840 if (d2 != null) { 841 errln("FAIL: AnnualTimeZoneRule#getSTartInYear returned non-null date for 2015 which is out of rule range"); 842 } 843 844 // AnnualTimeZone#getFirstStart 845 d1 = a1.getFirstStart(-3*HOUR, 0); 846 d2 = a1.getFirstStart(-4*HOUR, 1*HOUR); 847 if (d1 == null || d2 == null || !d1.equals(d2)) { 848 errln("FAIL: The same start time should be returned by getFirstStart"); 849 } 850 851 // AnnualTimeZone#getFinalStart 852 d1 = a1.getFinalStart(-3*HOUR, 0); 853 if (d1 != null) { 854 errln("FAIL: Non-null Date is returned by getFinalStart for a1"); 855 } 856 d1 = a1.getStartInYear(2010, -3*HOUR, 0); 857 d2 = a3.getFinalStart(-3*HOUR, 0); 858 if (d1 == null || d2 == null || !d1.equals(d2)) { 859 errln("FAIL: Bad date is returned by getFinalStart"); 860 } 861 862 // AnnualTimeZone#getNextStart / getPreviousStart 863 d1 = a1.getNextStart(time1, -3*HOUR, 0, false); 864 if (d1 == null) { 865 errln("FAIL: Null Date is returned by getNextStart"); 866 } else { 867 d2 = a1.getPreviousStart(d1.getTime(), -3*HOUR, 0, true); 868 if (d2 == null || !d1.equals(d2)) { 869 errln("FAIL: Bad Date is returned by getPreviousStart"); 870 } 871 } 872 d1 = a3.getNextStart(time2, -3*HOUR, 0, false); 873 if (d1 != null) { 874 errln("FAIL: getNextStart must return null when no start time is available after the base time"); 875 } 876 d1 = a3.getFinalStart(-3*HOUR, 0); 877 d2 = a3.getPreviousStart(time2, -3*HOUR, 0, false); 878 if (d1 == null || d2 == null || !d1.equals(d2)) { 879 errln("FAIL: getPreviousStart does not match with getFinalStart after the end year"); 880 } 881 882 // AnnualTimeZone#isEquavalentTo 883 if (!a1.isEquivalentTo(a2)) { 884 errln("FAIL: AnnualTimeZoneRule a1 is equivalent to a2, but returned false"); 885 } 886 if (a1.isEquivalentTo(a3)) { 887 errln("FAIL: AnnualTimeZoneRule a1 is not equivalent to a3, but returned true"); 888 } 889 if (!a1.isEquivalentTo(a1)) { 890 errln("FAIL: AnnualTimeZoneRule a1 is equivalent to itself, but returned false"); 891 } 892 if (a1.isEquivalentTo(t1)) { 893 errln("FAIL: AnnualTimeZoneRule is not equivalent to TimeArrayTimeZoneRule, but returned true"); 894 } 895 896 // AnnualTimeZone#isTransitionRule 897 if (!a1.isTransitionRule()) { 898 errln("FAIL: An AnnualTimeZoneRule is a transition rule, but returned false"); 899 } 900 901 // AnnualTimeZone#toString 902 String str = a1.toString(); 903 if (str == null || str.length() == 0) { 904 errln("FAIL: AnnualTimeZoneRule#toString for a1 returns null or empty string"); 905 } else { 906 logln("AnnualTimeZoneRule a1 : " + str); 907 } 908 str = a3.toString(); 909 if (str == null || str.length() == 0) { 910 errln("FAIL: AnnualTimeZoneRule#toString for a3 returns null or empty string"); 911 } else { 912 logln("AnnualTimeZoneRule a3 : " + str); 913 } 914 915 // InitialTimeZoneRule#isEquivalentRule 916 if (!i1.isEquivalentTo(i2)) { 917 errln("FAIL: InitialTimeZoneRule i1 is equivalent to i2, but returned false"); 918 } 919 if (i1.isEquivalentTo(i3)) { 920 errln("FAIL: InitialTimeZoneRule i1 is not equivalent to i3, but returned true"); 921 } 922 if (i1.isEquivalentTo(a1)) { 923 errln("FAIL: An InitialTimeZoneRule is not equivalent to an AnnualTimeZoneRule, but returned true"); 924 } 925 926 // InitialTimeZoneRule#getFirstStart/getFinalStart/getNextStart/getPreviousStart 927 d1 = i1.getFirstStart(0, 0); 928 if (d1 != null) { 929 errln("FAIL: Non-null Date is returned by InitialTimeZone#getFirstStart"); 930 } 931 d1 = i1.getFinalStart(0, 0); 932 if (d1 != null) { 933 errln("FAIL: Non-null Date is returned by InitialTimeZone#getFinalStart"); 934 } 935 d1 = i1.getNextStart(time1, 0, 0, false); 936 if (d1 != null) { 937 errln("FAIL: Non-null Date is returned by InitialTimeZone#getNextStart"); 938 } 939 d1 = i1.getPreviousStart(time1, 0, 0, false); 940 if (d1 != null) { 941 errln("FAIL: Non-null Date is returned by InitialTimeZone#getPreviousStart"); 942 } 943 944 // InitialTimeZoneRule#isTransitionRule 945 if (i1.isTransitionRule()) { 946 errln("FAIL: An InitialTimeZoneRule is not a transition rule, but returned true"); 947 } 948 949 // InitialTimeZoneRule#toString 950 str = i1.toString(); 951 if (str == null || str.length() == 0) { 952 errln("FAIL: InitialTimeZoneRule#toString returns null or empty string"); 953 } else { 954 logln("InitialTimeZoneRule i1 : " + str); 955 } 956 957 958 // TimeArrayTimeZoneRule#getStartTimes 959 long[] times = t1.getStartTimes(); 960 if (times == null || times.length == 0 || times[0] != 0) { 961 errln("FAIL: Bad start times are returned by TimeArrayTimeZoneRule#getStartTimes"); 962 } 963 964 // TimeArrayTimeZoneRule#getTimeType 965 if (t1.getTimeType() != DateTimeRule.UTC_TIME) { 966 errln("FAIL: TimeArrayTimeZoneRule t1 uses UTC_TIME, but different type is returned"); 967 } 968 if (t4.getTimeType() != DateTimeRule.STANDARD_TIME) { 969 errln("FAIL: TimeArrayTimeZoneRule t4 uses STANDARD_TIME, but different type is returned"); 970 } 971 if (t5.getTimeType() != DateTimeRule.WALL_TIME) { 972 errln("FAIL: TimeArrayTimeZoneRule t5 uses WALL_TIME, but different type is returned"); 973 } 974 975 // TimeArrayTimeZoneRule#getFirstStart/getFinalStart 976 d1 = t1.getFirstStart(0, 0); 977 if (d1 == null || d1.getTime() != trtimes1[0]) { 978 errln("FAIL: Bad first start time returned from TimeArrayTimeZoneRule t1"); 979 } 980 d1 = t1.getFinalStart(0, 0); 981 if (d1 == null || d1.getTime() != trtimes1[0]) { 982 errln("FAIL: Bad final start time returned from TimeArrayTimeZoneRule t1"); 983 } 984 d1 = t4.getFirstStart(-4*HOUR, 1*HOUR); 985 if (d1 == null || (d1.getTime() != trtimes1[0] + 4*HOUR)) { 986 errln("FAIL: Bad first start time returned from TimeArrayTimeZoneRule t4"); 987 } 988 d1 = t5.getFirstStart(-4*HOUR, 1*HOUR); 989 if (d1 == null || (d1.getTime() != trtimes1[0] + 3*HOUR)) { 990 errln("FAIL: Bad first start time returned from TimeArrayTimeZoneRule t5"); 991 } 992 993 // TimeArrayTimeZoneRule#getNextStart/getPreviousStart 994 d1 = t3.getNextStart(time1, -3*HOUR, 1*HOUR, false); 995 if (d1 != null) { 996 errln("FAIL: Non-null Date is returned by getNextStart after the final transition for t3"); 997 } 998 d1 = t3.getPreviousStart(time1, -3*HOUR, 1*HOUR, false); 999 if (d1 == null || d1.getTime() != trtimes2[1]) { 1000 errln("FAIL: Bad start time returned by getPreviousStart for t3"); 1001 } else { 1002 d2 = t3.getPreviousStart(d1.getTime(), -3*HOUR, 1*HOUR, false); 1003 if (d2 == null || d2.getTime() != trtimes2[0]) { 1004 errln("FAIL: Bad start time returned by getPreviousStart for t3"); 1005 } 1006 } 1007 d1 = t3.getPreviousStart(time3, -3*HOUR, 1*HOUR, false); //time3 - year 1950, no result expected 1008 if (d1 != null) { 1009 errln("FAIL: Non-null Date is returned by getPrevoousStart for t3"); 1010 } 1011 1012 // TimeArrayTimeZoneRule#isEquivalentTo 1013 if (!t1.isEquivalentTo(t2)) { 1014 errln("FAIL: TimeArrayTimeZoneRule t1 is equivalent to t2, but returned false"); 1015 } 1016 if (t1.isEquivalentTo(t3)) { 1017 errln("FAIL: TimeArrayTimeZoneRule t1 is not equivalent to t3, but returned true"); 1018 } 1019 if (t1.isEquivalentTo(t4)) { 1020 errln("FAIL: TimeArrayTimeZoneRule t1 is not equivalent to t4, but returned true"); 1021 } 1022 if (t1.isEquivalentTo(a1)) { 1023 errln("FAIL: TimeArrayTimeZoneRule is not equivalent to AnnualTimeZoneRule, but returned true"); 1024 } 1025 1026 // TimeArrayTimeZoneRule#isTransitionRule 1027 if (!t1.isTransitionRule()) { 1028 errln("FAIL: A TimeArrayTimeZoneRule is a transition rule, but returned false"); 1029 } 1030 1031 // TimeArrayTimeZoneRule#toString 1032 str = t3.toString(); 1033 if (str == null || str.length() == 0) { 1034 errln("FAIL: TimeArrayTimeZoneRule#toString returns null or empty string"); 1035 } else { 1036 logln("TimeArrayTimeZoneRule t3 : " + str); 1037 } 1038 1039 // DateTimeRule#toString 1040 str = dtr1.toString(); 1041 if (str == null || str.length() == 0) { 1042 errln("FAIL: DateTimeRule#toString for dtr1 returns null or empty string"); 1043 } else { 1044 logln("DateTimeRule dtr1 : " + str); 1045 } 1046 str = dtr2.toString(); 1047 if (str == null || str.length() == 0) { 1048 errln("FAIL: DateTimeRule#toString for dtr2 returns null or empty string"); 1049 } else { 1050 logln("DateTimeRule dtr1 : " + str); 1051 } 1052 str = dtr3.toString(); 1053 if (str == null || str.length() == 0) { 1054 errln("FAIL: DateTimeRule#toString for dtr3 returns null or empty string"); 1055 } else { 1056 logln("DateTimeRule dtr1 : " + str); 1057 } 1058 str = dtr4.toString(); 1059 if (str == null || str.length() == 0) { 1060 errln("FAIL: DateTimeRule#toString for dtr4 returns null or empty string"); 1061 } else { 1062 logln("DateTimeRule dtr1 : " + str); 1063 } 1064 } 1065 1066 /* 1067 * API coverage test for BasicTimeZone APIs in SimpleTimeZone 1068 */ 1069 @Test 1070 public void TestSimpleTimeZoneCoverage() { 1071 1072 long time1 = getUTCMillis(1990, Calendar.JUNE, 1); 1073 long time2 = getUTCMillis(2000, Calendar.JUNE, 1); 1074 1075 TimeZoneTransition tzt1, tzt2; 1076 1077 // BasicTimeZone API implementation in SimpleTimeZone 1078 SimpleTimeZone stz1 = new SimpleTimeZone(-5*HOUR, "GMT-5"); 1079 1080 tzt1 = stz1.getNextTransition(time1, false); 1081 if (tzt1 != null) { 1082 errln("FAIL: No transition must be returned by getNextTranstion for SimpleTimeZone with no DST rule"); 1083 } 1084 tzt1 = stz1.getPreviousTransition(time1, false); 1085 if (tzt1 != null) { 1086 errln("FAIL: No transition must be returned by getPreviousTransition for SimpleTimeZone with no DST rule"); 1087 } 1088 TimeZoneRule[] tzrules = stz1.getTimeZoneRules(); 1089 if (tzrules.length != 1 || !(tzrules[0] instanceof InitialTimeZoneRule)) { 1090 errln("FAIL: Invalid results returned by SimpleTimeZone#getTimeZoneRules"); 1091 } 1092 1093 // Set DST rule 1094 stz1.setStartRule(Calendar.MARCH, 11, 2*HOUR); // March 11 1095 stz1.setEndRule(Calendar.NOVEMBER, 1, Calendar.SUNDAY, 2*HOUR); // First Sunday in November 1096 tzt1 = stz1.getNextTransition(time1, false); 1097 if (tzt1 == null) { 1098 errln("FAIL: Non-null transition must be returned by getNextTranstion for SimpleTimeZone with a DST rule"); 1099 } else { 1100 String str = tzt1.toString(); 1101 if (str == null || str.length() == 0) { 1102 errln("FAIL: TimeZoneTransition#toString returns null or empty string"); 1103 } else { 1104 logln(str); 1105 } 1106 } 1107 tzt1 = stz1.getPreviousTransition(time1, false); 1108 if (tzt1 == null) { 1109 errln("FAIL: Non-null transition must be returned by getPreviousTransition for SimpleTimeZone with a DST rule"); 1110 } 1111 tzrules = stz1.getTimeZoneRules(); 1112 if (tzrules.length != 3 || !(tzrules[0] instanceof InitialTimeZoneRule) 1113 || !(tzrules[1] instanceof AnnualTimeZoneRule) 1114 || !(tzrules[2] instanceof AnnualTimeZoneRule)) { 1115 errln("FAIL: Invalid results returned by SimpleTimeZone#getTimeZoneRules for a SimpleTimeZone with DST"); 1116 } 1117 // Set DST start year 1118 stz1.setStartYear(2007); 1119 tzt1 = stz1.getPreviousTransition(time1, false); 1120 if (tzt1 != null) { 1121 errln("FAIL: No transition must be returned before 1990"); 1122 } 1123 tzt1 = stz1.getNextTransition(time1, false); // transition after 1990-06-01 1124 tzt2 = stz1.getNextTransition(time2, false); // transition after 2000-06-01 1125 if (tzt1 == null || tzt2 == null || !tzt1.equals(tzt2)) { 1126 errln("FAIL: Bad transition returned by SimpleTimeZone#getNextTransition"); 1127 } 1128 } 1129 1130 /* 1131 * API coverage test for VTimeZone 1132 */ 1133 @Test 1134 public void TestVTimeZoneCoverage() { 1135 final String TZID = "Europe/Moscow"; 1136 BasicTimeZone otz = (BasicTimeZone)TimeZone.getTimeZone(TZID, TimeZone.TIMEZONE_ICU); 1137 VTimeZone vtz = VTimeZone.create(TZID); 1138 1139 // getOffset(era, year, month, day, dayOfWeek, milliseconds) 1140 int offset1 = otz.getOffset(GregorianCalendar.AD, 2007, Calendar.JULY, 1, Calendar.SUNDAY, 0); 1141 int offset2 = vtz.getOffset(GregorianCalendar.AD, 2007, Calendar.JULY, 1, Calendar.SUNDAY, 0); 1142 if (offset1 != offset2) { 1143 errln("FAIL: getOffset(int,int,int,int,int,int) returned different results in VTimeZone and OlsonTimeZone"); 1144 } 1145 1146 // getOffset(date, local, offsets) 1147 int[] offsets1 = new int[2]; 1148 int[] offsets2 = new int[2]; 1149 long t = System.currentTimeMillis(); 1150 otz.getOffset(t, false, offsets1); 1151 vtz.getOffset(t, false, offsets2); 1152 if (offsets1[0] != offsets2[0] || offsets1[1] != offsets2[1]) { 1153 errln("FAIL: getOffset(long,boolean,int[]) returned different results in VTimeZone and OlsonTimeZone"); 1154 } 1155 1156 // getRawOffset 1157 if (otz.getRawOffset() != vtz.getRawOffset()) { 1158 errln("FAIL: getRawOffset returned different results in VTimeZone and OlsonTimeZone"); 1159 } 1160 1161 // inDaylightTime 1162 Date d = new Date(); 1163 if (otz.inDaylightTime(d) != vtz.inDaylightTime(d)) { 1164 errln("FAIL: inDaylightTime returned different results in VTimeZone and OlsonTimeZone"); 1165 } 1166 1167 // useDaylightTime 1168 if (otz.useDaylightTime() != vtz.useDaylightTime()) { 1169 errln("FAIL: useDaylightTime returned different results in VTimeZone and OlsonTimeZone"); 1170 } 1171 1172 // setRawOffset 1173 final int RAW = -10*HOUR; 1174 VTimeZone tmpvtz = (VTimeZone)vtz.clone(); 1175 tmpvtz.setRawOffset(RAW); 1176 if (tmpvtz.getRawOffset() != RAW) { 1177 logln("setRawOffset is implemented"); 1178 } 1179 1180 // hasSameRules 1181 boolean bSame = otz.hasSameRules(vtz); 1182 logln("OlsonTimeZone#hasSameRules(VTimeZone) should return false always for now - actual: " + bSame); 1183 1184 // getTZURL/setTZURL 1185 final String TZURL = "http://icu-project.org/timezone"; 1186 String tzurl = vtz.getTZURL(); 1187 if (tzurl != null) { 1188 errln("FAIL: getTZURL returned non-null value"); 1189 } 1190 vtz.setTZURL(TZURL); 1191 tzurl = vtz.getTZURL(); 1192 if (!TZURL.equals(tzurl)) { 1193 errln("FAIL: URL returned by getTZURL does not match the one set by setTZURL"); 1194 } 1195 1196 // getLastModified/setLastModified 1197 Date lastmod = vtz.getLastModified(); 1198 if (lastmod != null) { 1199 errln("FAIL: getLastModified returned non-null value"); 1200 } 1201 Date newdate = new Date(); 1202 vtz.setLastModified(newdate); 1203 lastmod = vtz.getLastModified(); 1204 if (!newdate.equals(lastmod)) { 1205 errln("FAIL: Date returned by getLastModified does not match the one set by setLastModified"); 1206 } 1207 1208 // getNextTransition/getPreviousTransition 1209 long base = getUTCMillis(2007, Calendar.JULY, 1); 1210 TimeZoneTransition tzt1 = otz.getNextTransition(base, true); 1211 TimeZoneTransition tzt2 = vtz.getNextTransition(base, true); 1212 if (tzt1.equals(tzt2)) { 1213 errln("FAIL: getNextTransition returned different results in VTimeZone and OlsonTimeZone"); 1214 } 1215 tzt1 = otz.getPreviousTransition(base, false); 1216 tzt2 = vtz.getPreviousTransition(base, false); 1217 if (tzt1.equals(tzt2)) { 1218 errln("FAIL: getPreviousTransition returned different results in VTimeZone and OlsonTimeZone"); 1219 } 1220 1221 // hasEquivalentTransitions 1222 long time1 = getUTCMillis(1950, Calendar.JANUARY, 1); 1223 long time2 = getUTCMillis(2020, Calendar.JANUARY, 1); 1224 if (!vtz.hasEquivalentTransitions(otz, time1, time2)) { 1225 errln("FAIL: hasEquivalentTransitons returned false for the same time zone"); 1226 } 1227 1228 // getTimeZoneRules 1229 TimeZoneRule[] rulesetAll = vtz.getTimeZoneRules(); 1230 TimeZoneRule[] ruleset1 = vtz.getTimeZoneRules(time1); 1231 TimeZoneRule[] ruleset2 = vtz.getTimeZoneRules(time2); 1232 if (rulesetAll.length < ruleset1.length || ruleset1.length < ruleset2.length) { 1233 errln("FAIL: Number of rules returned by getRules is invalid"); 1234 } 1235 1236 int[] offsets_vtzc = new int[2]; 1237 VTimeZone vtzc = VTimeZone.create("PST"); 1238 vtzc.getOffsetFromLocal(Calendar.getInstance(vtzc).getTimeInMillis(), VTimeZone.LOCAL_STD, VTimeZone.LOCAL_STD, offsets_vtzc); 1239 if (offsets_vtzc[0] > offsets_vtzc[1]) { 1240 errln("Error getOffsetFromLocal()"); 1241 } 1242 } 1243 1244 @Test 1245 public void TestVTimeZoneParse() { 1246 // Trying to create VTimeZone from empty data 1247 StringReader r = new StringReader(""); 1248 VTimeZone empty = VTimeZone.create(r); 1249 if (empty != null) { 1250 errln("FAIL: Non-null VTimeZone is returned for empty VTIMEZONE data"); 1251 } 1252 1253 // Create VTimeZone for Asia/Tokyo 1254 String asiaTokyo = 1255 "BEGIN:VTIMEZONE\r\n" + 1256 "TZID:Asia\r\n" + 1257 "\t/Tokyo\r\n" + 1258 "BEGIN:STANDARD\r\n" + 1259 "TZOFFSETFROM:+0900\r\n" + 1260 "TZOFFSETTO:+0900\r\n" + 1261 "TZNAME:JST\r\n" + 1262 "DTSTART:19700101\r\n" + 1263 " T000000\r\n" + 1264 "END:STANDARD\r\n" + 1265 "END:VTIMEZONE"; 1266 r = new StringReader(asiaTokyo); 1267 VTimeZone tokyo = VTimeZone.create(r); 1268 if (tokyo == null) { 1269 errln("FAIL: Failed to create a VTimeZone tokyo"); 1270 } else { 1271 // Make sure offsets are correct 1272 int[] offsets = new int[2]; 1273 tokyo.getOffset(System.currentTimeMillis(), false, offsets); 1274 if (offsets[0] != 9*HOUR || offsets[1] != 0) { 1275 errln("FAIL: Bad offsets returned by a VTimeZone created for Tokyo"); 1276 } 1277 } 1278 1279 // Create VTimeZone from VTIMEZONE data 1280 String fooData = 1281 "BEGIN:VCALENDAR\r\n" + 1282 "BEGIN:VTIMEZONE\r\n" + 1283 "TZID:FOO\r\n" + 1284 "BEGIN:STANDARD\r\n" + 1285 "TZOFFSETFROM:-0700\r\n" + 1286 "TZOFFSETTO:-0800\r\n" + 1287 "TZNAME:FST\r\n" + 1288 "DTSTART:20071010T010000\r\n" + 1289 "RRULE:FREQ=YEARLY;BYDAY=WE;BYMONTHDAY=10,11,12,13,14,15,16;BYMONTH=10\r\n" + 1290 "END:STANDARD\r\n" + 1291 "BEGIN:DAYLIGHT\r\n" + 1292 "TZOFFSETFROM:-0800\r\n" + 1293 "TZOFFSETTO:-0700\r\n" + 1294 "TZNAME:FDT\r\n" + 1295 "DTSTART:20070415T010000\r\n" + 1296 "RRULE:FREQ=YEARLY;BYMONTHDAY=15;BYMONTH=4\r\n" + 1297 "END:DAYLIGHT\r\n" + 1298 "END:VTIMEZONE\r\n" + 1299 "END:VCALENDAR"; 1300 1301 r = new StringReader(fooData); 1302 VTimeZone foo = VTimeZone.create(r); 1303 if (foo == null) { 1304 errln("FAIL: Failed to create a VTimeZone foo"); 1305 } else { 1306 // Write VTIMEZONE data 1307 StringWriter w = new StringWriter(); 1308 try { 1309 foo.write(w, getUTCMillis(2005, Calendar.JANUARY, 1)); 1310 } catch (IOException ioe) { 1311 errln("FAIL: IOException is thrown while writing VTIMEZONE data for foo"); 1312 } 1313 logln(w.toString()); 1314 } 1315 } 1316 1317 @Test 1318 public void TestT6216() { 1319 // Test case in #6216 1320 String tokyoTZ = 1321 "BEGIN:VCALENDAR\r\n" + 1322 "VERSION:2.0\r\n" + 1323 "PRODID:-//PYVOBJECT//NONSGML Version 1//EN\r\n" + 1324 "BEGIN:VTIMEZONE\r\n" + 1325 "TZID:Asia/Tokyo\r\n" + 1326 "BEGIN:STANDARD\r\n" + 1327 "DTSTART:20000101T000000\r\n" + 1328 "RRULE:FREQ=YEARLY;BYMONTH=1\r\n" + 1329 "TZNAME:Asia/Tokyo\r\n" + 1330 "TZOFFSETFROM:+0900\r\n" + 1331 "TZOFFSETTO:+0900\r\n" + 1332 "END:STANDARD\r\n" + 1333 "END:VTIMEZONE\r\n" + 1334 "END:VCALENDAR"; 1335 1336 // Single final rule, overlapping with another 1337 String finalOverlap = 1338 "BEGIN:VCALENDAR\r\n" + 1339 "BEGIN:VTIMEZONE\r\n" + 1340 "TZID:FinalOverlap\r\n" + 1341 "BEGIN:STANDARD\r\n" + 1342 "TZOFFSETFROM:-0200\r\n" + 1343 "TZOFFSETTO:-0300\r\n" + 1344 "TZNAME:STD\r\n" + 1345 "DTSTART:20001029T020000\r\n" + 1346 "RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10\r\n" + 1347 "END:STANDARD\r\n" + 1348 "BEGIN:DAYLIGHT\r\n" + 1349 "TZOFFSETFROM:-0300\r\n" + 1350 "TZOFFSETTO:-0200\r\n" + 1351 "TZNAME:DST\r\n" + 1352 "DTSTART:19990404T020000\r\n" + 1353 "RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4;UNTIL=20050403T040000Z\r\n" + 1354 "END:DAYLIGHT\r\n" + 1355 "END:VTIMEZONE\r\n" + 1356 "END:VCALENDAR"; 1357 1358 // Single final rule, no overlapping with another 1359 String finalNonOverlap = 1360 "BEGIN:VCALENDAR\r\n" + 1361 "BEGIN:VTIMEZONE\r\n" + 1362 "TZID:FinalNonOverlap\r\n" + 1363 "BEGIN:STANDARD\r\n" + 1364 "TZOFFSETFROM:-0200\r\n" + 1365 "TZOFFSETTO:-0300\r\n" + 1366 "TZNAME:STD\r\n" + 1367 "DTSTART:20001029T020000\r\n" + 1368 "RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10;UNTIL=20041031T040000Z\r\n" + 1369 "END:STANDARD\r\n" + 1370 "BEGIN:DAYLIGHT\r\n" + 1371 "TZOFFSETFROM:-0300\r\n" + 1372 "TZOFFSETTO:-0200\r\n" + 1373 "TZNAME:DST\r\n" + 1374 "DTSTART:19990404T020000\r\n" + 1375 "RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4;UNTIL=20050403T040000Z\r\n" + 1376 "END:DAYLIGHT\r\n" + 1377 "BEGIN:STANDARD\r\n" + 1378 "TZOFFSETFROM:-0200\r\n" + 1379 "TZOFFSETTO:-0300\r\n" + 1380 "TZNAME:STDFINAL\r\n" + 1381 "DTSTART:20071028T020000\r\n" + 1382 "RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10\r\n" + 1383 "END:STANDARD\r\n" + 1384 "END:VTIMEZONE\r\n" + 1385 "END:VCALENDAR"; 1386 1387 int[][] TestDates = { 1388 {1995, Calendar.JANUARY, 1}, 1389 {1995, Calendar.JULY, 1}, 1390 {2000, Calendar.JANUARY, 1}, 1391 {2000, Calendar.JULY, 1}, 1392 {2005, Calendar.JANUARY, 1}, 1393 {2005, Calendar.JULY, 1}, 1394 {2010, Calendar.JANUARY, 1}, 1395 {2010, Calendar.JULY, 1}, 1396 }; 1397 1398 String[] TestZones = { 1399 tokyoTZ, 1400 finalOverlap, 1401 finalNonOverlap, 1402 }; 1403 1404 int[][] Expected = { 1405 // JAN90 JUL90 JAN00 JUL00 JAN05 JUL05 JAN10 JUL10 1406 { 32400000, 32400000, 32400000, 32400000, 32400000, 32400000, 32400000, 32400000}, 1407 {-10800000, -10800000, -7200000, -7200000, -10800000, -7200000, -10800000, -10800000}, 1408 {-10800000, -10800000, -7200000, -7200000, -10800000, -7200000, -10800000, -10800000}, 1409 }; 1410 1411 // Get test times 1412 long[] times = new long[TestDates.length]; 1413 Calendar cal = new GregorianCalendar(TimeZone.getTimeZone("Etc/GMT")); 1414 for (int i = 0; i < TestDates.length; i++) { 1415 cal.clear(); 1416 cal.set(TestDates[i][0], TestDates[i][1], TestDates[i][2]); 1417 times[i] = cal.getTimeInMillis(); 1418 } 1419 1420 for (int i = 0; i < TestZones.length; i++) { 1421 try { 1422 VTimeZone vtz = VTimeZone.create(new StringReader(TestZones[i])); 1423 for (int j = 0; j < times.length; j++) { 1424 int offset = vtz.getOffset(times[j]); 1425 if (offset != Expected[i][j]) { 1426 errln("FAIL: Invalid offset at time(" + times[j] + "):" + offset + " Expected:" + Expected[i][j]); 1427 } 1428 } 1429 } catch (Exception e) { 1430 errln("FAIL: Failed to calculate the offset for VTIMEZONE data " + i); 1431 } 1432 } 1433 } 1434 1435 @Test 1436 public void TestT6669() { 1437 // getNext/PreviousTransition implementation in SimpleTimeZone 1438 // used to use a bad condition for detecting if DST is enabled or not. 1439 1440 SimpleTimeZone stz = new SimpleTimeZone(0, "CustomID", 1441 Calendar.JANUARY, 1, Calendar.SUNDAY, 0, 1442 Calendar.JULY, 1, Calendar.SUNDAY, 0); 1443 1444 long t = 1230681600000L; //2008-12-31T00:00:00 1445 long expectedNext = 1231027200000L; //2009-01-04T00:00:00 1446 long expectedPrev = 1215298800000L; //2008-07-06T00:00:00 1447 1448 TimeZoneTransition tzt = stz.getNextTransition(t, false); 1449 if (tzt == null) { 1450 errln("FAIL: No transition returned by getNextTransition."); 1451 } else if (tzt.getTime() != expectedNext){ 1452 errln("FAIL: Wrong transition time returned by getNextTransition - " 1453 + tzt.getTime() + " Expected: " + expectedNext); 1454 } 1455 1456 tzt = stz.getPreviousTransition(t, true); 1457 if (tzt == null) { 1458 errln("FAIL: No transition returned by getPreviousTransition."); 1459 } else if (tzt.getTime() != expectedPrev){ 1460 errln("FAIL: Wrong transition time returned by getPreviousTransition - " 1461 + tzt.getTime() + " Expected: " + expectedPrev); 1462 } 1463 } 1464 1465 @Test 1466 public void TestBasicTimeZoneCoverage() { 1467 TimeZone tz = TimeZone.getTimeZone("PST"); 1468 if (tz instanceof BasicTimeZone) { 1469 BasicTimeZone btz = (BasicTimeZone)tz; 1470 int []offsets = new int[2]; 1471 1472 btz.getOffsetFromLocal(Calendar.getInstance().getTimeInMillis(), BasicTimeZone.LOCAL_STD, BasicTimeZone.LOCAL_STD, offsets); 1473 if (offsets[0] > offsets[1]) { 1474 errln("Error calling getOffsetFromLocal()."); 1475 } 1476 } else { 1477 logln("Skipping TestBasicTimeZoneCoverage: ICU4J is configured to use JDK TimeZone"); 1478 } 1479 } 1480 1481 // Internal utility methods ----------------------------------------- 1482 1483 /* 1484 * Check if a time shift really happens on each transition returned by getNextTransition or 1485 * getPreviousTransition in the specified time range 1486 */ 1487 private void verifyTransitions(TimeZone tz, long start, long end) { 1488 BasicTimeZone icutz = (BasicTimeZone)tz; 1489 long time; 1490 int[] before = new int[2]; 1491 int[] after = new int[2]; 1492 TimeZoneTransition tzt0; 1493 1494 // Ascending 1495 tzt0 = null; 1496 time = start; 1497 while(true) { 1498 TimeZoneTransition tzt = icutz.getNextTransition(time, false); 1499 1500 if (tzt == null) { 1501 break; 1502 } 1503 time = tzt.getTime(); 1504 if (time >= end) { 1505 break; 1506 } 1507 icutz.getOffset(time, false, after); 1508 icutz.getOffset(time - 1, false, before); 1509 1510 if (after[0] == before[0] && after[1] == before[1]) { 1511 errln("FAIL: False transition returned by getNextTransition for " + icutz.getID() + " at " + time); 1512 } 1513 if (tzt0 != null && 1514 (tzt0.getTo().getRawOffset() != tzt.getFrom().getRawOffset() 1515 || tzt0.getTo().getDSTSavings() != tzt.getFrom().getDSTSavings())) { 1516 errln("FAIL: TO rule of the previous transition does not match FROM rule of this transtion at " 1517 + time + " for " + icutz.getID()); 1518 } 1519 tzt0 = tzt; 1520 } 1521 1522 // Descending 1523 tzt0 = null; 1524 time = end; 1525 while(true) { 1526 TimeZoneTransition tzt = icutz.getPreviousTransition(time, false); 1527 if (tzt == null) { 1528 break; 1529 } 1530 time = tzt.getTime(); 1531 if (time <= start) { 1532 break; 1533 } 1534 icutz.getOffset(time, false, after); 1535 icutz.getOffset(time - 1, false, before); 1536 1537 if (after[0] == before[0] && after[1] == before[1]) { 1538 errln("FAIL: False transition returned by getPreviousTransition for " + icutz.getID() + " at " + time); 1539 } 1540 1541 if (tzt0 != null && 1542 (tzt0.getFrom().getRawOffset() != tzt.getTo().getRawOffset() 1543 || tzt0.getFrom().getDSTSavings() != tzt.getTo().getDSTSavings())) { 1544 errln("FAIL: TO rule of the next transition does not match FROM rule in this transtion at " 1545 + time + " for " + icutz.getID()); 1546 } 1547 tzt0 = tzt; 1548 } 1549 } 1550 1551 /* 1552 * Compare all time transitions in 2 time zones in the specified time range in ascending order 1553 */ 1554 private void compareTransitionsAscending(TimeZone tz1, TimeZone tz2, long start, long end, boolean inclusive) { 1555 BasicTimeZone z1 = (BasicTimeZone)tz1; 1556 BasicTimeZone z2 = (BasicTimeZone)tz2; 1557 String zid1 = tz1.getID(); 1558 String zid2 = tz2.getID(); 1559 1560 long time = start; 1561 while(true) { 1562 TimeZoneTransition tzt1 = z1.getNextTransition(time, inclusive); 1563 TimeZoneTransition tzt2 = z2.getNextTransition(time, inclusive); 1564 boolean inRange1 = false; 1565 boolean inRange2 = false; 1566 if (tzt1 != null) { 1567 if (tzt1.getTime() < end || (inclusive && tzt1.getTime() == end)) { 1568 inRange1 = true; 1569 } 1570 } 1571 if (tzt2 != null) { 1572 if (tzt2.getTime() < end || (inclusive && tzt2.getTime() == end)) { 1573 inRange2 = true; 1574 } 1575 } 1576 if (!inRange1 && !inRange2) { 1577 // No more transition in the range 1578 break; 1579 } 1580 if (!inRange1) { 1581 errln("FAIL: " + zid1 + " does not have any transitions after " + time + " before " + end); 1582 break; 1583 } 1584 if (!inRange2) { 1585 errln("FAIL: " + zid2 + " does not have any transitions after " + time + " before " + end); 1586 break; 1587 } 1588 if (tzt1.getTime() != tzt2.getTime()) { 1589 errln("FAIL: First transition after " + time + " " 1590 + zid1 + "[" + tzt1.getTime() + "] " 1591 + zid2 + "[" + tzt2.getTime() + "]"); 1592 break; 1593 } 1594 time = tzt1.getTime(); 1595 if (inclusive) { 1596 time++; 1597 } 1598 } 1599 } 1600 1601 /* 1602 * Compare all time transitions in 2 time zones in the specified time range in descending order 1603 */ 1604 private void compareTransitionsDescending(TimeZone tz1, TimeZone tz2, long start, long end, boolean inclusive) { 1605 BasicTimeZone z1 = (BasicTimeZone)tz1; 1606 BasicTimeZone z2 = (BasicTimeZone)tz2; 1607 String zid1 = tz1.getID(); 1608 String zid2 = tz2.getID(); 1609 long time = end; 1610 while(true) { 1611 TimeZoneTransition tzt1 = z1.getPreviousTransition(time, inclusive); 1612 TimeZoneTransition tzt2 = z2.getPreviousTransition(time, inclusive); 1613 boolean inRange1 = false; 1614 boolean inRange2 = false; 1615 if (tzt1 != null) { 1616 if (tzt1.getTime() > start || (inclusive && tzt1.getTime() == start)) { 1617 inRange1 = true; 1618 } 1619 } 1620 if (tzt2 != null) { 1621 if (tzt2.getTime() > start || (inclusive && tzt2.getTime() == start)) { 1622 inRange2 = true; 1623 } 1624 } 1625 if (!inRange1 && !inRange2) { 1626 // No more transition in the range 1627 break; 1628 } 1629 if (!inRange1) { 1630 errln("FAIL: " + zid1 + " does not have any transitions before " + time + " after " + start); 1631 break; 1632 } 1633 if (!inRange2) { 1634 errln("FAIL: " + zid2 + " does not have any transitions before " + time + " after " + start); 1635 break; 1636 } 1637 if (tzt1.getTime() != tzt2.getTime()) { 1638 errln("FAIL: Last transition before " + time + " " 1639 + zid1 + "[" + tzt1.getTime() + "] " 1640 + zid2 + "[" + tzt2.getTime() + "]"); 1641 break; 1642 } 1643 time = tzt1.getTime(); 1644 if (inclusive) { 1645 time--; 1646 } 1647 } 1648 } 1649 1650 private static final String[] TESTZIDS = { 1651 "AGT", 1652 "America/New_York", 1653 "America/Los_Angeles", 1654 "America/Indiana/Indianapolis", 1655 "America/Havana", 1656 "Europe/Lisbon", 1657 "Europe/Paris", 1658 "Asia/Tokyo", 1659 "Asia/Sakhalin", 1660 "Africa/Cairo", 1661 "Africa/Windhoek", 1662 "Australia/Sydney", 1663 "Etc/GMT+8", 1664 "Asia/Amman", 1665 }; 1666 1667 private String[] getTestZIDs() { 1668 if (TestFmwk.getExhaustiveness() > 5) { 1669 return TimeZone.getAvailableIDs(); 1670 } 1671 return TESTZIDS; 1672 } 1673 1674 private static final int[][] TESTYEARS = { 1675 {1895, 1905}, // including int32 minimum second 1676 {1965, 1975}, // including the epoch 1677 {1995, 2015} // practical year range 1678 }; 1679 1680 private long[] getTestTimeRange(int idx) { 1681 int loyear, hiyear; 1682 if (idx < TESTYEARS.length) { 1683 loyear = TESTYEARS[idx][0]; 1684 hiyear = TESTYEARS[idx][1]; 1685 } else if (idx == TESTYEARS.length && TestFmwk.getExhaustiveness() > 5) { 1686 loyear = 1850; 1687 hiyear = 2050; 1688 } else { 1689 return null; 1690 } 1691 1692 long[] times = new long[2]; 1693 times[0] = getUTCMillis(loyear, Calendar.JANUARY, 1); 1694 times[1] = getUTCMillis(hiyear + 1, Calendar.JANUARY, 1); 1695 1696 return times; 1697 } 1698 1699 private GregorianCalendar utcCal; 1700 1701 private long getUTCMillis(int year, int month, int dayOfMonth) { 1702 if (utcCal == null) { 1703 utcCal = new GregorianCalendar(TimeZone.getTimeZone("UTC"), ULocale.ROOT); 1704 } 1705 utcCal.clear(); 1706 utcCal.set(year, month, dayOfMonth); 1707 return utcCal.getTimeInMillis(); 1708 } 1709 1710 /* 1711 * Slightly modified version of BasicTimeZone#hasEquivalentTransitions. 1712 * This version returns true if transition time delta is within the given 1713 * delta range. 1714 */ 1715 private static boolean hasEquivalentTransitions(BasicTimeZone tz1, BasicTimeZone tz2, 1716 long start, long end, 1717 boolean ignoreDstAmount, int maxTransitionTimeDelta) { 1718 if (tz1.hasSameRules(tz2)) { 1719 return true; 1720 } 1721 1722 // Check the offsets at the start time 1723 int[] offsets1 = new int[2]; 1724 int[] offsets2 = new int[2]; 1725 1726 tz1.getOffset(start, false, offsets1); 1727 tz2.getOffset(start, false, offsets2); 1728 1729 if (ignoreDstAmount) { 1730 if ((offsets1[0] + offsets1[1] != offsets2[0] + offsets2[1]) 1731 || (offsets1[1] != 0 && offsets2[1] == 0) 1732 || (offsets1[1] == 0 && offsets2[1] != 0)) { 1733 return false; 1734 } 1735 } else { 1736 if (offsets1[0] != offsets2[0] || offsets1[1] != offsets2[1]) { 1737 return false; 1738 } 1739 } 1740 1741 // Check transitions in the range 1742 long time = start; 1743 while (true) { 1744 TimeZoneTransition tr1 = tz1.getNextTransition(time, false); 1745 TimeZoneTransition tr2 = tz2.getNextTransition(time, false); 1746 1747 if (ignoreDstAmount) { 1748 // Skip a transition which only differ the amount of DST savings 1749 while (true) { 1750 if (tr1 != null 1751 && tr1.getTime() <= end 1752 && (tr1.getFrom().getRawOffset() + tr1.getFrom().getDSTSavings() 1753 == tr1.getTo().getRawOffset() + tr1.getTo().getDSTSavings()) 1754 && (tr1.getFrom().getDSTSavings() != 0 && tr1.getTo().getDSTSavings() != 0)) { 1755 tr1 = tz1.getNextTransition(tr1.getTime(), false); 1756 } else { 1757 break; 1758 } 1759 } 1760 while (true) { 1761 if (tr2 != null 1762 && tr2.getTime() <= end 1763 && (tr2.getFrom().getRawOffset() + tr2.getFrom().getDSTSavings() 1764 == tr2.getTo().getRawOffset() + tr2.getTo().getDSTSavings()) 1765 && (tr2.getFrom().getDSTSavings() != 0 && tr2.getTo().getDSTSavings() != 0)) { 1766 tr2 = tz2.getNextTransition(tr2.getTime(), false); 1767 } else { 1768 break; 1769 } 1770 } } 1771 1772 boolean inRange1 = false; 1773 boolean inRange2 = false; 1774 if (tr1 != null) { 1775 if (tr1.getTime() <= end) { 1776 inRange1 = true; 1777 } 1778 } 1779 if (tr2 != null) { 1780 if (tr2.getTime() <= end) { 1781 inRange2 = true; 1782 } 1783 } 1784 if (!inRange1 && !inRange2) { 1785 // No more transition in the range 1786 break; 1787 } 1788 if (!inRange1 || !inRange2) { 1789 return false; 1790 } 1791 if (Math.abs(tr1.getTime() - tr2.getTime()) > maxTransitionTimeDelta) { 1792 return false; 1793 } 1794 if (ignoreDstAmount) { 1795 if (tr1.getTo().getRawOffset() + tr1.getTo().getDSTSavings() 1796 != tr2.getTo().getRawOffset() + tr2.getTo().getDSTSavings() 1797 || tr1.getTo().getDSTSavings() != 0 && tr2.getTo().getDSTSavings() == 0 1798 || tr1.getTo().getDSTSavings() == 0 && tr2.getTo().getDSTSavings() != 0) { 1799 return false; 1800 } 1801 } else { 1802 if (tr1.getTo().getRawOffset() != tr2.getTo().getRawOffset() || 1803 tr1.getTo().getDSTSavings() != tr2.getTo().getDSTSavings()) { 1804 return false; 1805 } 1806 } 1807 time = tr1.getTime() > tr2.getTime() ? tr1.getTime() : tr2.getTime(); 1808 } 1809 return true; 1810 } 1811 1812 // Test case for ticket#8943 1813 // RuleBasedTimeZone#getOffsets throws NPE 1814 @Test 1815 public void TestT8943() { 1816 String id = "Ekaterinburg Time"; 1817 String stdName = "Ekaterinburg Standard Time"; 1818 String dstName = "Ekaterinburg Daylight Time"; 1819 1820 InitialTimeZoneRule initialRule = new InitialTimeZoneRule(stdName, 18000000, 0); 1821 RuleBasedTimeZone rbtz = new RuleBasedTimeZone(id, initialRule); 1822 1823 DateTimeRule dtRule = new DateTimeRule(Calendar.OCTOBER, -1, Calendar.SUNDAY, 10800000, DateTimeRule.WALL_TIME); 1824 AnnualTimeZoneRule atzRule = new AnnualTimeZoneRule(stdName, 18000000, 0, dtRule, 2000, 2010); 1825 rbtz.addTransitionRule(atzRule); 1826 1827 dtRule = new DateTimeRule(Calendar.MARCH, -1, Calendar.SUNDAY, 7200000, DateTimeRule.WALL_TIME); 1828 atzRule = new AnnualTimeZoneRule(dstName, 18000000, 3600000, dtRule, 2000, 2010); 1829 rbtz.addTransitionRule(atzRule); 1830 1831 dtRule = new DateTimeRule(Calendar.JANUARY, 1, 0, DateTimeRule.WALL_TIME); 1832 atzRule = new AnnualTimeZoneRule(stdName, 21600000, 0, dtRule, 2011, AnnualTimeZoneRule.MAX_YEAR); 1833 rbtz.addTransitionRule(atzRule); 1834 1835 dtRule = new DateTimeRule(Calendar.JANUARY, 1, 1, DateTimeRule.WALL_TIME); 1836 atzRule = new AnnualTimeZoneRule(dstName, 21600000, 0, dtRule, 2011, AnnualTimeZoneRule.MAX_YEAR); 1837 rbtz.addTransitionRule(atzRule); 1838 1839 int[] expected = {21600000, 0}; 1840 int[] offsets = new int[2]; 1841 try { 1842 rbtz.getOffset(1293822000000L /* 2010-12-31 19:00:00 UTC */, false, offsets); 1843 if (offsets[0] != expected[0] || offsets[1] != expected[1]) { 1844 errln("Fail: Wrong offsets: " + offsets[0] + "/" + offsets[1] + " Expected: " + expected[0] + "/" + expected[1]); 1845 } 1846 } catch (Exception e) { 1847 errln("Fail: Exception thrown - " + e.getMessage()); 1848 } 1849 } 1850 }