1 /*
   2  * Copyright (c) 1997, 2018, 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 4028006 4044013 4096694 4107276 4107570 4112869 4130885 7039469 7126465 7158483
  27  *      8008577 8077685 8098547 8133321 8138716 8148446 8151876 8159684 8166875 8181157
  28  * @modules java.base/sun.util.resources
  29  * @library /java/text/testlib
  30  * @summary test TimeZone
  31  */
  32 
  33 import java.io.*;
  34 import java.text.*;
  35 import java.util.*;
  36 import sun.util.resources.LocaleData;
  37 
  38 public class TimeZoneTest extends IntlTest
  39 {
  40     static final int millisPerHour = 3600000;
  41 
  42     public static void main(String[] args) throws Exception {
  43         new TimeZoneTest().run(args);
  44     }
  45 
  46     /**
  47      * Bug 4130885
  48      * Certain short zone IDs, used since 1.1.x, are incorrect.
  49      *
  50      * The worst of these is:
  51      *
  52      * "CAT" (Central African Time) should be GMT+2:00, but instead returns a
  53      * zone at GMT-1:00. The zone at GMT-1:00 should be called EGT, CVT, EGST,
  54      * or AZOST, depending on which zone is meant, but in no case is it CAT.
  55      *
  56      * Other wrong zone IDs:
  57      *
  58      * ECT (European Central Time) GMT+1:00: ECT is Ecuador Time,
  59      * GMT-5:00. European Central time is abbreviated CEST.
  60      *
  61      * SST (Solomon Island Time) GMT+11:00. SST is actually Samoa Standard Time,
  62      * GMT-11:00. Solomon Island time is SBT.
  63      *
  64      * NST (New Zealand Time) GMT+12:00. NST is the abbreviation for
  65      * Newfoundland Standard Time, GMT-3:30. New Zealanders use NZST.
  66      *
  67      * AST (Alaska Standard Time) GMT-9:00. [This has already been noted in
  68      * another bug.] It should be "AKST". AST is Atlantic Standard Time,
  69      * GMT-4:00.
  70      *
  71      * PNT (Phoenix Time) GMT-7:00. PNT usually means Pitcairn Time,
  72      * GMT-8:30. There is no standard abbreviation for Phoenix time, as distinct
  73      * from MST with daylight savings.
  74      *
  75      * In addition to these problems, a number of zones are FAKE. That is, they
  76      * don't match what people use in the real world.
  77      *
  78      * FAKE zones:
  79      *
  80      * EET (should be EEST)
  81      * ART (should be EET)
  82      * MET (should be IRST)
  83      * NET (should be AMST)
  84      * PLT (should be PKT)
  85      * BST (should be BDT)
  86      * VST (should be ICT)
  87      * CTT (should be CST) +
  88      * ACT (should be CST) +
  89      * AET (should be EST) +
  90      * MIT (should be WST) +
  91      * IET (should be EST) +
  92      * PRT (should be AST) +
  93      * CNT (should be NST)
  94      * AGT (should be ARST)
  95      * BET (should be EST) +
  96      *
  97      * + A zone with the correct name already exists and means something
  98      * else. E.g., EST usually indicates the US Eastern zone, so it cannot be
  99      * used for Brazil (BET).
 100      */
 101     public void TestShortZoneIDs() throws Exception {
 102 
 103         ZoneDescriptor[] JDK_116_REFERENCE_LIST = {
 104             new ZoneDescriptor("MIT", 780, true),
 105             new ZoneDescriptor("HST", -600, false),
 106             new ZoneDescriptor("AST", -540, true),
 107             new ZoneDescriptor("PST", -480, true),
 108             new ZoneDescriptor("PNT", -420, false),
 109             new ZoneDescriptor("MST", -420, false),
 110             new ZoneDescriptor("CST", -360, true),
 111             new ZoneDescriptor("IET", -300, true),
 112             new ZoneDescriptor("EST", -300, false),
 113             new ZoneDescriptor("PRT", -240, false),
 114             new ZoneDescriptor("CNT", -210, true),
 115             new ZoneDescriptor("AGT", -180, false),
 116             new ZoneDescriptor("BET", -180, true),
 117             // new ZoneDescriptor("CAT", -60, false), // Wrong:
 118             // As of bug 4130885, fix CAT (Central Africa)
 119             new ZoneDescriptor("CAT", 120, false), // Africa/Harare
 120             new ZoneDescriptor("GMT", 0, false),
 121             new ZoneDescriptor("UTC", 0, false),
 122             new ZoneDescriptor("ECT", 60, true),
 123             new ZoneDescriptor("ART", 120, false),
 124             new ZoneDescriptor("EET", 120, true),
 125             new ZoneDescriptor("EAT", 180, false),
 126             new ZoneDescriptor("MET", 60, true),
 127             new ZoneDescriptor("NET", 240, false),
 128             new ZoneDescriptor("PLT", 300, false),
 129             new ZoneDescriptor("IST", 330, false),
 130             new ZoneDescriptor("BST", 360, false),
 131             new ZoneDescriptor("VST", 420, false),
 132             new ZoneDescriptor("CTT", 480, false),
 133             new ZoneDescriptor("JST", 540, false),
 134             new ZoneDescriptor("ACT", 570, false),
 135             new ZoneDescriptor("AET", 600, true),
 136             new ZoneDescriptor("SST", 660, false),
 137             // new ZoneDescriptor("NST", 720, false),
 138             // As of bug 4130885, fix NST (New Zealand)
 139             new ZoneDescriptor("NST", 720, true), // Pacific/Auckland
 140         };
 141 
 142         Map<String, ZoneDescriptor> hash = new HashMap<>();
 143 
 144         String[] ids = TimeZone.getAvailableIDs();
 145         for (String id : ids) {
 146             if (id.length() == 3) {
 147                 hash.put(id, new ZoneDescriptor(TimeZone.getTimeZone(id)));
 148             }
 149         }
 150 
 151         for (int i = 0; i < JDK_116_REFERENCE_LIST.length; ++i) {
 152             ZoneDescriptor referenceZone = JDK_116_REFERENCE_LIST[i];
 153             ZoneDescriptor currentZone = hash.get(referenceZone.getID());
 154             if (referenceZone.equals(currentZone)) {
 155                 logln("ok " + referenceZone);
 156             }
 157             else {
 158                 errln("Fail: Expected " + referenceZone +
 159                       "; got " + currentZone);
 160             }
 161         }
 162     }
 163 
 164     /**
 165      * A descriptor for a zone; used to regress the short zone IDs.
 166      */
 167     static class ZoneDescriptor {
 168         String id;
 169         int offset; // In minutes
 170         boolean daylight;
 171 
 172         ZoneDescriptor(TimeZone zone) {
 173             this.id = zone.getID();
 174             this.offset = zone.getRawOffset() / 60000;
 175             this.daylight = zone.useDaylightTime();
 176         }
 177 
 178         ZoneDescriptor(String id, int offset, boolean daylight) {
 179             this.id = id;
 180             this.offset = offset;
 181             this.daylight = daylight;
 182         }
 183 
 184         public String getID() { return id; }
 185 
 186         @Override
 187         public boolean equals(Object o) {
 188             ZoneDescriptor that = (ZoneDescriptor)o;
 189             return that != null &&
 190                 id.equals(that.id) &&
 191                 offset == that.offset &&
 192                 daylight == that.daylight;
 193         }
 194 
 195         @Override
 196         public int hashCode() {
 197             return id.hashCode() ^ offset | (daylight ? 1 : 0);
 198         }
 199 
 200         @Override
 201         public String toString() {
 202             int min = offset;
 203             char sign = '+';
 204             if (min < 0) { sign = '-'; min = -min; }
 205 
 206             return "Zone[\"" + id + "\", GMT" + sign + (min/60) + ':' +
 207                 (min%60<10?"0":"") + (min%60) + ", " +
 208                 (daylight ? "Daylight" : "Standard") + "]";
 209         }
 210 
 211         public static int compare(Object o1, Object o2) {
 212             ZoneDescriptor i1 = (ZoneDescriptor)o1;
 213             ZoneDescriptor i2 = (ZoneDescriptor)o2;
 214             if (i1.offset > i2.offset) return 1;
 215             if (i1.offset < i2.offset) return -1;
 216             if (i1.daylight && !i2.daylight) return 1;
 217             if (!i1.daylight && i2.daylight) return -1;
 218             return i1.id.compareTo(i2.id);
 219         }
 220     }
 221 
 222     static final String formatMinutes(int min) {
 223         char sign = '+';
 224         if (min < 0) { sign = '-'; min = -min; }
 225         int h = min/60;
 226         min = min%60;
 227         return "" + sign + h + ":" + ((min<10) ? "0" : "") + min;
 228     }
 229     /**
 230      * As part of the VM fix (see CCC approved RFE 4028006, bug
 231      * 4044013), TimeZone.getTimeZone() has been modified to recognize
 232      * generic IDs of the form GMT[+-]hh:mm, GMT[+-]hhmm, and
 233      * GMT[+-]hh.  Test this behavior here.
 234      *
 235      * Bug 4044013
 236      *
 237      * ID "Custom" is no longer used for TimeZone objects created with
 238      * a custom time zone ID, such as "GMT-8". See 4322313.
 239      */
 240     public void TestCustomParse() throws Exception {
 241         Object[] DATA = {
 242             // ID        Expected offset in minutes
 243             "GMT",       null,
 244             "GMT+0",     new Integer(0),
 245             "GMT+1",     new Integer(60),
 246             "GMT-0030",  new Integer(-30),
 247             "GMT+15:99", null,
 248             "GMT+",      null,
 249             "GMT-",      null,
 250             "GMT+0:",    null,
 251             "GMT-:",     null,
 252             "GMT+0010",  new Integer(10), // Interpret this as 00:10
 253             "GMT-10",    new Integer(-10*60),
 254             "GMT+30",    null,
 255             "GMT-3:30",  new Integer(-(3*60+30)),
 256             "GMT-230",   new Integer(-(2*60+30)),
 257         };
 258         for (int i=0; i<DATA.length; i+=2) {
 259             String id = (String)DATA[i];
 260             Integer exp = (Integer)DATA[i+1];
 261             TimeZone zone = TimeZone.getTimeZone(id);
 262             if (zone.getID().equals("GMT")) {
 263                 logln(id + " -> generic GMT");
 264                 // When TimeZone.getTimeZone() can't parse the id, it
 265                 // returns GMT -- a dubious practice, but required for
 266                 // backward compatibility.
 267                 if (exp != null) {
 268                     throw new Exception("Expected offset of " + formatMinutes(exp.intValue()) +
 269                                         " for " + id + ", got parse failure");
 270                 }
 271             }
 272             else {
 273                 int ioffset = zone.getRawOffset()/60000;
 274                 String offset = formatMinutes(ioffset);
 275                 logln(id + " -> " + zone.getID() + " GMT" + offset);
 276                 if (exp == null) {
 277                     throw new Exception("Expected parse failure for " + id +
 278                                         ", got offset of " + offset +
 279                                         ", id " + zone.getID());
 280                 }
 281                 else if (ioffset != exp.intValue()) {
 282                     throw new Exception("Expected offset of " + formatMinutes(exp.intValue()) +
 283                                         ", id Custom, for " + id +
 284                                         ", got offset of " + offset +
 285                                         ", id " + zone.getID());
 286                 }
 287             }
 288         }
 289     }
 290 
 291     /**
 292      * Test the basic functionality of the getDisplayName() API.
 293      *
 294      * Bug 4112869
 295      * Bug 4028006
 296      *
 297      * See also API change request A41.
 298      *
 299      * 4/21/98 - make smarter, so the test works if the ext resources
 300      * are present or not.
 301      */
 302     public void TestDisplayName() {
 303         TimeZone zone = TimeZone.getTimeZone("PST");
 304         String name = zone.getDisplayName(Locale.ENGLISH);
 305         logln("PST->" + name);
 306         if (!name.equals("Pacific Standard Time"))
 307             errln("Fail: Expected \"Pacific Standard Time\"");
 308 
 309         //*****************************************************************
 310         // THE FOLLOWING LINES MUST BE UPDATED IF THE LOCALE DATA CHANGES
 311         // THE FOLLOWING LINES MUST BE UPDATED IF THE LOCALE DATA CHANGES
 312         // THE FOLLOWING LINES MUST BE UPDATED IF THE LOCALE DATA CHANGES
 313         //*****************************************************************
 314         Object[] DATA = {
 315             new Boolean(false), new Integer(TimeZone.SHORT), "PST",
 316             new Boolean(true),  new Integer(TimeZone.SHORT), "PDT",
 317             new Boolean(false), new Integer(TimeZone.LONG),  "Pacific Standard Time",
 318             new Boolean(true),  new Integer(TimeZone.LONG),  "Pacific Daylight Time",
 319         };
 320 
 321         for (int i=0; i<DATA.length; i+=3) {
 322             name = zone.getDisplayName(((Boolean)DATA[i]).booleanValue(),
 323                                        ((Integer)DATA[i+1]).intValue(),
 324                                        Locale.ENGLISH);
 325             if (!name.equals(DATA[i+2]))
 326                 errln("Fail: Expected " + DATA[i+2] + "; got " + name);
 327         }
 328 
 329         // Make sure that we don't display the DST name by constructing a fake
 330         // PST zone that has DST all year long.
 331         SimpleTimeZone zone2 = new SimpleTimeZone(0, "PST");
 332         zone2.setStartRule(Calendar.JANUARY, 1, 0);
 333         zone2.setEndRule(Calendar.DECEMBER, 31, 0);
 334         logln("Modified PST inDaylightTime->" + zone2.inDaylightTime(new Date()));
 335         name = zone2.getDisplayName(Locale.ENGLISH);
 336         logln("Modified PST->" + name);
 337         if (!name.equals("Pacific Standard Time"))
 338             errln("Fail: Expected \"Pacific Standard Time\"");
 339 
 340         // Make sure we get the default display format for Locales
 341         // with no display name data.
 342         Locale zh_CN = Locale.SIMPLIFIED_CHINESE;
 343         name = zone.getDisplayName(zh_CN);
 344         //*****************************************************************
 345         // THE FOLLOWING LINE MUST BE UPDATED IF THE LOCALE DATA CHANGES
 346         // THE FOLLOWING LINE MUST BE UPDATED IF THE LOCALE DATA CHANGES
 347         // THE FOLLOWING LINE MUST BE UPDATED IF THE LOCALE DATA CHANGES
 348         //*****************************************************************
 349         logln("PST(zh_CN)->" + name);
 350 
 351         // Now be smart -- check to see if zh resource is even present.
 352         // If not, we expect the en fallback behavior.
 353         ResourceBundle enRB = LocaleData.getBundle("sun.util.resources.TimeZoneNames",
 354                                                    Locale.ENGLISH);
 355         ResourceBundle zhRB = LocaleData.getBundle("sun.util.resources.TimeZoneNames",
 356                                                    zh_CN);
 357 
 358         boolean noZH = enRB == zhRB;
 359 
 360         if (noZH) {
 361             logln("Warning: Not testing the zh_CN behavior because resource is absent");
 362             if (!name.equals("Pacific Standard Time"))
 363                 errln("Fail: Expected Pacific Standard Time");
 364         }
 365         else if (!name.equals("Pacific Standard Time") &&
 366                  !name.equals("\u592a\u5e73\u6d0b\u6807\u51c6\u65f6\u95f4") &&
 367                  !name.equals("\u5317\u7f8e\u592a\u5e73\u6d0b\u6807\u51c6\u65f6\u95f4") &&
 368                  !name.equals("GMT-08:00") &&
 369                  !name.equals("GMT-8:00") &&
 370                  !name.equals("GMT-0800") &&
 371                  !name.equals("GMT-800")) {
 372             errln("Fail: Expected GMT-08:00 or something similar");
 373             errln("************************************************************");
 374             errln("THE ABOVE FAILURE MAY JUST MEAN THE LOCALE DATA HAS CHANGED");
 375             errln("************************************************************");
 376         }
 377 
 378         // Now try a non-existent zone
 379         zone2 = new SimpleTimeZone(90*60*1000, "xyzzy");
 380         name = zone2.getDisplayName(Locale.ENGLISH);
 381         logln("GMT+90min->" + name);
 382         if (!name.equals("GMT+01:30") &&
 383             !name.equals("GMT+1:30") &&
 384             !name.equals("GMT+0130") &&
 385             !name.equals("GMT+130"))
 386             errln("Fail: Expected GMT+01:30 or something similar");
 387     }
 388 
 389     public void TestGenericAPI() {
 390         String id = "NewGMT";
 391         int offset = 12345;
 392 
 393         SimpleTimeZone zone = new SimpleTimeZone(offset, id);
 394         if (zone.useDaylightTime()) {
 395             errln("FAIL: useDaylightTime should return false");
 396         }
 397 
 398         TimeZone zoneclone = (TimeZone)zone.clone();
 399         if (!zoneclone.equals(zone)) {
 400             errln("FAIL: clone or operator== failed");
 401         }
 402         zoneclone.setID("abc");
 403         if (zoneclone.equals(zone)) {
 404             errln("FAIL: clone or operator!= failed");
 405         }
 406 
 407         zoneclone = (TimeZone)zone.clone();
 408         if (!zoneclone.equals(zone)) {
 409             errln("FAIL: clone or operator== failed");
 410         }
 411         zoneclone.setRawOffset(45678);
 412         if (zoneclone.equals(zone)) {
 413             errln("FAIL: clone or operator!= failed");
 414         }
 415 
 416         TimeZone saveDefault = TimeZone.getDefault();
 417         try {
 418             TimeZone.setDefault(zone);
 419             TimeZone defaultzone = TimeZone.getDefault();
 420             if (defaultzone == zone) {
 421                 errln("FAIL: Default object is identical, not clone");
 422             }
 423             if (!defaultzone.equals(zone)) {
 424                 errln("FAIL: Default object is not equal");
 425             }
 426         }
 427         finally {
 428             TimeZone.setDefault(saveDefault);
 429         }
 430     }
 431 
 432     @SuppressWarnings("deprecation")
 433     public void TestRuleAPI()
 434     {
 435         // ErrorCode status = ZERO_ERROR;
 436 
 437         int offset = (int)(60*60*1000*1.75); // Pick a weird offset
 438         SimpleTimeZone zone = new SimpleTimeZone(offset, "TestZone");
 439         if (zone.useDaylightTime()) errln("FAIL: useDaylightTime should return false");
 440 
 441         // Establish our expected transition times.  Do this with a non-DST
 442         // calendar with the (above) declared local offset.
 443         GregorianCalendar gc = new GregorianCalendar(zone);
 444         gc.clear();
 445         gc.set(1990, Calendar.MARCH, 1);
 446         long marchOneStd = gc.getTime().getTime(); // Local Std time midnight
 447         gc.clear();
 448         gc.set(1990, Calendar.JULY, 1);
 449         long julyOneStd = gc.getTime().getTime(); // Local Std time midnight
 450 
 451         // Starting and ending hours, WALL TIME
 452         int startHour = (int)(2.25 * 3600000);
 453         int endHour   = (int)(3.5  * 3600000);
 454 
 455         zone.setStartRule(Calendar.MARCH, 1, 0, startHour);
 456         zone.setEndRule  (Calendar.JULY,  1, 0, endHour);
 457 
 458         gc = new GregorianCalendar(zone);
 459         // if (failure(status, "new GregorianCalendar")) return;
 460 
 461         long marchOne = marchOneStd + startHour;
 462         long julyOne = julyOneStd + endHour - 3600000; // Adjust from wall to Std time
 463 
 464         long expMarchOne = 636251400000L;
 465         if (marchOne != expMarchOne)
 466         {
 467             errln("FAIL: Expected start computed as " + marchOne +
 468                   " = " + new Date(marchOne));
 469             logln("      Should be                  " + expMarchOne +
 470                   " = " + new Date(expMarchOne));
 471         }
 472 
 473         long expJulyOne = 646793100000L;
 474         if (julyOne != expJulyOne)
 475         {
 476             errln("FAIL: Expected start computed as " + julyOne +
 477                   " = " + new Date(julyOne));
 478             logln("      Should be                  " + expJulyOne +
 479                   " = " + new Date(expJulyOne));
 480         }
 481 
 482         testUsingBinarySearch(zone, new Date(90, Calendar.JANUARY, 1).getTime(),
 483                               new Date(90, Calendar.JUNE, 15).getTime(), marchOne);
 484         testUsingBinarySearch(zone, new Date(90, Calendar.JUNE, 1).getTime(),
 485                               new Date(90, Calendar.DECEMBER, 31).getTime(), julyOne);
 486 
 487         if (zone.inDaylightTime(new Date(marchOne - 1000)) ||
 488             !zone.inDaylightTime(new Date(marchOne)))
 489             errln("FAIL: Start rule broken");
 490         if (!zone.inDaylightTime(new Date(julyOne - 1000)) ||
 491             zone.inDaylightTime(new Date(julyOne)))
 492             errln("FAIL: End rule broken");
 493 
 494         zone.setStartYear(1991);
 495         if (zone.inDaylightTime(new Date(marchOne)) ||
 496             zone.inDaylightTime(new Date(julyOne - 1000)))
 497             errln("FAIL: Start year broken");
 498 
 499         // failure(status, "TestRuleAPI");
 500         // delete gc;
 501         // delete zone;
 502     }
 503 
 504     void testUsingBinarySearch(SimpleTimeZone tz, long min, long max, long expectedBoundary)
 505     {
 506         // ErrorCode status = ZERO_ERROR;
 507         boolean startsInDST = tz.inDaylightTime(new Date(min));
 508         // if (failure(status, "SimpleTimeZone::inDaylightTime")) return;
 509         if (tz.inDaylightTime(new Date(max)) == startsInDST) {
 510             logln("Error: inDaylightTime(" + new Date(max) + ") != " + (!startsInDST));
 511             return;
 512         }
 513         // if (failure(status, "SimpleTimeZone::inDaylightTime")) return;
 514         while ((max - min) > INTERVAL) {
 515             long mid = (min + max) / 2;
 516             if (tz.inDaylightTime(new Date(mid)) == startsInDST) {
 517                 min = mid;
 518             }
 519             else {
 520                 max = mid;
 521             }
 522             // if (failure(status, "SimpleTimeZone::inDaylightTime")) return;
 523         }
 524         logln("Binary Search Before: " + min + " = " + new Date(min));
 525         logln("Binary Search After:  " + max + " = " + new Date(max));
 526         long mindelta = expectedBoundary - min;
 527         long maxdelta = max - expectedBoundary;
 528         if (mindelta >= 0 &&
 529             mindelta <= INTERVAL &&
 530             mindelta >= 0 &&
 531             mindelta <= INTERVAL)
 532             logln("PASS: Expected bdry:  " + expectedBoundary + " = " + new Date(expectedBoundary));
 533         else
 534             errln("FAIL: Expected bdry:  " + expectedBoundary + " = " + new Date(expectedBoundary));
 535     }
 536 
 537     static final int INTERVAL = 100;
 538 
 539     // Bug 006; verify the offset for a specific zone.
 540     public void TestPRTOffset()
 541     {
 542         TimeZone tz = TimeZone.getTimeZone( "PRT" );
 543         if( tz == null ) {
 544             errln( "FAIL: TimeZone(PRT) is null" );
 545         }
 546         else{
 547             if (tz.getRawOffset() != (-4*millisPerHour))
 548                 errln("FAIL: Offset for PRT should be -4");
 549         }
 550 
 551     }
 552 
 553     // Test various calls
 554     @SuppressWarnings("deprecation")
 555     public void TestVariousAPI518()
 556     {
 557         TimeZone time_zone = TimeZone.getTimeZone("PST");
 558         Date d = new Date(97, Calendar.APRIL, 30);
 559 
 560         logln("The timezone is " + time_zone.getID());
 561 
 562         if (time_zone.inDaylightTime(d) != true)
 563             errln("FAIL: inDaylightTime returned false");
 564 
 565         if (time_zone.useDaylightTime() != true)
 566             errln("FAIL: useDaylightTime returned false");
 567 
 568         if (time_zone.getRawOffset() != -8*millisPerHour)
 569             errln( "FAIL: getRawOffset returned wrong value");
 570 
 571         GregorianCalendar gc = new GregorianCalendar();
 572         gc.setTime(d);
 573         if (time_zone.getOffset(gc.AD, gc.get(gc.YEAR), gc.get(gc.MONTH),
 574                                 gc.get(gc.DAY_OF_MONTH),
 575                                 gc.get(gc.DAY_OF_WEEK), 0)
 576             != -7*millisPerHour)
 577             errln("FAIL: getOffset returned wrong value");
 578     }
 579 
 580     // Test getAvailableID API
 581     public void TestGetAvailableIDs913()
 582     {
 583         StringBuffer buf = new StringBuffer("TimeZone.getAvailableIDs() = { ");
 584         String[] s = TimeZone.getAvailableIDs();
 585         for (int i=0; i<s.length; ++i)
 586         {
 587             if (i > 0) buf.append(", ");
 588             buf.append(s[i]);
 589         }
 590         buf.append(" };");
 591         logln(buf.toString());
 592 
 593         buf.setLength(0);
 594         buf.append("TimeZone.getAvailableIDs(GMT+02:00) = { ");
 595         s = TimeZone.getAvailableIDs(+2 * 60 * 60 * 1000);
 596         for (int i=0; i<s.length; ++i)
 597         {
 598             if (i > 0) buf.append(", ");
 599             buf.append(s[i]);
 600         }
 601         buf.append(" };");
 602         logln(buf.toString());
 603 
 604         TimeZone tz = TimeZone.getTimeZone("PST");
 605         if (tz != null)
 606             logln("getTimeZone(PST) = " + tz.getID());
 607         else
 608             errln("FAIL: getTimeZone(PST) = null");
 609 
 610         tz = TimeZone.getTimeZone("America/Los_Angeles");
 611         if (tz != null)
 612             logln("getTimeZone(America/Los_Angeles) = " + tz.getID());
 613         else
 614             errln("FAIL: getTimeZone(PST) = null");
 615 
 616         // Bug 4096694
 617         tz = TimeZone.getTimeZone("NON_EXISTENT");
 618         if (tz == null)
 619             errln("FAIL: getTimeZone(NON_EXISTENT) = null");
 620         else if (!tz.getID().equals("GMT"))
 621             errln("FAIL: getTimeZone(NON_EXISTENT) = " + tz.getID());
 622     }
 623 
 624     /**
 625      * Bug 4107276
 626      */
 627     public void TestDSTSavings() {
 628         // It might be better to find a way to integrate this test into the main TimeZone
 629         // tests above, but I don't have time to figure out how to do this (or if it's
 630         // even really a good idea).  Let's consider that a future.  --rtg 1/27/98
 631         SimpleTimeZone tz = new SimpleTimeZone(-5 * millisPerHour, "dstSavingsTest",
 632                                                Calendar.MARCH, 1, 0, 0, Calendar.SEPTEMBER, 1, 0, 0,
 633                                                (int)(0.5 * millisPerHour));
 634 
 635         if (tz.getRawOffset() != -5 * millisPerHour)
 636             errln("Got back a raw offset of " + (tz.getRawOffset() / millisPerHour) +
 637                   " hours instead of -5 hours.");
 638         if (!tz.useDaylightTime())
 639             errln("Test time zone should use DST but claims it doesn't.");
 640         if (tz.getDSTSavings() != 0.5 * millisPerHour)
 641             errln("Set DST offset to 0.5 hour, but got back " + (tz.getDSTSavings() /
 642                                                                  millisPerHour) + " hours instead.");
 643 
 644         int offset = tz.getOffset(GregorianCalendar.AD, 1998, Calendar.JANUARY, 1,
 645                                   Calendar.THURSDAY, 10 * millisPerHour);
 646         if (offset != -5 * millisPerHour)
 647             errln("The offset for 10 AM, 1/1/98 should have been -5 hours, but we got "
 648                   + (offset / millisPerHour) + " hours.");
 649 
 650         offset = tz.getOffset(GregorianCalendar.AD, 1998, Calendar.JUNE, 1, Calendar.MONDAY,
 651                               10 * millisPerHour);
 652         if (offset != -4.5 * millisPerHour)
 653             errln("The offset for 10 AM, 6/1/98 should have been -4.5 hours, but we got "
 654                   + (offset / millisPerHour) + " hours.");
 655 
 656         tz.setDSTSavings(millisPerHour);
 657         offset = tz.getOffset(GregorianCalendar.AD, 1998, Calendar.JANUARY, 1,
 658                               Calendar.THURSDAY, 10 * millisPerHour);
 659         if (offset != -5 * millisPerHour)
 660             errln("The offset for 10 AM, 1/1/98 should have been -5 hours, but we got "
 661                   + (offset / millisPerHour) + " hours.");
 662 
 663         offset = tz.getOffset(GregorianCalendar.AD, 1998, Calendar.JUNE, 1, Calendar.MONDAY,
 664                               10 * millisPerHour);
 665         if (offset != -4 * millisPerHour)
 666             errln("The offset for 10 AM, 6/1/98 (with a 1-hour DST offset) should have been -4 hours, but we got "
 667                   + (offset / millisPerHour) + " hours.");
 668     }
 669 
 670     /**
 671      * Bug 4107570
 672      */
 673     public void TestAlternateRules() {
 674         // Like TestDSTSavings, this test should probably be integrated somehow with the main
 675         // test at the top of this class, but I didn't have time to figure out how to do that.
 676         //                      --rtg 1/28/98
 677 
 678         SimpleTimeZone tz = new SimpleTimeZone(-5 * millisPerHour, "alternateRuleTest");
 679 
 680         // test the day-of-month API
 681         tz.setStartRule(Calendar.MARCH, 10, 12 * millisPerHour);
 682         tz.setEndRule(Calendar.OCTOBER, 20, 12 * millisPerHour);
 683 
 684         int offset = tz.getOffset(GregorianCalendar.AD, 1998, Calendar.MARCH, 5,
 685                                   Calendar.THURSDAY, 10 * millisPerHour);
 686         if (offset != -5 * millisPerHour)
 687             errln("The offset for 10AM, 3/5/98 should have been -5 hours, but we got "
 688                   + (offset / millisPerHour) + " hours.");
 689 
 690         offset = tz.getOffset(GregorianCalendar.AD, 1998, Calendar.MARCH, 15,
 691                               Calendar.SUNDAY, 10 * millisPerHour);
 692         if (offset != -4 * millisPerHour)
 693             errln("The offset for 10AM, 3/15/98 should have been -4 hours, but we got "
 694                   + (offset / millisPerHour) + " hours.");
 695 
 696         offset = tz.getOffset(GregorianCalendar.AD, 1998, Calendar.OCTOBER, 15,
 697                               Calendar.THURSDAY, 10 * millisPerHour);
 698         if (offset != -4 * millisPerHour)
 699             errln("The offset for 10AM, 10/15/98 should have been -4 hours, but we got "
 700                   + (offset / millisPerHour) + " hours.");
 701 
 702         offset = tz.getOffset(GregorianCalendar.AD, 1998, Calendar.OCTOBER, 25,
 703                               Calendar.SUNDAY, 10 * millisPerHour);
 704         if (offset != -5 * millisPerHour)
 705             errln("The offset for 10AM, 10/25/98 should have been -5 hours, but we got "
 706                   + (offset / millisPerHour) + " hours.");
 707 
 708         // test the day-of-week-after-day-in-month API
 709         tz.setStartRule(Calendar.MARCH, 10, Calendar.FRIDAY, 12 * millisPerHour, true);
 710         tz.setEndRule(Calendar.OCTOBER, 20, Calendar.FRIDAY, 12 * millisPerHour, false);
 711 
 712         offset = tz.getOffset(GregorianCalendar.AD, 1998, Calendar.MARCH, 11,
 713                               Calendar.WEDNESDAY, 10 * millisPerHour);
 714         if (offset != -5 * millisPerHour)
 715             errln("The offset for 10AM, 3/11/98 should have been -5 hours, but we got "
 716                   + (offset / millisPerHour) + " hours.");
 717 
 718         offset = tz.getOffset(GregorianCalendar.AD, 1998, Calendar.MARCH, 14,
 719                               Calendar.SATURDAY, 10 * millisPerHour);
 720         if (offset != -4 * millisPerHour)
 721             errln("The offset for 10AM, 3/14/98 should have been -4 hours, but we got "
 722                   + (offset / millisPerHour) + " hours.");
 723 
 724         offset = tz.getOffset(GregorianCalendar.AD, 1998, Calendar.OCTOBER, 15,
 725                               Calendar.THURSDAY, 10 * millisPerHour);
 726         if (offset != -4 * millisPerHour)
 727             errln("The offset for 10AM, 10/15/98 should have been -4 hours, but we got "
 728                   + (offset / millisPerHour) + " hours.");
 729 
 730         offset = tz.getOffset(GregorianCalendar.AD, 1998, Calendar.OCTOBER, 17,
 731                               Calendar.SATURDAY, 10 * millisPerHour);
 732         if (offset != -5 * millisPerHour)
 733             errln("The offset for 10AM, 10/17/98 should have been -5 hours, but we got "
 734                   + (offset / millisPerHour) + " hours.");
 735     }
 736 }
 737 
 738 //eof