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