/* ******************************************************************************* * Copyright (C) 2007-2009, International Business Machines Corporation and * * others. All Rights Reserved. * ******************************************************************************* */ package com.ibm.icu.dev.test.timezone; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.io.StringReader; import java.io.StringWriter; import java.util.Date; import com.ibm.icu.dev.test.TestFmwk; import com.ibm.icu.util.AnnualTimeZoneRule; import com.ibm.icu.util.BasicTimeZone; import com.ibm.icu.util.Calendar; import com.ibm.icu.util.DateTimeRule; import com.ibm.icu.util.GregorianCalendar; import com.ibm.icu.util.InitialTimeZoneRule; import com.ibm.icu.util.RuleBasedTimeZone; import com.ibm.icu.util.SimpleTimeZone; import com.ibm.icu.util.TimeArrayTimeZoneRule; import com.ibm.icu.util.TimeZone; import com.ibm.icu.util.TimeZoneRule; import com.ibm.icu.util.TimeZoneTransition; import com.ibm.icu.util.ULocale; import com.ibm.icu.util.VTimeZone; /** * Test cases for TimeZoneRule and RuleBasedTimeZone */ public class TimeZoneRuleTest extends TestFmwk { private static final int HOUR = 60 * 60 * 1000; public static void main(String[] args) throws Exception { new TimeZoneRuleTest().run(args); } /* * RuleBasedTimeZone test cases */ public void TestSimpleRuleBasedTimeZone() { SimpleTimeZone stz = new SimpleTimeZone(-1*HOUR, "TestSTZ", Calendar.SEPTEMBER, -30, -Calendar.SATURDAY, 1*HOUR, SimpleTimeZone.WALL_TIME, Calendar.FEBRUARY, 2, Calendar.SUNDAY, 1*HOUR, SimpleTimeZone.WALL_TIME, 1*HOUR); DateTimeRule dtr; AnnualTimeZoneRule atzr; final int STARTYEAR = 2000; InitialTimeZoneRule ir = new InitialTimeZoneRule( "RBTZ_Initial", // Initial time Name -1*HOUR, // Raw offset 1*HOUR); // DST saving amount // RBTZ RuleBasedTimeZone rbtz1 = new RuleBasedTimeZone("RBTZ1", ir); dtr = new DateTimeRule(Calendar.SEPTEMBER, 30, Calendar.SATURDAY, false, 1*HOUR, DateTimeRule.WALL_TIME); // SUN<=30 in September, at 1AM wall time atzr = new AnnualTimeZoneRule("RBTZ_DST1", -1*HOUR /* rawOffset */, 1*HOUR /* dstSavings */, dtr, STARTYEAR, AnnualTimeZoneRule.MAX_YEAR); rbtz1.addTransitionRule(atzr); dtr = new DateTimeRule(Calendar.FEBRUARY, 2, Calendar.SUNDAY, 1*HOUR, DateTimeRule.WALL_TIME); // 2nd Sunday in February, at 1AM wall time atzr = new AnnualTimeZoneRule("RBTZ_STD1", -1*HOUR /* rawOffset */, 0 /* dstSavings */, dtr, STARTYEAR, AnnualTimeZoneRule.MAX_YEAR); rbtz1.addTransitionRule(atzr); // Equivalent, but different date rule type RuleBasedTimeZone rbtz2 = new RuleBasedTimeZone("RBTZ2", ir); dtr = new DateTimeRule(Calendar.SEPTEMBER, -1, Calendar.SATURDAY, 1*HOUR, DateTimeRule.WALL_TIME); // Last Sunday in September at 1AM wall time atzr = new AnnualTimeZoneRule("RBTZ_DST2", -1*HOUR, 1*HOUR, dtr, STARTYEAR, AnnualTimeZoneRule.MAX_YEAR); rbtz2.addTransitionRule(atzr); dtr = new DateTimeRule(Calendar.FEBRUARY, 8, Calendar.SUNDAY, true, 1*HOUR, DateTimeRule.WALL_TIME); // SUN>=8 in February, at 1AM wall time atzr = new AnnualTimeZoneRule("RBTZ_STD2", -1*HOUR, 0, dtr, STARTYEAR, AnnualTimeZoneRule.MAX_YEAR); rbtz2.addTransitionRule(atzr); // Equivalent, but different time rule type RuleBasedTimeZone rbtz3 = new RuleBasedTimeZone("RBTZ3", ir); dtr = new DateTimeRule(Calendar.SEPTEMBER, 30, Calendar.SATURDAY, false, 2*HOUR, DateTimeRule.UTC_TIME); atzr = new AnnualTimeZoneRule("RBTZ_DST3", -1*HOUR, 1*HOUR, dtr, STARTYEAR, AnnualTimeZoneRule.MAX_YEAR); rbtz3.addTransitionRule(atzr); dtr = new DateTimeRule(Calendar.FEBRUARY, 2, Calendar.SUNDAY, 0*HOUR, DateTimeRule.STANDARD_TIME); atzr = new AnnualTimeZoneRule("RBTZ_STD3", -1*HOUR, 0, dtr, STARTYEAR, AnnualTimeZoneRule.MAX_YEAR); rbtz3.addTransitionRule(atzr); // Check equivalency for 10 years long start = getUTCMillis(STARTYEAR, Calendar.JANUARY, 1); long until = getUTCMillis(STARTYEAR + 10, Calendar.JANUARY, 1); if (!(stz.hasEquivalentTransitions(rbtz1, start, until))) { errln("FAIL: rbtz1 must be equivalent to the SimpleTimeZone in the time range."); } if (!(stz.hasEquivalentTransitions(rbtz2, start, until))) { errln("FAIL: rbtz2 must be equivalent to the SimpleTimeZone in the time range."); } if (!(stz.hasEquivalentTransitions(rbtz3, start, until))) { errln("FAIL: rbtz3 must be equivalent to the SimpleTimeZone in the time range."); } // hasSameRules if (rbtz1.hasSameRules(rbtz2)) { errln("FAIL: rbtz1 and rbtz2 have different rules, but returned true."); } if (rbtz1.hasSameRules(rbtz3)) { errln("FAIL: rbtz1 and rbtz3 have different rules, but returned true."); } RuleBasedTimeZone rbtz1c = (RuleBasedTimeZone)rbtz1.clone(); if (!rbtz1.hasSameRules(rbtz1c)) { errln("FAIL: Cloned RuleBasedTimeZone must have the same rules with the original."); } // getOffset GregorianCalendar cal = new GregorianCalendar(); int[] offsets = new int[2]; int offset; boolean dst; cal.setTimeZone(rbtz1); cal.clear(); // Jan 1, 1000 BC cal.set(Calendar.ERA, GregorianCalendar.BC); cal.set(1000, Calendar.JANUARY, 1); offset = rbtz1.getOffset(cal.get(Calendar.ERA), cal.get(Calendar.YEAR), cal.get(Calendar.MONTH), cal.get(Calendar.DAY_OF_MONTH), cal.get(Calendar.DAY_OF_WEEK), cal.get(Calendar.MILLISECONDS_IN_DAY)); if (offset != 0) { errln("FAIL: Invalid time zone offset: " + offset + " /expected: 0"); } dst = rbtz1.inDaylightTime(cal.getTime()); if (!dst) { errln("FAIL: Invalid daylight saving time"); } rbtz1.getOffset(cal.getTimeInMillis(), true, offsets); if (offsets[0] != -3600000) { errln("FAIL: Invalid time zone raw offset: " + offsets[0] + " /expected: -3600000"); } if (offsets[1] != 3600000) { errln("FAIL: Invalid DST amount: " + offsets[1] + " /expected: 3600000"); } // July 1, 2000, AD cal.set(Calendar.ERA, GregorianCalendar.AD); cal.set(2000, Calendar.JULY, 1); offset = rbtz1.getOffset(cal.get(Calendar.ERA), cal.get(Calendar.YEAR), cal.get(Calendar.MONTH), cal.get(Calendar.DAY_OF_MONTH), cal.get(Calendar.DAY_OF_WEEK), cal.get(Calendar.MILLISECONDS_IN_DAY)); if (offset != -3600000) { errln("FAIL: Invalid time zone offset: " + offset + " /expected: -3600000"); } dst = rbtz1.inDaylightTime(cal.getTime()); if (dst) { errln("FAIL: Invalid daylight saving time"); } rbtz1.getOffset(cal.getTimeInMillis(), true, offsets); if (offsets[0] != -3600000) { errln("FAIL: Invalid time zone raw offset: " + offsets[0] + " /expected: -3600000"); } if (offsets[1] != 0) { errln("FAIL: Invalid DST amount: " + offsets[1] + " /expected: 0"); } // July 1, 2000, AD // Try to add 3rd final rule dtr = new DateTimeRule(Calendar.OCTOBER, 15, 1*HOUR, DateTimeRule.WALL_TIME); atzr = new AnnualTimeZoneRule("3RD_ATZ", -1*HOUR, 2*HOUR, dtr, STARTYEAR, AnnualTimeZoneRule.MAX_YEAR); boolean bException = false; try { rbtz1.addTransitionRule(atzr); } catch (IllegalStateException ise) { bException = true; } if (!bException) { errln("FAIL: 3rd final rule must be rejected"); } // Try to add an initial rule bException = false; try { rbtz1.addTransitionRule(new InitialTimeZoneRule("Test Initial", 2*HOUR, 0)); } catch (IllegalArgumentException iae) { bException = true; } if (!bException) { errln("FAIL: InitialTimeZoneRule must be rejected"); } } /* * Test equivalency between OlsonTimeZone and custom RBTZ representing the * equivalent rules in a certain time range */ public void TestHistoricalRuleBasedTimeZone() { // Compare to America/New_York with equivalent RBTZ TimeZone ny = TimeZone.getTimeZone("America/New_York", TimeZone.TIMEZONE_ICU); //RBTZ InitialTimeZoneRule ir = new InitialTimeZoneRule("EST", -5*HOUR, 0); RuleBasedTimeZone rbtz = new RuleBasedTimeZone("EST5EDT", ir); DateTimeRule dtr; AnnualTimeZoneRule tzr; // Standard time dtr = new DateTimeRule(Calendar.OCTOBER, -1, Calendar.SUNDAY, 2*HOUR, DateTimeRule.WALL_TIME); // Last Sunday in October, at 2AM wall time tzr = new AnnualTimeZoneRule("EST", -5*HOUR /* rawOffset */, 0 /* dstSavings */, dtr, 1967, 2006); rbtz.addTransitionRule(tzr); dtr = new DateTimeRule(Calendar.NOVEMBER, 1, Calendar.SUNDAY, true, 2*HOUR, DateTimeRule.WALL_TIME); // SUN>=1 in November, at 2AM wall time tzr = new AnnualTimeZoneRule("EST", -5*HOUR, 0, dtr, 2007, AnnualTimeZoneRule.MAX_YEAR); rbtz.addTransitionRule(tzr); // Daylight saving time dtr = new DateTimeRule(Calendar.APRIL, -1, Calendar.SUNDAY, 2*HOUR, DateTimeRule.WALL_TIME); // Last Sunday in April, at 2AM wall time tzr = new AnnualTimeZoneRule("EDT", -5*HOUR, 1*HOUR, dtr, 1967, 1973); rbtz.addTransitionRule(tzr); dtr = new DateTimeRule(Calendar.JANUARY, 6, 2*HOUR, DateTimeRule.WALL_TIME); // January 6, at 2AM wall time tzr = new AnnualTimeZoneRule("EDT", -5*HOUR, 1*HOUR, dtr, 1974, 1974); rbtz.addTransitionRule(tzr); dtr = new DateTimeRule(Calendar.FEBRUARY, 23, 2*HOUR, DateTimeRule.WALL_TIME); // February 23, at 2AM wall time tzr = new AnnualTimeZoneRule("EDT", -5*HOUR, 1*HOUR, dtr, 1975, 1975); rbtz.addTransitionRule(tzr); dtr = new DateTimeRule(Calendar.APRIL, -1, Calendar.SUNDAY, 2*HOUR, DateTimeRule.WALL_TIME); // Last Sunday in April, at 2AM wall time tzr = new AnnualTimeZoneRule("EDT", -5*HOUR, 1*HOUR, dtr, 1976, 1986); rbtz.addTransitionRule(tzr); dtr = new DateTimeRule(Calendar.APRIL, 1, Calendar.SUNDAY, true, 2*HOUR, DateTimeRule.WALL_TIME); // SUN>=1 in April, at 2AM wall time tzr = new AnnualTimeZoneRule("EDT", -5*HOUR, 1*HOUR, dtr, 1987, 2006); rbtz.addTransitionRule(tzr); dtr = new DateTimeRule(Calendar.MARCH, 8, Calendar.SUNDAY, true, 2*HOUR, DateTimeRule.WALL_TIME); // SUN>=8 in March, at 2AM wall time tzr = new AnnualTimeZoneRule("EDT", -5*HOUR, 1*HOUR, dtr, 2007, AnnualTimeZoneRule.MAX_YEAR); rbtz.addTransitionRule(tzr); // hasEquivalentTransitions long jan1_1950 = getUTCMillis(1950, Calendar.JANUARY, 1); long jan1_1967 = getUTCMillis(1971, Calendar.JANUARY, 1); long jan1_2010 = getUTCMillis(2010, Calendar.JANUARY, 1); if (!(((BasicTimeZone)ny).hasEquivalentTransitions(rbtz, jan1_1967, jan1_2010))) { errln("FAIL: The RBTZ must be equivalent to America/New_York between 1967 and 2010"); } if (((BasicTimeZone)ny).hasEquivalentTransitions(rbtz, jan1_1950, jan1_2010)) { errln("FAIL: The RBTZ must not be equivalent to America/New_York between 1950 and 2010"); } // Same with above, but calling RBTZ#hasEquivalentTransitions against OlsonTimeZone if (!rbtz.hasEquivalentTransitions(ny, jan1_1967, jan1_2010)) { errln("FAIL: The RBTZ must be equivalent to America/New_York between 1967 and 2010"); } if (rbtz.hasEquivalentTransitions(ny, jan1_1950, jan1_2010)) { errln("FAIL: The RBTZ must not be equivalent to America/New_York between 1950 and 2010"); } // TimeZone APIs if (ny.hasSameRules(rbtz) || rbtz.hasSameRules(ny)) { errln("FAIL: hasSameRules must return false"); } RuleBasedTimeZone rbtzc = (RuleBasedTimeZone)rbtz.clone(); if (!rbtz.hasSameRules(rbtzc) || !rbtz.hasEquivalentTransitions(rbtzc, jan1_1950, jan1_2010)) { errln("FAIL: hasSameRules/hasEquivalentTransitions must return true for cloned RBTZs"); } long times[] = { getUTCMillis(2006, Calendar.MARCH, 15), getUTCMillis(2006, Calendar.NOVEMBER, 1), getUTCMillis(2007, Calendar.MARCH, 15), getUTCMillis(2007, Calendar.NOVEMBER, 1), getUTCMillis(2008, Calendar.MARCH, 15), getUTCMillis(2008, Calendar.NOVEMBER, 1) }; int[] offsets1 = new int[2]; int[] offsets2 = new int[2]; for (int i = 0; i < times.length; i++) { // Check getOffset - must return the same results for these time data rbtz.getOffset(times[i], false, offsets1); ny.getOffset(times[i], false, offsets2); if (offsets1[0] != offsets2[0] || offsets1[1] != offsets2[1]) { errln("FAIL: Incompatible time zone offsets for ny and rbtz"); } // Check inDaylightTime Date d = new Date(times[i]); if (rbtz.inDaylightTime(d) != ny.inDaylightTime(d)) { errln("FAIL: Incompatible daylight saving time for ny and rbtz"); } } } /* * Check if transitions returned by getNextTransition/getPreviousTransition * are actual time transitions. */ public void TestOlsonTransition() { String[] zids = getTestZIDs(); for (int i = 0; i < zids.length; i++) { TimeZone tz = TimeZone.getTimeZone(zids[i], TimeZone.TIMEZONE_ICU); if (tz == null) { break; } int j = 0; while (true) { long[] timerange = getTestTimeRange(j++); if (timerange == null) { break; } verifyTransitions(tz, timerange[0], timerange[1]); } } } /* * Check if an OlsonTimeZone and its equivalent RBTZ have the exact same * transitions. */ public void TestRBTZTransition() { int[] STARTYEARS = { 1950, 1975, 2000, 2010 }; String[] zids = getTestZIDs(); for (int i = 0; i < zids.length; i++) { TimeZone tz = TimeZone.getTimeZone(zids[i], TimeZone.TIMEZONE_ICU); if (tz == null) { break; } for (int j = 0; j < STARTYEARS.length; j++) { long startTime = getUTCMillis(STARTYEARS[j], Calendar.JANUARY, 1); TimeZoneRule[] rules = ((BasicTimeZone)tz).getTimeZoneRules(startTime); RuleBasedTimeZone rbtz = new RuleBasedTimeZone(tz.getID() + "(RBTZ)", (InitialTimeZoneRule)rules[0]); for (int k = 1; k < rules.length; k++) { rbtz.addTransitionRule(rules[k]); } // Compare the original OlsonTimeZone with the RBTZ starting the startTime for 20 years long until = getUTCMillis(STARTYEARS[j] + 20, Calendar.JANUARY, 1); // Ascending compareTransitionsAscending(tz, rbtz, startTime, until, false); // Ascending/inclusive compareTransitionsAscending(tz, rbtz, startTime + 1, until, true); // Descending compareTransitionsDescending(tz, rbtz, startTime, until, false); // Descending/inclusive compareTransitionsDescending(tz, rbtz, startTime + 1, until, true); } } } /* * Test cases for HasTimeZoneRules#hasEquivalentTransitions */ public void TestHasEquivalentTransitions() { // America/New_York and America/Indiana/Indianapolis are equivalent // since 2006 TimeZone newyork = TimeZone.getTimeZone("America/New_York", TimeZone.TIMEZONE_ICU); TimeZone indianapolis = TimeZone.getTimeZone("America/Indiana/Indianapolis", TimeZone.TIMEZONE_ICU); TimeZone gmt_5 = TimeZone.getTimeZone("Etc/GMT+5", TimeZone.TIMEZONE_ICU); long jan1_1971 = getUTCMillis(1971, Calendar.JANUARY, 1); long jan1_2005 = getUTCMillis(2005, Calendar.JANUARY, 1); long jan1_2006 = getUTCMillis(2006, Calendar.JANUARY, 1); long jan1_2007 = getUTCMillis(2007, Calendar.JANUARY, 1); long jan1_2011 = getUTCMillis(2010, Calendar.JANUARY, 1); if (((BasicTimeZone)newyork).hasEquivalentTransitions(indianapolis, jan1_2005, jan1_2011)) { errln("FAIL: New_York is not equivalent to Indianapolis between 2005 and 2010, but returned true"); } if (!((BasicTimeZone)newyork).hasEquivalentTransitions(indianapolis, jan1_2006, jan1_2011)) { errln("FAIL: New_York is equivalent to Indianapolis between 2006 and 2010, but returned false"); } if (!((BasicTimeZone)indianapolis).hasEquivalentTransitions(gmt_5, jan1_1971, jan1_2006)) { errln("FAIL: Indianapolis is equivalent to GMT+5 between 1971 and 2005, but returned false"); } if (((BasicTimeZone)indianapolis).hasEquivalentTransitions(gmt_5, jan1_1971, jan1_2007)) { errln("FAIL: Indianapolis is not equivalent to GMT+5 between 1971 and 2006, but returned true"); } // Cloned TimeZone TimeZone newyork2 = (TimeZone)newyork.clone(); if (!((BasicTimeZone)newyork).hasEquivalentTransitions(newyork2, jan1_1971, jan1_2011)) { errln("FAIL: Cloned TimeZone must have the same transitions"); } if (!((BasicTimeZone)newyork).hasEquivalentTransitions(newyork2, jan1_1971, jan1_2011, true /*ignoreDstAmount*/)) { errln("FAIL: Cloned TimeZone must have the same transitions"); } // America/New_York and America/Los_Angeles has same DST start rules, but // raw offsets are different TimeZone losangeles = TimeZone.getTimeZone("America/Los_Angeles", TimeZone.TIMEZONE_ICU); if (((BasicTimeZone)newyork).hasEquivalentTransitions(losangeles, jan1_2006, jan1_2011)) { errln("FAIL: New_York is not equivalent to Los Angeles, but returned true"); } } /* * Write out time zone rules of OlsonTimeZone into VTIMEZONE format, create a new * VTimeZone from the VTIMEZONE data, then compare transitions */ public void TestVTimeZoneRoundTrip() { long startTime = getUTCMillis(1850, Calendar.JANUARY, 1); long endTime = getUTCMillis(2050, Calendar.JANUARY, 1); String[] tzids = getTestZIDs(); for (int i = 0; i < tzids.length; i++) { if (skipIfBeforeICU(4,5,0) && tzids[i].equals("Asia/Amman")) { // ticket#7008 continue; } BasicTimeZone olsontz = (BasicTimeZone)TimeZone.getTimeZone(tzids[i], TimeZone.TIMEZONE_ICU); VTimeZone vtz_org = VTimeZone.create(tzids[i]); vtz_org.setTZURL("http://source.icu-project.org/timezone"); vtz_org.setLastModified(new Date()); VTimeZone vtz_new = null; try { // Write out VTIMEZONE ByteArrayOutputStream baos = new ByteArrayOutputStream(); OutputStreamWriter writer = new OutputStreamWriter(baos); vtz_org.write(writer); writer.close(); byte[] vtzdata = baos.toByteArray(); // Read VTIMEZONE ByteArrayInputStream bais = new ByteArrayInputStream(vtzdata); InputStreamReader reader = new InputStreamReader(bais); vtz_new = VTimeZone.create(reader); reader.close(); // Write out VTIMEZONE one more time ByteArrayOutputStream baos1 = new ByteArrayOutputStream(); OutputStreamWriter writer1 = new OutputStreamWriter(baos1); vtz_new.write(writer1); writer1.close(); byte[] vtzdata1 = baos1.toByteArray(); // Make sure VTIMEZONE data is exactly same with the first one if (vtzdata.length != vtzdata1.length) { errln("FAIL: different VTIMEZONE data length"); } for (int j = 0; j < vtzdata.length; j++) { if (vtzdata[j] != vtzdata1[j]) { errln("FAIL: different VTIMEZONE data"); break; } } } catch (IOException ioe) { errln("FAIL: IO error while writing/reading VTIMEZONE data"); } // Check equivalency after the first transition. // The DST information before the first transition might be lost // because there is no good way to represent the initial time with // VTIMEZONE. if (vtz_new.getOffset(startTime) != olsontz.getOffset(startTime)) { errln("FAIL: VTimeZone for " + tzids[i] + " is not equivalent to its OlsonTimeZone corresponding at " + startTime); } TimeZoneTransition tzt = olsontz.getNextTransition(startTime, false); if (tzt != null) { if (!vtz_new.hasEquivalentTransitions(olsontz, tzt.getTime(), endTime, true)) { errln("FAIL: VTimeZone for " + tzids[i] + " is not equivalent to its OlsonTimeZone corresponding."); } if (!vtz_new.hasEquivalentTransitions(olsontz, tzt.getTime(), endTime, false)) { logln("VTimeZone for " + tzids[i] + " is not equivalent to its OlsonTimeZone corresponding in strict comparison mode."); } } } } /* * Write out time zone rules of OlsonTimeZone after a cutoff date into VTIMEZONE format, * create a new VTimeZone from the VTIMEZONE data, then compare transitions */ public void TestVTimeZoneRoundTripPartial() { long[] startTimes = new long[] { getUTCMillis(1900, Calendar.JANUARY, 1), getUTCMillis(1950, Calendar.JANUARY, 1), getUTCMillis(2020, Calendar.JANUARY, 1) }; long endTime = getUTCMillis(2050, Calendar.JANUARY, 1); String[] tzids = getTestZIDs(); for (int n = 0; n < startTimes.length; n++) { long startTime = startTimes[n]; for (int i = 0; i < tzids.length; i++) { if (skipIfBeforeICU(4,5,0) && tzids[i].equals("Asia/Amman")) { // ticket#7008 continue; } BasicTimeZone olsontz = (BasicTimeZone)TimeZone.getTimeZone(tzids[i], TimeZone.TIMEZONE_ICU); VTimeZone vtz_org = VTimeZone.create(tzids[i]); VTimeZone vtz_new = null; try { // Write out VTIMEZONE ByteArrayOutputStream baos = new ByteArrayOutputStream(); OutputStreamWriter writer = new OutputStreamWriter(baos); vtz_org.write(writer, startTime); writer.close(); byte[] vtzdata = baos.toByteArray(); // Read VTIMEZONE ByteArrayInputStream bais = new ByteArrayInputStream(vtzdata); InputStreamReader reader = new InputStreamReader(bais); vtz_new = VTimeZone.create(reader); reader.close(); } catch (IOException ioe) { errln("FAIL: IO error while writing/reading VTIMEZONE data"); } // Check equivalency after the first transition. // The DST information before the first transition might be lost // because there is no good way to represent the initial time with // VTIMEZONE. if (vtz_new.getOffset(startTime) != olsontz.getOffset(startTime)) { errln("FAIL: VTimeZone for " + tzids[i] + " is not equivalent to its OlsonTimeZone corresponding at " + startTime); } TimeZoneTransition tzt = olsontz.getNextTransition(startTime, false); if (tzt != null) { if (!vtz_new.hasEquivalentTransitions(olsontz, tzt.getTime(), endTime, true)) { errln("FAIL: VTimeZone for " + tzids[i] + "(>=" + startTime + ") is not equivalent to its OlsonTimeZone corresponding."); } } } } } /* * Write out simple time zone rules from an OlsonTimeZone at various time into VTIMEZONE * format and create a new VTimeZone from the VTIMEZONE data, then make sure the raw offset * and DST savings are same in these two time zones. */ public void TestVTimeZoneSimpleWrite() { long[] testTimes = new long[] { getUTCMillis(2006, Calendar.JANUARY, 1), getUTCMillis(2006, Calendar.MARCH, 15), getUTCMillis(2006, Calendar.MARCH, 31), getUTCMillis(2006, Calendar.APRIL, 5), getUTCMillis(2006, Calendar.OCTOBER, 25), getUTCMillis(2006, Calendar.NOVEMBER, 1), getUTCMillis(2006, Calendar.NOVEMBER, 5), getUTCMillis(2007, Calendar.JANUARY, 1) }; String[] tzids = getTestZIDs(); for (int n = 0; n < testTimes.length; n++) { long time = testTimes[n]; int[] offsets1 = new int[2]; int[] offsets2 = new int[2]; for (int i = 0; i < tzids.length; i++) { VTimeZone vtz_org = VTimeZone.create(tzids[i]); VTimeZone vtz_new = null; try { // Write out VTIMEZONE ByteArrayOutputStream baos = new ByteArrayOutputStream(); OutputStreamWriter writer = new OutputStreamWriter(baos); vtz_org.writeSimple(writer, time); writer.close(); byte[] vtzdata = baos.toByteArray(); // Read VTIMEZONE ByteArrayInputStream bais = new ByteArrayInputStream(vtzdata); InputStreamReader reader = new InputStreamReader(bais); vtz_new = VTimeZone.create(reader); reader.close(); } catch (IOException ioe) { errln("FAIL: IO error while writing/reading VTIMEZONE data"); } // Check equivalency vtz_org.getOffset(time, false, offsets1); vtz_new.getOffset(time, false, offsets2); if (offsets1[0] != offsets2[0] || offsets1[1] != offsets2[1]) { errln("FAIL: VTimeZone writeSimple for " + tzids[i] + " at time " + time + " failed to the round trip."); } } } } /* * Write out time zone rules of OlsonTimeZone into VTIMEZONE format with RFC2445 header TZURL and * LAST-MODIFIED, create a new VTimeZone from the VTIMEZONE data to see if the headers are preserved. */ public void TestVTimeZoneHeaderProps() { String tzid = "America/Chicago"; String tzurl = "http://source.icu-project.org"; Date lastmod = new Date(getUTCMillis(2007, Calendar.JUNE, 1)); VTimeZone vtz = VTimeZone.create(tzid); vtz.setTZURL(tzurl); vtz.setLastModified(lastmod); // Roundtrip conversion VTimeZone newvtz1 = null; try { // Write out VTIMEZONE ByteArrayOutputStream baos = new ByteArrayOutputStream(); OutputStreamWriter writer = new OutputStreamWriter(baos); vtz.write(writer); writer.close(); byte[] vtzdata = baos.toByteArray(); // Read VTIMEZONE ByteArrayInputStream bais = new ByteArrayInputStream(vtzdata); InputStreamReader reader = new InputStreamReader(bais); newvtz1 = VTimeZone.create(reader); reader.close(); // Check if TZURL and LAST-MODIFIED headers are preserved if (!(tzurl.equals(newvtz1.getTZURL()))) { errln("FAIL: TZURL property is not preserved during the roundtrip conversion. Before:" + tzurl + "/After:" + newvtz1.getTZURL()); } if (!(lastmod.equals(newvtz1.getLastModified()))) { errln("FAIL: LAST-MODIFIED property is not preserved during the roundtrip conversion. Before:" + lastmod.getTime() + "/After:" + newvtz1.getLastModified().getTime()); } } catch (IOException ioe) { errln("FAIL: IO error while writing/reading VTIMEZONE data"); } // Second roundtrip, with a cutoff VTimeZone newvtz2 = null; try { // Set different tzurl String newtzurl = "http://www.ibm.com"; newvtz1.setTZURL(newtzurl); // Write out VTIMEZONE ByteArrayOutputStream baos = new ByteArrayOutputStream(); OutputStreamWriter writer = new OutputStreamWriter(baos); newvtz1.write(writer, getUTCMillis(2000, Calendar.JANUARY, 1)); writer.close(); byte[] vtzdata = baos.toByteArray(); // Read VTIMEZONE ByteArrayInputStream bais = new ByteArrayInputStream(vtzdata); InputStreamReader reader = new InputStreamReader(bais); newvtz2 = VTimeZone.create(reader); reader.close(); // Check if TZURL and LAST-MODIFIED headers are preserved if (!(newtzurl.equals(newvtz2.getTZURL()))) { errln("FAIL: TZURL property is not preserved during the second roundtrip conversion. Before:" + newtzurl + "/After:" + newvtz2.getTZURL()); } if (!(lastmod.equals(newvtz2.getLastModified()))) { errln("FAIL: LAST-MODIFIED property is not preserved during the second roundtrip conversion. Before:" + lastmod.getTime() + "/After:" + newvtz2.getLastModified().getTime()); } } catch (IOException ioe) { errln("FAIL: IO error while writing/reading VTIMEZONE data"); } } /* * Extract simple rules from an OlsonTimeZone and make sure the rule format matches * the expected format. */ public void TestGetSimpleRules() { long[] testTimes = new long[] { getUTCMillis(1970, Calendar.JANUARY, 1), getUTCMillis(2000, Calendar.MARCH, 31), getUTCMillis(2005, Calendar.JULY, 1), getUTCMillis(2010, Calendar.NOVEMBER, 1), }; String[] tzids = getTestZIDs(); for (int n = 0; n < testTimes.length; n++) { long time = testTimes[n]; for (int i = 0; i < tzids.length; i++) { BasicTimeZone tz = (BasicTimeZone)TimeZone.getTimeZone(tzids[i], TimeZone.TIMEZONE_ICU); TimeZoneRule[] rules = tz.getSimpleTimeZoneRulesNear(time); if (rules == null) { errln("FAIL: Failed to extract simple rules for " + tzids[i] + " at " + time); } else { if (rules.length == 1) { if (!(rules[0] instanceof InitialTimeZoneRule)) { errln("FAIL: Unexpected rule object type is returned for " + tzids[i] + " at " + time); } } else if (rules.length == 3) { if (!(rules[0] instanceof InitialTimeZoneRule) || !(rules[1] instanceof AnnualTimeZoneRule) || !(rules[2] instanceof AnnualTimeZoneRule)) { errln("FAIL: Unexpected rule object type is returned for " + tzids[i] + " at " + time); } for (int idx = 1; idx <= 2; idx++) { DateTimeRule dtr = ((AnnualTimeZoneRule)rules[idx]).getRule(); if (dtr.getTimeRuleType() != DateTimeRule.WALL_TIME) { errln("FAIL: WALL_TIME is not used as the time rule in the time zone rule(" + idx + ") for " + tzids[i] + " at " + time); } if (dtr.getDateRuleType() != DateTimeRule.DOW) { errln("FAIL: DOW is not used as the date rule in the time zone rule(" + idx + ") for " + tzids[i] + " at " + time); } } } else { errln("FAIL: Unexpected number of rules returned for " + tzids[i] + " at " + time); } } } } } /* * API coverage tests for TimeZoneRule */ public void TestTimeZoneRuleCoverage() { long time1 = getUTCMillis(2005, Calendar.JULY, 4); long time2 = getUTCMillis(2015, Calendar.JULY, 4); long time3 = getUTCMillis(1950, Calendar.JULY, 4); DateTimeRule dtr1 = new DateTimeRule(Calendar.FEBRUARY, 29, Calendar.SUNDAY, false, 3*HOUR, DateTimeRule.WALL_TIME); // Last Sunday on or before Feb 29, at 3 AM, wall time DateTimeRule dtr2 = new DateTimeRule(Calendar.MARCH, 11, 2*HOUR, DateTimeRule.STANDARD_TIME); // Mar 11, at 2 AM, standard time DateTimeRule dtr3 = new DateTimeRule(Calendar.OCTOBER, -1, Calendar.SATURDAY, 6*HOUR, DateTimeRule.UTC_TIME); //Last Saturday in Oct, at 6 AM, UTC DateTimeRule dtr4 = new DateTimeRule(Calendar.MARCH, 8, Calendar.SUNDAY, true, 2*HOUR, DateTimeRule.WALL_TIME); // First Sunday on or after Mar 8, at 2 AM, wall time AnnualTimeZoneRule a1 = new AnnualTimeZoneRule("a1", -3*HOUR, 1*HOUR, dtr1, 2000, AnnualTimeZoneRule.MAX_YEAR); AnnualTimeZoneRule a2 = new AnnualTimeZoneRule("a2", -3*HOUR, 1*HOUR, dtr1, 2000, AnnualTimeZoneRule.MAX_YEAR); AnnualTimeZoneRule a3 = new AnnualTimeZoneRule("a3", -3*HOUR, 1*HOUR, dtr1, 2000, 2010); InitialTimeZoneRule i1 = new InitialTimeZoneRule("i1", -3*HOUR, 0); InitialTimeZoneRule i2 = new InitialTimeZoneRule("i2", -3*HOUR, 0); InitialTimeZoneRule i3 = new InitialTimeZoneRule("i3", -3*HOUR, 1*HOUR); long[] emptytimes = {}; long[] trtimes1 = {0}; long[] trtimes2 = {0, 10000000}; TimeArrayTimeZoneRule t0 = null; try { // Try to construct TimeArrayTimeZoneRule with null transition times t0 = new TimeArrayTimeZoneRule("nulltimes", -3*HOUR, 0, null, DateTimeRule.UTC_TIME); } catch (IllegalArgumentException iae) { logln("TimeArrayTimeZoneRule constructor throws IllegalArgumentException as expected."); t0 = null; } if (t0 != null) { errln("FAIL: TimeArrayTimeZoneRule constructor did not throw IllegalArgumentException for null times"); } try { // Try to construct TimeArrayTimeZoneRule with empty transition times t0 = new TimeArrayTimeZoneRule("nulltimes", -3*HOUR, 0, emptytimes, DateTimeRule.UTC_TIME); } catch (IllegalArgumentException iae) { logln("TimeArrayTimeZoneRule constructor throws IllegalArgumentException as expected."); t0 = null; } if (t0 != null) { errln("FAIL: TimeArrayTimeZoneRule constructor did not throw IllegalArgumentException for empty times"); } TimeArrayTimeZoneRule t1 = new TimeArrayTimeZoneRule("t1", -3*HOUR, 0, trtimes1, DateTimeRule.UTC_TIME); TimeArrayTimeZoneRule t2 = new TimeArrayTimeZoneRule("t2", -3*HOUR, 0, trtimes1, DateTimeRule.UTC_TIME); TimeArrayTimeZoneRule t3 = new TimeArrayTimeZoneRule("t3", -3*HOUR, 0, trtimes2, DateTimeRule.UTC_TIME); TimeArrayTimeZoneRule t4 = new TimeArrayTimeZoneRule("t4", -3*HOUR, 0, trtimes1, DateTimeRule.STANDARD_TIME); TimeArrayTimeZoneRule t5 = new TimeArrayTimeZoneRule("t5", -4*HOUR, 1*HOUR, trtimes1, DateTimeRule.WALL_TIME); // AnnualTimeZoneRule#getRule if (!a1.getRule().equals(a2.getRule())) { errln("FAIL: The same DateTimeRule must be returned from AnnualTimeZoneRule a1 and a2"); } // AnnualTimeZoneRule#getStartYear int startYear = a1.getStartYear(); if (startYear != 2000) { errln("FAIL: The start year of AnnualTimeZoneRule a1 must be 2000 - returned: " + startYear); } // AnnualTimeZoneRule#getEndYear int endYear = a1.getEndYear(); if (endYear != AnnualTimeZoneRule.MAX_YEAR) { errln("FAIL: The start year of AnnualTimeZoneRule a1 must be MAX_YEAR - returned: " + endYear); } endYear = a3.getEndYear(); if (endYear != 2010) { errln("FAIL: The start year of AnnualTimeZoneRule a3 must be 2010 - returned: " + endYear); } // AnnualTimeZone#getStartInYear Date d1 = a1.getStartInYear(2005, -3*HOUR, 0); Date d2 = a3.getStartInYear(2005, -3*HOUR, 0); if (d1 == null || d2 == null || !d1.equals(d2)) { errln("FAIL: AnnualTimeZoneRule#getStartInYear did not work as expected"); } d2 = a3.getStartInYear(2015, -3*HOUR, 0); if (d2 != null) { errln("FAIL: AnnualTimeZoneRule#getSTartInYear returned non-null date for 2015 which is out of rule range"); } // AnnualTimeZone#getFirstStart d1 = a1.getFirstStart(-3*HOUR, 0); d2 = a1.getFirstStart(-4*HOUR, 1*HOUR); if (d1 == null || d2 == null || !d1.equals(d2)) { errln("FAIL: The same start time should be returned by getFirstStart"); } // AnnualTimeZone#getFinalStart d1 = a1.getFinalStart(-3*HOUR, 0); if (d1 != null) { errln("FAIL: Non-null Date is returned by getFinalStart for a1"); } d1 = a1.getStartInYear(2010, -3*HOUR, 0); d2 = a3.getFinalStart(-3*HOUR, 0); if (d1 == null || d2 == null || !d1.equals(d2)) { errln("FAIL: Bad date is returned by getFinalStart"); } // AnnualTimeZone#getNextStart / getPreviousStart d1 = a1.getNextStart(time1, -3*HOUR, 0, false); if (d1 == null) { errln("FAIL: Null Date is returned by getNextStart"); } else { d2 = a1.getPreviousStart(d1.getTime(), -3*HOUR, 0, true); if (d2 == null || !d1.equals(d2)) { errln("FAIL: Bad Date is returned by getPreviousStart"); } } d1 = a3.getNextStart(time2, -3*HOUR, 0, false); if (d1 != null) { errln("FAIL: getNextStart must return null when no start time is available after the base time"); } d1 = a3.getFinalStart(-3*HOUR, 0); d2 = a3.getPreviousStart(time2, -3*HOUR, 0, false); if (d1 == null || d2 == null || !d1.equals(d2)) { errln("FAIL: getPreviousStart does not match with getFinalStart after the end year"); } // AnnualTimeZone#isEquavalentTo if (!a1.isEquivalentTo(a2)) { errln("FAIL: AnnualTimeZoneRule a1 is equivalent to a2, but returned false"); } if (a1.isEquivalentTo(a3)) { errln("FAIL: AnnualTimeZoneRule a1 is not equivalent to a3, but returned true"); } if (!a1.isEquivalentTo(a1)) { errln("FAIL: AnnualTimeZoneRule a1 is equivalent to itself, but returned false"); } if (a1.isEquivalentTo(t1)) { errln("FAIL: AnnualTimeZoneRule is not equivalent to TimeArrayTimeZoneRule, but returned true"); } // AnnualTimeZone#isTransitionRule if (!a1.isTransitionRule()) { errln("FAIL: An AnnualTimeZoneRule is a transition rule, but returned false"); } // AnnualTimeZone#toString String str = a1.toString(); if (str == null || str.length() == 0) { errln("FAIL: AnnualTimeZoneRule#toString for a1 returns null or empty string"); } else { logln("AnnualTimeZoneRule a1 : " + str); } str = a3.toString(); if (str == null || str.length() == 0) { errln("FAIL: AnnualTimeZoneRule#toString for a3 returns null or empty string"); } else { logln("AnnualTimeZoneRule a3 : " + str); } // InitialTimeZoneRule#isEquivalentRule if (!i1.isEquivalentTo(i2)) { errln("FAIL: InitialTimeZoneRule i1 is equivalent to i2, but returned false"); } if (i1.isEquivalentTo(i3)) { errln("FAIL: InitialTimeZoneRule i1 is not equivalent to i3, but returned true"); } if (i1.isEquivalentTo(a1)) { errln("FAIL: An InitialTimeZoneRule is not equivalent to an AnnualTimeZoneRule, but returned true"); } // InitialTimeZoneRule#getFirstStart/getFinalStart/getNextStart/getPreviousStart d1 = i1.getFirstStart(0, 0); if (d1 != null) { errln("FAIL: Non-null Date is returned by InitialTimeZone#getFirstStart"); } d1 = i1.getFinalStart(0, 0); if (d1 != null) { errln("FAIL: Non-null Date is returned by InitialTimeZone#getFinalStart"); } d1 = i1.getNextStart(time1, 0, 0, false); if (d1 != null) { errln("FAIL: Non-null Date is returned by InitialTimeZone#getNextStart"); } d1 = i1.getPreviousStart(time1, 0, 0, false); if (d1 != null) { errln("FAIL: Non-null Date is returned by InitialTimeZone#getPreviousStart"); } // InitialTimeZoneRule#isTransitionRule if (i1.isTransitionRule()) { errln("FAIL: An InitialTimeZoneRule is not a transition rule, but returned true"); } // InitialTimeZoneRule#toString str = i1.toString(); if (str == null || str.length() == 0) { errln("FAIL: InitialTimeZoneRule#toString returns null or empty string"); } else { logln("InitialTimeZoneRule i1 : " + str); } // TimeArrayTimeZoneRule#getStartTimes long[] times = t1.getStartTimes(); if (times == null || times.length == 0 || times[0] != 0) { errln("FAIL: Bad start times are returned by TimeArrayTimeZoneRule#getStartTimes"); } // TimeArrayTimeZoneRule#getTimeType if (t1.getTimeType() != DateTimeRule.UTC_TIME) { errln("FAIL: TimeArrayTimeZoneRule t1 uses UTC_TIME, but different type is returned"); } if (t4.getTimeType() != DateTimeRule.STANDARD_TIME) { errln("FAIL: TimeArrayTimeZoneRule t4 uses STANDARD_TIME, but different type is returned"); } if (t5.getTimeType() != DateTimeRule.WALL_TIME) { errln("FAIL: TimeArrayTimeZoneRule t5 uses WALL_TIME, but different type is returned"); } // TimeArrayTimeZoneRule#getFirstStart/getFinalStart d1 = t1.getFirstStart(0, 0); if (d1 == null || d1.getTime() != trtimes1[0]) { errln("FAIL: Bad first start time returned from TimeArrayTimeZoneRule t1"); } d1 = t1.getFinalStart(0, 0); if (d1 == null || d1.getTime() != trtimes1[0]) { errln("FAIL: Bad final start time returned from TimeArrayTimeZoneRule t1"); } d1 = t4.getFirstStart(-4*HOUR, 1*HOUR); if (d1 == null || (d1.getTime() != trtimes1[0] + 4*HOUR)) { errln("FAIL: Bad first start time returned from TimeArrayTimeZoneRule t4"); } d1 = t5.getFirstStart(-4*HOUR, 1*HOUR); if (d1 == null || (d1.getTime() != trtimes1[0] + 3*HOUR)) { errln("FAIL: Bad first start time returned from TimeArrayTimeZoneRule t5"); } // TimeArrayTimeZoneRule#getNextStart/getPreviousStart d1 = t3.getNextStart(time1, -3*HOUR, 1*HOUR, false); if (d1 != null) { errln("FAIL: Non-null Date is returned by getNextStart after the final transition for t3"); } d1 = t3.getPreviousStart(time1, -3*HOUR, 1*HOUR, false); if (d1 == null || d1.getTime() != trtimes2[1]) { errln("FAIL: Bad start time returned by getPreviousStart for t3"); } else { d2 = t3.getPreviousStart(d1.getTime(), -3*HOUR, 1*HOUR, false); if (d2 == null || d2.getTime() != trtimes2[0]) { errln("FAIL: Bad start time returned by getPreviousStart for t3"); } } d1 = t3.getPreviousStart(time3, -3*HOUR, 1*HOUR, false); //time3 - year 1950, no result expected if (d1 != null) { errln("FAIL: Non-null Date is returned by getPrevoousStart for t3"); } // TimeArrayTimeZoneRule#isEquivalentTo if (!t1.isEquivalentTo(t2)) { errln("FAIL: TimeArrayTimeZoneRule t1 is equivalent to t2, but returned false"); } if (t1.isEquivalentTo(t3)) { errln("FAIL: TimeArrayTimeZoneRule t1 is not equivalent to t3, but returned true"); } if (t1.isEquivalentTo(t4)) { errln("FAIL: TimeArrayTimeZoneRule t1 is not equivalent to t4, but returned true"); } if (t1.isEquivalentTo(a1)) { errln("FAIL: TimeArrayTimeZoneRule is not equivalent to AnnualTimeZoneRule, but returned true"); } // TimeArrayTimeZoneRule#isTransitionRule if (!t1.isTransitionRule()) { errln("FAIL: A TimeArrayTimeZoneRule is a transition rule, but returned false"); } // TimeArrayTimeZoneRule#toString str = t3.toString(); if (str == null || str.length() == 0) { errln("FAIL: TimeArrayTimeZoneRule#toString returns null or empty string"); } else { logln("TimeArrayTimeZoneRule t3 : " + str); } // DateTimeRule#toString str = dtr1.toString(); if (str == null || str.length() == 0) { errln("FAIL: DateTimeRule#toString for dtr1 returns null or empty string"); } else { logln("DateTimeRule dtr1 : " + str); } str = dtr2.toString(); if (str == null || str.length() == 0) { errln("FAIL: DateTimeRule#toString for dtr2 returns null or empty string"); } else { logln("DateTimeRule dtr1 : " + str); } str = dtr3.toString(); if (str == null || str.length() == 0) { errln("FAIL: DateTimeRule#toString for dtr3 returns null or empty string"); } else { logln("DateTimeRule dtr1 : " + str); } str = dtr4.toString(); if (str == null || str.length() == 0) { errln("FAIL: DateTimeRule#toString for dtr4 returns null or empty string"); } else { logln("DateTimeRule dtr1 : " + str); } } /* * API coverage test for BasicTimeZone APIs in SimpleTimeZone */ public void TestSimpleTimeZoneCoverage() { long time1 = getUTCMillis(1990, Calendar.JUNE, 1); long time2 = getUTCMillis(2000, Calendar.JUNE, 1); TimeZoneTransition tzt1, tzt2; // BasicTimeZone API implementation in SimpleTimeZone SimpleTimeZone stz1 = new SimpleTimeZone(-5*HOUR, "GMT-5"); tzt1 = stz1.getNextTransition(time1, false); if (tzt1 != null) { errln("FAIL: No transition must be returned by getNextTranstion for SimpleTimeZone with no DST rule"); } tzt1 = stz1.getPreviousTransition(time1, false); if (tzt1 != null) { errln("FAIL: No transition must be returned by getPreviousTransition for SimpleTimeZone with no DST rule"); } TimeZoneRule[] tzrules = stz1.getTimeZoneRules(); if (tzrules.length != 1 || !(tzrules[0] instanceof InitialTimeZoneRule)) { errln("FAIL: Invalid results returned by SimpleTimeZone#getTimeZoneRules"); } // Set DST rule stz1.setStartRule(Calendar.MARCH, 11, 2*HOUR); // March 11 stz1.setEndRule(Calendar.NOVEMBER, 1, Calendar.SUNDAY, 2*HOUR); // First Sunday in November tzt1 = stz1.getNextTransition(time1, false); if (tzt1 == null) { errln("FAIL: Non-null transition must be returned by getNextTranstion for SimpleTimeZone with a DST rule"); } else { String str = tzt1.toString(); if (str == null || str.length() == 0) { errln("FAIL: TimeZoneTransition#toString returns null or empty string"); } else { logln(str); } } tzt1 = stz1.getPreviousTransition(time1, false); if (tzt1 == null) { errln("FAIL: Non-null transition must be returned by getPreviousTransition for SimpleTimeZone with a DST rule"); } tzrules = stz1.getTimeZoneRules(); if (tzrules.length != 3 || !(tzrules[0] instanceof InitialTimeZoneRule) || !(tzrules[1] instanceof AnnualTimeZoneRule) || !(tzrules[2] instanceof AnnualTimeZoneRule)) { errln("FAIL: Invalid results returned by SimpleTimeZone#getTimeZoneRules for a SimpleTimeZone with DST"); } // Set DST start year stz1.setStartYear(2007); tzt1 = stz1.getPreviousTransition(time1, false); if (tzt1 != null) { errln("FAIL: No transition must be returned before 1990"); } tzt1 = stz1.getNextTransition(time1, false); // transition after 1990-06-01 tzt2 = stz1.getNextTransition(time2, false); // transition after 2000-06-01 if (tzt1 == null || tzt2 == null || !tzt1.equals(tzt2)) { errln("FAIL: Bad transition returned by SimpleTimeZone#getNextTransition"); } } /* * API coverage test for VTimeZone */ public void TestVTimeZoneCoverage() { final String TZID = "Europe/Moscow"; BasicTimeZone otz = (BasicTimeZone)TimeZone.getTimeZone(TZID, TimeZone.TIMEZONE_ICU); VTimeZone vtz = VTimeZone.create(TZID); // getOffset(era, year, month, day, dayOfWeek, milliseconds) int offset1 = otz.getOffset(GregorianCalendar.AD, 2007, Calendar.JULY, 1, Calendar.SUNDAY, 0); int offset2 = vtz.getOffset(GregorianCalendar.AD, 2007, Calendar.JULY, 1, Calendar.SUNDAY, 0); if (offset1 != offset2) { errln("FAIL: getOffset(int,int,int,int,int,int) returned different results in VTimeZone and OlsonTimeZone"); } // getOffset(date, local, offsets) int[] offsets1 = new int[2]; int[] offsets2 = new int[2]; long t = System.currentTimeMillis(); otz.getOffset(t, false, offsets1); vtz.getOffset(t, false, offsets2); if (offsets1[0] != offsets2[0] || offsets1[1] != offsets2[1]) { errln("FAIL: getOffset(long,boolean,int[]) returned different results in VTimeZone and OlsonTimeZone"); } // getRawOffset if (otz.getRawOffset() != vtz.getRawOffset()) { errln("FAIL: getRawOffset returned different results in VTimeZone and OlsonTimeZone"); } // inDaylightTime Date d = new Date(); if (otz.inDaylightTime(d) != vtz.inDaylightTime(d)) { errln("FAIL: inDaylightTime returned different results in VTimeZone and OlsonTimeZone"); } // useDaylightTime if (otz.useDaylightTime() != vtz.useDaylightTime()) { errln("FAIL: useDaylightTime returned different results in VTimeZone and OlsonTimeZone"); } // setRawOffset final int RAW = -10*HOUR; VTimeZone tmpvtz = (VTimeZone)vtz.clone(); tmpvtz.setRawOffset(RAW); if (tmpvtz.getRawOffset() != RAW) { logln("setRawOffset is implemented"); } // hasSameRules boolean bSame = otz.hasSameRules(vtz); logln("OlsonTimeZone#hasSameRules(VTimeZone) should return false always for now - actual: " + bSame); // getTZURL/setTZURL final String TZURL = "http://icu-project.org/timezone"; String tzurl = vtz.getTZURL(); if (tzurl != null) { errln("FAIL: getTZURL returned non-null value"); } vtz.setTZURL(TZURL); tzurl = vtz.getTZURL(); if (!TZURL.equals(tzurl)) { errln("FAIL: URL returned by getTZURL does not match the one set by setTZURL"); } // getLastModified/setLastModified Date lastmod = vtz.getLastModified(); if (lastmod != null) { errln("FAIL: getLastModified returned non-null value"); } Date newdate = new Date(); vtz.setLastModified(newdate); lastmod = vtz.getLastModified(); if (!newdate.equals(lastmod)) { errln("FAIL: Date returned by getLastModified does not match the one set by setLastModified"); } // getNextTransition/getPreviousTransition long base = getUTCMillis(2007, Calendar.JULY, 1); TimeZoneTransition tzt1 = otz.getNextTransition(base, true); TimeZoneTransition tzt2 = vtz.getNextTransition(base, true); if (tzt1.equals(tzt2)) { errln("FAIL: getNextTransition returned different results in VTimeZone and OlsonTimeZone"); } tzt1 = otz.getPreviousTransition(base, false); tzt2 = vtz.getPreviousTransition(base, false); if (tzt1.equals(tzt2)) { errln("FAIL: getPreviousTransition returned different results in VTimeZone and OlsonTimeZone"); } // hasEquivalentTransitions long time1 = getUTCMillis(1950, Calendar.JANUARY, 1); long time2 = getUTCMillis(2020, Calendar.JANUARY, 1); if (!vtz.hasEquivalentTransitions(otz, time1, time2)) { errln("FAIL: hasEquivalentTransitons returned false for the same time zone"); } // getTimeZoneRules TimeZoneRule[] rulesetAll = vtz.getTimeZoneRules(); TimeZoneRule[] ruleset1 = vtz.getTimeZoneRules(time1); TimeZoneRule[] ruleset2 = vtz.getTimeZoneRules(time2); if (rulesetAll.length < ruleset1.length || ruleset1.length < ruleset2.length) { errln("FAIL: Number of rules returned by getRules is invalid"); } } public void TestVTimeZoneParse() { // Trying to create VTimeZone from empty data StringReader r = new StringReader(""); VTimeZone empty = VTimeZone.create(r); if (empty != null) { errln("FAIL: Non-null VTimeZone is returned for empty VTIMEZONE data"); } // Create VTimeZone for Asia/Tokyo String asiaTokyo = "BEGIN:VTIMEZONE\r\n" + "TZID:Asia\r\n" + "\t/Tokyo\r\n" + "BEGIN:STANDARD\r\n" + "TZOFFSETFROM:+0900\r\n" + "TZOFFSETTO:+0900\r\n" + "TZNAME:JST\r\n" + "DTSTART:19700101\r\n" + " T000000\r\n" + "END:STANDARD\r\n" + "END:VTIMEZONE"; r = new StringReader(asiaTokyo); VTimeZone tokyo = VTimeZone.create(r); if (tokyo == null) { errln("FAIL: Failed to create a VTimeZone tokyo"); } else { // Make sure offsets are correct int[] offsets = new int[2]; tokyo.getOffset(System.currentTimeMillis(), false, offsets); if (offsets[0] != 9*HOUR || offsets[1] != 0) { errln("FAIL: Bad offsets returned by a VTimeZone created for Tokyo"); } } // Create VTimeZone from VTIMEZONE data String fooData = "BEGIN:VCALENDAR\r\n" + "BEGIN:VTIMEZONE\r\n" + "TZID:FOO\r\n" + "BEGIN:STANDARD\r\n" + "TZOFFSETFROM:-0700\r\n" + "TZOFFSETTO:-0800\r\n" + "TZNAME:FST\r\n" + "DTSTART:20071010T010000\r\n" + "RRULE:FREQ=YEARLY;BYDAY=WE;BYMONTHDAY=10,11,12,13,14,15,16;BYMONTH=10\r\n" + "END:STANDARD\r\n" + "BEGIN:DAYLIGHT\r\n" + "TZOFFSETFROM:-0800\r\n" + "TZOFFSETTO:-0700\r\n" + "TZNAME:FDT\r\n" + "DTSTART:20070415T010000\r\n" + "RRULE:FREQ=YEARLY;BYMONTHDAY=15;BYMONTH=4\r\n" + "END:DAYLIGHT\r\n" + "END:VTIMEZONE\r\n" + "END:VCALENDAR"; r = new StringReader(fooData); VTimeZone foo = VTimeZone.create(r); if (foo == null) { errln("FAIL: Failed to create a VTimeZone foo"); } else { // Write VTIMEZONE data StringWriter w = new StringWriter(); try { foo.write(w, getUTCMillis(2005, Calendar.JANUARY, 1)); } catch (IOException ioe) { errln("FAIL: IOException is thrown while writing VTIMEZONE data for foo"); } logln(w.toString()); } } public void TestT6216() { // Test case in #6216 String tokyoTZ = "BEGIN:VCALENDAR\r\n" + "VERSION:2.0\r\n" + "PRODID:-//PYVOBJECT//NONSGML Version 1//EN\r\n" + "BEGIN:VTIMEZONE\r\n" + "TZID:Asia/Tokyo\r\n" + "BEGIN:STANDARD\r\n" + "DTSTART:20000101T000000\r\n" + "RRULE:FREQ=YEARLY;BYMONTH=1\r\n" + "TZNAME:Asia/Tokyo\r\n" + "TZOFFSETFROM:+0900\r\n" + "TZOFFSETTO:+0900\r\n" + "END:STANDARD\r\n" + "END:VTIMEZONE\r\n" + "END:VCALENDAR"; // Single final rule, overlapping with another String finalOverlap = "BEGIN:VCALENDAR\r\n" + "BEGIN:VTIMEZONE\r\n" + "TZID:FinalOverlap\r\n" + "BEGIN:STANDARD\r\n" + "TZOFFSETFROM:-0200\r\n" + "TZOFFSETTO:-0300\r\n" + "TZNAME:STD\r\n" + "DTSTART:20001029T020000\r\n" + "RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10\r\n" + "END:STANDARD\r\n" + "BEGIN:DAYLIGHT\r\n" + "TZOFFSETFROM:-0300\r\n" + "TZOFFSETTO:-0200\r\n" + "TZNAME:DST\r\n" + "DTSTART:19990404T020000\r\n" + "RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4;UNTIL=20050403T040000Z\r\n" + "END:DAYLIGHT\r\n" + "END:VTIMEZONE\r\n" + "END:VCALENDAR"; // Single final rule, no overlapping with another String finalNonOverlap = "BEGIN:VCALENDAR\r\n" + "BEGIN:VTIMEZONE\r\n" + "TZID:FinalNonOverlap\r\n" + "BEGIN:STANDARD\r\n" + "TZOFFSETFROM:-0200\r\n" + "TZOFFSETTO:-0300\r\n" + "TZNAME:STD\r\n" + "DTSTART:20001029T020000\r\n" + "RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10;UNTIL=20041031T040000Z\r\n" + "END:STANDARD\r\n" + "BEGIN:DAYLIGHT\r\n" + "TZOFFSETFROM:-0300\r\n" + "TZOFFSETTO:-0200\r\n" + "TZNAME:DST\r\n" + "DTSTART:19990404T020000\r\n" + "RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4;UNTIL=20050403T040000Z\r\n" + "END:DAYLIGHT\r\n" + "BEGIN:STANDARD\r\n" + "TZOFFSETFROM:-0200\r\n" + "TZOFFSETTO:-0300\r\n" + "TZNAME:STDFINAL\r\n" + "DTSTART:20071028T020000\r\n" + "RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10\r\n" + "END:STANDARD\r\n" + "END:VTIMEZONE\r\n" + "END:VCALENDAR"; int[][] TestDates = { {1995, Calendar.JANUARY, 1}, {1995, Calendar.JULY, 1}, {2000, Calendar.JANUARY, 1}, {2000, Calendar.JULY, 1}, {2005, Calendar.JANUARY, 1}, {2005, Calendar.JULY, 1}, {2010, Calendar.JANUARY, 1}, {2010, Calendar.JULY, 1}, }; String[] TestZones = { tokyoTZ, finalOverlap, finalNonOverlap, }; int[][] Expected = { // JAN90 JUL90 JAN00 JUL00 JAN05 JUL05 JAN10 JUL10 { 32400000, 32400000, 32400000, 32400000, 32400000, 32400000, 32400000, 32400000}, {-10800000, -10800000, -7200000, -7200000, -10800000, -7200000, -10800000, -10800000}, {-10800000, -10800000, -7200000, -7200000, -10800000, -7200000, -10800000, -10800000}, }; // Get test times long[] times = new long[TestDates.length]; Calendar cal = new GregorianCalendar(TimeZone.getTimeZone("Etc/GMT")); for (int i = 0; i < TestDates.length; i++) { cal.clear(); cal.set(TestDates[i][0], TestDates[i][1], TestDates[i][2]); times[i] = cal.getTimeInMillis(); } for (int i = 0; i < TestZones.length; i++) { try { VTimeZone vtz = VTimeZone.create(new StringReader(TestZones[i])); for (int j = 0; j < times.length; j++) { int offset = vtz.getOffset(times[j]); if (offset != Expected[i][j]) { errln("FAIL: Invalid offset at time(" + times[j] + "):" + offset + " Expected:" + Expected[i][j]); } } } catch (Exception e) { errln("FAIL: Failed to calculate the offset for VTIMEZONE data " + i); } } } public void TestT6669() { // getNext/PreviousTransition implementation in SimpleTimeZone // used to use a bad condition for detecting if DST is enabled or not. SimpleTimeZone stz = new SimpleTimeZone(0, "CustomID", Calendar.JANUARY, 1, Calendar.SUNDAY, 0, Calendar.JULY, 1, Calendar.SUNDAY, 0); long t = 1230681600000L; //2008-12-31T00:00:00 long expectedNext = 1231027200000L; //2009-01-04T00:00:00 long expectedPrev = 1215298800000L; //2008-07-06T00:00:00 TimeZoneTransition tzt = stz.getNextTransition(t, false); if (tzt == null) { errln("FAIL: No transition returned by getNextTransition."); } else if (tzt.getTime() != expectedNext){ errln("FAIL: Wrong transition time returned by getNextTransition - " + tzt.getTime() + " Expected: " + expectedNext); } tzt = stz.getPreviousTransition(t, true); if (tzt == null) { errln("FAIL: No transition returned by getPreviousTransition."); } else if (tzt.getTime() != expectedPrev){ errln("FAIL: Wrong transition time returned by getPreviousTransition - " + tzt.getTime() + " Expected: " + expectedPrev); } } // Internal utility methods ----------------------------------------- /* * Check if a time shift really happens on each transition returned by getNextTransition or * getPreviousTransition in the specified time range */ private void verifyTransitions(TimeZone tz, long start, long end) { BasicTimeZone icutz = (BasicTimeZone)tz; long time; int[] before = new int[2]; int[] after = new int[2]; TimeZoneTransition tzt0; // Ascending tzt0 = null; time = start; while(true) { TimeZoneTransition tzt = icutz.getNextTransition(time, false); if (tzt == null) { break; } time = tzt.getTime(); if (time >= end) { break; } icutz.getOffset(time, false, after); icutz.getOffset(time - 1, false, before); if (after[0] == before[0] && after[1] == before[1]) { errln("FAIL: False transition returned by getNextTransition for " + icutz.getID() + " at " + time); } if (tzt0 != null && (tzt0.getTo().getRawOffset() != tzt.getFrom().getRawOffset() || tzt0.getTo().getDSTSavings() != tzt.getFrom().getDSTSavings())) { errln("FAIL: TO rule of the previous transition does not match FROM rule of this transtion at " + time + " for " + icutz.getID()); } tzt0 = tzt; } // Descending tzt0 = null; time = end; while(true) { TimeZoneTransition tzt = icutz.getPreviousTransition(time, false); if (tzt == null) { break; } time = tzt.getTime(); if (time <= start) { break; } icutz.getOffset(time, false, after); icutz.getOffset(time - 1, false, before); if (after[0] == before[0] && after[1] == before[1]) { errln("FAIL: False transition returned by getPreviousTransition for " + icutz.getID() + " at " + time); } if (tzt0 != null && (tzt0.getFrom().getRawOffset() != tzt.getTo().getRawOffset() || tzt0.getFrom().getDSTSavings() != tzt.getTo().getDSTSavings())) { errln("FAIL: TO rule of the next transition does not match FROM rule in this transtion at " + time + " for " + icutz.getID()); } tzt0 = tzt; } } /* * Compare all time transitions in 2 time zones in the specified time range in ascending order */ private void compareTransitionsAscending(TimeZone tz1, TimeZone tz2, long start, long end, boolean inclusive) { BasicTimeZone z1 = (BasicTimeZone)tz1; BasicTimeZone z2 = (BasicTimeZone)tz2; String zid1 = tz1.getID(); String zid2 = tz2.getID(); long time = start; while(true) { TimeZoneTransition tzt1 = z1.getNextTransition(time, inclusive); TimeZoneTransition tzt2 = z2.getNextTransition(time, inclusive); boolean inRange1 = false; boolean inRange2 = false; if (tzt1 != null) { if (tzt1.getTime() < end || (inclusive && tzt1.getTime() == end)) { inRange1 = true; } } if (tzt2 != null) { if (tzt2.getTime() < end || (inclusive && tzt2.getTime() == end)) { inRange2 = true; } } if (!inRange1 && !inRange2) { // No more transition in the range break; } if (!inRange1) { errln("FAIL: " + zid1 + " does not have any transitions after " + time + " before " + end); break; } if (!inRange2) { errln("FAIL: " + zid2 + " does not have any transitions after " + time + " before " + end); break; } if (tzt1.getTime() != tzt2.getTime()) { errln("FAIL: First transition after " + time + " " + zid1 + "[" + tzt1.getTime() + "] " + zid2 + "[" + tzt2.getTime() + "]"); break; } time = tzt1.getTime(); if (inclusive) { time++; } } } /* * Compare all time transitions in 2 time zones in the specified time range in descending order */ private void compareTransitionsDescending(TimeZone tz1, TimeZone tz2, long start, long end, boolean inclusive) { BasicTimeZone z1 = (BasicTimeZone)tz1; BasicTimeZone z2 = (BasicTimeZone)tz2; String zid1 = tz1.getID(); String zid2 = tz2.getID(); long time = end; while(true) { TimeZoneTransition tzt1 = z1.getPreviousTransition(time, inclusive); TimeZoneTransition tzt2 = z2.getPreviousTransition(time, inclusive); boolean inRange1 = false; boolean inRange2 = false; if (tzt1 != null) { if (tzt1.getTime() > start || (inclusive && tzt1.getTime() == start)) { inRange1 = true; } } if (tzt2 != null) { if (tzt2.getTime() > start || (inclusive && tzt2.getTime() == start)) { inRange2 = true; } } if (!inRange1 && !inRange2) { // No more transition in the range break; } if (!inRange1) { errln("FAIL: " + zid1 + " does not have any transitions before " + time + " after " + start); break; } if (!inRange2) { errln("FAIL: " + zid2 + " does not have any transitions before " + time + " after " + start); break; } if (tzt1.getTime() != tzt2.getTime()) { errln("FAIL: Last transition before " + time + " " + zid1 + "[" + tzt1.getTime() + "] " + zid2 + "[" + tzt2.getTime() + "]"); break; } time = tzt1.getTime(); if (inclusive) { time--; } } } private static final String[] TESTZIDS = { "AGT", "America/New_York", "America/Los_Angeles", "America/Indiana/Indianapolis", "America/Havana", "Europe/Lisbon", "Europe/Paris", "Asia/Tokyo", "Asia/Sakhalin", "Africa/Cairo", "Africa/Windhoek", "Australia/Sydney", "Etc/GMT+8" }; private String[] getTestZIDs() { if (getInclusion() > 5) { return TimeZone.getAvailableIDs(); } return TESTZIDS; } private static final int[][] TESTYEARS = { {1895, 1905}, // including int32 minimum second {1965, 1975}, // including the epoch {1995, 2015} // practical year range }; private long[] getTestTimeRange(int idx) { int loyear, hiyear; if (idx < TESTYEARS.length) { loyear = TESTYEARS[idx][0]; hiyear = TESTYEARS[idx][1]; } else if (idx == TESTYEARS.length && getInclusion() > 5) { loyear = 1850; hiyear = 2050; } else { return null; } long[] times = new long[2]; times[0] = getUTCMillis(loyear, Calendar.JANUARY, 1); times[1] = getUTCMillis(hiyear + 1, Calendar.JANUARY, 1); return times; } private GregorianCalendar utcCal; private long getUTCMillis(int year, int month, int dayOfMonth) { if (utcCal == null) { utcCal = new GregorianCalendar(TimeZone.getTimeZone("UTC"), ULocale.ROOT); } utcCal.clear(); utcCal.set(year, month, dayOfMonth); return utcCal.getTimeInMillis(); } }