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