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