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 Test4052967() {
  44         logln("*** CHECK TIMEZONE AGAINST HOST OS SETTING ***");
  45         String id = TimeZone.getDefault().getID();
  46         logln("user.timezone: " + System.getProperty("user.timezone", "<not set>"));
  47         logln("TimeZone.getDefault().getID(): " + id);
  48         logln(new Date().toString());
  49         logln("*** THE RESULTS OF THIS TEST MUST BE VERIFIED MANUALLY ***");
  50     }
  51 
  52     public void Test4073209() {
  53         TimeZone z1 = TimeZone.getTimeZone("PST");
  54         TimeZone z2 = TimeZone.getTimeZone("PST");
  55         if (z1 == z2) {
  56             errln("Fail: TimeZone should return clones");
  57         }
  58     }
  59 
  60     @SuppressWarnings("deprecation")
  61     public void Test4073215() {
  62         SimpleTimeZone z = new SimpleTimeZone(0, "GMT");
  63         if (z.useDaylightTime()) {
  64             errln("Fail: Fix test to start with non-DST zone");
  65         }
  66         z.setStartRule(Calendar.FEBRUARY, 1, Calendar.SUNDAY, 0);
  67         z.setEndRule(Calendar.MARCH, -1, Calendar.SUNDAY, 0);
  68         if (!z.useDaylightTime()) {
  69             errln("Fail: DST not active");
  70         }
  71         if (z.inDaylightTime(new Date(97, Calendar.JANUARY, 31)) ||
  72             !z.inDaylightTime(new Date(97, Calendar.MARCH, 1)) ||
  73             z.inDaylightTime(new Date(97, Calendar.MARCH, 31))) {
  74             errln("Fail: DST not working as expected");
  75         }
  76     }
  77 
  78     /**
  79      * The expected behavior of TimeZone around the boundaries is:
  80      * (Assume transition time of 2:00 AM)
  81      *    day of onset 1:59 AM STD  = display name 1:59 AM ST
  82      *                 2:00 AM STD  = display name 3:00 AM DT
  83      *    day of end   0:59 AM STD  = display name 1:59 AM DT
  84      *                 1:00 AM STD  = display name 1:00 AM ST
  85      */
  86     public void Test4084933() {
  87         // test both SimpleTimeZone and ZoneInfo objects.
  88         // @since 1.4
  89         sub4084933(getPST());
  90         sub4084933(TimeZone.getTimeZone("PST"));
  91     }
  92 
  93     private void sub4084933(TimeZone tz) {
  94         long offset1 = tz.getOffset(1,
  95             1997, Calendar.OCTOBER, 26, Calendar.SUNDAY, (2*60*60*1000));
  96         long offset2 = tz.getOffset(1,
  97             1997, Calendar.OCTOBER, 26, Calendar.SUNDAY, (2*60*60*1000)-1);
  98 
  99         long offset3 = tz.getOffset(1,
 100             1997, Calendar.OCTOBER, 26, Calendar.SUNDAY, (1*60*60*1000));
 101         long offset4 = tz.getOffset(1,
 102             1997, Calendar.OCTOBER, 26, Calendar.SUNDAY, (1*60*60*1000)-1);
 103 
 104         /*
 105          *  The following was added just for consistency.  It shows that going *to* Daylight
 106          *  Savings Time (PDT) does work at 2am.
 107          */
 108 
 109         long offset5 = tz.getOffset(1,
 110             1997, Calendar.APRIL, 6, Calendar.SUNDAY, (2*60*60*1000));
 111         long offset6 = tz.getOffset(1,
 112             1997, Calendar.APRIL, 6, Calendar.SUNDAY, (2*60*60*1000)-1);
 113 
 114         long offset7 = tz.getOffset(1,
 115             1997, Calendar.APRIL, 6, Calendar.SUNDAY, (1*60*60*1000));
 116         long offset8 = tz.getOffset(1,
 117             1997, Calendar.APRIL, 6, Calendar.SUNDAY, (1*60*60*1000)-1);
 118 
 119         long SToffset = -8 * 60*60*1000L;
 120         long DToffset = -7 * 60*60*1000L;
 121         if (offset1 != SToffset || offset2 != SToffset ||
 122             offset3 != SToffset || offset4 != DToffset ||
 123             offset5 != DToffset || offset6 != SToffset ||
 124             offset7 != SToffset || offset8 != SToffset)
 125             errln("Fail: TimeZone misbehaving"); {
 126         }
 127     }
 128 
 129     public void Test4096952() {
 130         String[] ZONES = { "GMT", "MET", "IST" };
 131         boolean pass = true;
 132         try {
 133             for (int i=0; i<ZONES.length; ++i) {
 134                 TimeZone zone = TimeZone.getTimeZone(ZONES[i]);
 135                 if (!zone.getID().equals(ZONES[i]))
 136                     errln("Fail: Test broken; zones not instantiating");
 137 
 138                 ByteArrayOutputStream baos;
 139                 ObjectOutputStream ostream =
 140                     new ObjectOutputStream(baos = new
 141                                            ByteArrayOutputStream());
 142                 ostream.writeObject(zone);
 143                 ostream.close();
 144                 baos.close();
 145                 ObjectInputStream istream =
 146                     new ObjectInputStream(new
 147                                           ByteArrayInputStream(baos.toByteArray()));
 148                 TimeZone frankenZone = (TimeZone) istream.readObject();
 149                 //logln("Zone:        " + zone);
 150                 //logln("FrankenZone: " + frankenZone);
 151                 if (!zone.equals(frankenZone)) {
 152                     logln("TimeZone " + zone.getID() +
 153                           " not equal to serialized/deserialized one");
 154                     pass = false;
 155                 }
 156             }
 157             if (!pass) errln("Fail: TimeZone serialization/equality bug");
 158         }
 159         catch (IOException e) {
 160             errln("Fail: " + e);
 161             e.printStackTrace();
 162         }
 163         catch (ClassNotFoundException e) {
 164             errln("Fail: " + e);
 165             e.printStackTrace();
 166         }
 167     }
 168 
 169     public void Test4109314() {
 170         // test both SimpleTimeZone and ZoneInfo objects.
 171         // @since 1.4
 172         if (Locale.getDefault().equals(new Locale("th", "TH"))) {
 173             return;
 174         }
 175         sub4109314(getPST());
 176         sub4109314(TimeZone.getTimeZone("PST"));
 177     }
 178 
 179 
 180     @SuppressWarnings("deprecation")
 181     private void sub4109314(TimeZone PST) {
 182         GregorianCalendar testCal = (GregorianCalendar)Calendar.getInstance();
 183         Object[] testData = {
 184             PST, new Date(98,Calendar.APRIL,4,22,0), new Date(98, Calendar.APRIL, 5,6,0),
 185             PST, new Date(98,Calendar.OCTOBER,24,22,0), new Date(98,Calendar.OCTOBER,25,6,0),
 186         };
 187         boolean pass=true;
 188         for (int i=0; i<testData.length; i+=3) {
 189             testCal.setTimeZone((TimeZone) testData[i]);
 190             long t = ((Date)testData[i+1]).getTime();
 191             Date end = (Date) testData[i+2];
 192             while (t < end.getTime()) {
 193                 testCal.setTime(new Date(t));
 194                 if (!checkCalendar314(testCal, (TimeZone) testData[i]))
 195                     pass = false;
 196                 t += 60*60*1000L;
 197             }
 198         }
 199         if (!pass) errln("Fail: TZ API inconsistent");
 200     }
 201 
 202     boolean checkCalendar314(GregorianCalendar testCal, TimeZone testTZ) {
 203         // GregorianCalendar testCal = (GregorianCalendar)aCal.clone();
 204 
 205         final int ONE_DAY = 24*60*60*1000;
 206 
 207         int tzOffset, tzRawOffset;
 208         Float tzOffsetFloat,tzRawOffsetFloat;
 209         // Here is where the user made an error.  They were passing in the value of
 210         // the MILLSECOND field; you need to pass in the millis in the day in STANDARD
 211         // time.
 212         int millis = testCal.get(Calendar.MILLISECOND) +
 213             1000 * (testCal.get(Calendar.SECOND) +
 214                     60 * (testCal.get(Calendar.MINUTE) +
 215                           60 * (testCal.get(Calendar.HOUR_OF_DAY)))) -
 216             testCal.get(Calendar.DST_OFFSET);
 217 
 218         /* Fix up millis to be in range.  ASSUME THAT WE ARE NOT AT THE
 219          * BEGINNING OR END OF A MONTH.  We must add this code because
 220          * getOffset() has been changed to be more strict about the parameters
 221          * it receives -- it turns out that this test was passing in illegal
 222          * values. */
 223         int date = testCal.get(Calendar.DATE);
 224         int dow  = testCal.get(Calendar.DAY_OF_WEEK);
 225         while (millis < 0) {
 226             millis += ONE_DAY;
 227             --date;
 228             dow = Calendar.SUNDAY + ((dow - Calendar.SUNDAY + 6) % 7);
 229         }
 230         while (millis >= ONE_DAY) {
 231             millis -= ONE_DAY;
 232             ++date;
 233             dow = Calendar.SUNDAY + ((dow - Calendar.SUNDAY + 1) % 7);
 234         }
 235 
 236         tzOffset = testTZ.getOffset(testCal.get(Calendar.ERA),
 237                                     testCal.get(Calendar.YEAR),
 238                                     testCal.get(Calendar.MONTH),
 239                                     date,
 240                                     dow,
 241                                     millis);
 242         tzRawOffset = testTZ.getRawOffset();
 243         tzOffsetFloat = new Float((float)tzOffset/(float)3600000);
 244         tzRawOffsetFloat = new Float((float)tzRawOffset/(float)3600000);
 245 
 246         Date testDate = testCal.getTime();
 247 
 248         boolean inDaylightTime = testTZ.inDaylightTime(testDate);
 249         SimpleDateFormat sdf = new SimpleDateFormat("MM/dd/yyyy HH:mm");
 250         sdf.setCalendar(testCal);
 251         String inDaylightTimeString;
 252 
 253         boolean passed;
 254 
 255         if (inDaylightTime)
 256         {
 257             inDaylightTimeString = " DST ";
 258             passed = (tzOffset == (tzRawOffset + 3600000));
 259         }
 260         else
 261         {
 262             inDaylightTimeString = "     ";
 263             passed = (tzOffset == tzRawOffset);
 264         }
 265 
 266         String output = testTZ.getID() + " " + sdf.format(testDate) +
 267             " Offset(" + tzOffsetFloat + ")" +
 268             " RawOffset(" + tzRawOffsetFloat + ")" +
 269             " " + millis/(float)3600000 + " " +
 270             inDaylightTimeString;
 271 
 272         if (passed)
 273             output += "     ";
 274         else
 275             output += "ERROR";
 276 
 277         if (passed) logln(output); else errln(output);
 278         return passed;
 279     }
 280 
 281     /**
 282      * CANNOT REPRODUDE
 283      *
 284      * Yet another _alleged_ bug in TimeZone.getOffset(), a method that never
 285      * should have been made public.  It's simply too hard to use correctly.
 286      *
 287      * The original test code failed to do the following:
 288      * (1) Call Calendar.setTime() before getting the fields!
 289      * (2) Use the right millis (as usual) for getOffset(); they were passing
 290      *     in the MILLIS field, instead of the STANDARD MILLIS IN DAY.
 291      * When you fix these two problems, the test passes, as expected.
 292      */
 293     public void Test4126678() {
 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