1 /*
  2  * Copyright (c) 1998, 2016, Oracle and/or its affiliates. All rights reserved.
  3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  4  *
  5  * This code is free software; you can redistribute it and/or modify it
  6  * under the terms of the GNU General Public License version 2 only, as
  7  * published by the Free Software Foundation.
  8  *
  9  * This code is distributed in the hope that it will be useful, but WITHOUT
 10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 12  * version 2 for more details (a copy is included in the LICENSE file that
 13  * accompanied this code).
 14  *
 15  * You should have received a copy of the GNU General Public License version
 16  * 2 along with this work; if not, write to the Free Software Foundation,
 17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 18  *
 19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 20  * or visit www.oracle.com if you need additional information or have any
 21  * questions.
 22  */
 23 
 24 /*
 25  * @test
 26  * @bug 4052967 4073209 4073215 4084933 4096952 4109314 4126678 4151406 4151429
 27  * 4154525 4154537 4154542 4154650 4159922 4162593 4173604 4176686 4184229 4208960
 28  * 4966229 6433179 6851214 8007520 8008577
 29  * @library /java/text/testlib
 30  * @run main/othervm -Djava.locale.providers=COMPAT,SPI TimeZoneRegression
 31  */
 32 
 33 import java.util.*;
 34 import java.io.*;
 35 import java.text.*;
 36 
 37 public class TimeZoneRegression extends IntlTest {
 38 
 39     public static void main(String[] args) throws Exception {
 40         new TimeZoneRegression().run(args);
 41     }
 42 
 43     public void Test4073209() {
 44         TimeZone z1 = TimeZone.getTimeZone("PST");
 45         TimeZone z2 = TimeZone.getTimeZone("PST");
 46         if (z1 == z2) {
 47             errln("Fail: TimeZone should return clones");
 48         }
 49     }
 50 
 51     @SuppressWarnings("deprecation")
 52     public void Test4073215() {
 53         SimpleTimeZone z = new SimpleTimeZone(0, "GMT");
 54         if (z.useDaylightTime()) {
 55             errln("Fail: Fix test to start with non-DST zone");
 56         }
 57         z.setStartRule(Calendar.FEBRUARY, 1, Calendar.SUNDAY, 0);
 58         z.setEndRule(Calendar.MARCH, -1, Calendar.SUNDAY, 0);
 59         if (!z.useDaylightTime()) {
 60             errln("Fail: DST not active");
 61         }
 62         if (z.inDaylightTime(new Date(97, Calendar.JANUARY, 31)) ||
 63             !z.inDaylightTime(new Date(97, Calendar.MARCH, 1)) ||
 64             z.inDaylightTime(new Date(97, Calendar.MARCH, 31))) {
 65             errln("Fail: DST not working as expected");
 66         }
 67     }
 68 
 69     /**
 70      * The expected behavior of TimeZone around the boundaries is:
 71      * (Assume transition time of 2:00 AM)
 72      *    day of onset 1:59 AM STD  = display name 1:59 AM ST
 73      *                 2:00 AM STD  = display name 3:00 AM DT
 74      *    day of end   0:59 AM STD  = display name 1:59 AM DT
 75      *                 1:00 AM STD  = display name 1:00 AM ST
 76      */
 77     public void Test4084933() {
 78         // test both SimpleTimeZone and ZoneInfo objects.
 79         // @since 1.4
 80         sub4084933(getPST());
 81         sub4084933(TimeZone.getTimeZone("PST"));
 82     }
 83 
 84     private void sub4084933(TimeZone tz) {
 85         long offset1 = tz.getOffset(1,
 86             1997, Calendar.OCTOBER, 26, Calendar.SUNDAY, (2*60*60*1000));
 87         long offset2 = tz.getOffset(1,
 88             1997, Calendar.OCTOBER, 26, Calendar.SUNDAY, (2*60*60*1000)-1);
 89 
 90         long offset3 = tz.getOffset(1,
 91             1997, Calendar.OCTOBER, 26, Calendar.SUNDAY, (1*60*60*1000));
 92         long offset4 = tz.getOffset(1,
 93             1997, Calendar.OCTOBER, 26, Calendar.SUNDAY, (1*60*60*1000)-1);
 94 
 95         /*
 96          *  The following was added just for consistency.  It shows that going *to* Daylight
 97          *  Savings Time (PDT) does work at 2am.
 98          */
 99 
100         long offset5 = tz.getOffset(1,
101             1997, Calendar.APRIL, 6, Calendar.SUNDAY, (2*60*60*1000));
102         long offset6 = tz.getOffset(1,
103             1997, Calendar.APRIL, 6, Calendar.SUNDAY, (2*60*60*1000)-1);
104 
105         long offset7 = tz.getOffset(1,
106             1997, Calendar.APRIL, 6, Calendar.SUNDAY, (1*60*60*1000));
107         long offset8 = tz.getOffset(1,
108             1997, Calendar.APRIL, 6, Calendar.SUNDAY, (1*60*60*1000)-1);
109 
110         long SToffset = -8 * 60*60*1000L;
111         long DToffset = -7 * 60*60*1000L;
112         if (offset1 != SToffset || offset2 != SToffset ||
113             offset3 != SToffset || offset4 != DToffset ||
114             offset5 != DToffset || offset6 != SToffset ||
115             offset7 != SToffset || offset8 != SToffset)
116             errln("Fail: TimeZone misbehaving"); {
117         }
118     }
119 
120     public void Test4096952() {
121         String[] ZONES = { "GMT", "MET", "IST" };
122         boolean pass = true;
123         try {
124             for (int i=0; i<ZONES.length; ++i) {
125                 TimeZone zone = TimeZone.getTimeZone(ZONES[i]);
126                 if (!zone.getID().equals(ZONES[i]))
127                     errln("Fail: Test broken; zones not instantiating");
128 
129                 ByteArrayOutputStream baos;
130                 ObjectOutputStream ostream =
131                     new ObjectOutputStream(baos = new
132                                            ByteArrayOutputStream());
133                 ostream.writeObject(zone);
134                 ostream.close();
135                 baos.close();
136                 ObjectInputStream istream =
137                     new ObjectInputStream(new
138                                           ByteArrayInputStream(baos.toByteArray()));
139                 TimeZone frankenZone = (TimeZone) istream.readObject();
140                 //logln("Zone:        " + zone);
141                 //logln("FrankenZone: " + frankenZone);
142                 if (!zone.equals(frankenZone)) {
143                     logln("TimeZone " + zone.getID() +
144                           " not equal to serialized/deserialized one");
145                     pass = false;
146                 }
147             }
148             if (!pass) errln("Fail: TimeZone serialization/equality bug");
149         }
150         catch (IOException e) {
151             errln("Fail: " + e);
152             e.printStackTrace();
153         }
154         catch (ClassNotFoundException e) {
155             errln("Fail: " + e);
156             e.printStackTrace();
157         }
158     }
159 
160     public void Test4109314() {
161         Locale locale = Locale.getDefault();
162         if (!TestUtils.usesGregorianCalendar(locale)) {
163             logln("Skipping this test because locale is " + locale);
164             return;
165         }
166 
167         // test both SimpleTimeZone and ZoneInfo objects.
168         // @since 1.4
169         sub4109314(getPST());
170         sub4109314(TimeZone.getTimeZone("PST"));
171     }
172 
173 
174     @SuppressWarnings("deprecation")
175     private void sub4109314(TimeZone PST) {
176         GregorianCalendar testCal = (GregorianCalendar)Calendar.getInstance();
177         Object[] testData = {
178             PST, new Date(98,Calendar.APRIL,4,22,0), new Date(98, Calendar.APRIL, 5,6,0),
179             PST, new Date(98,Calendar.OCTOBER,24,22,0), new Date(98,Calendar.OCTOBER,25,6,0),
180         };
181         boolean pass=true;
182         for (int i=0; i<testData.length; i+=3) {
183             testCal.setTimeZone((TimeZone) testData[i]);
184             long t = ((Date)testData[i+1]).getTime();
185             Date end = (Date) testData[i+2];
186             while (t < end.getTime()) {
187                 testCal.setTime(new Date(t));
188                 if (!checkCalendar314(testCal, (TimeZone) testData[i]))
189                     pass = false;
190                 t += 60*60*1000L;
191             }
192         }
193         if (!pass) errln("Fail: TZ API inconsistent");
194     }
195 
196     boolean checkCalendar314(GregorianCalendar testCal, TimeZone testTZ) {
197         // GregorianCalendar testCal = (GregorianCalendar)aCal.clone();
198 
199         final int ONE_DAY = 24*60*60*1000;
200 
201         int tzOffset, tzRawOffset;
202         Float tzOffsetFloat,tzRawOffsetFloat;
203         // Here is where the user made an error.  They were passing in the value of
204         // the MILLSECOND field; you need to pass in the millis in the day in STANDARD
205         // time.
206         int millis = testCal.get(Calendar.MILLISECOND) +
207             1000 * (testCal.get(Calendar.SECOND) +
208                     60 * (testCal.get(Calendar.MINUTE) +
209                           60 * (testCal.get(Calendar.HOUR_OF_DAY)))) -
210             testCal.get(Calendar.DST_OFFSET);
211 
212         /* Fix up millis to be in range.  ASSUME THAT WE ARE NOT AT THE
213          * BEGINNING OR END OF A MONTH.  We must add this code because
214          * getOffset() has been changed to be more strict about the parameters
215          * it receives -- it turns out that this test was passing in illegal
216          * values. */
217         int date = testCal.get(Calendar.DATE);
218         int dow  = testCal.get(Calendar.DAY_OF_WEEK);
219         while (millis < 0) {
220             millis += ONE_DAY;
221             --date;
222             dow = Calendar.SUNDAY + ((dow - Calendar.SUNDAY + 6) % 7);
223         }
224         while (millis >= ONE_DAY) {
225             millis -= ONE_DAY;
226             ++date;
227             dow = Calendar.SUNDAY + ((dow - Calendar.SUNDAY + 1) % 7);
228         }
229 
230         tzOffset = testTZ.getOffset(testCal.get(Calendar.ERA),
231                                     testCal.get(Calendar.YEAR),
232                                     testCal.get(Calendar.MONTH),
233                                     date,
234                                     dow,
235                                     millis);
236         tzRawOffset = testTZ.getRawOffset();
237         tzOffsetFloat = new Float((float)tzOffset/(float)3600000);
238         tzRawOffsetFloat = new Float((float)tzRawOffset/(float)3600000);
239 
240         Date testDate = testCal.getTime();
241 
242         boolean inDaylightTime = testTZ.inDaylightTime(testDate);
243         SimpleDateFormat sdf = new SimpleDateFormat("MM/dd/yyyy HH:mm");
244         sdf.setCalendar(testCal);
245         String inDaylightTimeString;
246 
247         boolean passed;
248 
249         if (inDaylightTime)
250         {
251             inDaylightTimeString = " DST ";
252             passed = (tzOffset == (tzRawOffset + 3600000));
253         }
254         else
255         {
256             inDaylightTimeString = "     ";
257             passed = (tzOffset == tzRawOffset);
258         }
259 
260         String output = testTZ.getID() + " " + sdf.format(testDate) +
261             " Offset(" + tzOffsetFloat + ")" +
262             " RawOffset(" + tzRawOffsetFloat + ")" +
263             " " + millis/(float)3600000 + " " +
264             inDaylightTimeString;
265 
266         if (passed)
267             output += "     ";
268         else
269             output += "ERROR";
270 
271         if (passed) logln(output); else errln(output);
272         return passed;
273     }
274 
275     /**
276      * CANNOT REPRODUDE
277      *
278      * Yet another _alleged_ bug in TimeZone.getOffset(), a method that never
279      * should have been made public.  It's simply too hard to use correctly.
280      *
281      * The original test code failed to do the following:
282      * (1) Call Calendar.setTime() before getting the fields!
283      * (2) Use the right millis (as usual) for getOffset(); they were passing
284      *     in the MILLIS field, instead of the STANDARD MILLIS IN DAY.
285      * When you fix these two problems, the test passes, as expected.
286      */
287     public void Test4126678() {
288         Locale locale = Locale.getDefault();
289         if (!TestUtils.usesGregorianCalendar(locale)) {
290             logln("Skipping this test because locale is " + locale);
291             return;
292         }
293 
294         // Note: this test depends on the PST time zone.
295         TimeZone initialZone = TimeZone.getDefault();
296 
297         // test both SimpleTimeZone and ZoneInfo objects.
298         // @since 1.4
299         sub4126678(getPST());
300         sub4126678(TimeZone.getTimeZone("PST"));
301 
302         // restore the initial time zone so that this test case
303         // doesn't affect the others.
304         TimeZone.setDefault(initialZone);
305     }
306 
307     @SuppressWarnings("deprecation")
308     private void sub4126678(TimeZone tz) {
309         Calendar cal = Calendar.getInstance();
310         TimeZone.setDefault(tz);
311         cal.setTimeZone(tz);
312 
313         Date dt = new Date(1998-1900, Calendar.APRIL, 5, 10, 0);
314         // the dt value is local time in PST.
315         if (!tz.inDaylightTime(dt))
316             errln("We're not in Daylight Savings Time and we should be.\n");
317 
318         cal.setTime(dt);
319         int era = cal.get(Calendar.ERA);
320         int year = cal.get(Calendar.YEAR);
321         int month = cal.get(Calendar.MONTH);
322         int day = cal.get(Calendar.DATE);
323         int dayOfWeek = cal.get(Calendar.DAY_OF_WEEK);
324         int millis = cal.get(Calendar.MILLISECOND) +
325             (cal.get(Calendar.SECOND) +
326              (cal.get(Calendar.MINUTE) +
327               (cal.get(Calendar.HOUR) * 60) * 60) * 1000) -
328             cal.get(Calendar.DST_OFFSET);
329 
330         long offset = tz.getOffset(era, year, month, day, dayOfWeek, millis);
331         long raw_offset = tz.getRawOffset();
332         if (offset == raw_offset) {
333             errln("Offsets should not match when in DST");
334         }
335     }
336 
337     /**
338      * TimeZone.getAvailableIDs(int) throws exception for certain values,
339      * due to a faulty constant in TimeZone.java.
340      */
341     public void Test4151406() {
342         int max = 0;
343         for (int h=-28; h<=30; ++h) {
344             // h is in half-hours from GMT; rawoffset is in millis
345             int rawoffset = h * 1800000;
346             int hh = (h<0) ? -h : h;
347             String hname = ((h<0) ? "GMT-" : "GMT+") +
348                 ((hh/2 < 10) ? "0" : "") +
349                 (hh/2) + ':' +
350                 ((hh%2==0) ? "00" : "30");
351             try {
352                 String[] ids = TimeZone.getAvailableIDs(rawoffset);
353                 if (ids.length > max) max = ids.length;
354                 logln(hname + ' ' + ids.length +
355                       ((ids.length > 0) ? (" e.g. " + ids[0]) : ""));
356             } catch (Exception e) {
357                 errln(hname + ' ' + "Fail: " + e);
358             }
359         }
360         logln("Maximum zones per offset = " + max);
361     }
362 
363     public void Test4151429() {
364         try {
365             TimeZone tz = TimeZone.getTimeZone("GMT");
366             String name = tz.getDisplayName(true, Integer.MAX_VALUE,
367                                             Locale.getDefault());
368             errln("IllegalArgumentException not thrown by TimeZone.getDisplayName()");
369         } catch(IllegalArgumentException e) {}
370     }
371 
372     /**
373      * SimpleTimeZone accepts illegal DST savings values.  These values
374      * must be non-zero.  There is no upper limit at this time.
375      */
376     public void Test4154525() {
377         final int GOOD = 1, BAD = 0;
378         int[] DATA = {
379             1, GOOD,
380             0, BAD,
381             -1, BAD,
382             60*60*1000, GOOD,
383             Integer.MIN_VALUE, BAD,
384             // Integer.MAX_VALUE, ?, // no upper limit on DST savings at this time
385         };
386         for (int i=0; i<DATA.length; i+=2) {
387             int savings = DATA[i];
388             boolean valid = DATA[i+1] == GOOD;
389             String method = null;
390             for (int j=0; j<2; ++j) {
391                 try {
392                     switch (j) {
393                     case 0:
394                         method = "constructor";
395                         SimpleTimeZone z = new SimpleTimeZone(0, "id",
396                             Calendar.JANUARY, 1, 0, 0,
397                             Calendar.MARCH, 1, 0, 0,
398                             savings); // <- what we're interested in
399                         break;
400                     case 1:
401                         method = "setDSTSavings()";
402                         z = new SimpleTimeZone(0, "GMT");
403                         z.setDSTSavings(savings);
404                         break;
405                     }
406                     if (valid) {
407                         logln("Pass: DST savings of " + savings + " accepted by " + method);
408                     } else {
409                         errln("Fail: DST savings of " + savings + " accepted by " + method);
410                     }
411                 } catch (IllegalArgumentException e) {
412                     if (valid) {
413                         errln("Fail: DST savings of " + savings + " to " + method + " gave " + e);
414                     } else {
415                         logln("Pass: DST savings of " + savings + " to " + method + " gave " + e);
416                     }
417                 }
418             }
419         }
420     }
421 
422     /**
423      * SimpleTimeZone.hasSameRules() doesn't work for zones with no DST
424      * and different DST parameters.
425      */
426     public void Test4154537() {
427         // tz1 and tz2 have no DST and different rule parameters
428         SimpleTimeZone tz1 = new SimpleTimeZone(0, "1", 0, 0, 0, 0, 2, 0, 0, 0);
429         SimpleTimeZone tz2 = new SimpleTimeZone(0, "2", 1, 0, 0, 0, 3, 0, 0, 0);
430         // tza and tzA have the same rule params
431         SimpleTimeZone tza = new SimpleTimeZone(0, "a", 0, 1, 0, 0, 3, 2, 0, 0);
432         SimpleTimeZone tzA = new SimpleTimeZone(0, "A", 0, 1, 0, 0, 3, 2, 0, 0);
433         // tzb differs from tza
434         SimpleTimeZone tzb = new SimpleTimeZone(0, "b", 0, 1, 0, 0, 3, 1, 0, 0);
435         if (tz1.useDaylightTime() || tz2.useDaylightTime() ||
436             !tza.useDaylightTime() || !tzA.useDaylightTime() ||
437             !tzb.useDaylightTime()) {
438             errln("Test is broken -- rewrite it");
439         }
440         if (!tza.hasSameRules(tzA) || tza.hasSameRules(tzb)) {
441             errln("Fail: hasSameRules() broken for zones with rules");
442         }
443         if (!tz1.hasSameRules(tz2)) {
444             errln("Fail: hasSameRules() returns false for zones without rules");
445             errln("zone 1 = " + tz1);
446             errln("zone 2 = " + tz2);
447         }
448     }
449 
450     /**
451      * SimpleTimeZone constructors, setStartRule(), and setEndRule() don't
452      * check for out-of-range arguments.
453      */
454     public void Test4154542() {
455         final int GOOD = 1;
456         final int BAD  = 0;
457 
458         final int GOOD_MONTH       = Calendar.JANUARY;
459         final int GOOD_DAY         = 1;
460         final int GOOD_DAY_OF_WEEK = Calendar.SUNDAY;
461         final int GOOD_TIME        = 0;
462 
463         int[] DATA = {
464             GOOD, Integer.MIN_VALUE,    0,  Integer.MAX_VALUE,   Integer.MIN_VALUE,
465             GOOD, Calendar.JANUARY,    -5,  Calendar.SUNDAY,     0,
466             GOOD, Calendar.DECEMBER,    5,  Calendar.SATURDAY,   24*60*60*1000-1,
467             GOOD, Calendar.DECEMBER,    5,  Calendar.SATURDAY,   24*60*60*1000,
468             BAD,  Calendar.DECEMBER,    5,  Calendar.SATURDAY,   24*60*60*1000+1,
469             BAD,  Calendar.DECEMBER,    5,  Calendar.SATURDAY,  -1,
470             BAD,  Calendar.JANUARY,    -6,  Calendar.SUNDAY,     0,
471             BAD,  Calendar.DECEMBER,    6,  Calendar.SATURDAY,   24*60*60*1000,
472             GOOD, Calendar.DECEMBER,    1,  0,                   0,
473             GOOD, Calendar.DECEMBER,   31,  0,                   0,
474             BAD,  Calendar.APRIL,      31,  0,                   0,
475             BAD,  Calendar.DECEMBER,   32,  0,                   0,
476             BAD,  Calendar.JANUARY-1,   1,  Calendar.SUNDAY,     0,
477             BAD,  Calendar.DECEMBER+1,  1,  Calendar.SUNDAY,     0,
478             GOOD, Calendar.DECEMBER,   31, -Calendar.SUNDAY,     0,
479             GOOD, Calendar.DECEMBER,   31, -Calendar.SATURDAY,   0,
480             BAD,  Calendar.DECEMBER,   32, -Calendar.SATURDAY,   0,
481             BAD,  Calendar.DECEMBER,  -32, -Calendar.SATURDAY,   0,
482             BAD,  Calendar.DECEMBER,   31, -Calendar.SATURDAY-1, 0,
483         };
484         SimpleTimeZone zone = new SimpleTimeZone(0, "Z");
485         for (int i=0; i<DATA.length; i+=5) {
486             boolean shouldBeGood = (DATA[i] == GOOD);
487             int month     = DATA[i+1];
488             int day       = DATA[i+2];
489             int dayOfWeek = DATA[i+3];
490             int time      = DATA[i+4];
491 
492             Exception ex = null;
493             try {
494                 zone.setStartRule(month, day, dayOfWeek, time);
495             } catch (IllegalArgumentException e) {
496                 ex = e;
497             }
498             if ((ex == null) != shouldBeGood) {
499                 errln("setStartRule(month=" + month + ", day=" + day +
500                       ", dayOfWeek=" + dayOfWeek + ", time=" + time +
501                       (shouldBeGood ? (") should work but throws " + ex)
502                        : ") should fail but doesn't"));
503             }
504 
505             ex = null;
506             try {
507                 zone.setEndRule(month, day, dayOfWeek, time);
508             } catch (IllegalArgumentException e) {
509                 ex = e;
510             }
511             if ((ex == null) != shouldBeGood) {
512                 errln("setEndRule(month=" + month + ", day=" + day +
513                       ", dayOfWeek=" + dayOfWeek + ", time=" + time +
514                       (shouldBeGood ? (") should work but throws " + ex)
515                        : ") should fail but doesn't"));
516             }
517 
518             ex = null;
519             try {
520                 SimpleTimeZone temp = new SimpleTimeZone(0, "Z",
521                         month, day, dayOfWeek, time,
522                         GOOD_MONTH, GOOD_DAY, GOOD_DAY_OF_WEEK, GOOD_TIME);
523             } catch (IllegalArgumentException e) {
524                 ex = e;
525             }
526             if ((ex == null) != shouldBeGood) {
527                 errln("SimpleTimeZone(month=" + month + ", day=" + day +
528                       ", dayOfWeek=" + dayOfWeek + ", time=" + time +
529                       (shouldBeGood ? (", <end>) should work but throws " + ex)
530                        : ", <end>) should fail but doesn't"));
531             }
532 
533             ex = null;
534             try {
535                 SimpleTimeZone temp = new SimpleTimeZone(0, "Z",
536                         GOOD_MONTH, GOOD_DAY, GOOD_DAY_OF_WEEK, GOOD_TIME,
537                         month, day, dayOfWeek, time);
538             } catch (IllegalArgumentException e) {
539                 ex = e;
540             }
541             if ((ex == null) != shouldBeGood) {
542                 errln("SimpleTimeZone(<start>, month=" + month + ", day=" + day +
543                       ", dayOfWeek=" + dayOfWeek + ", time=" + time +
544                       (shouldBeGood ? (") should work but throws " + ex)
545                        : ") should fail but doesn't"));
546             }
547         }
548     }
549 
550     /**
551      * SimpleTimeZone.getOffset accepts illegal arguments.
552      */
553     public void Test4154650() {
554         final int GOOD=1, BAD=0;
555         final int GOOD_ERA=GregorianCalendar.AD, GOOD_YEAR=1998, GOOD_MONTH=Calendar.AUGUST;
556         final int GOOD_DAY=2, GOOD_DOW=Calendar.SUNDAY, GOOD_TIME=16*3600000;
557         int[] DATA = {
558             GOOD, GOOD_ERA, GOOD_YEAR, GOOD_MONTH, GOOD_DAY, GOOD_DOW, GOOD_TIME,
559 
560             GOOD, GregorianCalendar.BC, GOOD_YEAR, GOOD_MONTH, GOOD_DAY, GOOD_DOW, GOOD_TIME,
561             GOOD, GregorianCalendar.AD, GOOD_YEAR, GOOD_MONTH, GOOD_DAY, GOOD_DOW, GOOD_TIME,
562             BAD,  GregorianCalendar.BC-1, GOOD_YEAR, GOOD_MONTH, GOOD_DAY, GOOD_DOW, GOOD_TIME,
563             BAD,  GregorianCalendar.AD+1, GOOD_YEAR, GOOD_MONTH, GOOD_DAY, GOOD_DOW, GOOD_TIME,
564 
565             GOOD, GOOD_ERA, GOOD_YEAR, Calendar.JANUARY, GOOD_DAY, GOOD_DOW, GOOD_TIME,
566             GOOD, GOOD_ERA, GOOD_YEAR, Calendar.DECEMBER, GOOD_DAY, GOOD_DOW, GOOD_TIME,
567             BAD,  GOOD_ERA, GOOD_YEAR, Calendar.JANUARY-1, GOOD_DAY, GOOD_DOW, GOOD_TIME,
568             BAD,  GOOD_ERA, GOOD_YEAR, Calendar.DECEMBER+1, GOOD_DAY, GOOD_DOW, GOOD_TIME,
569 
570             GOOD, GOOD_ERA, GOOD_YEAR, Calendar.JANUARY, 1, GOOD_DOW, GOOD_TIME,
571             GOOD, GOOD_ERA, GOOD_YEAR, Calendar.JANUARY, 31, GOOD_DOW, GOOD_TIME,
572             BAD,  GOOD_ERA, GOOD_YEAR, Calendar.JANUARY, 0, GOOD_DOW, GOOD_TIME,
573             BAD,  GOOD_ERA, GOOD_YEAR, Calendar.JANUARY, 32, GOOD_DOW, GOOD_TIME,
574 
575             GOOD, GOOD_ERA, GOOD_YEAR, GOOD_MONTH, GOOD_DAY, Calendar.SUNDAY, GOOD_TIME,
576             GOOD, GOOD_ERA, GOOD_YEAR, GOOD_MONTH, GOOD_DAY, Calendar.SATURDAY, GOOD_TIME,
577             BAD,  GOOD_ERA, GOOD_YEAR, GOOD_MONTH, GOOD_DAY, Calendar.SUNDAY-1, GOOD_TIME,
578             BAD,  GOOD_ERA, GOOD_YEAR, GOOD_MONTH, GOOD_DAY, Calendar.SATURDAY+1, GOOD_TIME,
579 
580             GOOD, GOOD_ERA, GOOD_YEAR, GOOD_MONTH, GOOD_DAY, GOOD_DOW, 0,
581             GOOD, GOOD_ERA, GOOD_YEAR, GOOD_MONTH, GOOD_DAY, GOOD_DOW, 24*3600000-1,
582             BAD,  GOOD_ERA, GOOD_YEAR, GOOD_MONTH, GOOD_DAY, GOOD_DOW, -1,
583             BAD,  GOOD_ERA, GOOD_YEAR, GOOD_MONTH, GOOD_DAY, GOOD_DOW, 24*3600000,
584         };
585 
586         TimeZone tz = TimeZone.getDefault();
587         for (int i=0; i<DATA.length; i+=7) {
588             boolean good = DATA[i] == GOOD;
589             IllegalArgumentException e = null;
590             try {
591                 int offset = tz.getOffset(DATA[i+1], DATA[i+2], DATA[i+3],
592                                           DATA[i+4], DATA[i+5], DATA[i+6]);
593            } catch (IllegalArgumentException ex) {
594                 e = ex;
595             }
596             if (good != (e == null)) {
597                 errln("Fail: getOffset(" +
598                       DATA[i+1] + ", " + DATA[i+2] + ", " + DATA[i+3] + ", " +
599                       DATA[i+4] + ", " + DATA[i+5] + ", " + DATA[i+6] +
600                       (good ? (") threw " + e) : ") accepts invalid args"));
601             }
602         }
603     }
604 
605     /**
606      * TimeZone constructors allow null IDs.
607      */
608     public void Test4159922() {
609         TimeZone z = null;
610 
611         // TimeZone API.  Only hasSameRules() and setDefault() should
612         // allow null.
613         try {
614             z = TimeZone.getTimeZone((String)null);
615             errln("FAIL: Null allowed in getTimeZone");
616         } catch (NullPointerException e) {}
617         z = TimeZone.getTimeZone("GMT");
618         try {
619             z.getDisplayName(false, TimeZone.SHORT, null);
620             errln("FAIL: Null allowed in getDisplayName(3)");
621         } catch (NullPointerException e) {}
622         try {
623             z.getDisplayName(null);
624             errln("FAIL: Null allowed in getDisplayName(1)");
625         } catch (NullPointerException e) {}
626         try {
627             if (z.hasSameRules(null)) {
628                 errln("FAIL: hasSameRules returned true");
629             }
630         } catch (NullPointerException e) {
631             errln("FAIL: Null NOT allowed in hasSameRules");
632         }
633         try {
634             z.inDaylightTime(null);
635             errln("FAIL: Null allowed in inDaylightTime");
636         } catch (NullPointerException e) {}
637         try {
638             z.setID(null);
639             errln("FAIL: Null allowed in setID");
640         } catch (NullPointerException e) {}
641 
642         TimeZone save = TimeZone.getDefault();
643         try {
644             TimeZone.setDefault(null);
645         } catch (NullPointerException e) {
646             errln("FAIL: Null NOT allowed in setDefault");
647         } finally {
648             TimeZone.setDefault(save);
649         }
650 
651         // SimpleTimeZone API
652         SimpleTimeZone s = null;
653         try {
654             s = new SimpleTimeZone(0, null);
655             errln("FAIL: Null allowed in SimpleTimeZone(2)");
656         } catch (NullPointerException e) {}
657         try {
658             s = new SimpleTimeZone(0, null, 0, 1, 0, 0, 0, 1, 0, 0);
659             errln("FAIL: Null allowed in SimpleTimeZone(10)");
660         } catch (NullPointerException e) {}
661         try {
662             s = new SimpleTimeZone(0, null, 0, 1, 0, 0, 0, 1, 0, 0, 1000);
663             errln("FAIL: Null allowed in SimpleTimeZone(11)");
664         } catch (NullPointerException e) {}
665     }
666 
667     /**
668      * TimeZone broken at midnight.  The TimeZone code fails to handle
669      * transitions at midnight correctly.
670      */
671     @SuppressWarnings("deprecation")
672     public void Test4162593() {
673         SimpleDateFormat fmt = new SimpleDateFormat("z", Locale.US);
674         final int ONE_HOUR = 60*60*1000;
675         TimeZone initialZone = TimeZone.getDefault();
676 
677         SimpleTimeZone asuncion = new SimpleTimeZone(-4*ONE_HOUR, "America/Asuncion" /*PY%sT*/,
678             Calendar.OCTOBER, 1, 0 /*DOM*/, 0*ONE_HOUR,
679             Calendar.MARCH, 1, 0 /*DOM*/, 0*ONE_HOUR, 1*ONE_HOUR);
680 
681         /* Zone
682          * Starting time
683          * Transition expected between start+1H and start+2H
684          */
685         Object[] DATA = {
686             new SimpleTimeZone(2*ONE_HOUR, "Asia/Damascus" /*EE%sT*/,
687                 Calendar.APRIL, 1, 0 /*DOM*/, 0*ONE_HOUR,
688                 Calendar.OCTOBER, 1, 0 /*DOM*/, 0*ONE_HOUR, 1*ONE_HOUR),
689             new int[] {98, Calendar.SEPTEMBER, 30, 22, 0},
690             Boolean.TRUE,
691 
692             asuncion,
693             new int[] {100, Calendar.FEBRUARY, 28, 22, 0},
694             Boolean.FALSE,
695 
696             asuncion,
697             new int[] {100, Calendar.FEBRUARY, 29, 22, 0},
698             Boolean.TRUE,
699         };
700 
701         String[] zone = new String[4];
702 
703         try {
704             for (int j=0; j<DATA.length; j+=3) {
705                 TimeZone tz = (TimeZone)DATA[j];
706                 TimeZone.setDefault(tz);
707                 fmt.setTimeZone(tz);
708 
709                 // Must construct the Date object AFTER setting the default zone
710                 int[] p = (int[])DATA[j+1];
711                 Date d = new Date(p[0], p[1], p[2], p[3], p[4]);
712                 boolean transitionExpected = ((Boolean)DATA[j+2]).booleanValue();
713 
714                 logln(tz.getID() + ":");
715                 for (int i=0; i<4; ++i) {
716                     zone[i] = fmt.format(d);
717                     logln("" + i + ": " + d);
718                     d = new Date(d.getTime() + ONE_HOUR);
719                 }
720                 if (zone[0].equals(zone[1]) &&
721                     (zone[1].equals(zone[2]) != transitionExpected) &&
722                     zone[2].equals(zone[3])) {
723                     logln("Ok: transition " + transitionExpected);
724                 } else {
725                     errln("Fail: boundary transition incorrect");
726                 }
727             }
728         }
729         finally {
730             // restore the initial time zone so that this test case
731             // doesn't affect the others.
732             TimeZone.setDefault(initialZone);
733         }
734     }
735 
736     /**
737      * TimeZone broken in last hour of year
738      */
739     public void Test4173604() {
740         // test both SimpleTimeZone and ZoneInfo objects.
741         // @since 1.4
742         sub4173604(getPST());
743         sub4173604(TimeZone.getTimeZone("PST"));
744     }
745 
746     private void sub4173604(TimeZone pst) {
747         int o22 = pst.getOffset(1, 1998, 11, 31, Calendar.THURSDAY, 22*60*60*1000);
748         int o23 = pst.getOffset(1, 1998, 11, 31, Calendar.THURSDAY, 23*60*60*1000);
749         int o00 = pst.getOffset(1, 1999, 0, 1, Calendar.FRIDAY, 0);
750         if (o22 != o23 || o22 != o00) {
751             errln("Offsets should be the same (for PST), but got: " +
752                   "12/31 22:00 " + o22 +
753                   ", 12/31 23:00 " + o23 +
754                   ", 01/01 00:00 " + o00);
755         }
756 
757         GregorianCalendar cal = new GregorianCalendar();
758         cal.setTimeZone(pst);
759         cal.clear();
760         cal.set(1998, Calendar.JANUARY, 1);
761         int lastDST = cal.get(Calendar.DST_OFFSET);
762         int transitions = 0;
763         int delta = 5;
764         while (cal.get(Calendar.YEAR) < 2000) {
765             cal.add(Calendar.MINUTE, delta);
766             if (cal.get(Calendar.DST_OFFSET) != lastDST) {
767                 ++transitions;
768                 Calendar t = (Calendar)cal.clone();
769                 t.add(Calendar.MINUTE, -delta);
770                 logln(t.getTime() + "  " + t.get(Calendar.DST_OFFSET));
771                 logln(cal.getTime() + "  " + (lastDST=cal.get(Calendar.DST_OFFSET)));
772             }
773         }
774         if (transitions != 4) {
775             errln("Saw " + transitions + " transitions; should have seen 4");
776         }
777     }
778 
779     /**
780      * getDisplayName doesn't work with unusual savings/offsets.
781      */
782     @SuppressWarnings("deprecation")
783     public void Test4176686() {
784         // Construct a zone that does not observe DST but
785         // that does have a DST savings (which should be ignored).
786         int offset = 90 * 60000; // 1:30
787         SimpleTimeZone z1 = new SimpleTimeZone(offset, "_std_zone_");
788         z1.setDSTSavings(45 * 60000); // 0:45
789 
790         // Construct a zone that observes DST for the first 6 months.
791         SimpleTimeZone z2 = new SimpleTimeZone(offset, "_dst_zone_");
792         z2.setDSTSavings(45 * 60000); // 0:45
793         z2.setStartRule(Calendar.JANUARY, 1, 0);
794         z2.setEndRule(Calendar.JULY, 1, 0);
795 
796         // Also check DateFormat
797         DateFormat fmt1 = new SimpleDateFormat("z");
798         fmt1.setTimeZone(z1); // Format uses standard zone
799         DateFormat fmt2 = new SimpleDateFormat("z");
800         fmt2.setTimeZone(z2); // Format uses DST zone
801         Date dst = new Date(1970-1900, Calendar.FEBRUARY, 1); // Time in DST
802         Date std = new Date(1970-1900, Calendar.AUGUST, 1); // Time in standard
803 
804         // Description, Result, Expected Result
805         String[] DATA = {
806             "getDisplayName(false, SHORT)/std zone",
807             z1.getDisplayName(false, TimeZone.SHORT), "GMT+01:30",
808             "getDisplayName(false, LONG)/std zone",
809             z1.getDisplayName(false, TimeZone.LONG ), "GMT+01:30",
810             "getDisplayName(true, SHORT)/std zone",
811             z1.getDisplayName(true, TimeZone.SHORT), "GMT+01:30",
812             "getDisplayName(true, LONG)/std zone",
813             z1.getDisplayName(true, TimeZone.LONG ), "GMT+01:30",
814             "getDisplayName(false, SHORT)/dst zone",
815             z2.getDisplayName(false, TimeZone.SHORT), "GMT+01:30",
816             "getDisplayName(false, LONG)/dst zone",
817             z2.getDisplayName(false, TimeZone.LONG ), "GMT+01:30",
818             "getDisplayName(true, SHORT)/dst zone",
819             z2.getDisplayName(true, TimeZone.SHORT), "GMT+02:15",
820             "getDisplayName(true, LONG)/dst zone",
821             z2.getDisplayName(true, TimeZone.LONG ), "GMT+02:15",
822             "DateFormat.format(std)/std zone", fmt1.format(std), "GMT+01:30",
823             "DateFormat.format(dst)/std zone", fmt1.format(dst), "GMT+01:30",
824             "DateFormat.format(std)/dst zone", fmt2.format(std), "GMT+01:30",
825             "DateFormat.format(dst)/dst zone", fmt2.format(dst), "GMT+02:15",
826         };
827 
828         for (int i=0; i<DATA.length; i+=3) {
829             if (!DATA[i+1].equals(DATA[i+2])) {
830                 errln("FAIL: " + DATA[i] + " -> " + DATA[i+1] + ", exp " + DATA[i+2]);
831             }
832         }
833     }
834 
835     /**
836      * SimpleTimeZone allows invalid DOM values.
837      */
838     public void Test4184229() {
839         SimpleTimeZone zone = null;
840         try {
841             zone = new SimpleTimeZone(0, "A", 0, -1, 0, 0, 0, 0, 0, 0);
842             errln("Failed. No exception has been thrown for DOM -1 startDay");
843         } catch(IllegalArgumentException e) {
844             logln("(a) " + e.getMessage());
845         }
846         try {
847             zone = new SimpleTimeZone(0, "A", 0, 0, 0, 0, 0, -1, 0, 0);
848             errln("Failed. No exception has been thrown for DOM -1 endDay");
849         } catch(IllegalArgumentException e) {
850             logln("(b) " + e.getMessage());
851         }
852         try {
853             zone = new SimpleTimeZone(0, "A", 0, -1, 0, 0, 0, 0, 0, 0, 1000);
854             errln("Failed. No exception has been thrown for DOM -1 startDay +savings");
855         } catch(IllegalArgumentException e) {
856             logln("(c) " + e.getMessage());
857         }
858         try {
859             zone = new SimpleTimeZone(0, "A", 0, 0, 0, 0, 0, -1, 0, 0, 1000);
860             errln("Failed. No exception has been thrown for DOM -1 endDay +savings");
861         } catch(IllegalArgumentException e) {
862             logln("(d) " + e.getMessage());
863         }
864         // Make a valid constructor call for subsequent tests.
865         zone = new SimpleTimeZone(0, "A", 0, 1, 0, 0, 0, 1, 0, 0);
866         try {
867             zone.setStartRule(0, -1, 0, 0);
868             errln("Failed. No exception has been thrown for DOM -1 setStartRule +savings");
869         } catch(IllegalArgumentException e) {
870             logln("(e) " + e.getMessage());
871         }
872         try {
873             zone.setStartRule(0, -1, 0);
874             errln("Failed. No exception has been thrown for DOM -1 setStartRule");
875         } catch(IllegalArgumentException e) {
876             logln("(f) " + e.getMessage());
877         }
878         try {
879             zone.setEndRule(0, -1, 0, 0);
880             errln("Failed. No exception has been thrown for DOM -1 setEndRule +savings");
881         } catch(IllegalArgumentException e) {
882             logln("(g) " + e.getMessage());
883         }
884         try {
885             zone.setEndRule(0, -1, 0);
886             errln("Failed. No exception has been thrown for DOM -1 setEndRule");
887         } catch(IllegalArgumentException e) {
888             logln("(h) " + e.getMessage());
889         }
890     }
891 
892     /**
893      * SimpleTimeZone.getOffset() throws IllegalArgumentException when to get
894      * of 2/29/1996 (leap day).
895      */
896     public void Test4208960 () {
897         // test both SimpleTimeZone and ZoneInfo objects.
898         // @since 1.4
899         sub4208960(getPST());
900         sub4208960(TimeZone.getTimeZone("PST"));
901     }
902 
903     private void sub4208960(TimeZone tz) {
904         try {
905             int offset = tz.getOffset(GregorianCalendar.AD, 1996, Calendar.FEBRUARY, 29,
906                                       Calendar.THURSDAY, 0);
907         } catch (IllegalArgumentException e) {
908             errln("FAILED: to get TimeZone.getOffset(2/29/96)");
909         }
910         try {
911             int offset = tz.getOffset(GregorianCalendar.AD, 1997, Calendar.FEBRUARY, 29,
912                                       Calendar.THURSDAY, 0);
913             errln("FAILED: TimeZone.getOffset(2/29/97) expected to throw Exception.");
914         } catch (IllegalArgumentException e) {
915             logln("got IllegalArgumentException");
916         }
917     }
918 
919     /**
920      * 4966229: java.util.Date methods may works incorrect.
921      * sun.util.calendar.ZoneInfo doesn't clone properly.
922      */
923     @SuppressWarnings("deprecation")
924     public void Test4966229() {
925         TimeZone savedTZ = TimeZone.getDefault();
926         try {
927             TimeZone.setDefault(TimeZone.getTimeZone("GMT"));
928             Date d = new Date(2100-1900, 5, 1); // specify year >2037
929             TimeZone tz = TimeZone.getTimeZone("America/Los_Angeles");
930 
931             Calendar cal = new GregorianCalendar(tz);
932             cal.setTime(d);
933 
934             // Change the raw offset in tz
935             int offset = tz.getRawOffset();
936             tz.setRawOffset(0);
937 
938             TimeZone tz2 = (TimeZone) tz.clone();
939             Calendar cal2 = new GregorianCalendar(tz2);
940             cal2.setTime(d);
941             int expectedHourOfDay = cal2.get(cal.HOUR_OF_DAY);
942 
943             // Restore the GMT offset in tz which shouldn't affect tz2
944             tz.setRawOffset(offset);
945             cal2.setTime(d);
946             int hourOfDay = cal2.get(cal.HOUR_OF_DAY);
947             if (hourOfDay != expectedHourOfDay) {
948                 errln("wrong hour of day: got: " + hourOfDay
949                       + ", expected: " + expectedHourOfDay);
950             }
951         } finally {
952             TimeZone.setDefault(savedTZ);
953         }
954     }
955 
956     /**
957      * 6433179: (tz) Incorrect DST end for America/Winnipeg and Canada/Central in 2038+
958      */
959     public void Test6433179() {
960         // Use the old America/Winnipeg rule for testing. Note that
961         // startMode is WALL_TIME for testing. It's actually
962         // STANDARD_TIME, though.
963         //Rule  Winn    1966    2005    -       Oct     lastSun 2:00s   0       S
964         //Rule  Winn    1987    2005    -       Apr     Sun>=1  2:00s   1:00    D
965         TimeZone tz = new SimpleTimeZone(-6*ONE_HOUR, "America/Winnipeg",
966           Calendar.APRIL, 1, -Calendar.SUNDAY, 2*ONE_HOUR, SimpleTimeZone.WALL_TIME,
967           Calendar.OCTOBER, -1, Calendar.SUNDAY, 2*ONE_HOUR, SimpleTimeZone.STANDARD_TIME,
968           1*ONE_HOUR);
969         Calendar cal = Calendar.getInstance(tz, Locale.US);
970         cal.clear();
971         cal.set(2039, Calendar.OCTOBER, 1);
972         cal.getTime();
973         cal.set(cal.DAY_OF_WEEK, cal.SUNDAY);
974         cal.set(cal.DAY_OF_WEEK_IN_MONTH, -1);
975         cal.add(Calendar.HOUR_OF_DAY, 2);
976         if (cal.get(cal.DST_OFFSET) == 0) {
977             errln("Should still be in DST.");
978         }
979     }
980 
981     private static final int ONE_HOUR = 60 * 60 * 1000;
982     /**
983      * Returns an instance of SimpleTimeZone for
984      * "PST". (TimeZone.getTimeZone() no longer returns a
985      * SimpleTimeZone object.)
986      * @since 1.4
987      */
988     private SimpleTimeZone getPST() {
989         return new SimpleTimeZone(-8*ONE_HOUR, "PST",
990                                   Calendar.APRIL, 1, -Calendar.SUNDAY, 2*ONE_HOUR,
991                                   Calendar.OCTOBER, -1, Calendar.SUNDAY, 2*ONE_HOUR,
992                                   1*ONE_HOUR);
993     }
994 }
995 //eof