2 *******************************************************************************
3 * Copyright (C) 2007-2010, International Business Machines Corporation and *
4 * others. All Rights Reserved. *
5 *******************************************************************************
7 package com.ibm.icu.dev.test.timezone;
9 import java.io.ByteArrayInputStream;
10 import java.io.ByteArrayOutputStream;
11 import java.io.IOException;
12 import java.io.InputStreamReader;
13 import java.io.OutputStreamWriter;
14 import java.io.StringReader;
15 import java.io.StringWriter;
16 import java.util.Date;
18 import com.ibm.icu.dev.test.TestFmwk;
19 import com.ibm.icu.util.AnnualTimeZoneRule;
20 import com.ibm.icu.util.BasicTimeZone;
21 import com.ibm.icu.util.Calendar;
22 import com.ibm.icu.util.DateTimeRule;
23 import com.ibm.icu.util.GregorianCalendar;
24 import com.ibm.icu.util.InitialTimeZoneRule;
25 import com.ibm.icu.util.RuleBasedTimeZone;
26 import com.ibm.icu.util.SimpleTimeZone;
27 import com.ibm.icu.util.TimeArrayTimeZoneRule;
28 import com.ibm.icu.util.TimeZone;
29 import com.ibm.icu.util.TimeZoneRule;
30 import com.ibm.icu.util.TimeZoneTransition;
31 import com.ibm.icu.util.ULocale;
32 import com.ibm.icu.util.VTimeZone;
35 * Test cases for TimeZoneRule and RuleBasedTimeZone
37 public class TimeZoneRuleTest extends TestFmwk {
39 private static final int HOUR = 60 * 60 * 1000;
41 public static void main(String[] args) throws Exception {
42 new TimeZoneRuleTest().run(args);
46 * RuleBasedTimeZone test cases
48 public void TestSimpleRuleBasedTimeZone() {
49 SimpleTimeZone stz = new SimpleTimeZone(-1*HOUR, "TestSTZ",
50 Calendar.SEPTEMBER, -30, -Calendar.SATURDAY, 1*HOUR, SimpleTimeZone.WALL_TIME,
51 Calendar.FEBRUARY, 2, Calendar.SUNDAY, 1*HOUR, SimpleTimeZone.WALL_TIME,
56 AnnualTimeZoneRule atzr;
57 final int STARTYEAR = 2000;
59 InitialTimeZoneRule ir = new InitialTimeZoneRule(
60 "RBTZ_Initial", // Initial time Name
61 -1*HOUR, // Raw offset
62 1*HOUR); // DST saving amount
65 RuleBasedTimeZone rbtz1 = new RuleBasedTimeZone("RBTZ1", ir);
66 dtr = new DateTimeRule(Calendar.SEPTEMBER, 30, Calendar.SATURDAY, false,
67 1*HOUR, DateTimeRule.WALL_TIME); // SUN<=30 in September, at 1AM wall time
68 atzr = new AnnualTimeZoneRule("RBTZ_DST1",
69 -1*HOUR /* rawOffset */, 1*HOUR /* dstSavings */, dtr,
70 STARTYEAR, AnnualTimeZoneRule.MAX_YEAR);
71 rbtz1.addTransitionRule(atzr);
72 dtr = new DateTimeRule(Calendar.FEBRUARY, 2, Calendar.SUNDAY,
73 1*HOUR, DateTimeRule.WALL_TIME); // 2nd Sunday in February, at 1AM wall time
74 atzr = new AnnualTimeZoneRule("RBTZ_STD1",
75 -1*HOUR /* rawOffset */, 0 /* dstSavings */, dtr,
76 STARTYEAR, AnnualTimeZoneRule.MAX_YEAR);
77 rbtz1.addTransitionRule(atzr);
79 // Equivalent, but different date rule type
80 RuleBasedTimeZone rbtz2 = new RuleBasedTimeZone("RBTZ2", ir);
81 dtr = new DateTimeRule(Calendar.SEPTEMBER, -1, Calendar.SATURDAY,
82 1*HOUR, DateTimeRule.WALL_TIME); // Last Sunday in September at 1AM wall time
83 atzr = new AnnualTimeZoneRule("RBTZ_DST2", -1*HOUR, 1*HOUR, dtr, STARTYEAR, AnnualTimeZoneRule.MAX_YEAR);
84 rbtz2.addTransitionRule(atzr);
85 dtr = new DateTimeRule(Calendar.FEBRUARY, 8, Calendar.SUNDAY, true,
86 1*HOUR, DateTimeRule.WALL_TIME); // SUN>=8 in February, at 1AM wall time
87 atzr = new AnnualTimeZoneRule("RBTZ_STD2", -1*HOUR, 0, dtr, STARTYEAR, AnnualTimeZoneRule.MAX_YEAR);
88 rbtz2.addTransitionRule(atzr);
90 // Equivalent, but different time rule type
91 RuleBasedTimeZone rbtz3 = new RuleBasedTimeZone("RBTZ3", ir);
92 dtr = new DateTimeRule(Calendar.SEPTEMBER, 30, Calendar.SATURDAY, false,
93 2*HOUR, DateTimeRule.UTC_TIME);
94 atzr = new AnnualTimeZoneRule("RBTZ_DST3", -1*HOUR, 1*HOUR, dtr, STARTYEAR, AnnualTimeZoneRule.MAX_YEAR);
95 rbtz3.addTransitionRule(atzr);
96 dtr = new DateTimeRule(Calendar.FEBRUARY, 2, Calendar.SUNDAY,
97 0*HOUR, DateTimeRule.STANDARD_TIME);
98 atzr = new AnnualTimeZoneRule("RBTZ_STD3", -1*HOUR, 0, dtr, STARTYEAR, AnnualTimeZoneRule.MAX_YEAR);
99 rbtz3.addTransitionRule(atzr);
101 // Check equivalency for 10 years
102 long start = getUTCMillis(STARTYEAR, Calendar.JANUARY, 1);
103 long until = getUTCMillis(STARTYEAR + 10, Calendar.JANUARY, 1);
105 if (!(stz.hasEquivalentTransitions(rbtz1, start, until))) {
106 errln("FAIL: rbtz1 must be equivalent to the SimpleTimeZone in the time range.");
108 if (!(stz.hasEquivalentTransitions(rbtz2, start, until))) {
109 errln("FAIL: rbtz2 must be equivalent to the SimpleTimeZone in the time range.");
111 if (!(stz.hasEquivalentTransitions(rbtz3, start, until))) {
112 errln("FAIL: rbtz3 must be equivalent to the SimpleTimeZone in the time range.");
116 if (rbtz1.hasSameRules(rbtz2)) {
117 errln("FAIL: rbtz1 and rbtz2 have different rules, but returned true.");
119 if (rbtz1.hasSameRules(rbtz3)) {
120 errln("FAIL: rbtz1 and rbtz3 have different rules, but returned true.");
122 RuleBasedTimeZone rbtz1c = (RuleBasedTimeZone)rbtz1.clone();
123 if (!rbtz1.hasSameRules(rbtz1c)) {
124 errln("FAIL: Cloned RuleBasedTimeZone must have the same rules with the original.");
128 GregorianCalendar cal = new GregorianCalendar();
129 int[] offsets = new int[2];
133 cal.setTimeZone(rbtz1);
137 cal.set(Calendar.ERA, GregorianCalendar.BC);
138 cal.set(1000, Calendar.JANUARY, 1);
140 offset = rbtz1.getOffset(cal.get(Calendar.ERA), cal.get(Calendar.YEAR), cal.get(Calendar.MONTH),
141 cal.get(Calendar.DAY_OF_MONTH), cal.get(Calendar.DAY_OF_WEEK), cal.get(Calendar.MILLISECONDS_IN_DAY));
143 errln("FAIL: Invalid time zone offset: " + offset + " /expected: 0");
145 dst = rbtz1.inDaylightTime(cal.getTime());
147 errln("FAIL: Invalid daylight saving time");
149 rbtz1.getOffset(cal.getTimeInMillis(), true, offsets);
150 if (offsets[0] != -3600000) {
151 errln("FAIL: Invalid time zone raw offset: " + offsets[0] + " /expected: -3600000");
153 if (offsets[1] != 3600000) {
154 errln("FAIL: Invalid DST amount: " + offsets[1] + " /expected: 3600000");
158 cal.set(Calendar.ERA, GregorianCalendar.AD);
159 cal.set(2000, Calendar.JULY, 1);
161 offset = rbtz1.getOffset(cal.get(Calendar.ERA), cal.get(Calendar.YEAR), cal.get(Calendar.MONTH),
162 cal.get(Calendar.DAY_OF_MONTH), cal.get(Calendar.DAY_OF_WEEK), cal.get(Calendar.MILLISECONDS_IN_DAY));
163 if (offset != -3600000) {
164 errln("FAIL: Invalid time zone offset: " + offset + " /expected: -3600000");
166 dst = rbtz1.inDaylightTime(cal.getTime());
168 errln("FAIL: Invalid daylight saving time");
170 rbtz1.getOffset(cal.getTimeInMillis(), true, offsets);
171 if (offsets[0] != -3600000) {
172 errln("FAIL: Invalid time zone raw offset: " + offsets[0] + " /expected: -3600000");
174 if (offsets[1] != 0) {
175 errln("FAIL: Invalid DST amount: " + offsets[1] + " /expected: 0");
180 // Try to add 3rd final rule
181 dtr = new DateTimeRule(Calendar.OCTOBER, 15, 1*HOUR, DateTimeRule.WALL_TIME);
182 atzr = new AnnualTimeZoneRule("3RD_ATZ", -1*HOUR, 2*HOUR, dtr, STARTYEAR, AnnualTimeZoneRule.MAX_YEAR);
183 boolean bException = false;
185 rbtz1.addTransitionRule(atzr);
186 } catch (IllegalStateException ise) {
190 errln("FAIL: 3rd final rule must be rejected");
193 // Try to add an initial rule
196 rbtz1.addTransitionRule(new InitialTimeZoneRule("Test Initial", 2*HOUR, 0));
197 } catch (IllegalArgumentException iae) {
201 errln("FAIL: InitialTimeZoneRule must be rejected");
206 * Test equivalency between OlsonTimeZone and custom RBTZ representing the
207 * equivalent rules in a certain time range
209 public void TestHistoricalRuleBasedTimeZone() {
210 // Compare to America/New_York with equivalent RBTZ
211 TimeZone ny = TimeZone.getTimeZone("America/New_York", TimeZone.TIMEZONE_ICU);
214 InitialTimeZoneRule ir = new InitialTimeZoneRule("EST", -5*HOUR, 0);
215 RuleBasedTimeZone rbtz = new RuleBasedTimeZone("EST5EDT", ir);
218 AnnualTimeZoneRule tzr;
221 dtr = new DateTimeRule(Calendar.OCTOBER, -1, Calendar.SUNDAY,
222 2*HOUR, DateTimeRule.WALL_TIME); // Last Sunday in October, at 2AM wall time
223 tzr = new AnnualTimeZoneRule("EST", -5*HOUR /* rawOffset */, 0 /* dstSavings */, dtr, 1967, 2006);
224 rbtz.addTransitionRule(tzr);
226 dtr = new DateTimeRule(Calendar.NOVEMBER, 1, Calendar.SUNDAY,
227 true, 2*HOUR, DateTimeRule.WALL_TIME); // SUN>=1 in November, at 2AM wall time
228 tzr = new AnnualTimeZoneRule("EST", -5*HOUR, 0, dtr, 2007, AnnualTimeZoneRule.MAX_YEAR);
229 rbtz.addTransitionRule(tzr);
231 // Daylight saving time
232 dtr = new DateTimeRule(Calendar.APRIL, -1, Calendar.SUNDAY,
233 2*HOUR, DateTimeRule.WALL_TIME); // Last Sunday in April, at 2AM wall time
234 tzr = new AnnualTimeZoneRule("EDT", -5*HOUR, 1*HOUR, dtr, 1967, 1973);
235 rbtz.addTransitionRule(tzr);
237 dtr = new DateTimeRule(Calendar.JANUARY, 6,
238 2*HOUR, DateTimeRule.WALL_TIME); // January 6, at 2AM wall time
239 tzr = new AnnualTimeZoneRule("EDT", -5*HOUR, 1*HOUR, dtr, 1974, 1974);
240 rbtz.addTransitionRule(tzr);
242 dtr = new DateTimeRule(Calendar.FEBRUARY, 23,
243 2*HOUR, DateTimeRule.WALL_TIME); // February 23, at 2AM wall time
244 tzr = new AnnualTimeZoneRule("EDT", -5*HOUR, 1*HOUR, dtr, 1975, 1975);
245 rbtz.addTransitionRule(tzr);
247 dtr = new DateTimeRule(Calendar.APRIL, -1, Calendar.SUNDAY,
248 2*HOUR, DateTimeRule.WALL_TIME); // Last Sunday in April, at 2AM wall time
249 tzr = new AnnualTimeZoneRule("EDT", -5*HOUR, 1*HOUR, dtr, 1976, 1986);
250 rbtz.addTransitionRule(tzr);
252 dtr = new DateTimeRule(Calendar.APRIL, 1, Calendar.SUNDAY,
253 true, 2*HOUR, DateTimeRule.WALL_TIME); // SUN>=1 in April, at 2AM wall time
254 tzr = new AnnualTimeZoneRule("EDT", -5*HOUR, 1*HOUR, dtr, 1987, 2006);
255 rbtz.addTransitionRule(tzr);
257 dtr = new DateTimeRule(Calendar.MARCH, 8, Calendar.SUNDAY,
258 true, 2*HOUR, DateTimeRule.WALL_TIME); // SUN>=8 in March, at 2AM wall time
259 tzr = new AnnualTimeZoneRule("EDT", -5*HOUR, 1*HOUR, dtr, 2007, AnnualTimeZoneRule.MAX_YEAR);
260 rbtz.addTransitionRule(tzr);
262 // hasEquivalentTransitions
263 long jan1_1950 = getUTCMillis(1950, Calendar.JANUARY, 1);
264 long jan1_1967 = getUTCMillis(1971, Calendar.JANUARY, 1);
265 long jan1_2010 = getUTCMillis(2010, Calendar.JANUARY, 1);
267 if (!(((BasicTimeZone)ny).hasEquivalentTransitions(rbtz, jan1_1967, jan1_2010))) {
268 errln("FAIL: The RBTZ must be equivalent to America/New_York between 1967 and 2010");
270 if (((BasicTimeZone)ny).hasEquivalentTransitions(rbtz, jan1_1950, jan1_2010)) {
271 errln("FAIL: The RBTZ must not be equivalent to America/New_York between 1950 and 2010");
274 // Same with above, but calling RBTZ#hasEquivalentTransitions against OlsonTimeZone
275 if (!rbtz.hasEquivalentTransitions(ny, jan1_1967, jan1_2010)) {
276 errln("FAIL: The RBTZ must be equivalent to America/New_York between 1967 and 2010");
278 if (rbtz.hasEquivalentTransitions(ny, jan1_1950, jan1_2010)) {
279 errln("FAIL: The RBTZ must not be equivalent to America/New_York between 1950 and 2010");
283 if (ny.hasSameRules(rbtz) || rbtz.hasSameRules(ny)) {
284 errln("FAIL: hasSameRules must return false");
286 RuleBasedTimeZone rbtzc = (RuleBasedTimeZone)rbtz.clone();
287 if (!rbtz.hasSameRules(rbtzc) || !rbtz.hasEquivalentTransitions(rbtzc, jan1_1950, jan1_2010)) {
288 errln("FAIL: hasSameRules/hasEquivalentTransitions must return true for cloned RBTZs");
292 getUTCMillis(2006, Calendar.MARCH, 15),
293 getUTCMillis(2006, Calendar.NOVEMBER, 1),
294 getUTCMillis(2007, Calendar.MARCH, 15),
295 getUTCMillis(2007, Calendar.NOVEMBER, 1),
296 getUTCMillis(2008, Calendar.MARCH, 15),
297 getUTCMillis(2008, Calendar.NOVEMBER, 1)
299 int[] offsets1 = new int[2];
300 int[] offsets2 = new int[2];
302 for (int i = 0; i < times.length; i++) {
303 // Check getOffset - must return the same results for these time data
304 rbtz.getOffset(times[i], false, offsets1);
305 ny.getOffset(times[i], false, offsets2);
306 if (offsets1[0] != offsets2[0] || offsets1[1] != offsets2[1]) {
307 errln("FAIL: Incompatible time zone offsets for ny and rbtz");
309 // Check inDaylightTime
310 Date d = new Date(times[i]);
311 if (rbtz.inDaylightTime(d) != ny.inDaylightTime(d)) {
312 errln("FAIL: Incompatible daylight saving time for ny and rbtz");
318 * Check if transitions returned by getNextTransition/getPreviousTransition
319 * are actual time transitions.
321 public void TestOlsonTransition() {
322 String[] zids = getTestZIDs();
323 for (int i = 0; i < zids.length; i++) {
324 TimeZone tz = TimeZone.getTimeZone(zids[i], TimeZone.TIMEZONE_ICU);
330 long[] timerange = getTestTimeRange(j++);
331 if (timerange == null) {
334 verifyTransitions(tz, timerange[0], timerange[1]);
340 * Check if an OlsonTimeZone and its equivalent RBTZ have the exact same
343 public void TestRBTZTransition() {
351 String[] zids = getTestZIDs();
352 for (int i = 0; i < zids.length; i++) {
353 TimeZone tz = TimeZone.getTimeZone(zids[i], TimeZone.TIMEZONE_ICU);
357 for (int j = 0; j < STARTYEARS.length; j++) {
358 long startTime = getUTCMillis(STARTYEARS[j], Calendar.JANUARY, 1);
359 TimeZoneRule[] rules = ((BasicTimeZone)tz).getTimeZoneRules(startTime);
360 RuleBasedTimeZone rbtz = new RuleBasedTimeZone(tz.getID() + "(RBTZ)",
361 (InitialTimeZoneRule)rules[0]);
362 for (int k = 1; k < rules.length; k++) {
363 rbtz.addTransitionRule(rules[k]);
366 // Compare the original OlsonTimeZone with the RBTZ starting the startTime for 20 years
367 long until = getUTCMillis(STARTYEARS[j] + 20, Calendar.JANUARY, 1);
370 compareTransitionsAscending(tz, rbtz, startTime, until, false);
371 // Ascending/inclusive
372 compareTransitionsAscending(tz, rbtz, startTime + 1, until, true);
374 compareTransitionsDescending(tz, rbtz, startTime, until, false);
375 // Descending/inclusive
376 compareTransitionsDescending(tz, rbtz, startTime + 1, until, true);
383 * Test cases for HasTimeZoneRules#hasEquivalentTransitions
385 public void TestHasEquivalentTransitions() {
386 // America/New_York and America/Indiana/Indianapolis are equivalent
388 TimeZone newyork = TimeZone.getTimeZone("America/New_York", TimeZone.TIMEZONE_ICU);
389 TimeZone indianapolis = TimeZone.getTimeZone("America/Indiana/Indianapolis", TimeZone.TIMEZONE_ICU);
390 TimeZone gmt_5 = TimeZone.getTimeZone("Etc/GMT+5", TimeZone.TIMEZONE_ICU);
392 long jan1_1971 = getUTCMillis(1971, Calendar.JANUARY, 1);
393 long jan1_2005 = getUTCMillis(2005, Calendar.JANUARY, 1);
394 long jan1_2006 = getUTCMillis(2006, Calendar.JANUARY, 1);
395 long jan1_2007 = getUTCMillis(2007, Calendar.JANUARY, 1);
396 long jan1_2011 = getUTCMillis(2010, Calendar.JANUARY, 1);
398 if (((BasicTimeZone)newyork).hasEquivalentTransitions(indianapolis, jan1_2005, jan1_2011)) {
399 errln("FAIL: New_York is not equivalent to Indianapolis between 2005 and 2010, but returned true");
401 if (!((BasicTimeZone)newyork).hasEquivalentTransitions(indianapolis, jan1_2006, jan1_2011)) {
402 errln("FAIL: New_York is equivalent to Indianapolis between 2006 and 2010, but returned false");
405 if (!((BasicTimeZone)indianapolis).hasEquivalentTransitions(gmt_5, jan1_1971, jan1_2006)) {
406 errln("FAIL: Indianapolis is equivalent to GMT+5 between 1971 and 2005, but returned false");
408 if (((BasicTimeZone)indianapolis).hasEquivalentTransitions(gmt_5, jan1_1971, jan1_2007)) {
409 errln("FAIL: Indianapolis is not equivalent to GMT+5 between 1971 and 2006, but returned true");
413 TimeZone newyork2 = (TimeZone)newyork.clone();
414 if (!((BasicTimeZone)newyork).hasEquivalentTransitions(newyork2, jan1_1971, jan1_2011)) {
415 errln("FAIL: Cloned TimeZone must have the same transitions");
417 if (!((BasicTimeZone)newyork).hasEquivalentTransitions(newyork2, jan1_1971, jan1_2011, true /*ignoreDstAmount*/)) {
418 errln("FAIL: Cloned TimeZone must have the same transitions");
421 // America/New_York and America/Los_Angeles has same DST start rules, but
422 // raw offsets are different
423 TimeZone losangeles = TimeZone.getTimeZone("America/Los_Angeles", TimeZone.TIMEZONE_ICU);
424 if (((BasicTimeZone)newyork).hasEquivalentTransitions(losangeles, jan1_2006, jan1_2011)) {
425 errln("FAIL: New_York is not equivalent to Los Angeles, but returned true");
430 * Write out time zone rules of OlsonTimeZone into VTIMEZONE format, create a new
431 * VTimeZone from the VTIMEZONE data, then compare transitions
433 public void TestVTimeZoneRoundTrip() {
434 long startTime = getUTCMillis(1850, Calendar.JANUARY, 1);
435 long endTime = getUTCMillis(2050, Calendar.JANUARY, 1);
437 String[] tzids = getTestZIDs();
438 for (int i = 0; i < tzids.length; i++) {
439 BasicTimeZone olsontz = (BasicTimeZone)TimeZone.getTimeZone(tzids[i], TimeZone.TIMEZONE_ICU);
440 VTimeZone vtz_org = VTimeZone.create(tzids[i]);
441 vtz_org.setTZURL("http://source.icu-project.org/timezone");
442 vtz_org.setLastModified(new Date());
443 VTimeZone vtz_new = null;
445 // Write out VTIMEZONE
446 ByteArrayOutputStream baos = new ByteArrayOutputStream();
447 OutputStreamWriter writer = new OutputStreamWriter(baos);
448 vtz_org.write(writer);
450 byte[] vtzdata = baos.toByteArray();
452 ByteArrayInputStream bais = new ByteArrayInputStream(vtzdata);
453 InputStreamReader reader = new InputStreamReader(bais);
454 vtz_new = VTimeZone.create(reader);
457 // Write out VTIMEZONE one more time
458 ByteArrayOutputStream baos1 = new ByteArrayOutputStream();
459 OutputStreamWriter writer1 = new OutputStreamWriter(baos1);
460 vtz_new.write(writer1);
462 byte[] vtzdata1 = baos1.toByteArray();
464 // Make sure VTIMEZONE data is exactly same with the first one
465 if (vtzdata.length != vtzdata1.length) {
466 errln("FAIL: different VTIMEZONE data length");
468 for (int j = 0; j < vtzdata.length; j++) {
469 if (vtzdata[j] != vtzdata1[j]) {
470 errln("FAIL: different VTIMEZONE data");
474 } catch (IOException ioe) {
475 errln("FAIL: IO error while writing/reading VTIMEZONE data");
477 // Check equivalency after the first transition.
478 // The DST information before the first transition might be lost
479 // because there is no good way to represent the initial time with
481 if (vtz_new.getOffset(startTime) != olsontz.getOffset(startTime)) {
482 errln("FAIL: VTimeZone for " + tzids[i]
483 + " is not equivalent to its OlsonTimeZone corresponding at " + startTime);
485 TimeZoneTransition tzt = olsontz.getNextTransition(startTime, false);
487 if (!vtz_new.hasEquivalentTransitions(olsontz, tzt.getTime(), endTime, true)) {
489 if (!hasEquivalentTransitions(vtz_new, olsontz, tzt.getTime() + maxDelta, endTime, true, maxDelta)) {
490 errln("FAIL: VTimeZone for " + tzids[i] + " is not equivalent to its OlsonTimeZone corresponding.");
492 logln("VTimeZone for " + tzids[i] + " differs from its OlsonTimeZone corresponding with maximum transition time delta - " + maxDelta);
495 if (!vtz_new.hasEquivalentTransitions(olsontz, tzt.getTime(), endTime, false)) {
496 logln("VTimeZone for " + tzids[i] + " is not equivalent to its OlsonTimeZone corresponding in strict comparison mode.");
503 * Write out time zone rules of OlsonTimeZone after a cutoff date into VTIMEZONE format,
504 * create a new VTimeZone from the VTIMEZONE data, then compare transitions
506 public void TestVTimeZoneRoundTripPartial() {
507 long[] startTimes = new long[] {
508 getUTCMillis(1900, Calendar.JANUARY, 1),
509 getUTCMillis(1950, Calendar.JANUARY, 1),
510 getUTCMillis(2020, Calendar.JANUARY, 1)
512 long endTime = getUTCMillis(2050, Calendar.JANUARY, 1);
514 String[] tzids = getTestZIDs();
515 for (int n = 0; n < startTimes.length; n++) {
516 long startTime = startTimes[n];
517 for (int i = 0; i < tzids.length; i++) {
518 BasicTimeZone olsontz = (BasicTimeZone)TimeZone.getTimeZone(tzids[i], TimeZone.TIMEZONE_ICU);
519 VTimeZone vtz_org = VTimeZone.create(tzids[i]);
520 VTimeZone vtz_new = null;
522 // Write out VTIMEZONE
523 ByteArrayOutputStream baos = new ByteArrayOutputStream();
524 OutputStreamWriter writer = new OutputStreamWriter(baos);
525 vtz_org.write(writer, startTime);
527 byte[] vtzdata = baos.toByteArray();
529 ByteArrayInputStream bais = new ByteArrayInputStream(vtzdata);
530 InputStreamReader reader = new InputStreamReader(bais);
531 vtz_new = VTimeZone.create(reader);
534 } catch (IOException ioe) {
535 errln("FAIL: IO error while writing/reading VTIMEZONE data");
537 // Check equivalency after the first transition.
538 // The DST information before the first transition might be lost
539 // because there is no good way to represent the initial time with
541 if (vtz_new.getOffset(startTime) != olsontz.getOffset(startTime)) {
542 errln("FAIL: VTimeZone for " + tzids[i]
543 + " is not equivalent to its OlsonTimeZone corresponding at " + startTime);
545 TimeZoneTransition tzt = olsontz.getNextTransition(startTime, false);
547 if (!vtz_new.hasEquivalentTransitions(olsontz, tzt.getTime(), endTime, true)) {
549 if (!hasEquivalentTransitions(vtz_new, olsontz, tzt.getTime() + maxDelta, endTime, true, maxDelta)) {
550 errln("FAIL: VTimeZone for " + tzids[i] + "(>=" + startTime + ") is not equivalent to its OlsonTimeZone corresponding.");
552 logln("VTimeZone for " + tzids[i] + "(>=" + startTime + ") differs from its OlsonTimeZone corresponding with maximum transition time delta - " + maxDelta);
561 * Write out simple time zone rules from an OlsonTimeZone at various time into VTIMEZONE
562 * format and create a new VTimeZone from the VTIMEZONE data, then make sure the raw offset
563 * and DST savings are same in these two time zones.
565 public void TestVTimeZoneSimpleWrite() {
566 long[] testTimes = new long[] {
567 getUTCMillis(2006, Calendar.JANUARY, 1),
568 getUTCMillis(2006, Calendar.MARCH, 15),
569 getUTCMillis(2006, Calendar.MARCH, 31),
570 getUTCMillis(2006, Calendar.APRIL, 5),
571 getUTCMillis(2006, Calendar.OCTOBER, 25),
572 getUTCMillis(2006, Calendar.NOVEMBER, 1),
573 getUTCMillis(2006, Calendar.NOVEMBER, 5),
574 getUTCMillis(2007, Calendar.JANUARY, 1)
577 String[] tzids = getTestZIDs();
578 for (int n = 0; n < testTimes.length; n++) {
579 long time = testTimes[n];
581 int[] offsets1 = new int[2];
582 int[] offsets2 = new int[2];
584 for (int i = 0; i < tzids.length; i++) {
585 VTimeZone vtz_org = VTimeZone.create(tzids[i]);
586 VTimeZone vtz_new = null;
588 // Write out VTIMEZONE
589 ByteArrayOutputStream baos = new ByteArrayOutputStream();
590 OutputStreamWriter writer = new OutputStreamWriter(baos);
591 vtz_org.writeSimple(writer, time);
593 byte[] vtzdata = baos.toByteArray();
595 ByteArrayInputStream bais = new ByteArrayInputStream(vtzdata);
596 InputStreamReader reader = new InputStreamReader(bais);
597 vtz_new = VTimeZone.create(reader);
599 } catch (IOException ioe) {
600 errln("FAIL: IO error while writing/reading VTIMEZONE data");
604 vtz_org.getOffset(time, false, offsets1);
605 vtz_new.getOffset(time, false, offsets2);
606 if (offsets1[0] != offsets2[0] || offsets1[1] != offsets2[1]) {
607 errln("FAIL: VTimeZone writeSimple for " + tzids[i] + " at time " + time + " failed to the round trip.");
614 * Write out time zone rules of OlsonTimeZone into VTIMEZONE format with RFC2445 header TZURL and
615 * LAST-MODIFIED, create a new VTimeZone from the VTIMEZONE data to see if the headers are preserved.
617 public void TestVTimeZoneHeaderProps() {
618 String tzid = "America/Chicago";
619 String tzurl = "http://source.icu-project.org";
620 Date lastmod = new Date(getUTCMillis(2007, Calendar.JUNE, 1));
622 VTimeZone vtz = VTimeZone.create(tzid);
624 vtz.setLastModified(lastmod);
626 // Roundtrip conversion
627 VTimeZone newvtz1 = null;
629 // Write out VTIMEZONE
630 ByteArrayOutputStream baos = new ByteArrayOutputStream();
631 OutputStreamWriter writer = new OutputStreamWriter(baos);
634 byte[] vtzdata = baos.toByteArray();
636 ByteArrayInputStream bais = new ByteArrayInputStream(vtzdata);
637 InputStreamReader reader = new InputStreamReader(bais);
638 newvtz1 = VTimeZone.create(reader);
641 // Check if TZURL and LAST-MODIFIED headers are preserved
642 if (!(tzurl.equals(newvtz1.getTZURL()))) {
643 errln("FAIL: TZURL property is not preserved during the roundtrip conversion. Before:"
644 + tzurl + "/After:" + newvtz1.getTZURL());
646 if (!(lastmod.equals(newvtz1.getLastModified()))) {
647 errln("FAIL: LAST-MODIFIED property is not preserved during the roundtrip conversion. Before:"
648 + lastmod.getTime() + "/After:" + newvtz1.getLastModified().getTime());
650 } catch (IOException ioe) {
651 errln("FAIL: IO error while writing/reading VTIMEZONE data");
654 // Second roundtrip, with a cutoff
655 VTimeZone newvtz2 = null;
657 // Set different tzurl
658 String newtzurl = "http://www.ibm.com";
659 newvtz1.setTZURL(newtzurl);
660 // Write out VTIMEZONE
661 ByteArrayOutputStream baos = new ByteArrayOutputStream();
662 OutputStreamWriter writer = new OutputStreamWriter(baos);
663 newvtz1.write(writer, getUTCMillis(2000, Calendar.JANUARY, 1));
665 byte[] vtzdata = baos.toByteArray();
667 ByteArrayInputStream bais = new ByteArrayInputStream(vtzdata);
668 InputStreamReader reader = new InputStreamReader(bais);
669 newvtz2 = VTimeZone.create(reader);
672 // Check if TZURL and LAST-MODIFIED headers are preserved
673 if (!(newtzurl.equals(newvtz2.getTZURL()))) {
674 errln("FAIL: TZURL property is not preserved during the second roundtrip conversion. Before:"
675 + newtzurl + "/After:" + newvtz2.getTZURL());
677 if (!(lastmod.equals(newvtz2.getLastModified()))) {
678 errln("FAIL: LAST-MODIFIED property is not preserved during the second roundtrip conversion. Before:"
679 + lastmod.getTime() + "/After:" + newvtz2.getLastModified().getTime());
681 } catch (IOException ioe) {
682 errln("FAIL: IO error while writing/reading VTIMEZONE data");
688 * Extract simple rules from an OlsonTimeZone and make sure the rule format matches
689 * the expected format.
691 public void TestGetSimpleRules() {
692 long[] testTimes = new long[] {
693 getUTCMillis(1970, Calendar.JANUARY, 1),
694 getUTCMillis(2000, Calendar.MARCH, 31),
695 getUTCMillis(2005, Calendar.JULY, 1),
696 getUTCMillis(2010, Calendar.NOVEMBER, 1),
699 String[] tzids = getTestZIDs();
700 for (int n = 0; n < testTimes.length; n++) {
701 long time = testTimes[n];
702 for (int i = 0; i < tzids.length; i++) {
703 BasicTimeZone tz = (BasicTimeZone)TimeZone.getTimeZone(tzids[i], TimeZone.TIMEZONE_ICU);
704 TimeZoneRule[] rules = tz.getSimpleTimeZoneRulesNear(time);
706 errln("FAIL: Failed to extract simple rules for " + tzids[i] + " at " + time);
708 if (rules.length == 1) {
709 if (!(rules[0] instanceof InitialTimeZoneRule)) {
710 errln("FAIL: Unexpected rule object type is returned for " + tzids[i] + " at " + time);
712 } else if (rules.length == 3) {
713 if (!(rules[0] instanceof InitialTimeZoneRule)
714 || !(rules[1] instanceof AnnualTimeZoneRule)
715 || !(rules[2] instanceof AnnualTimeZoneRule)) {
716 errln("FAIL: Unexpected rule object type is returned for " + tzids[i] + " at " + time);
718 for (int idx = 1; idx <= 2; idx++) {
719 DateTimeRule dtr = ((AnnualTimeZoneRule)rules[idx]).getRule();
720 if (dtr.getTimeRuleType() != DateTimeRule.WALL_TIME) {
721 errln("FAIL: WALL_TIME is not used as the time rule in the time zone rule(" + idx + ") for " + tzids[i] + " at " + time);
723 if (dtr.getDateRuleType() != DateTimeRule.DOW) {
724 errln("FAIL: DOW is not used as the date rule in the time zone rule(" + idx + ") for " + tzids[i] + " at " + time);
728 errln("FAIL: Unexpected number of rules returned for " + tzids[i] + " at " + time);
736 * API coverage tests for TimeZoneRule
738 public void TestTimeZoneRuleCoverage() {
739 long time1 = getUTCMillis(2005, Calendar.JULY, 4);
740 long time2 = getUTCMillis(2015, Calendar.JULY, 4);
741 long time3 = getUTCMillis(1950, Calendar.JULY, 4);
743 DateTimeRule dtr1 = new DateTimeRule(Calendar.FEBRUARY, 29, Calendar.SUNDAY, false,
744 3*HOUR, DateTimeRule.WALL_TIME); // Last Sunday on or before Feb 29, at 3 AM, wall time
745 DateTimeRule dtr2 = new DateTimeRule(Calendar.MARCH, 11, 2*HOUR,
746 DateTimeRule.STANDARD_TIME); // Mar 11, at 2 AM, standard time
747 DateTimeRule dtr3 = new DateTimeRule(Calendar.OCTOBER, -1, Calendar.SATURDAY,
748 6*HOUR, DateTimeRule.UTC_TIME); //Last Saturday in Oct, at 6 AM, UTC
749 DateTimeRule dtr4 = new DateTimeRule(Calendar.MARCH, 8, Calendar.SUNDAY, true,
750 2*HOUR, DateTimeRule.WALL_TIME); // First Sunday on or after Mar 8, at 2 AM, wall time
752 AnnualTimeZoneRule a1 = new AnnualTimeZoneRule("a1", -3*HOUR, 1*HOUR, dtr1,
753 2000, AnnualTimeZoneRule.MAX_YEAR);
754 AnnualTimeZoneRule a2 = new AnnualTimeZoneRule("a2", -3*HOUR, 1*HOUR, dtr1,
755 2000, AnnualTimeZoneRule.MAX_YEAR);
756 AnnualTimeZoneRule a3 = new AnnualTimeZoneRule("a3", -3*HOUR, 1*HOUR, dtr1,
759 InitialTimeZoneRule i1 = new InitialTimeZoneRule("i1", -3*HOUR, 0);
760 InitialTimeZoneRule i2 = new InitialTimeZoneRule("i2", -3*HOUR, 0);
761 InitialTimeZoneRule i3 = new InitialTimeZoneRule("i3", -3*HOUR, 1*HOUR);
763 long[] emptytimes = {};
764 long[] trtimes1 = {0};
765 long[] trtimes2 = {0, 10000000};
767 TimeArrayTimeZoneRule t0 = null;
769 // Try to construct TimeArrayTimeZoneRule with null transition times
770 t0 = new TimeArrayTimeZoneRule("nulltimes", -3*HOUR, 0,
771 null, DateTimeRule.UTC_TIME);
772 } catch (IllegalArgumentException iae) {
773 logln("TimeArrayTimeZoneRule constructor throws IllegalArgumentException as expected.");
777 errln("FAIL: TimeArrayTimeZoneRule constructor did not throw IllegalArgumentException for null times");
781 // Try to construct TimeArrayTimeZoneRule with empty transition times
782 t0 = new TimeArrayTimeZoneRule("nulltimes", -3*HOUR, 0,
783 emptytimes, DateTimeRule.UTC_TIME);
784 } catch (IllegalArgumentException iae) {
785 logln("TimeArrayTimeZoneRule constructor throws IllegalArgumentException as expected.");
789 errln("FAIL: TimeArrayTimeZoneRule constructor did not throw IllegalArgumentException for empty times");
792 TimeArrayTimeZoneRule t1 = new TimeArrayTimeZoneRule("t1", -3*HOUR, 0, trtimes1, DateTimeRule.UTC_TIME);
793 TimeArrayTimeZoneRule t2 = new TimeArrayTimeZoneRule("t2", -3*HOUR, 0, trtimes1, DateTimeRule.UTC_TIME);
794 TimeArrayTimeZoneRule t3 = new TimeArrayTimeZoneRule("t3", -3*HOUR, 0, trtimes2, DateTimeRule.UTC_TIME);
795 TimeArrayTimeZoneRule t4 = new TimeArrayTimeZoneRule("t4", -3*HOUR, 0, trtimes1, DateTimeRule.STANDARD_TIME);
796 TimeArrayTimeZoneRule t5 = new TimeArrayTimeZoneRule("t5", -4*HOUR, 1*HOUR, trtimes1, DateTimeRule.WALL_TIME);
798 // AnnualTimeZoneRule#getRule
799 if (!a1.getRule().equals(a2.getRule())) {
800 errln("FAIL: The same DateTimeRule must be returned from AnnualTimeZoneRule a1 and a2");
803 // AnnualTimeZoneRule#getStartYear
804 int startYear = a1.getStartYear();
805 if (startYear != 2000) {
806 errln("FAIL: The start year of AnnualTimeZoneRule a1 must be 2000 - returned: " + startYear);
809 // AnnualTimeZoneRule#getEndYear
810 int endYear = a1.getEndYear();
811 if (endYear != AnnualTimeZoneRule.MAX_YEAR) {
812 errln("FAIL: The start year of AnnualTimeZoneRule a1 must be MAX_YEAR - returned: " + endYear);
814 endYear = a3.getEndYear();
815 if (endYear != 2010) {
816 errln("FAIL: The start year of AnnualTimeZoneRule a3 must be 2010 - returned: " + endYear);
819 // AnnualTimeZone#getStartInYear
820 Date d1 = a1.getStartInYear(2005, -3*HOUR, 0);
821 Date d2 = a3.getStartInYear(2005, -3*HOUR, 0);
822 if (d1 == null || d2 == null || !d1.equals(d2)) {
823 errln("FAIL: AnnualTimeZoneRule#getStartInYear did not work as expected");
825 d2 = a3.getStartInYear(2015, -3*HOUR, 0);
827 errln("FAIL: AnnualTimeZoneRule#getSTartInYear returned non-null date for 2015 which is out of rule range");
830 // AnnualTimeZone#getFirstStart
831 d1 = a1.getFirstStart(-3*HOUR, 0);
832 d2 = a1.getFirstStart(-4*HOUR, 1*HOUR);
833 if (d1 == null || d2 == null || !d1.equals(d2)) {
834 errln("FAIL: The same start time should be returned by getFirstStart");
837 // AnnualTimeZone#getFinalStart
838 d1 = a1.getFinalStart(-3*HOUR, 0);
840 errln("FAIL: Non-null Date is returned by getFinalStart for a1");
842 d1 = a1.getStartInYear(2010, -3*HOUR, 0);
843 d2 = a3.getFinalStart(-3*HOUR, 0);
844 if (d1 == null || d2 == null || !d1.equals(d2)) {
845 errln("FAIL: Bad date is returned by getFinalStart");
848 // AnnualTimeZone#getNextStart / getPreviousStart
849 d1 = a1.getNextStart(time1, -3*HOUR, 0, false);
851 errln("FAIL: Null Date is returned by getNextStart");
853 d2 = a1.getPreviousStart(d1.getTime(), -3*HOUR, 0, true);
854 if (d2 == null || !d1.equals(d2)) {
855 errln("FAIL: Bad Date is returned by getPreviousStart");
858 d1 = a3.getNextStart(time2, -3*HOUR, 0, false);
860 errln("FAIL: getNextStart must return null when no start time is available after the base time");
862 d1 = a3.getFinalStart(-3*HOUR, 0);
863 d2 = a3.getPreviousStart(time2, -3*HOUR, 0, false);
864 if (d1 == null || d2 == null || !d1.equals(d2)) {
865 errln("FAIL: getPreviousStart does not match with getFinalStart after the end year");
868 // AnnualTimeZone#isEquavalentTo
869 if (!a1.isEquivalentTo(a2)) {
870 errln("FAIL: AnnualTimeZoneRule a1 is equivalent to a2, but returned false");
872 if (a1.isEquivalentTo(a3)) {
873 errln("FAIL: AnnualTimeZoneRule a1 is not equivalent to a3, but returned true");
875 if (!a1.isEquivalentTo(a1)) {
876 errln("FAIL: AnnualTimeZoneRule a1 is equivalent to itself, but returned false");
878 if (a1.isEquivalentTo(t1)) {
879 errln("FAIL: AnnualTimeZoneRule is not equivalent to TimeArrayTimeZoneRule, but returned true");
882 // AnnualTimeZone#isTransitionRule
883 if (!a1.isTransitionRule()) {
884 errln("FAIL: An AnnualTimeZoneRule is a transition rule, but returned false");
887 // AnnualTimeZone#toString
888 String str = a1.toString();
889 if (str == null || str.length() == 0) {
890 errln("FAIL: AnnualTimeZoneRule#toString for a1 returns null or empty string");
892 logln("AnnualTimeZoneRule a1 : " + str);
895 if (str == null || str.length() == 0) {
896 errln("FAIL: AnnualTimeZoneRule#toString for a3 returns null or empty string");
898 logln("AnnualTimeZoneRule a3 : " + str);
901 // InitialTimeZoneRule#isEquivalentRule
902 if (!i1.isEquivalentTo(i2)) {
903 errln("FAIL: InitialTimeZoneRule i1 is equivalent to i2, but returned false");
905 if (i1.isEquivalentTo(i3)) {
906 errln("FAIL: InitialTimeZoneRule i1 is not equivalent to i3, but returned true");
908 if (i1.isEquivalentTo(a1)) {
909 errln("FAIL: An InitialTimeZoneRule is not equivalent to an AnnualTimeZoneRule, but returned true");
912 // InitialTimeZoneRule#getFirstStart/getFinalStart/getNextStart/getPreviousStart
913 d1 = i1.getFirstStart(0, 0);
915 errln("FAIL: Non-null Date is returned by InitialTimeZone#getFirstStart");
917 d1 = i1.getFinalStart(0, 0);
919 errln("FAIL: Non-null Date is returned by InitialTimeZone#getFinalStart");
921 d1 = i1.getNextStart(time1, 0, 0, false);
923 errln("FAIL: Non-null Date is returned by InitialTimeZone#getNextStart");
925 d1 = i1.getPreviousStart(time1, 0, 0, false);
927 errln("FAIL: Non-null Date is returned by InitialTimeZone#getPreviousStart");
930 // InitialTimeZoneRule#isTransitionRule
931 if (i1.isTransitionRule()) {
932 errln("FAIL: An InitialTimeZoneRule is not a transition rule, but returned true");
935 // InitialTimeZoneRule#toString
937 if (str == null || str.length() == 0) {
938 errln("FAIL: InitialTimeZoneRule#toString returns null or empty string");
940 logln("InitialTimeZoneRule i1 : " + str);
944 // TimeArrayTimeZoneRule#getStartTimes
945 long[] times = t1.getStartTimes();
946 if (times == null || times.length == 0 || times[0] != 0) {
947 errln("FAIL: Bad start times are returned by TimeArrayTimeZoneRule#getStartTimes");
950 // TimeArrayTimeZoneRule#getTimeType
951 if (t1.getTimeType() != DateTimeRule.UTC_TIME) {
952 errln("FAIL: TimeArrayTimeZoneRule t1 uses UTC_TIME, but different type is returned");
954 if (t4.getTimeType() != DateTimeRule.STANDARD_TIME) {
955 errln("FAIL: TimeArrayTimeZoneRule t4 uses STANDARD_TIME, but different type is returned");
957 if (t5.getTimeType() != DateTimeRule.WALL_TIME) {
958 errln("FAIL: TimeArrayTimeZoneRule t5 uses WALL_TIME, but different type is returned");
961 // TimeArrayTimeZoneRule#getFirstStart/getFinalStart
962 d1 = t1.getFirstStart(0, 0);
963 if (d1 == null || d1.getTime() != trtimes1[0]) {
964 errln("FAIL: Bad first start time returned from TimeArrayTimeZoneRule t1");
966 d1 = t1.getFinalStart(0, 0);
967 if (d1 == null || d1.getTime() != trtimes1[0]) {
968 errln("FAIL: Bad final start time returned from TimeArrayTimeZoneRule t1");
970 d1 = t4.getFirstStart(-4*HOUR, 1*HOUR);
971 if (d1 == null || (d1.getTime() != trtimes1[0] + 4*HOUR)) {
972 errln("FAIL: Bad first start time returned from TimeArrayTimeZoneRule t4");
974 d1 = t5.getFirstStart(-4*HOUR, 1*HOUR);
975 if (d1 == null || (d1.getTime() != trtimes1[0] + 3*HOUR)) {
976 errln("FAIL: Bad first start time returned from TimeArrayTimeZoneRule t5");
979 // TimeArrayTimeZoneRule#getNextStart/getPreviousStart
980 d1 = t3.getNextStart(time1, -3*HOUR, 1*HOUR, false);
982 errln("FAIL: Non-null Date is returned by getNextStart after the final transition for t3");
984 d1 = t3.getPreviousStart(time1, -3*HOUR, 1*HOUR, false);
985 if (d1 == null || d1.getTime() != trtimes2[1]) {
986 errln("FAIL: Bad start time returned by getPreviousStart for t3");
988 d2 = t3.getPreviousStart(d1.getTime(), -3*HOUR, 1*HOUR, false);
989 if (d2 == null || d2.getTime() != trtimes2[0]) {
990 errln("FAIL: Bad start time returned by getPreviousStart for t3");
993 d1 = t3.getPreviousStart(time3, -3*HOUR, 1*HOUR, false); //time3 - year 1950, no result expected
995 errln("FAIL: Non-null Date is returned by getPrevoousStart for t3");
998 // TimeArrayTimeZoneRule#isEquivalentTo
999 if (!t1.isEquivalentTo(t2)) {
1000 errln("FAIL: TimeArrayTimeZoneRule t1 is equivalent to t2, but returned false");
1002 if (t1.isEquivalentTo(t3)) {
1003 errln("FAIL: TimeArrayTimeZoneRule t1 is not equivalent to t3, but returned true");
1005 if (t1.isEquivalentTo(t4)) {
1006 errln("FAIL: TimeArrayTimeZoneRule t1 is not equivalent to t4, but returned true");
1008 if (t1.isEquivalentTo(a1)) {
1009 errln("FAIL: TimeArrayTimeZoneRule is not equivalent to AnnualTimeZoneRule, but returned true");
1012 // TimeArrayTimeZoneRule#isTransitionRule
1013 if (!t1.isTransitionRule()) {
1014 errln("FAIL: A TimeArrayTimeZoneRule is a transition rule, but returned false");
1017 // TimeArrayTimeZoneRule#toString
1018 str = t3.toString();
1019 if (str == null || str.length() == 0) {
1020 errln("FAIL: TimeArrayTimeZoneRule#toString returns null or empty string");
1022 logln("TimeArrayTimeZoneRule t3 : " + str);
1025 // DateTimeRule#toString
1026 str = dtr1.toString();
1027 if (str == null || str.length() == 0) {
1028 errln("FAIL: DateTimeRule#toString for dtr1 returns null or empty string");
1030 logln("DateTimeRule dtr1 : " + str);
1032 str = dtr2.toString();
1033 if (str == null || str.length() == 0) {
1034 errln("FAIL: DateTimeRule#toString for dtr2 returns null or empty string");
1036 logln("DateTimeRule dtr1 : " + str);
1038 str = dtr3.toString();
1039 if (str == null || str.length() == 0) {
1040 errln("FAIL: DateTimeRule#toString for dtr3 returns null or empty string");
1042 logln("DateTimeRule dtr1 : " + str);
1044 str = dtr4.toString();
1045 if (str == null || str.length() == 0) {
1046 errln("FAIL: DateTimeRule#toString for dtr4 returns null or empty string");
1048 logln("DateTimeRule dtr1 : " + str);
1053 * API coverage test for BasicTimeZone APIs in SimpleTimeZone
1055 public void TestSimpleTimeZoneCoverage() {
1057 long time1 = getUTCMillis(1990, Calendar.JUNE, 1);
1058 long time2 = getUTCMillis(2000, Calendar.JUNE, 1);
1060 TimeZoneTransition tzt1, tzt2;
1062 // BasicTimeZone API implementation in SimpleTimeZone
1063 SimpleTimeZone stz1 = new SimpleTimeZone(-5*HOUR, "GMT-5");
1065 tzt1 = stz1.getNextTransition(time1, false);
1067 errln("FAIL: No transition must be returned by getNextTranstion for SimpleTimeZone with no DST rule");
1069 tzt1 = stz1.getPreviousTransition(time1, false);
1071 errln("FAIL: No transition must be returned by getPreviousTransition for SimpleTimeZone with no DST rule");
1073 TimeZoneRule[] tzrules = stz1.getTimeZoneRules();
1074 if (tzrules.length != 1 || !(tzrules[0] instanceof InitialTimeZoneRule)) {
1075 errln("FAIL: Invalid results returned by SimpleTimeZone#getTimeZoneRules");
1079 stz1.setStartRule(Calendar.MARCH, 11, 2*HOUR); // March 11
1080 stz1.setEndRule(Calendar.NOVEMBER, 1, Calendar.SUNDAY, 2*HOUR); // First Sunday in November
1081 tzt1 = stz1.getNextTransition(time1, false);
1083 errln("FAIL: Non-null transition must be returned by getNextTranstion for SimpleTimeZone with a DST rule");
1085 String str = tzt1.toString();
1086 if (str == null || str.length() == 0) {
1087 errln("FAIL: TimeZoneTransition#toString returns null or empty string");
1092 tzt1 = stz1.getPreviousTransition(time1, false);
1094 errln("FAIL: Non-null transition must be returned by getPreviousTransition for SimpleTimeZone with a DST rule");
1096 tzrules = stz1.getTimeZoneRules();
1097 if (tzrules.length != 3 || !(tzrules[0] instanceof InitialTimeZoneRule)
1098 || !(tzrules[1] instanceof AnnualTimeZoneRule)
1099 || !(tzrules[2] instanceof AnnualTimeZoneRule)) {
1100 errln("FAIL: Invalid results returned by SimpleTimeZone#getTimeZoneRules for a SimpleTimeZone with DST");
1102 // Set DST start year
1103 stz1.setStartYear(2007);
1104 tzt1 = stz1.getPreviousTransition(time1, false);
1106 errln("FAIL: No transition must be returned before 1990");
1108 tzt1 = stz1.getNextTransition(time1, false); // transition after 1990-06-01
1109 tzt2 = stz1.getNextTransition(time2, false); // transition after 2000-06-01
1110 if (tzt1 == null || tzt2 == null || !tzt1.equals(tzt2)) {
1111 errln("FAIL: Bad transition returned by SimpleTimeZone#getNextTransition");
1116 * API coverage test for VTimeZone
1118 public void TestVTimeZoneCoverage() {
1119 final String TZID = "Europe/Moscow";
1120 BasicTimeZone otz = (BasicTimeZone)TimeZone.getTimeZone(TZID, TimeZone.TIMEZONE_ICU);
1121 VTimeZone vtz = VTimeZone.create(TZID);
1123 // getOffset(era, year, month, day, dayOfWeek, milliseconds)
1124 int offset1 = otz.getOffset(GregorianCalendar.AD, 2007, Calendar.JULY, 1, Calendar.SUNDAY, 0);
1125 int offset2 = vtz.getOffset(GregorianCalendar.AD, 2007, Calendar.JULY, 1, Calendar.SUNDAY, 0);
1126 if (offset1 != offset2) {
1127 errln("FAIL: getOffset(int,int,int,int,int,int) returned different results in VTimeZone and OlsonTimeZone");
1130 // getOffset(date, local, offsets)
1131 int[] offsets1 = new int[2];
1132 int[] offsets2 = new int[2];
1133 long t = System.currentTimeMillis();
1134 otz.getOffset(t, false, offsets1);
1135 vtz.getOffset(t, false, offsets2);
1136 if (offsets1[0] != offsets2[0] || offsets1[1] != offsets2[1]) {
1137 errln("FAIL: getOffset(long,boolean,int[]) returned different results in VTimeZone and OlsonTimeZone");
1141 if (otz.getRawOffset() != vtz.getRawOffset()) {
1142 errln("FAIL: getRawOffset returned different results in VTimeZone and OlsonTimeZone");
1146 Date d = new Date();
1147 if (otz.inDaylightTime(d) != vtz.inDaylightTime(d)) {
1148 errln("FAIL: inDaylightTime returned different results in VTimeZone and OlsonTimeZone");
1152 if (otz.useDaylightTime() != vtz.useDaylightTime()) {
1153 errln("FAIL: useDaylightTime returned different results in VTimeZone and OlsonTimeZone");
1157 final int RAW = -10*HOUR;
1158 VTimeZone tmpvtz = (VTimeZone)vtz.clone();
1159 tmpvtz.setRawOffset(RAW);
1160 if (tmpvtz.getRawOffset() != RAW) {
1161 logln("setRawOffset is implemented");
1165 boolean bSame = otz.hasSameRules(vtz);
1166 logln("OlsonTimeZone#hasSameRules(VTimeZone) should return false always for now - actual: " + bSame);
1168 // getTZURL/setTZURL
1169 final String TZURL = "http://icu-project.org/timezone";
1170 String tzurl = vtz.getTZURL();
1171 if (tzurl != null) {
1172 errln("FAIL: getTZURL returned non-null value");
1174 vtz.setTZURL(TZURL);
1175 tzurl = vtz.getTZURL();
1176 if (!TZURL.equals(tzurl)) {
1177 errln("FAIL: URL returned by getTZURL does not match the one set by setTZURL");
1180 // getLastModified/setLastModified
1181 Date lastmod = vtz.getLastModified();
1182 if (lastmod != null) {
1183 errln("FAIL: getLastModified returned non-null value");
1185 Date newdate = new Date();
1186 vtz.setLastModified(newdate);
1187 lastmod = vtz.getLastModified();
1188 if (!newdate.equals(lastmod)) {
1189 errln("FAIL: Date returned by getLastModified does not match the one set by setLastModified");
1192 // getNextTransition/getPreviousTransition
1193 long base = getUTCMillis(2007, Calendar.JULY, 1);
1194 TimeZoneTransition tzt1 = otz.getNextTransition(base, true);
1195 TimeZoneTransition tzt2 = vtz.getNextTransition(base, true);
1196 if (tzt1.equals(tzt2)) {
1197 errln("FAIL: getNextTransition returned different results in VTimeZone and OlsonTimeZone");
1199 tzt1 = otz.getPreviousTransition(base, false);
1200 tzt2 = vtz.getPreviousTransition(base, false);
1201 if (tzt1.equals(tzt2)) {
1202 errln("FAIL: getPreviousTransition returned different results in VTimeZone and OlsonTimeZone");
1205 // hasEquivalentTransitions
1206 long time1 = getUTCMillis(1950, Calendar.JANUARY, 1);
1207 long time2 = getUTCMillis(2020, Calendar.JANUARY, 1);
1208 if (!vtz.hasEquivalentTransitions(otz, time1, time2)) {
1209 errln("FAIL: hasEquivalentTransitons returned false for the same time zone");
1213 TimeZoneRule[] rulesetAll = vtz.getTimeZoneRules();
1214 TimeZoneRule[] ruleset1 = vtz.getTimeZoneRules(time1);
1215 TimeZoneRule[] ruleset2 = vtz.getTimeZoneRules(time2);
1216 if (rulesetAll.length < ruleset1.length || ruleset1.length < ruleset2.length) {
1217 errln("FAIL: Number of rules returned by getRules is invalid");
1220 int[] offsets_vtzc = new int[2];
1221 VTimeZone vtzc = VTimeZone.create("PST");
1222 vtzc.getOffsetFromLocal(Calendar.getInstance(vtzc).getTimeInMillis(), VTimeZone.LOCAL_STD, VTimeZone.LOCAL_STD, offsets_vtzc);
1223 if (offsets_vtzc[0] > offsets_vtzc[1]) {
1224 errln("Error getOffsetFromLocal()");
1228 public void TestVTimeZoneParse() {
1229 // Trying to create VTimeZone from empty data
1230 StringReader r = new StringReader("");
1231 VTimeZone empty = VTimeZone.create(r);
1232 if (empty != null) {
1233 errln("FAIL: Non-null VTimeZone is returned for empty VTIMEZONE data");
1236 // Create VTimeZone for Asia/Tokyo
1238 "BEGIN:VTIMEZONE\r\n" +
1241 "BEGIN:STANDARD\r\n" +
1242 "TZOFFSETFROM:+0900\r\n" +
1243 "TZOFFSETTO:+0900\r\n" +
1245 "DTSTART:19700101\r\n" +
1247 "END:STANDARD\r\n" +
1249 r = new StringReader(asiaTokyo);
1250 VTimeZone tokyo = VTimeZone.create(r);
1251 if (tokyo == null) {
1252 errln("FAIL: Failed to create a VTimeZone tokyo");
1254 // Make sure offsets are correct
1255 int[] offsets = new int[2];
1256 tokyo.getOffset(System.currentTimeMillis(), false, offsets);
1257 if (offsets[0] != 9*HOUR || offsets[1] != 0) {
1258 errln("FAIL: Bad offsets returned by a VTimeZone created for Tokyo");
1262 // Create VTimeZone from VTIMEZONE data
1264 "BEGIN:VCALENDAR\r\n" +
1265 "BEGIN:VTIMEZONE\r\n" +
1267 "BEGIN:STANDARD\r\n" +
1268 "TZOFFSETFROM:-0700\r\n" +
1269 "TZOFFSETTO:-0800\r\n" +
1271 "DTSTART:20071010T010000\r\n" +
1272 "RRULE:FREQ=YEARLY;BYDAY=WE;BYMONTHDAY=10,11,12,13,14,15,16;BYMONTH=10\r\n" +
1273 "END:STANDARD\r\n" +
1274 "BEGIN:DAYLIGHT\r\n" +
1275 "TZOFFSETFROM:-0800\r\n" +
1276 "TZOFFSETTO:-0700\r\n" +
1278 "DTSTART:20070415T010000\r\n" +
1279 "RRULE:FREQ=YEARLY;BYMONTHDAY=15;BYMONTH=4\r\n" +
1280 "END:DAYLIGHT\r\n" +
1281 "END:VTIMEZONE\r\n" +
1284 r = new StringReader(fooData);
1285 VTimeZone foo = VTimeZone.create(r);
1287 errln("FAIL: Failed to create a VTimeZone foo");
1289 // Write VTIMEZONE data
1290 StringWriter w = new StringWriter();
1292 foo.write(w, getUTCMillis(2005, Calendar.JANUARY, 1));
1293 } catch (IOException ioe) {
1294 errln("FAIL: IOException is thrown while writing VTIMEZONE data for foo");
1296 logln(w.toString());
1300 public void TestT6216() {
1301 // Test case in #6216
1303 "BEGIN:VCALENDAR\r\n" +
1305 "PRODID:-//PYVOBJECT//NONSGML Version 1//EN\r\n" +
1306 "BEGIN:VTIMEZONE\r\n" +
1307 "TZID:Asia/Tokyo\r\n" +
1308 "BEGIN:STANDARD\r\n" +
1309 "DTSTART:20000101T000000\r\n" +
1310 "RRULE:FREQ=YEARLY;BYMONTH=1\r\n" +
1311 "TZNAME:Asia/Tokyo\r\n" +
1312 "TZOFFSETFROM:+0900\r\n" +
1313 "TZOFFSETTO:+0900\r\n" +
1314 "END:STANDARD\r\n" +
1315 "END:VTIMEZONE\r\n" +
1318 // Single final rule, overlapping with another
1319 String finalOverlap =
1320 "BEGIN:VCALENDAR\r\n" +
1321 "BEGIN:VTIMEZONE\r\n" +
1322 "TZID:FinalOverlap\r\n" +
1323 "BEGIN:STANDARD\r\n" +
1324 "TZOFFSETFROM:-0200\r\n" +
1325 "TZOFFSETTO:-0300\r\n" +
1327 "DTSTART:20001029T020000\r\n" +
1328 "RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10\r\n" +
1329 "END:STANDARD\r\n" +
1330 "BEGIN:DAYLIGHT\r\n" +
1331 "TZOFFSETFROM:-0300\r\n" +
1332 "TZOFFSETTO:-0200\r\n" +
1334 "DTSTART:19990404T020000\r\n" +
1335 "RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4;UNTIL=20050403T040000Z\r\n" +
1336 "END:DAYLIGHT\r\n" +
1337 "END:VTIMEZONE\r\n" +
1340 // Single final rule, no overlapping with another
1341 String finalNonOverlap =
1342 "BEGIN:VCALENDAR\r\n" +
1343 "BEGIN:VTIMEZONE\r\n" +
1344 "TZID:FinalNonOverlap\r\n" +
1345 "BEGIN:STANDARD\r\n" +
1346 "TZOFFSETFROM:-0200\r\n" +
1347 "TZOFFSETTO:-0300\r\n" +
1349 "DTSTART:20001029T020000\r\n" +
1350 "RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10;UNTIL=20041031T040000Z\r\n" +
1351 "END:STANDARD\r\n" +
1352 "BEGIN:DAYLIGHT\r\n" +
1353 "TZOFFSETFROM:-0300\r\n" +
1354 "TZOFFSETTO:-0200\r\n" +
1356 "DTSTART:19990404T020000\r\n" +
1357 "RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4;UNTIL=20050403T040000Z\r\n" +
1358 "END:DAYLIGHT\r\n" +
1359 "BEGIN:STANDARD\r\n" +
1360 "TZOFFSETFROM:-0200\r\n" +
1361 "TZOFFSETTO:-0300\r\n" +
1362 "TZNAME:STDFINAL\r\n" +
1363 "DTSTART:20071028T020000\r\n" +
1364 "RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10\r\n" +
1365 "END:STANDARD\r\n" +
1366 "END:VTIMEZONE\r\n" +
1369 int[][] TestDates = {
1370 {1995, Calendar.JANUARY, 1},
1371 {1995, Calendar.JULY, 1},
1372 {2000, Calendar.JANUARY, 1},
1373 {2000, Calendar.JULY, 1},
1374 {2005, Calendar.JANUARY, 1},
1375 {2005, Calendar.JULY, 1},
1376 {2010, Calendar.JANUARY, 1},
1377 {2010, Calendar.JULY, 1},
1380 String[] TestZones = {
1386 int[][] Expected = {
1387 // JAN90 JUL90 JAN00 JUL00 JAN05 JUL05 JAN10 JUL10
1388 { 32400000, 32400000, 32400000, 32400000, 32400000, 32400000, 32400000, 32400000},
1389 {-10800000, -10800000, -7200000, -7200000, -10800000, -7200000, -10800000, -10800000},
1390 {-10800000, -10800000, -7200000, -7200000, -10800000, -7200000, -10800000, -10800000},
1394 long[] times = new long[TestDates.length];
1395 Calendar cal = new GregorianCalendar(TimeZone.getTimeZone("Etc/GMT"));
1396 for (int i = 0; i < TestDates.length; i++) {
1398 cal.set(TestDates[i][0], TestDates[i][1], TestDates[i][2]);
1399 times[i] = cal.getTimeInMillis();
1402 for (int i = 0; i < TestZones.length; i++) {
1404 VTimeZone vtz = VTimeZone.create(new StringReader(TestZones[i]));
1405 for (int j = 0; j < times.length; j++) {
1406 int offset = vtz.getOffset(times[j]);
1407 if (offset != Expected[i][j]) {
1408 errln("FAIL: Invalid offset at time(" + times[j] + "):" + offset + " Expected:" + Expected[i][j]);
1411 } catch (Exception e) {
1412 errln("FAIL: Failed to calculate the offset for VTIMEZONE data " + i);
1417 public void TestT6669() {
1418 // getNext/PreviousTransition implementation in SimpleTimeZone
1419 // used to use a bad condition for detecting if DST is enabled or not.
1421 SimpleTimeZone stz = new SimpleTimeZone(0, "CustomID",
1422 Calendar.JANUARY, 1, Calendar.SUNDAY, 0,
1423 Calendar.JULY, 1, Calendar.SUNDAY, 0);
1425 long t = 1230681600000L; //2008-12-31T00:00:00
1426 long expectedNext = 1231027200000L; //2009-01-04T00:00:00
1427 long expectedPrev = 1215298800000L; //2008-07-06T00:00:00
1429 TimeZoneTransition tzt = stz.getNextTransition(t, false);
1431 errln("FAIL: No transition returned by getNextTransition.");
1432 } else if (tzt.getTime() != expectedNext){
1433 errln("FAIL: Wrong transition time returned by getNextTransition - "
1434 + tzt.getTime() + " Expected: " + expectedNext);
1437 tzt = stz.getPreviousTransition(t, true);
1439 errln("FAIL: No transition returned by getPreviousTransition.");
1440 } else if (tzt.getTime() != expectedPrev){
1441 errln("FAIL: Wrong transition time returned by getPreviousTransition - "
1442 + tzt.getTime() + " Expected: " + expectedPrev);
1446 public void TestBasicTimeZoneCoverage() {
1447 TimeZone tz = TimeZone.getTimeZone("PST");
1448 if (tz instanceof BasicTimeZone) {
1449 BasicTimeZone btz = (BasicTimeZone)tz;
1450 int []offsets = new int[2];
1452 btz.getOffsetFromLocal(Calendar.getInstance().getTimeInMillis(), BasicTimeZone.LOCAL_STD, BasicTimeZone.LOCAL_STD, offsets);
1453 if (offsets[0] > offsets[1]) {
1454 errln("Error calling getOffsetFromLocal().");
1457 logln("Skipping TestBasicTimeZoneCoverage: ICU4J is configured to use JDK TimeZone");
1461 // Internal utility methods -----------------------------------------
1464 * Check if a time shift really happens on each transition returned by getNextTransition or
1465 * getPreviousTransition in the specified time range
1467 private void verifyTransitions(TimeZone tz, long start, long end) {
1468 BasicTimeZone icutz = (BasicTimeZone)tz;
1470 int[] before = new int[2];
1471 int[] after = new int[2];
1472 TimeZoneTransition tzt0;
1478 TimeZoneTransition tzt = icutz.getNextTransition(time, false);
1483 time = tzt.getTime();
1487 icutz.getOffset(time, false, after);
1488 icutz.getOffset(time - 1, false, before);
1490 if (after[0] == before[0] && after[1] == before[1]) {
1491 errln("FAIL: False transition returned by getNextTransition for " + icutz.getID() + " at " + time);
1494 (tzt0.getTo().getRawOffset() != tzt.getFrom().getRawOffset()
1495 || tzt0.getTo().getDSTSavings() != tzt.getFrom().getDSTSavings())) {
1496 errln("FAIL: TO rule of the previous transition does not match FROM rule of this transtion at "
1497 + time + " for " + icutz.getID());
1506 TimeZoneTransition tzt = icutz.getPreviousTransition(time, false);
1510 time = tzt.getTime();
1511 if (time <= start) {
1514 icutz.getOffset(time, false, after);
1515 icutz.getOffset(time - 1, false, before);
1517 if (after[0] == before[0] && after[1] == before[1]) {
1518 errln("FAIL: False transition returned by getPreviousTransition for " + icutz.getID() + " at " + time);
1522 (tzt0.getFrom().getRawOffset() != tzt.getTo().getRawOffset()
1523 || tzt0.getFrom().getDSTSavings() != tzt.getTo().getDSTSavings())) {
1524 errln("FAIL: TO rule of the next transition does not match FROM rule in this transtion at "
1525 + time + " for " + icutz.getID());
1532 * Compare all time transitions in 2 time zones in the specified time range in ascending order
1534 private void compareTransitionsAscending(TimeZone tz1, TimeZone tz2, long start, long end, boolean inclusive) {
1535 BasicTimeZone z1 = (BasicTimeZone)tz1;
1536 BasicTimeZone z2 = (BasicTimeZone)tz2;
1537 String zid1 = tz1.getID();
1538 String zid2 = tz2.getID();
1542 TimeZoneTransition tzt1 = z1.getNextTransition(time, inclusive);
1543 TimeZoneTransition tzt2 = z2.getNextTransition(time, inclusive);
1544 boolean inRange1 = false;
1545 boolean inRange2 = false;
1547 if (tzt1.getTime() < end || (inclusive && tzt1.getTime() == end)) {
1552 if (tzt2.getTime() < end || (inclusive && tzt2.getTime() == end)) {
1556 if (!inRange1 && !inRange2) {
1557 // No more transition in the range
1561 errln("FAIL: " + zid1 + " does not have any transitions after " + time + " before " + end);
1565 errln("FAIL: " + zid2 + " does not have any transitions after " + time + " before " + end);
1568 if (tzt1.getTime() != tzt2.getTime()) {
1569 errln("FAIL: First transition after " + time + " "
1570 + zid1 + "[" + tzt1.getTime() + "] "
1571 + zid2 + "[" + tzt2.getTime() + "]");
1574 time = tzt1.getTime();
1582 * Compare all time transitions in 2 time zones in the specified time range in descending order
1584 private void compareTransitionsDescending(TimeZone tz1, TimeZone tz2, long start, long end, boolean inclusive) {
1585 BasicTimeZone z1 = (BasicTimeZone)tz1;
1586 BasicTimeZone z2 = (BasicTimeZone)tz2;
1587 String zid1 = tz1.getID();
1588 String zid2 = tz2.getID();
1591 TimeZoneTransition tzt1 = z1.getPreviousTransition(time, inclusive);
1592 TimeZoneTransition tzt2 = z2.getPreviousTransition(time, inclusive);
1593 boolean inRange1 = false;
1594 boolean inRange2 = false;
1596 if (tzt1.getTime() > start || (inclusive && tzt1.getTime() == start)) {
1601 if (tzt2.getTime() > start || (inclusive && tzt2.getTime() == start)) {
1605 if (!inRange1 && !inRange2) {
1606 // No more transition in the range
1610 errln("FAIL: " + zid1 + " does not have any transitions before " + time + " after " + start);
1614 errln("FAIL: " + zid2 + " does not have any transitions before " + time + " after " + start);
1617 if (tzt1.getTime() != tzt2.getTime()) {
1618 errln("FAIL: Last transition before " + time + " "
1619 + zid1 + "[" + tzt1.getTime() + "] "
1620 + zid2 + "[" + tzt2.getTime() + "]");
1623 time = tzt1.getTime();
1630 private static final String[] TESTZIDS = {
1633 "America/Los_Angeles",
1634 "America/Indiana/Indianapolis",
1647 private String[] getTestZIDs() {
1648 if (getInclusion() > 5) {
1649 return TimeZone.getAvailableIDs();
1654 private static final int[][] TESTYEARS = {
1655 {1895, 1905}, // including int32 minimum second
1656 {1965, 1975}, // including the epoch
1657 {1995, 2015} // practical year range
1660 private long[] getTestTimeRange(int idx) {
1662 if (idx < TESTYEARS.length) {
1663 loyear = TESTYEARS[idx][0];
1664 hiyear = TESTYEARS[idx][1];
1665 } else if (idx == TESTYEARS.length && getInclusion() > 5) {
1672 long[] times = new long[2];
1673 times[0] = getUTCMillis(loyear, Calendar.JANUARY, 1);
1674 times[1] = getUTCMillis(hiyear + 1, Calendar.JANUARY, 1);
1679 private GregorianCalendar utcCal;
1681 private long getUTCMillis(int year, int month, int dayOfMonth) {
1682 if (utcCal == null) {
1683 utcCal = new GregorianCalendar(TimeZone.getTimeZone("UTC"), ULocale.ROOT);
1686 utcCal.set(year, month, dayOfMonth);
1687 return utcCal.getTimeInMillis();
1691 * Slightly modified version of BasicTimeZone#hasEquivalentTransitions.
1692 * This version returns true if transition time delta is within the given
1695 private static boolean hasEquivalentTransitions(BasicTimeZone tz1, BasicTimeZone tz2,
1696 long start, long end,
1697 boolean ignoreDstAmount, int maxTransitionTimeDelta) {
1698 if (tz1.hasSameRules(tz2)) {
1702 // Check the offsets at the start time
1703 int[] offsets1 = new int[2];
1704 int[] offsets2 = new int[2];
1706 tz1.getOffset(start, false, offsets1);
1707 tz2.getOffset(start, false, offsets2);
1709 if (ignoreDstAmount) {
1710 if ((offsets1[0] + offsets1[1] != offsets2[0] + offsets2[1])
1711 || (offsets1[1] != 0 && offsets2[1] == 0)
1712 || (offsets1[1] == 0 && offsets2[1] != 0)) {
1716 if (offsets1[0] != offsets2[0] || offsets1[1] != offsets2[1]) {
1721 // Check transitions in the range
1724 TimeZoneTransition tr1 = tz1.getNextTransition(time, false);
1725 TimeZoneTransition tr2 = tz2.getNextTransition(time, false);
1727 if (ignoreDstAmount) {
1728 // Skip a transition which only differ the amount of DST savings
1731 && tr1.getTime() <= end
1732 && (tr1.getFrom().getRawOffset() + tr1.getFrom().getDSTSavings()
1733 == tr1.getTo().getRawOffset() + tr1.getTo().getDSTSavings())
1734 && (tr1.getFrom().getDSTSavings() != 0 && tr1.getTo().getDSTSavings() != 0)) {
1735 tr1 = tz1.getNextTransition(tr1.getTime(), false);
1742 && tr2.getTime() <= end
1743 && (tr2.getFrom().getRawOffset() + tr2.getFrom().getDSTSavings()
1744 == tr2.getTo().getRawOffset() + tr2.getTo().getDSTSavings())
1745 && (tr2.getFrom().getDSTSavings() != 0 && tr2.getTo().getDSTSavings() != 0)) {
1746 tr2 = tz2.getNextTransition(tr2.getTime(), false);
1752 boolean inRange1 = false;
1753 boolean inRange2 = false;
1755 if (tr1.getTime() <= end) {
1760 if (tr2.getTime() <= end) {
1764 if (!inRange1 && !inRange2) {
1765 // No more transition in the range
1768 if (!inRange1 || !inRange2) {
1771 if (Math.abs(tr1.getTime() - tr2.getTime()) > maxTransitionTimeDelta) {
1774 if (ignoreDstAmount) {
1775 if (tr1.getTo().getRawOffset() + tr1.getTo().getDSTSavings()
1776 != tr2.getTo().getRawOffset() + tr2.getTo().getDSTSavings()
1777 || tr1.getTo().getDSTSavings() != 0 && tr2.getTo().getDSTSavings() == 0
1778 || tr1.getTo().getDSTSavings() == 0 && tr2.getTo().getDSTSavings() != 0) {
1782 if (tr1.getTo().getRawOffset() != tr2.getTo().getRawOffset() ||
1783 tr1.getTo().getDSTSavings() != tr2.getTo().getDSTSavings()) {
1787 time = tr1.getTime() > tr2.getTime() ? tr1.getTime() : tr2.getTime();