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