]> gitweb.fperrin.net Git - Dictionary.git/blob - jars/icu4j-4_4_2-src/main/tests/core/src/com/ibm/icu/dev/test/timezone/TimeZoneRuleTest.java
go
[Dictionary.git] / jars / icu4j-4_4_2-src / main / tests / core / 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,5,0) && tzids[i].equals("Asia/Amman")) { // ticket#7008\r
440                 continue;\r
441             }\r
442             BasicTimeZone olsontz = (BasicTimeZone)TimeZone.getTimeZone(tzids[i], TimeZone.TIMEZONE_ICU);\r
443             VTimeZone vtz_org = VTimeZone.create(tzids[i]);\r
444             vtz_org.setTZURL("http://source.icu-project.org/timezone");\r
445             vtz_org.setLastModified(new Date());\r
446             VTimeZone vtz_new = null;\r
447             try {\r
448                 // Write out VTIMEZONE\r
449                 ByteArrayOutputStream baos = new ByteArrayOutputStream();\r
450                 OutputStreamWriter writer = new OutputStreamWriter(baos);\r
451                 vtz_org.write(writer);\r
452                 writer.close();\r
453                 byte[] vtzdata = baos.toByteArray();\r
454                 // Read VTIMEZONE\r
455                 ByteArrayInputStream bais = new ByteArrayInputStream(vtzdata);\r
456                 InputStreamReader reader = new InputStreamReader(bais);\r
457                 vtz_new = VTimeZone.create(reader);\r
458                 reader.close();\r
459 \r
460                 // Write out VTIMEZONE one more time\r
461                 ByteArrayOutputStream baos1 = new ByteArrayOutputStream();\r
462                 OutputStreamWriter writer1 = new OutputStreamWriter(baos1);\r
463                 vtz_new.write(writer1);\r
464                 writer1.close();\r
465                 byte[] vtzdata1 = baos1.toByteArray();\r
466 \r
467                 // Make sure VTIMEZONE data is exactly same with the first one\r
468                 if (vtzdata.length != vtzdata1.length) {\r
469                     errln("FAIL: different VTIMEZONE data length");\r
470                 }\r
471                 for (int j = 0; j < vtzdata.length; j++) {\r
472                     if (vtzdata[j] != vtzdata1[j]) {\r
473                         errln("FAIL: different VTIMEZONE data");\r
474                         break;\r
475                     }\r
476                 }\r
477             } catch (IOException ioe) {\r
478                 errln("FAIL: IO error while writing/reading VTIMEZONE data");\r
479             }\r
480             // Check equivalency after the first transition.\r
481             // The DST information before the first transition might be lost\r
482             // because there is no good way to represent the initial time with\r
483             // VTIMEZONE.\r
484             if (vtz_new.getOffset(startTime) != olsontz.getOffset(startTime)) {\r
485                 errln("FAIL: VTimeZone for " + tzids[i]\r
486                          + " is not equivalent to its OlsonTimeZone corresponding at " + startTime);\r
487             }\r
488             TimeZoneTransition tzt = olsontz.getNextTransition(startTime, false);\r
489             if (tzt != null) {\r
490                 if (!vtz_new.hasEquivalentTransitions(olsontz, tzt.getTime(), endTime, true)) {\r
491                     errln("FAIL: VTimeZone for " + tzids[i] + " is not equivalent to its OlsonTimeZone corresponding.");\r
492                 }\r
493                 if (!vtz_new.hasEquivalentTransitions(olsontz, tzt.getTime(), endTime, false)) {\r
494                     logln("VTimeZone for " + tzids[i] + " is not equivalent to its OlsonTimeZone corresponding in strict comparison mode.");\r
495                 }\r
496             }\r
497         }\r
498     }\r
499 \r
500     /*\r
501      * Write out time zone rules of OlsonTimeZone after a cutoff date into VTIMEZONE format,\r
502      * create a new VTimeZone from the VTIMEZONE data, then compare transitions\r
503      */\r
504     public void TestVTimeZoneRoundTripPartial() {\r
505         long[] startTimes = new long[] {\r
506             getUTCMillis(1900, Calendar.JANUARY, 1),\r
507             getUTCMillis(1950, Calendar.JANUARY, 1),\r
508             getUTCMillis(2020, Calendar.JANUARY, 1)\r
509         };\r
510         long endTime = getUTCMillis(2050, Calendar.JANUARY, 1);\r
511 \r
512         String[] tzids = getTestZIDs();\r
513         for (int n = 0; n < startTimes.length; n++) {\r
514             long startTime = startTimes[n];\r
515             for (int i = 0; i < tzids.length; i++) {\r
516                 if (skipIfBeforeICU(4,5,0) && tzids[i].equals("Asia/Amman")) { // ticket#7008\r
517                     continue;\r
518                 }\r
519                 BasicTimeZone olsontz = (BasicTimeZone)TimeZone.getTimeZone(tzids[i], TimeZone.TIMEZONE_ICU);\r
520                 VTimeZone vtz_org = VTimeZone.create(tzids[i]);\r
521                 VTimeZone vtz_new = null;\r
522                 try {\r
523                     // Write out VTIMEZONE\r
524                     ByteArrayOutputStream baos = new ByteArrayOutputStream();\r
525                     OutputStreamWriter writer = new OutputStreamWriter(baos);\r
526                     vtz_org.write(writer, startTime);\r
527                     writer.close();\r
528                     byte[] vtzdata = baos.toByteArray();\r
529                     // Read VTIMEZONE\r
530                     ByteArrayInputStream bais = new ByteArrayInputStream(vtzdata);\r
531                     InputStreamReader reader = new InputStreamReader(bais);\r
532                     vtz_new = VTimeZone.create(reader);\r
533                     reader.close();\r
534 \r
535                 } catch (IOException ioe) {\r
536                     errln("FAIL: IO error while writing/reading VTIMEZONE data");\r
537                 }\r
538                 // Check equivalency after the first transition.\r
539                 // The DST information before the first transition might be lost\r
540                 // because there is no good way to represent the initial time with\r
541                 // VTIMEZONE.\r
542                 if (vtz_new.getOffset(startTime) != olsontz.getOffset(startTime)) {\r
543                     errln("FAIL: VTimeZone for " + tzids[i]\r
544                              + " is not equivalent to its OlsonTimeZone corresponding at " + startTime);\r
545                 }\r
546                 TimeZoneTransition tzt = olsontz.getNextTransition(startTime, false);\r
547                 if (tzt != null) {\r
548                     if (!vtz_new.hasEquivalentTransitions(olsontz, tzt.getTime(), endTime, true)) {\r
549                         errln("FAIL: VTimeZone for " + tzids[i] + "(>=" + startTime + ") is not equivalent to its OlsonTimeZone corresponding.");\r
550                     }\r
551                 }\r
552             }            \r
553         }\r
554     }\r
555 \r
556     /*\r
557      * Write out simple time zone rules from an OlsonTimeZone at various time into VTIMEZONE\r
558      * format and create a new VTimeZone from the VTIMEZONE data, then make sure the raw offset\r
559      * and DST savings are same in these two time zones.\r
560      */\r
561     public void TestVTimeZoneSimpleWrite() {\r
562         long[] testTimes = new long[] {\r
563                 getUTCMillis(2006, Calendar.JANUARY, 1),\r
564                 getUTCMillis(2006, Calendar.MARCH, 15),\r
565                 getUTCMillis(2006, Calendar.MARCH, 31),\r
566                 getUTCMillis(2006, Calendar.APRIL, 5),\r
567                 getUTCMillis(2006, Calendar.OCTOBER, 25),\r
568                 getUTCMillis(2006, Calendar.NOVEMBER, 1),\r
569                 getUTCMillis(2006, Calendar.NOVEMBER, 5),\r
570                 getUTCMillis(2007, Calendar.JANUARY, 1)\r
571         };\r
572 \r
573         String[] tzids = getTestZIDs();\r
574         for (int n = 0; n < testTimes.length; n++) {\r
575             long time = testTimes[n];\r
576 \r
577             int[] offsets1 = new int[2];\r
578             int[] offsets2 = new int[2];\r
579 \r
580             for (int i = 0; i < tzids.length; i++) {\r
581                 VTimeZone vtz_org = VTimeZone.create(tzids[i]);\r
582                 VTimeZone vtz_new = null;\r
583                 try {\r
584                     // Write out VTIMEZONE\r
585                     ByteArrayOutputStream baos = new ByteArrayOutputStream();\r
586                     OutputStreamWriter writer = new OutputStreamWriter(baos);\r
587                     vtz_org.writeSimple(writer, time);\r
588                     writer.close();\r
589                     byte[] vtzdata = baos.toByteArray();\r
590                     // Read VTIMEZONE\r
591                     ByteArrayInputStream bais = new ByteArrayInputStream(vtzdata);\r
592                     InputStreamReader reader = new InputStreamReader(bais);\r
593                     vtz_new = VTimeZone.create(reader);\r
594                     reader.close();\r
595                 } catch (IOException ioe) {\r
596                     errln("FAIL: IO error while writing/reading VTIMEZONE data");\r
597                 }\r
598 \r
599                 // Check equivalency\r
600                 vtz_org.getOffset(time, false, offsets1);\r
601                 vtz_new.getOffset(time, false, offsets2);\r
602                 if (offsets1[0] != offsets2[0] || offsets1[1] != offsets2[1]) {\r
603                     errln("FAIL: VTimeZone writeSimple for " + tzids[i] + " at time " + time + " failed to the round trip.");\r
604                 }\r
605             }\r
606         }\r
607     }\r
608 \r
609     /*\r
610      * Write out time zone rules of OlsonTimeZone into VTIMEZONE format with RFC2445 header TZURL and\r
611      * LAST-MODIFIED, create a new VTimeZone from the VTIMEZONE data to see if the headers are preserved.\r
612      */\r
613     public void TestVTimeZoneHeaderProps() {\r
614         String tzid = "America/Chicago";\r
615         String tzurl = "http://source.icu-project.org";\r
616         Date lastmod = new Date(getUTCMillis(2007, Calendar.JUNE, 1));\r
617 \r
618         VTimeZone vtz = VTimeZone.create(tzid);\r
619         vtz.setTZURL(tzurl);\r
620         vtz.setLastModified(lastmod);\r
621 \r
622         // Roundtrip conversion\r
623         VTimeZone newvtz1 = null;\r
624         try {\r
625             // Write out VTIMEZONE\r
626             ByteArrayOutputStream baos = new ByteArrayOutputStream();\r
627             OutputStreamWriter writer = new OutputStreamWriter(baos);\r
628             vtz.write(writer);\r
629             writer.close();\r
630             byte[] vtzdata = baos.toByteArray();\r
631             // Read VTIMEZONE\r
632             ByteArrayInputStream bais = new ByteArrayInputStream(vtzdata);\r
633             InputStreamReader reader = new InputStreamReader(bais);\r
634             newvtz1 = VTimeZone.create(reader);\r
635             reader.close();\r
636 \r
637             // Check if TZURL and LAST-MODIFIED headers are preserved\r
638             if (!(tzurl.equals(newvtz1.getTZURL()))) {\r
639                 errln("FAIL: TZURL property is not preserved during the roundtrip conversion.  Before:"\r
640                         + tzurl + "/After:" + newvtz1.getTZURL());\r
641             }\r
642             if (!(lastmod.equals(newvtz1.getLastModified()))) {\r
643                 errln("FAIL: LAST-MODIFIED property is not preserved during the roundtrip conversion.  Before:"\r
644                         + lastmod.getTime() + "/After:" + newvtz1.getLastModified().getTime());\r
645             }\r
646         } catch (IOException ioe) {\r
647             errln("FAIL: IO error while writing/reading VTIMEZONE data");\r
648         }\r
649 \r
650         // Second roundtrip, with a cutoff\r
651         VTimeZone newvtz2 = null;\r
652         try {\r
653             // Set different tzurl\r
654             String newtzurl = "http://www.ibm.com";\r
655             newvtz1.setTZURL(newtzurl);\r
656             // Write out VTIMEZONE\r
657             ByteArrayOutputStream baos = new ByteArrayOutputStream();\r
658             OutputStreamWriter writer = new OutputStreamWriter(baos);\r
659             newvtz1.write(writer, getUTCMillis(2000, Calendar.JANUARY, 1));\r
660             writer.close();\r
661             byte[] vtzdata = baos.toByteArray();\r
662             // Read VTIMEZONE\r
663             ByteArrayInputStream bais = new ByteArrayInputStream(vtzdata);\r
664             InputStreamReader reader = new InputStreamReader(bais);\r
665             newvtz2 = VTimeZone.create(reader);\r
666             reader.close();\r
667 \r
668             // Check if TZURL and LAST-MODIFIED headers are preserved\r
669             if (!(newtzurl.equals(newvtz2.getTZURL()))) {\r
670                 errln("FAIL: TZURL property is not preserved during the second roundtrip conversion.  Before:"\r
671                         + newtzurl + "/After:" + newvtz2.getTZURL());\r
672             }\r
673             if (!(lastmod.equals(newvtz2.getLastModified()))) {\r
674                 errln("FAIL: LAST-MODIFIED property is not preserved during the second roundtrip conversion.  Before:"\r
675                         + lastmod.getTime() + "/After:" + newvtz2.getLastModified().getTime());\r
676             }\r
677         } catch (IOException ioe) {\r
678             errln("FAIL: IO error while writing/reading VTIMEZONE data");\r
679         }\r
680         \r
681     }\r
682 \r
683     /*\r
684      * Extract simple rules from an OlsonTimeZone and make sure the rule format matches\r
685      * the expected format.\r
686      */\r
687     public void TestGetSimpleRules() {\r
688         long[] testTimes = new long[] {\r
689                 getUTCMillis(1970, Calendar.JANUARY, 1),\r
690                 getUTCMillis(2000, Calendar.MARCH, 31),\r
691                 getUTCMillis(2005, Calendar.JULY, 1),\r
692                 getUTCMillis(2010, Calendar.NOVEMBER, 1),\r
693             };\r
694 \r
695         String[] tzids = getTestZIDs();\r
696         for (int n = 0; n < testTimes.length; n++) {\r
697             long time = testTimes[n];\r
698             for (int i = 0; i < tzids.length; i++) {\r
699                 BasicTimeZone tz = (BasicTimeZone)TimeZone.getTimeZone(tzids[i], TimeZone.TIMEZONE_ICU);\r
700                 TimeZoneRule[] rules = tz.getSimpleTimeZoneRulesNear(time);\r
701                 if (rules == null) {\r
702                     errln("FAIL: Failed to extract simple rules for " + tzids[i] + " at " + time);\r
703                 } else {\r
704                     if (rules.length == 1) {\r
705                         if (!(rules[0] instanceof InitialTimeZoneRule)) {\r
706                             errln("FAIL: Unexpected rule object type is returned for " + tzids[i] + " at " + time);\r
707                         }\r
708                     } else if (rules.length == 3) {\r
709                         if (!(rules[0] instanceof InitialTimeZoneRule)\r
710                                 || !(rules[1] instanceof AnnualTimeZoneRule)\r
711                                 || !(rules[2] instanceof AnnualTimeZoneRule)) {\r
712                             errln("FAIL: Unexpected rule object type is returned for " + tzids[i] + " at " + time);\r
713                         }\r
714                         for (int idx = 1; idx <= 2; idx++) {\r
715                             DateTimeRule dtr = ((AnnualTimeZoneRule)rules[idx]).getRule();\r
716                             if (dtr.getTimeRuleType() != DateTimeRule.WALL_TIME) {\r
717                                 errln("FAIL: WALL_TIME is not used as the time rule in the time zone rule(" + idx + ") for " + tzids[i] + " at " + time);\r
718                             }\r
719                             if (dtr.getDateRuleType() != DateTimeRule.DOW) {\r
720                                 errln("FAIL: DOW is not used as the date rule in the time zone rule(" + idx + ") for " + tzids[i] + " at " + time);\r
721                             }\r
722                         }\r
723                     } else {\r
724                         errln("FAIL: Unexpected number of rules returned for " + tzids[i] + " at " + time);\r
725                     }\r
726                 }\r
727             }\r
728         }\r
729     }\r
730 \r
731     /*\r
732      * API coverage tests for TimeZoneRule \r
733      */\r
734     public void TestTimeZoneRuleCoverage() {\r
735         long time1 = getUTCMillis(2005, Calendar.JULY, 4);\r
736         long time2 = getUTCMillis(2015, Calendar.JULY, 4);\r
737         long time3 = getUTCMillis(1950, Calendar.JULY, 4);\r
738 \r
739         DateTimeRule dtr1 = new DateTimeRule(Calendar.FEBRUARY, 29, Calendar.SUNDAY, false,\r
740                 3*HOUR, DateTimeRule.WALL_TIME); // Last Sunday on or before Feb 29, at 3 AM, wall time\r
741         DateTimeRule dtr2 = new DateTimeRule(Calendar.MARCH, 11, 2*HOUR,\r
742                 DateTimeRule.STANDARD_TIME); // Mar 11, at 2 AM, standard time\r
743         DateTimeRule dtr3 = new DateTimeRule(Calendar.OCTOBER, -1, Calendar.SATURDAY,\r
744                 6*HOUR, DateTimeRule.UTC_TIME); //Last Saturday in Oct, at 6 AM, UTC\r
745         DateTimeRule dtr4 = new DateTimeRule(Calendar.MARCH, 8, Calendar.SUNDAY, true,\r
746                 2*HOUR, DateTimeRule.WALL_TIME); // First Sunday on or after Mar 8, at 2 AM, wall time\r
747 \r
748         AnnualTimeZoneRule a1 = new AnnualTimeZoneRule("a1", -3*HOUR, 1*HOUR, dtr1,\r
749                 2000, AnnualTimeZoneRule.MAX_YEAR);\r
750         AnnualTimeZoneRule a2 = new AnnualTimeZoneRule("a2", -3*HOUR, 1*HOUR, dtr1,\r
751                 2000, AnnualTimeZoneRule.MAX_YEAR);\r
752         AnnualTimeZoneRule a3 = new AnnualTimeZoneRule("a3", -3*HOUR, 1*HOUR, dtr1,\r
753                 2000, 2010);\r
754         \r
755         InitialTimeZoneRule i1 = new InitialTimeZoneRule("i1", -3*HOUR, 0);\r
756         InitialTimeZoneRule i2 = new InitialTimeZoneRule("i2", -3*HOUR, 0);\r
757         InitialTimeZoneRule i3 = new InitialTimeZoneRule("i3", -3*HOUR, 1*HOUR);\r
758         \r
759         long[] emptytimes = {};\r
760         long[] trtimes1 = {0};\r
761         long[] trtimes2 = {0, 10000000};\r
762 \r
763         TimeArrayTimeZoneRule t0 = null;\r
764         try {\r
765             // Try to construct TimeArrayTimeZoneRule with null transition times\r
766             t0 = new TimeArrayTimeZoneRule("nulltimes", -3*HOUR, 0,\r
767                     null, DateTimeRule.UTC_TIME);\r
768         } catch (IllegalArgumentException iae) {\r
769             logln("TimeArrayTimeZoneRule constructor throws IllegalArgumentException as expected.");\r
770             t0 = null;\r
771         }\r
772         if (t0 != null) {\r
773             errln("FAIL: TimeArrayTimeZoneRule constructor did not throw IllegalArgumentException for null times");\r
774         }\r
775         \r
776         try {\r
777             // Try to construct TimeArrayTimeZoneRule with empty transition times\r
778             t0 = new TimeArrayTimeZoneRule("nulltimes", -3*HOUR, 0,\r
779                     emptytimes, DateTimeRule.UTC_TIME);\r
780         } catch (IllegalArgumentException iae) {\r
781             logln("TimeArrayTimeZoneRule constructor throws IllegalArgumentException as expected.");\r
782             t0 = null;\r
783         }\r
784         if (t0 != null) {\r
785             errln("FAIL: TimeArrayTimeZoneRule constructor did not throw IllegalArgumentException for empty times");\r
786         }\r
787 \r
788         TimeArrayTimeZoneRule t1 = new TimeArrayTimeZoneRule("t1", -3*HOUR, 0, trtimes1, DateTimeRule.UTC_TIME);\r
789         TimeArrayTimeZoneRule t2 = new TimeArrayTimeZoneRule("t2", -3*HOUR, 0, trtimes1, DateTimeRule.UTC_TIME);\r
790         TimeArrayTimeZoneRule t3 = new TimeArrayTimeZoneRule("t3", -3*HOUR, 0, trtimes2, DateTimeRule.UTC_TIME);\r
791         TimeArrayTimeZoneRule t4 = new TimeArrayTimeZoneRule("t4", -3*HOUR, 0, trtimes1, DateTimeRule.STANDARD_TIME);\r
792         TimeArrayTimeZoneRule t5 = new TimeArrayTimeZoneRule("t5", -4*HOUR, 1*HOUR, trtimes1, DateTimeRule.WALL_TIME);\r
793 \r
794         // AnnualTimeZoneRule#getRule\r
795         if (!a1.getRule().equals(a2.getRule())) {\r
796             errln("FAIL: The same DateTimeRule must be returned from AnnualTimeZoneRule a1 and a2");\r
797         }\r
798     \r
799         // AnnualTimeZoneRule#getStartYear\r
800         int startYear = a1.getStartYear();\r
801         if (startYear != 2000) {\r
802             errln("FAIL: The start year of AnnualTimeZoneRule a1 must be 2000 - returned: " + startYear);\r
803         }\r
804 \r
805         // AnnualTimeZoneRule#getEndYear\r
806         int endYear = a1.getEndYear();\r
807         if (endYear != AnnualTimeZoneRule.MAX_YEAR) {\r
808             errln("FAIL: The start year of AnnualTimeZoneRule a1 must be MAX_YEAR - returned: " + endYear);\r
809         }\r
810         endYear = a3.getEndYear();\r
811         if (endYear != 2010) {\r
812             errln("FAIL: The start year of AnnualTimeZoneRule a3 must be 2010 - returned: " + endYear);\r
813         }\r
814         \r
815         // AnnualTimeZone#getStartInYear\r
816         Date d1 = a1.getStartInYear(2005, -3*HOUR, 0);\r
817         Date d2 = a3.getStartInYear(2005, -3*HOUR, 0);\r
818         if (d1 == null || d2 == null || !d1.equals(d2)) {\r
819             errln("FAIL: AnnualTimeZoneRule#getStartInYear did not work as expected");\r
820         }\r
821         d2 = a3.getStartInYear(2015, -3*HOUR, 0);\r
822         if (d2 != null) {\r
823             errln("FAIL: AnnualTimeZoneRule#getSTartInYear returned non-null date for 2015 which is out of rule range");\r
824         }\r
825 \r
826         // AnnualTimeZone#getFirstStart\r
827         d1 = a1.getFirstStart(-3*HOUR, 0);\r
828         d2 = a1.getFirstStart(-4*HOUR, 1*HOUR);\r
829         if (d1 == null || d2 == null || !d1.equals(d2)) {\r
830             errln("FAIL: The same start time should be returned by getFirstStart");\r
831         }\r
832 \r
833         // AnnualTimeZone#getFinalStart\r
834         d1 = a1.getFinalStart(-3*HOUR, 0);\r
835         if (d1 != null) {\r
836             errln("FAIL: Non-null Date is returned by getFinalStart for a1");\r
837         }\r
838         d1 = a1.getStartInYear(2010, -3*HOUR, 0);\r
839         d2 = a3.getFinalStart(-3*HOUR, 0);\r
840         if (d1 == null || d2 == null || !d1.equals(d2)) {\r
841             errln("FAIL: Bad date is returned by getFinalStart");\r
842         }\r
843 \r
844         // AnnualTimeZone#getNextStart / getPreviousStart\r
845         d1 = a1.getNextStart(time1, -3*HOUR, 0, false);\r
846         if (d1 == null) {\r
847             errln("FAIL: Null Date is returned by getNextStart");\r
848         } else {\r
849             d2 = a1.getPreviousStart(d1.getTime(), -3*HOUR, 0, true);\r
850             if (d2 == null || !d1.equals(d2)) {\r
851                 errln("FAIL: Bad Date is returned by getPreviousStart");\r
852             }\r
853         }\r
854         d1 = a3.getNextStart(time2, -3*HOUR, 0, false);\r
855         if (d1 != null) {\r
856             errln("FAIL: getNextStart must return null when no start time is available after the base time");\r
857         }\r
858         d1 = a3.getFinalStart(-3*HOUR, 0);\r
859         d2 = a3.getPreviousStart(time2, -3*HOUR, 0, false);\r
860         if (d1 == null || d2 == null || !d1.equals(d2)) {\r
861             errln("FAIL: getPreviousStart does not match with getFinalStart after the end year");\r
862         }\r
863 \r
864         // AnnualTimeZone#isEquavalentTo\r
865         if (!a1.isEquivalentTo(a2)) {\r
866             errln("FAIL: AnnualTimeZoneRule a1 is equivalent to a2, but returned false");\r
867         }\r
868         if (a1.isEquivalentTo(a3)) {\r
869             errln("FAIL: AnnualTimeZoneRule a1 is not equivalent to a3, but returned true");\r
870         }\r
871         if (!a1.isEquivalentTo(a1)) {\r
872             errln("FAIL: AnnualTimeZoneRule a1 is equivalent to itself, but returned false");\r
873         }\r
874         if (a1.isEquivalentTo(t1)) {\r
875             errln("FAIL: AnnualTimeZoneRule is not equivalent to TimeArrayTimeZoneRule, but returned true");\r
876         }\r
877 \r
878         // AnnualTimeZone#isTransitionRule\r
879         if (!a1.isTransitionRule()) {\r
880             errln("FAIL: An AnnualTimeZoneRule is a transition rule, but returned false");\r
881         }\r
882 \r
883         // AnnualTimeZone#toString\r
884         String str = a1.toString();\r
885         if (str == null || str.length() == 0) {\r
886             errln("FAIL: AnnualTimeZoneRule#toString for a1 returns null or empty string");\r
887         } else {\r
888             logln("AnnualTimeZoneRule a1 : " + str);\r
889         }\r
890         str = a3.toString();\r
891         if (str == null || str.length() == 0) {\r
892             errln("FAIL: AnnualTimeZoneRule#toString for a3 returns null or empty string");\r
893         } else {\r
894             logln("AnnualTimeZoneRule a3 : " + str);\r
895         }\r
896 \r
897         // InitialTimeZoneRule#isEquivalentRule\r
898         if (!i1.isEquivalentTo(i2)) {\r
899             errln("FAIL: InitialTimeZoneRule i1 is equivalent to i2, but returned false");\r
900         }\r
901         if (i1.isEquivalentTo(i3)) {\r
902             errln("FAIL: InitialTimeZoneRule i1 is not equivalent to i3, but returned true");\r
903         }\r
904         if (i1.isEquivalentTo(a1)) {\r
905             errln("FAIL: An InitialTimeZoneRule is not equivalent to an AnnualTimeZoneRule, but returned true");\r
906         }\r
907 \r
908         // InitialTimeZoneRule#getFirstStart/getFinalStart/getNextStart/getPreviousStart\r
909         d1 = i1.getFirstStart(0, 0);\r
910         if (d1 != null) {\r
911             errln("FAIL: Non-null Date is returned by InitialTimeZone#getFirstStart");\r
912         }\r
913         d1 = i1.getFinalStart(0, 0);\r
914         if (d1 != null) {\r
915             errln("FAIL: Non-null Date is returned by InitialTimeZone#getFinalStart");\r
916         }\r
917         d1 = i1.getNextStart(time1, 0, 0, false);\r
918         if (d1 != null) {\r
919             errln("FAIL: Non-null Date is returned by InitialTimeZone#getNextStart");\r
920         }\r
921         d1 = i1.getPreviousStart(time1, 0, 0, false);\r
922         if (d1 != null) {\r
923             errln("FAIL: Non-null Date is returned by InitialTimeZone#getPreviousStart");\r
924         }\r
925 \r
926         // InitialTimeZoneRule#isTransitionRule\r
927         if (i1.isTransitionRule()) {\r
928             errln("FAIL: An InitialTimeZoneRule is not a transition rule, but returned true");\r
929         }\r
930 \r
931         // InitialTimeZoneRule#toString\r
932         str = i1.toString();\r
933         if (str == null || str.length() == 0) {\r
934             errln("FAIL: InitialTimeZoneRule#toString returns null or empty string");\r
935         } else {\r
936             logln("InitialTimeZoneRule i1 : " + str);\r
937         }\r
938         \r
939         \r
940         // TimeArrayTimeZoneRule#getStartTimes\r
941         long[] times = t1.getStartTimes();\r
942         if (times == null || times.length == 0 || times[0] != 0) {\r
943             errln("FAIL: Bad start times are returned by TimeArrayTimeZoneRule#getStartTimes");\r
944         }\r
945 \r
946         // TimeArrayTimeZoneRule#getTimeType\r
947         if (t1.getTimeType() != DateTimeRule.UTC_TIME) {\r
948             errln("FAIL: TimeArrayTimeZoneRule t1 uses UTC_TIME, but different type is returned");\r
949         }\r
950         if (t4.getTimeType() != DateTimeRule.STANDARD_TIME) {\r
951             errln("FAIL: TimeArrayTimeZoneRule t4 uses STANDARD_TIME, but different type is returned");\r
952         }\r
953         if (t5.getTimeType() != DateTimeRule.WALL_TIME) {\r
954             errln("FAIL: TimeArrayTimeZoneRule t5 uses WALL_TIME, but different type is returned");\r
955         }\r
956 \r
957         // TimeArrayTimeZoneRule#getFirstStart/getFinalStart\r
958         d1 = t1.getFirstStart(0, 0);\r
959         if (d1 == null || d1.getTime() != trtimes1[0]) {\r
960             errln("FAIL: Bad first start time returned from TimeArrayTimeZoneRule t1");\r
961         }\r
962         d1 = t1.getFinalStart(0, 0);\r
963         if (d1 == null || d1.getTime() != trtimes1[0]) {\r
964             errln("FAIL: Bad final start time returned from TimeArrayTimeZoneRule t1");\r
965         }\r
966         d1 = t4.getFirstStart(-4*HOUR, 1*HOUR);\r
967         if (d1 == null || (d1.getTime() != trtimes1[0] + 4*HOUR)) {\r
968             errln("FAIL: Bad first start time returned from TimeArrayTimeZoneRule t4");\r
969         }\r
970         d1 = t5.getFirstStart(-4*HOUR, 1*HOUR);\r
971         if (d1 == null || (d1.getTime() != trtimes1[0] + 3*HOUR)) {\r
972             errln("FAIL: Bad first start time returned from TimeArrayTimeZoneRule t5");\r
973         }\r
974 \r
975         // TimeArrayTimeZoneRule#getNextStart/getPreviousStart\r
976         d1 = t3.getNextStart(time1, -3*HOUR, 1*HOUR, false);\r
977         if (d1 != null) {\r
978             errln("FAIL: Non-null Date is returned by getNextStart after the final transition for t3");\r
979         }\r
980         d1 = t3.getPreviousStart(time1, -3*HOUR, 1*HOUR, false);\r
981         if (d1 == null || d1.getTime() != trtimes2[1]) {\r
982             errln("FAIL: Bad start time returned by getPreviousStart for t3");\r
983         } else {\r
984             d2 = t3.getPreviousStart(d1.getTime(), -3*HOUR, 1*HOUR, false);\r
985             if (d2 == null || d2.getTime() != trtimes2[0]) {\r
986                 errln("FAIL: Bad start time returned by getPreviousStart for t3");\r
987             }\r
988         }\r
989         d1 = t3.getPreviousStart(time3, -3*HOUR, 1*HOUR, false); //time3 - year 1950, no result expected\r
990         if (d1 != null) {\r
991             errln("FAIL: Non-null Date is returned by getPrevoousStart for t3");\r
992         }\r
993 \r
994         // TimeArrayTimeZoneRule#isEquivalentTo\r
995         if (!t1.isEquivalentTo(t2)) {\r
996             errln("FAIL: TimeArrayTimeZoneRule t1 is equivalent to t2, but returned false");\r
997         }\r
998         if (t1.isEquivalentTo(t3)) {\r
999             errln("FAIL: TimeArrayTimeZoneRule t1 is not equivalent to t3, but returned true");\r
1000         }\r
1001         if (t1.isEquivalentTo(t4)) {\r
1002             errln("FAIL: TimeArrayTimeZoneRule t1 is not equivalent to t4, but returned true");\r
1003         }\r
1004         if (t1.isEquivalentTo(a1)) {\r
1005             errln("FAIL: TimeArrayTimeZoneRule is not equivalent to AnnualTimeZoneRule, but returned true");\r
1006         }\r
1007 \r
1008         // TimeArrayTimeZoneRule#isTransitionRule\r
1009         if (!t1.isTransitionRule()) {\r
1010             errln("FAIL: A TimeArrayTimeZoneRule is a transition rule, but returned false");\r
1011         }\r
1012 \r
1013         // TimeArrayTimeZoneRule#toString\r
1014         str = t3.toString();\r
1015         if (str == null || str.length() == 0) {\r
1016             errln("FAIL: TimeArrayTimeZoneRule#toString returns null or empty string");\r
1017         } else {\r
1018             logln("TimeArrayTimeZoneRule t3 : " + str);\r
1019         }\r
1020 \r
1021         // DateTimeRule#toString\r
1022         str = dtr1.toString();\r
1023         if (str == null || str.length() == 0) {\r
1024             errln("FAIL: DateTimeRule#toString for dtr1 returns null or empty string");\r
1025         } else {\r
1026             logln("DateTimeRule dtr1 : " + str);\r
1027         }\r
1028         str = dtr2.toString();\r
1029         if (str == null || str.length() == 0) {\r
1030             errln("FAIL: DateTimeRule#toString for dtr2 returns null or empty string");\r
1031         } else {\r
1032             logln("DateTimeRule dtr1 : " + str);\r
1033         }\r
1034         str = dtr3.toString();\r
1035         if (str == null || str.length() == 0) {\r
1036             errln("FAIL: DateTimeRule#toString for dtr3 returns null or empty string");\r
1037         } else {\r
1038             logln("DateTimeRule dtr1 : " + str);\r
1039         }\r
1040         str = dtr4.toString();\r
1041         if (str == null || str.length() == 0) {\r
1042             errln("FAIL: DateTimeRule#toString for dtr4 returns null or empty string");\r
1043         } else {\r
1044             logln("DateTimeRule dtr1 : " + str);\r
1045         }\r
1046     }\r
1047 \r
1048     /*\r
1049      * API coverage test for BasicTimeZone APIs in SimpleTimeZone\r
1050      */\r
1051     public void TestSimpleTimeZoneCoverage() {\r
1052 \r
1053         long time1 = getUTCMillis(1990, Calendar.JUNE, 1);\r
1054         long time2 = getUTCMillis(2000, Calendar.JUNE, 1);\r
1055 \r
1056         TimeZoneTransition tzt1, tzt2;\r
1057 \r
1058         // BasicTimeZone API implementation in SimpleTimeZone\r
1059         SimpleTimeZone stz1 = new SimpleTimeZone(-5*HOUR, "GMT-5");\r
1060 \r
1061         tzt1 = stz1.getNextTransition(time1, false);\r
1062         if (tzt1 != null) {\r
1063             errln("FAIL: No transition must be returned by getNextTranstion for SimpleTimeZone with no DST rule");\r
1064         }\r
1065         tzt1 = stz1.getPreviousTransition(time1, false);\r
1066         if (tzt1 != null) {\r
1067             errln("FAIL: No transition must be returned by getPreviousTransition  for SimpleTimeZone with no DST rule");\r
1068         }\r
1069         TimeZoneRule[] tzrules = stz1.getTimeZoneRules();\r
1070         if (tzrules.length != 1 || !(tzrules[0] instanceof InitialTimeZoneRule)) {\r
1071             errln("FAIL: Invalid results returned by SimpleTimeZone#getTimeZoneRules");\r
1072         }\r
1073 \r
1074         // Set DST rule\r
1075         stz1.setStartRule(Calendar.MARCH, 11, 2*HOUR); // March 11\r
1076         stz1.setEndRule(Calendar.NOVEMBER, 1, Calendar.SUNDAY, 2*HOUR); // First Sunday in November\r
1077         tzt1 = stz1.getNextTransition(time1, false);\r
1078         if (tzt1 == null) {\r
1079             errln("FAIL: Non-null transition must be returned by getNextTranstion for SimpleTimeZone with a DST rule");\r
1080         } else {\r
1081             String str = tzt1.toString();\r
1082             if (str == null || str.length() == 0) {\r
1083                 errln("FAIL: TimeZoneTransition#toString returns null or empty string");\r
1084             } else {\r
1085                 logln(str);\r
1086             }\r
1087         }\r
1088         tzt1 = stz1.getPreviousTransition(time1, false);\r
1089         if (tzt1 == null) {\r
1090             errln("FAIL: Non-null transition must be returned by getPreviousTransition  for SimpleTimeZone with a DST rule");\r
1091         }\r
1092         tzrules = stz1.getTimeZoneRules();\r
1093         if (tzrules.length != 3 || !(tzrules[0] instanceof InitialTimeZoneRule)\r
1094                 || !(tzrules[1] instanceof AnnualTimeZoneRule)\r
1095                 || !(tzrules[2] instanceof AnnualTimeZoneRule)) {\r
1096             errln("FAIL: Invalid results returned by SimpleTimeZone#getTimeZoneRules for a SimpleTimeZone with DST");\r
1097         }\r
1098         // Set DST start year\r
1099         stz1.setStartYear(2007);\r
1100         tzt1 = stz1.getPreviousTransition(time1, false);\r
1101         if (tzt1 != null) {\r
1102             errln("FAIL: No transition must be returned before 1990");\r
1103         }\r
1104         tzt1 = stz1.getNextTransition(time1, false); // transition after 1990-06-01\r
1105         tzt2 = stz1.getNextTransition(time2, false); // transition after 2000-06-01\r
1106         if (tzt1 == null || tzt2 == null || !tzt1.equals(tzt2)) {\r
1107             errln("FAIL: Bad transition returned by SimpleTimeZone#getNextTransition");\r
1108         }\r
1109     }\r
1110     \r
1111     /*\r
1112      * API coverage test for VTimeZone\r
1113      */\r
1114     public void TestVTimeZoneCoverage() {\r
1115         final String TZID = "Europe/Moscow";\r
1116         BasicTimeZone otz = (BasicTimeZone)TimeZone.getTimeZone(TZID, TimeZone.TIMEZONE_ICU);\r
1117         VTimeZone vtz = VTimeZone.create(TZID);\r
1118 \r
1119         // getOffset(era, year, month, day, dayOfWeek, milliseconds)\r
1120         int offset1 = otz.getOffset(GregorianCalendar.AD, 2007, Calendar.JULY, 1, Calendar.SUNDAY, 0);\r
1121         int offset2 = vtz.getOffset(GregorianCalendar.AD, 2007, Calendar.JULY, 1, Calendar.SUNDAY, 0);\r
1122         if (offset1 != offset2) {\r
1123             errln("FAIL: getOffset(int,int,int,int,int,int) returned different results in VTimeZone and OlsonTimeZone");\r
1124         }\r
1125 \r
1126         // getOffset(date, local, offsets)\r
1127         int[] offsets1 = new int[2];\r
1128         int[] offsets2 = new int[2];\r
1129         long t = System.currentTimeMillis();\r
1130         otz.getOffset(t, false, offsets1);\r
1131         vtz.getOffset(t, false, offsets2);\r
1132         if (offsets1[0] != offsets2[0] || offsets1[1] != offsets2[1]) {\r
1133             errln("FAIL: getOffset(long,boolean,int[]) returned different results in VTimeZone and OlsonTimeZone");\r
1134         }\r
1135 \r
1136         // getRawOffset\r
1137         if (otz.getRawOffset() != vtz.getRawOffset()) {\r
1138             errln("FAIL: getRawOffset returned different results in VTimeZone and OlsonTimeZone");\r
1139         }\r
1140 \r
1141         // inDaylightTime\r
1142         Date d = new Date();\r
1143         if (otz.inDaylightTime(d) != vtz.inDaylightTime(d)) {\r
1144             errln("FAIL: inDaylightTime returned different results in VTimeZone and OlsonTimeZone");\r
1145         }\r
1146 \r
1147         // useDaylightTime\r
1148         if (otz.useDaylightTime() != vtz.useDaylightTime()) {\r
1149             errln("FAIL: useDaylightTime returned different results in VTimeZone and OlsonTimeZone");\r
1150         }\r
1151 \r
1152         // setRawOffset\r
1153         final int RAW = -10*HOUR;\r
1154         VTimeZone tmpvtz = (VTimeZone)vtz.clone();\r
1155         tmpvtz.setRawOffset(RAW);\r
1156         if (tmpvtz.getRawOffset() != RAW) {\r
1157             logln("setRawOffset is implemented");\r
1158         }\r
1159 \r
1160         // hasSameRules\r
1161         boolean bSame = otz.hasSameRules(vtz);\r
1162         logln("OlsonTimeZone#hasSameRules(VTimeZone) should return false always for now - actual: " + bSame);\r
1163 \r
1164         // getTZURL/setTZURL\r
1165         final String TZURL = "http://icu-project.org/timezone";\r
1166         String tzurl = vtz.getTZURL();\r
1167         if (tzurl != null) {\r
1168             errln("FAIL: getTZURL returned non-null value");\r
1169         }\r
1170         vtz.setTZURL(TZURL);\r
1171         tzurl = vtz.getTZURL();\r
1172         if (!TZURL.equals(tzurl)) {\r
1173             errln("FAIL: URL returned by getTZURL does not match the one set by setTZURL");\r
1174         }\r
1175 \r
1176         // getLastModified/setLastModified\r
1177         Date lastmod = vtz.getLastModified();\r
1178         if (lastmod != null) {\r
1179             errln("FAIL: getLastModified returned non-null value");\r
1180         }\r
1181         Date newdate = new Date();\r
1182         vtz.setLastModified(newdate);\r
1183         lastmod = vtz.getLastModified();\r
1184         if (!newdate.equals(lastmod)) {\r
1185             errln("FAIL: Date returned by getLastModified does not match the one set by setLastModified");\r
1186         }\r
1187 \r
1188         // getNextTransition/getPreviousTransition\r
1189         long base = getUTCMillis(2007, Calendar.JULY, 1);\r
1190         TimeZoneTransition tzt1 = otz.getNextTransition(base, true);\r
1191         TimeZoneTransition tzt2 = vtz.getNextTransition(base, true);\r
1192         if (tzt1.equals(tzt2)) {\r
1193             errln("FAIL: getNextTransition returned different results in VTimeZone and OlsonTimeZone");\r
1194         }\r
1195         tzt1 = otz.getPreviousTransition(base, false);\r
1196         tzt2 = vtz.getPreviousTransition(base, false);\r
1197         if (tzt1.equals(tzt2)) {\r
1198             errln("FAIL: getPreviousTransition returned different results in VTimeZone and OlsonTimeZone");\r
1199         }\r
1200 \r
1201         // hasEquivalentTransitions\r
1202         long time1 = getUTCMillis(1950, Calendar.JANUARY, 1);\r
1203         long time2 = getUTCMillis(2020, Calendar.JANUARY, 1);\r
1204         if (!vtz.hasEquivalentTransitions(otz, time1, time2)) {\r
1205             errln("FAIL: hasEquivalentTransitons returned false for the same time zone");\r
1206         }\r
1207 \r
1208         // getTimeZoneRules\r
1209         TimeZoneRule[] rulesetAll = vtz.getTimeZoneRules();\r
1210         TimeZoneRule[] ruleset1 = vtz.getTimeZoneRules(time1);\r
1211         TimeZoneRule[] ruleset2 = vtz.getTimeZoneRules(time2);\r
1212         if (rulesetAll.length < ruleset1.length || ruleset1.length < ruleset2.length) {\r
1213             errln("FAIL: Number of rules returned by getRules is invalid");\r
1214         }\r
1215     }\r
1216 \r
1217     public void TestVTimeZoneParse() {\r
1218         // Trying to create VTimeZone from empty data\r
1219         StringReader r = new StringReader("");\r
1220         VTimeZone empty = VTimeZone.create(r);\r
1221         if (empty != null) {\r
1222             errln("FAIL: Non-null VTimeZone is returned for empty VTIMEZONE data");\r
1223         }\r
1224 \r
1225         // Create VTimeZone for Asia/Tokyo\r
1226         String asiaTokyo =\r
1227                 "BEGIN:VTIMEZONE\r\n" +\r
1228                 "TZID:Asia\r\n" +\r
1229                 "\t/Tokyo\r\n" +\r
1230                 "BEGIN:STANDARD\r\n" +\r
1231                 "TZOFFSETFROM:+0900\r\n" +\r
1232                 "TZOFFSETTO:+0900\r\n" +\r
1233                 "TZNAME:JST\r\n" +\r
1234                 "DTSTART:19700101\r\n" +\r
1235                 " T000000\r\n" +\r
1236                 "END:STANDARD\r\n" +\r
1237                 "END:VTIMEZONE";\r
1238         r = new StringReader(asiaTokyo);\r
1239         VTimeZone tokyo = VTimeZone.create(r);\r
1240         if (tokyo == null) {\r
1241             errln("FAIL: Failed to create a VTimeZone tokyo");\r
1242         } else {\r
1243             // Make sure offsets are correct\r
1244             int[] offsets = new int[2];\r
1245             tokyo.getOffset(System.currentTimeMillis(), false, offsets);\r
1246             if (offsets[0] != 9*HOUR || offsets[1] != 0) {\r
1247                 errln("FAIL: Bad offsets returned by a VTimeZone created for Tokyo");\r
1248             }\r
1249         }        \r
1250 \r
1251         // Create VTimeZone from VTIMEZONE data\r
1252         String fooData = \r
1253             "BEGIN:VCALENDAR\r\n" +\r
1254             "BEGIN:VTIMEZONE\r\n" +\r
1255             "TZID:FOO\r\n" +\r
1256             "BEGIN:STANDARD\r\n" +\r
1257             "TZOFFSETFROM:-0700\r\n" +\r
1258             "TZOFFSETTO:-0800\r\n" +\r
1259             "TZNAME:FST\r\n" +\r
1260             "DTSTART:20071010T010000\r\n" +\r
1261             "RRULE:FREQ=YEARLY;BYDAY=WE;BYMONTHDAY=10,11,12,13,14,15,16;BYMONTH=10\r\n" +\r
1262             "END:STANDARD\r\n" +\r
1263             "BEGIN:DAYLIGHT\r\n" +\r
1264             "TZOFFSETFROM:-0800\r\n" +\r
1265             "TZOFFSETTO:-0700\r\n" +\r
1266             "TZNAME:FDT\r\n" +\r
1267             "DTSTART:20070415T010000\r\n" +\r
1268             "RRULE:FREQ=YEARLY;BYMONTHDAY=15;BYMONTH=4\r\n" +\r
1269             "END:DAYLIGHT\r\n" +\r
1270             "END:VTIMEZONE\r\n" +\r
1271             "END:VCALENDAR";\r
1272 \r
1273         r = new StringReader(fooData);\r
1274         VTimeZone foo = VTimeZone.create(r);\r
1275         if (foo == null) {\r
1276             errln("FAIL: Failed to create a VTimeZone foo");\r
1277         } else {\r
1278             // Write VTIMEZONE data\r
1279             StringWriter w = new StringWriter();\r
1280             try {\r
1281                 foo.write(w, getUTCMillis(2005, Calendar.JANUARY, 1));\r
1282             } catch (IOException ioe) {\r
1283                 errln("FAIL: IOException is thrown while writing VTIMEZONE data for foo");\r
1284             }\r
1285             logln(w.toString());\r
1286         }\r
1287     }\r
1288 \r
1289     public void TestT6216() {\r
1290         // Test case in #6216\r
1291         String tokyoTZ =\r
1292             "BEGIN:VCALENDAR\r\n" +\r
1293             "VERSION:2.0\r\n" +\r
1294             "PRODID:-//PYVOBJECT//NONSGML Version 1//EN\r\n" +\r
1295             "BEGIN:VTIMEZONE\r\n" +\r
1296             "TZID:Asia/Tokyo\r\n" +\r
1297             "BEGIN:STANDARD\r\n" +\r
1298             "DTSTART:20000101T000000\r\n" +\r
1299             "RRULE:FREQ=YEARLY;BYMONTH=1\r\n" +\r
1300             "TZNAME:Asia/Tokyo\r\n" +\r
1301             "TZOFFSETFROM:+0900\r\n" +\r
1302             "TZOFFSETTO:+0900\r\n" +\r
1303             "END:STANDARD\r\n" +\r
1304             "END:VTIMEZONE\r\n" +\r
1305             "END:VCALENDAR";\r
1306 \r
1307         // Single final rule, overlapping with another\r
1308         String finalOverlap =\r
1309             "BEGIN:VCALENDAR\r\n" +\r
1310             "BEGIN:VTIMEZONE\r\n" +\r
1311             "TZID:FinalOverlap\r\n" +\r
1312             "BEGIN:STANDARD\r\n" +\r
1313             "TZOFFSETFROM:-0200\r\n" +\r
1314             "TZOFFSETTO:-0300\r\n" +\r
1315             "TZNAME:STD\r\n" +\r
1316             "DTSTART:20001029T020000\r\n" +\r
1317             "RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10\r\n" +\r
1318             "END:STANDARD\r\n" +\r
1319             "BEGIN:DAYLIGHT\r\n" +\r
1320             "TZOFFSETFROM:-0300\r\n" +\r
1321             "TZOFFSETTO:-0200\r\n" +\r
1322             "TZNAME:DST\r\n" +\r
1323             "DTSTART:19990404T020000\r\n" +\r
1324             "RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4;UNTIL=20050403T040000Z\r\n" +\r
1325             "END:DAYLIGHT\r\n" +\r
1326             "END:VTIMEZONE\r\n" +\r
1327             "END:VCALENDAR";\r
1328 \r
1329         // Single final rule, no overlapping with another\r
1330         String finalNonOverlap = \r
1331             "BEGIN:VCALENDAR\r\n" +\r
1332             "BEGIN:VTIMEZONE\r\n" +\r
1333             "TZID:FinalNonOverlap\r\n" +\r
1334             "BEGIN:STANDARD\r\n" +\r
1335             "TZOFFSETFROM:-0200\r\n" +\r
1336             "TZOFFSETTO:-0300\r\n" +\r
1337             "TZNAME:STD\r\n" +\r
1338             "DTSTART:20001029T020000\r\n" +\r
1339             "RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10;UNTIL=20041031T040000Z\r\n" +\r
1340             "END:STANDARD\r\n" +\r
1341             "BEGIN:DAYLIGHT\r\n" +\r
1342             "TZOFFSETFROM:-0300\r\n" +\r
1343             "TZOFFSETTO:-0200\r\n" +\r
1344             "TZNAME:DST\r\n" +\r
1345             "DTSTART:19990404T020000\r\n" +\r
1346             "RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4;UNTIL=20050403T040000Z\r\n" +\r
1347             "END:DAYLIGHT\r\n" +\r
1348             "BEGIN:STANDARD\r\n" +\r
1349             "TZOFFSETFROM:-0200\r\n" +\r
1350             "TZOFFSETTO:-0300\r\n" +\r
1351             "TZNAME:STDFINAL\r\n" +\r
1352             "DTSTART:20071028T020000\r\n" +\r
1353             "RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10\r\n" +\r
1354             "END:STANDARD\r\n" +\r
1355             "END:VTIMEZONE\r\n" +\r
1356             "END:VCALENDAR";\r
1357 \r
1358         int[][] TestDates = {\r
1359                 {1995, Calendar.JANUARY, 1},\r
1360                 {1995, Calendar.JULY, 1},\r
1361                 {2000, Calendar.JANUARY, 1},\r
1362                 {2000, Calendar.JULY, 1},\r
1363                 {2005, Calendar.JANUARY, 1},\r
1364                 {2005, Calendar.JULY, 1},\r
1365                 {2010, Calendar.JANUARY, 1},\r
1366                 {2010, Calendar.JULY, 1},\r
1367         };\r
1368 \r
1369         String[] TestZones = {\r
1370             tokyoTZ,\r
1371             finalOverlap,\r
1372             finalNonOverlap,\r
1373         };\r
1374 \r
1375         int[][] Expected = {\r
1376           //  JAN90      JUL90      JAN00      JUL00      JAN05      JUL05      JAN10      JUL10\r
1377             { 32400000,  32400000,  32400000,  32400000,  32400000,  32400000,  32400000,  32400000},\r
1378             {-10800000, -10800000,  -7200000,  -7200000, -10800000,  -7200000, -10800000, -10800000},\r
1379             {-10800000, -10800000,  -7200000,  -7200000, -10800000,  -7200000, -10800000, -10800000},\r
1380         };\r
1381 \r
1382         // Get test times\r
1383         long[] times = new long[TestDates.length];\r
1384         Calendar cal = new GregorianCalendar(TimeZone.getTimeZone("Etc/GMT"));\r
1385         for (int i = 0; i < TestDates.length; i++) {\r
1386             cal.clear();\r
1387             cal.set(TestDates[i][0], TestDates[i][1], TestDates[i][2]);\r
1388             times[i] = cal.getTimeInMillis();\r
1389         }\r
1390 \r
1391         for (int i = 0; i < TestZones.length; i++) {\r
1392             try {\r
1393                 VTimeZone vtz = VTimeZone.create(new StringReader(TestZones[i]));\r
1394                 for (int j = 0; j < times.length; j++) {\r
1395                     int offset = vtz.getOffset(times[j]);\r
1396                     if (offset != Expected[i][j]) {\r
1397                         errln("FAIL: Invalid offset at time(" + times[j] + "):" + offset + " Expected:" + Expected[i][j]);\r
1398                     }\r
1399                 }\r
1400             } catch (Exception e) {\r
1401                 errln("FAIL: Failed to calculate the offset for VTIMEZONE data " + i);\r
1402             }\r
1403         }\r
1404     }\r
1405 \r
1406     public void TestT6669() {\r
1407         // getNext/PreviousTransition implementation in SimpleTimeZone\r
1408         // used to use a bad condition for detecting if DST is enabled or not.\r
1409 \r
1410         SimpleTimeZone stz = new SimpleTimeZone(0, "CustomID",\r
1411                 Calendar.JANUARY, 1, Calendar.SUNDAY, 0,\r
1412                 Calendar.JULY, 1, Calendar.SUNDAY, 0);\r
1413 \r
1414         long t = 1230681600000L; //2008-12-31T00:00:00\r
1415         long expectedNext = 1231027200000L; //2009-01-04T00:00:00\r
1416         long expectedPrev = 1215298800000L; //2008-07-06T00:00:00\r
1417 \r
1418         TimeZoneTransition tzt = stz.getNextTransition(t, false);\r
1419         if (tzt == null) {\r
1420             errln("FAIL: No transition returned by getNextTransition.");\r
1421         } else if (tzt.getTime() != expectedNext){\r
1422             errln("FAIL: Wrong transition time returned by getNextTransition - "\r
1423                     + tzt.getTime() + " Expected: " + expectedNext);\r
1424         }\r
1425 \r
1426         tzt = stz.getPreviousTransition(t, true);\r
1427         if (tzt == null) {\r
1428             errln("FAIL: No transition returned by getPreviousTransition.");\r
1429         } else if (tzt.getTime() != expectedPrev){\r
1430             errln("FAIL: Wrong transition time returned by getPreviousTransition - "\r
1431                     + tzt.getTime() + " Expected: " + expectedPrev);\r
1432         }\r
1433     }\r
1434 \r
1435     // Internal utility methods -----------------------------------------\r
1436 \r
1437     /*\r
1438      * Check if a time shift really happens on each transition returned by getNextTransition or\r
1439      * getPreviousTransition in the specified time range\r
1440      */\r
1441     private void verifyTransitions(TimeZone tz, long start, long end) {\r
1442         BasicTimeZone icutz = (BasicTimeZone)tz;\r
1443         long time;\r
1444         int[] before = new int[2];\r
1445         int[] after = new int[2];\r
1446         TimeZoneTransition tzt0;\r
1447 \r
1448         // Ascending\r
1449         tzt0 = null;\r
1450         time = start;\r
1451         while(true) {\r
1452             TimeZoneTransition tzt = icutz.getNextTransition(time, false);\r
1453 \r
1454             if (tzt == null) {\r
1455                 break;\r
1456             }\r
1457             time = tzt.getTime();\r
1458             if (time >= end) {\r
1459                 break;\r
1460             }\r
1461             icutz.getOffset(time, false, after);\r
1462             icutz.getOffset(time - 1, false, before);\r
1463 \r
1464             if (after[0] == before[0] && after[1] == before[1]) {\r
1465                 errln("FAIL: False transition returned by getNextTransition for " + icutz.getID() + " at " + time);\r
1466             }\r
1467             if (tzt0 != null &&\r
1468                     (tzt0.getTo().getRawOffset() != tzt.getFrom().getRawOffset()\r
1469                     || tzt0.getTo().getDSTSavings() != tzt.getFrom().getDSTSavings())) {\r
1470                 errln("FAIL: TO rule of the previous transition does not match FROM rule of this transtion at "\r
1471                         + time + " for " + icutz.getID());                \r
1472             }\r
1473             tzt0 = tzt;\r
1474         }\r
1475 \r
1476         // Descending\r
1477         tzt0 = null;\r
1478         time = end;\r
1479         while(true) {\r
1480             TimeZoneTransition tzt = icutz.getPreviousTransition(time, false);\r
1481             if (tzt == null) {\r
1482                 break;\r
1483             }\r
1484             time = tzt.getTime();\r
1485             if (time <= start) {\r
1486                 break;\r
1487             }\r
1488             icutz.getOffset(time, false, after);\r
1489             icutz.getOffset(time - 1, false, before);\r
1490 \r
1491             if (after[0] == before[0] && after[1] == before[1]) {\r
1492                 errln("FAIL: False transition returned by getPreviousTransition for " + icutz.getID() + " at " + time);\r
1493             }\r
1494 \r
1495             if (tzt0 != null &&\r
1496                     (tzt0.getFrom().getRawOffset() != tzt.getTo().getRawOffset()\r
1497                     || tzt0.getFrom().getDSTSavings() != tzt.getTo().getDSTSavings())) {\r
1498                 errln("FAIL: TO rule of the next transition does not match FROM rule in this transtion at "\r
1499                         + time + " for " + icutz.getID());                \r
1500             }\r
1501             tzt0 = tzt;\r
1502         }\r
1503     }\r
1504 \r
1505     /*\r
1506      * Compare all time transitions in 2 time zones in the specified time range in ascending order\r
1507      */\r
1508     private void compareTransitionsAscending(TimeZone tz1, TimeZone tz2, long start, long end, boolean inclusive) {\r
1509         BasicTimeZone z1 = (BasicTimeZone)tz1;\r
1510         BasicTimeZone z2 = (BasicTimeZone)tz2;\r
1511         String zid1 = tz1.getID();\r
1512         String zid2 = tz2.getID();\r
1513 \r
1514         long time = start;\r
1515         while(true) {\r
1516             TimeZoneTransition tzt1 = z1.getNextTransition(time, inclusive);\r
1517             TimeZoneTransition tzt2 = z2.getNextTransition(time, inclusive);\r
1518             boolean inRange1 = false;\r
1519             boolean inRange2 = false;\r
1520             if (tzt1 != null) {\r
1521                 if (tzt1.getTime() < end || (inclusive && tzt1.getTime() == end)) {\r
1522                     inRange1 = true;\r
1523                 }\r
1524             }\r
1525             if (tzt2 != null) {\r
1526                 if (tzt2.getTime() < end || (inclusive && tzt2.getTime() == end)) {\r
1527                     inRange2 = true;\r
1528                 }\r
1529             }\r
1530             if (!inRange1 && !inRange2) {\r
1531                 // No more transition in the range\r
1532                 break;\r
1533             }\r
1534             if (!inRange1) {\r
1535                 errln("FAIL: " + zid1 + " does not have any transitions after " + time + " before " + end);\r
1536                 break;\r
1537             }\r
1538             if (!inRange2) {\r
1539                 errln("FAIL: " + zid2 + " does not have any transitions after " + time + " before " + end);\r
1540                 break;\r
1541             }\r
1542             if (tzt1.getTime() != tzt2.getTime()) {\r
1543                 errln("FAIL: First transition after " + time + " "\r
1544                         + zid1 + "[" + tzt1.getTime() + "] "\r
1545                         + zid2 + "[" + tzt2.getTime() + "]");\r
1546                 break;\r
1547             }\r
1548             time = tzt1.getTime();\r
1549             if (inclusive) {\r
1550                 time++;\r
1551             }\r
1552         }\r
1553     }\r
1554 \r
1555     /*\r
1556      * Compare all time transitions in 2 time zones in the specified time range in descending order\r
1557      */\r
1558     private void compareTransitionsDescending(TimeZone tz1, TimeZone tz2, long start, long end, boolean inclusive) {\r
1559         BasicTimeZone z1 = (BasicTimeZone)tz1;\r
1560         BasicTimeZone z2 = (BasicTimeZone)tz2;\r
1561         String zid1 = tz1.getID();\r
1562         String zid2 = tz2.getID();\r
1563         long time = end;\r
1564         while(true) {\r
1565             TimeZoneTransition tzt1 = z1.getPreviousTransition(time, inclusive);\r
1566             TimeZoneTransition tzt2 = z2.getPreviousTransition(time, inclusive);\r
1567             boolean inRange1 = false;\r
1568             boolean inRange2 = false;\r
1569             if (tzt1 != null) {\r
1570                 if (tzt1.getTime() > start || (inclusive && tzt1.getTime() == start)) {\r
1571                     inRange1 = true;\r
1572                 }\r
1573             }\r
1574             if (tzt2 != null) {\r
1575                 if (tzt2.getTime() > start || (inclusive && tzt2.getTime() == start)) {\r
1576                     inRange2 = true;\r
1577                 }\r
1578             }\r
1579             if (!inRange1 && !inRange2) {\r
1580                 // No more transition in the range\r
1581                 break;\r
1582             }\r
1583             if (!inRange1) {\r
1584                 errln("FAIL: " + zid1 + " does not have any transitions before " + time + " after " + start);\r
1585                 break;\r
1586             }\r
1587             if (!inRange2) {\r
1588                 errln("FAIL: " + zid2 + " does not have any transitions before " + time + " after " + start);\r
1589                 break;\r
1590             }\r
1591             if (tzt1.getTime() != tzt2.getTime()) {\r
1592                 errln("FAIL: Last transition before " + time + " "\r
1593                         + zid1 + "[" + tzt1.getTime() + "] "\r
1594                         + zid2 + "[" + tzt2.getTime() + "]");\r
1595                 break;\r
1596             }\r
1597             time = tzt1.getTime();\r
1598             if (inclusive) {\r
1599                 time--;\r
1600             }\r
1601         }\r
1602     }\r
1603 \r
1604     private static final String[] TESTZIDS = {\r
1605         "AGT",\r
1606         "America/New_York",\r
1607         "America/Los_Angeles",\r
1608         "America/Indiana/Indianapolis",\r
1609         "America/Havana",\r
1610         "Europe/Lisbon",\r
1611         "Europe/Paris",\r
1612         "Asia/Tokyo",\r
1613         "Asia/Sakhalin",\r
1614         "Africa/Cairo",\r
1615         "Africa/Windhoek",\r
1616         "Australia/Sydney",\r
1617         "Etc/GMT+8"\r
1618     };\r
1619 \r
1620     private String[] getTestZIDs() {\r
1621         if (getInclusion() > 5) {\r
1622             return TimeZone.getAvailableIDs();\r
1623         }\r
1624         return TESTZIDS;\r
1625     }\r
1626 \r
1627     private static final int[][] TESTYEARS = {\r
1628         {1895, 1905}, // including int32 minimum second\r
1629         {1965, 1975}, // including the epoch\r
1630         {1995, 2015}  // practical year range\r
1631     };\r
1632 \r
1633     private long[] getTestTimeRange(int idx) {\r
1634         int loyear, hiyear;\r
1635         if (idx < TESTYEARS.length) {\r
1636             loyear = TESTYEARS[idx][0];\r
1637             hiyear = TESTYEARS[idx][1];\r
1638         } else if (idx == TESTYEARS.length && getInclusion() > 5) {\r
1639             loyear = 1850;\r
1640             hiyear = 2050;\r
1641         } else {\r
1642             return null;\r
1643         }\r
1644 \r
1645         long[] times = new long[2];\r
1646         times[0] = getUTCMillis(loyear, Calendar.JANUARY, 1);\r
1647         times[1] = getUTCMillis(hiyear + 1, Calendar.JANUARY, 1);\r
1648 \r
1649         return times;\r
1650     }\r
1651 \r
1652     private GregorianCalendar utcCal;\r
1653 \r
1654     private long getUTCMillis(int year, int month, int dayOfMonth) {\r
1655         if (utcCal == null) {\r
1656             utcCal = new GregorianCalendar(TimeZone.getTimeZone("UTC"), ULocale.ROOT);\r
1657         }\r
1658         utcCal.clear();\r
1659         utcCal.set(year, month, dayOfMonth);\r
1660         return utcCal.getTimeInMillis();\r
1661     }\r
1662 }