1/*
2 * Copyright (c) 1997, 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 4028006 4044013 4096694 4107276 4107570 4112869 4130885 7039469 7126465 7158483
27 *      8008577 8077685 8098547 8133321 8138716 8148446 8151876 8159684 8166875
28 * @modules java.base/sun.util.resources
29 * @library /java/text/testlib
30 * @summary test TimeZone
31 */
32
33import java.io.*;
34import java.text.*;
35import java.util.*;
36import sun.util.resources.LocaleData;
37
38public 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("GMT-08:00") &&
368                 !name.equals("GMT-8:00") &&
369                 !name.equals("GMT-0800") &&
370                 !name.equals("GMT-800")) {
371            errln("Fail: Expected GMT-08:00 or something similar");
372            errln("************************************************************");
373            errln("THE ABOVE FAILURE MAY JUST MEAN THE LOCALE DATA HAS CHANGED");
374            errln("************************************************************");
375        }
376
377        // Now try a non-existent zone
378        zone2 = new SimpleTimeZone(90*60*1000, "xyzzy");
379        name = zone2.getDisplayName(Locale.ENGLISH);
380        logln("GMT+90min->" + name);
381        if (!name.equals("GMT+01:30") &&
382            !name.equals("GMT+1:30") &&
383            !name.equals("GMT+0130") &&
384            !name.equals("GMT+130"))
385            errln("Fail: Expected GMT+01:30 or something similar");
386    }
387
388    public void TestGenericAPI() {
389        String id = "NewGMT";
390        int offset = 12345;
391
392        SimpleTimeZone zone = new SimpleTimeZone(offset, id);
393        if (zone.useDaylightTime()) {
394            errln("FAIL: useDaylightTime should return false");
395        }
396
397        TimeZone zoneclone = (TimeZone)zone.clone();
398        if (!zoneclone.equals(zone)) {
399            errln("FAIL: clone or operator== failed");
400        }
401        zoneclone.setID("abc");
402        if (zoneclone.equals(zone)) {
403            errln("FAIL: clone or operator!= failed");
404        }
405
406        zoneclone = (TimeZone)zone.clone();
407        if (!zoneclone.equals(zone)) {
408            errln("FAIL: clone or operator== failed");
409        }
410        zoneclone.setRawOffset(45678);
411        if (zoneclone.equals(zone)) {
412            errln("FAIL: clone or operator!= failed");
413        }
414
415        TimeZone saveDefault = TimeZone.getDefault();
416        try {
417            TimeZone.setDefault(zone);
418            TimeZone defaultzone = TimeZone.getDefault();
419            if (defaultzone == zone) {
420                errln("FAIL: Default object is identical, not clone");
421            }
422            if (!defaultzone.equals(zone)) {
423                errln("FAIL: Default object is not equal");
424            }
425        }
426        finally {
427            TimeZone.setDefault(saveDefault);
428        }
429    }
430
431    @SuppressWarnings("deprecation")
432    public void TestRuleAPI()
433    {
434        // ErrorCode status = ZERO_ERROR;
435
436        int offset = (int)(60*60*1000*1.75); // Pick a weird offset
437        SimpleTimeZone zone = new SimpleTimeZone(offset, "TestZone");
438        if (zone.useDaylightTime()) errln("FAIL: useDaylightTime should return false");
439
440        // Establish our expected transition times.  Do this with a non-DST
441        // calendar with the (above) declared local offset.
442        GregorianCalendar gc = new GregorianCalendar(zone);
443        gc.clear();
444        gc.set(1990, Calendar.MARCH, 1);
445        long marchOneStd = gc.getTime().getTime(); // Local Std time midnight
446        gc.clear();
447        gc.set(1990, Calendar.JULY, 1);
448        long julyOneStd = gc.getTime().getTime(); // Local Std time midnight
449
450        // Starting and ending hours, WALL TIME
451        int startHour = (int)(2.25 * 3600000);
452        int endHour   = (int)(3.5  * 3600000);
453
454        zone.setStartRule(Calendar.MARCH, 1, 0, startHour);
455        zone.setEndRule  (Calendar.JULY,  1, 0, endHour);
456
457        gc = new GregorianCalendar(zone);
458        // if (failure(status, "new GregorianCalendar")) return;
459
460        long marchOne = marchOneStd + startHour;
461        long julyOne = julyOneStd + endHour - 3600000; // Adjust from wall to Std time
462
463        long expMarchOne = 636251400000L;
464        if (marchOne != expMarchOne)
465        {
466            errln("FAIL: Expected start computed as " + marchOne +
467                  " = " + new Date(marchOne));
468            logln("      Should be                  " + expMarchOne +
469                  " = " + new Date(expMarchOne));
470        }
471
472        long expJulyOne = 646793100000L;
473        if (julyOne != expJulyOne)
474        {
475            errln("FAIL: Expected start computed as " + julyOne +
476                  " = " + new Date(julyOne));
477            logln("      Should be                  " + expJulyOne +
478                  " = " + new Date(expJulyOne));
479        }
480
481        testUsingBinarySearch(zone, new Date(90, Calendar.JANUARY, 1).getTime(),
482                              new Date(90, Calendar.JUNE, 15).getTime(), marchOne);
483        testUsingBinarySearch(zone, new Date(90, Calendar.JUNE, 1).getTime(),
484                              new Date(90, Calendar.DECEMBER, 31).getTime(), julyOne);
485
486        if (zone.inDaylightTime(new Date(marchOne - 1000)) ||
487            !zone.inDaylightTime(new Date(marchOne)))
488            errln("FAIL: Start rule broken");
489        if (!zone.inDaylightTime(new Date(julyOne - 1000)) ||
490            zone.inDaylightTime(new Date(julyOne)))
491            errln("FAIL: End rule broken");
492
493        zone.setStartYear(1991);
494        if (zone.inDaylightTime(new Date(marchOne)) ||
495            zone.inDaylightTime(new Date(julyOne - 1000)))
496            errln("FAIL: Start year broken");
497
498        // failure(status, "TestRuleAPI");
499        // delete gc;
500        // delete zone;
501    }
502
503    void testUsingBinarySearch(SimpleTimeZone tz, long min, long max, long expectedBoundary)
504    {
505        // ErrorCode status = ZERO_ERROR;
506        boolean startsInDST = tz.inDaylightTime(new Date(min));
507        // if (failure(status, "SimpleTimeZone::inDaylightTime")) return;
508        if (tz.inDaylightTime(new Date(max)) == startsInDST) {
509            logln("Error: inDaylightTime(" + new Date(max) + ") != " + (!startsInDST));
510            return;
511        }
512        // if (failure(status, "SimpleTimeZone::inDaylightTime")) return;
513        while ((max - min) > INTERVAL) {
514            long mid = (min + max) / 2;
515            if (tz.inDaylightTime(new Date(mid)) == startsInDST) {
516                min = mid;
517            }
518            else {
519                max = mid;
520            }
521            // if (failure(status, "SimpleTimeZone::inDaylightTime")) return;
522        }
523        logln("Binary Search Before: " + min + " = " + new Date(min));
524        logln("Binary Search After:  " + max + " = " + new Date(max));
525        long mindelta = expectedBoundary - min;
526        long maxdelta = max - expectedBoundary;
527        if (mindelta >= 0 &&
528            mindelta <= INTERVAL &&
529            mindelta >= 0 &&
530            mindelta <= INTERVAL)
531            logln("PASS: Expected bdry:  " + expectedBoundary + " = " + new Date(expectedBoundary));
532        else
533            errln("FAIL: Expected bdry:  " + expectedBoundary + " = " + new Date(expectedBoundary));
534    }
535
536    static final int INTERVAL = 100;
537
538    // Bug 006; verify the offset for a specific zone.
539    public void TestPRTOffset()
540    {
541        TimeZone tz = TimeZone.getTimeZone( "PRT" );
542        if( tz == null ) {
543            errln( "FAIL: TimeZone(PRT) is null" );
544        }
545        else{
546            if (tz.getRawOffset() != (-4*millisPerHour))
547                errln("FAIL: Offset for PRT should be -4");
548        }
549
550    }
551
552    // Test various calls
553    @SuppressWarnings("deprecation")
554    public void TestVariousAPI518()
555    {
556        TimeZone time_zone = TimeZone.getTimeZone("PST");
557        Date d = new Date(97, Calendar.APRIL, 30);
558
559        logln("The timezone is " + time_zone.getID());
560
561        if (time_zone.inDaylightTime(d) != true)
562            errln("FAIL: inDaylightTime returned false");
563
564        if (time_zone.useDaylightTime() != true)
565            errln("FAIL: useDaylightTime returned false");
566
567        if (time_zone.getRawOffset() != -8*millisPerHour)
568            errln( "FAIL: getRawOffset returned wrong value");
569
570        GregorianCalendar gc = new GregorianCalendar();
571        gc.setTime(d);
572        if (time_zone.getOffset(gc.AD, gc.get(gc.YEAR), gc.get(gc.MONTH),
573                                gc.get(gc.DAY_OF_MONTH),
574                                gc.get(gc.DAY_OF_WEEK), 0)
575            != -7*millisPerHour)
576            errln("FAIL: getOffset returned wrong value");
577    }
578
579    // Test getAvailableID API
580    public void TestGetAvailableIDs913()
581    {
582        StringBuffer buf = new StringBuffer("TimeZone.getAvailableIDs() = { ");
583        String[] s = TimeZone.getAvailableIDs();
584        for (int i=0; i<s.length; ++i)
585        {
586            if (i > 0) buf.append(", ");
587            buf.append(s[i]);
588        }
589        buf.append(" };");
590        logln(buf.toString());
591
592        buf.setLength(0);
593        buf.append("TimeZone.getAvailableIDs(GMT+02:00) = { ");
594        s = TimeZone.getAvailableIDs(+2 * 60 * 60 * 1000);
595        for (int i=0; i<s.length; ++i)
596        {
597            if (i > 0) buf.append(", ");
598            buf.append(s[i]);
599        }
600        buf.append(" };");
601        logln(buf.toString());
602
603        TimeZone tz = TimeZone.getTimeZone("PST");
604        if (tz != null)
605            logln("getTimeZone(PST) = " + tz.getID());
606        else
607            errln("FAIL: getTimeZone(PST) = null");
608
609        tz = TimeZone.getTimeZone("America/Los_Angeles");
610        if (tz != null)
611            logln("getTimeZone(America/Los_Angeles) = " + tz.getID());
612        else
613            errln("FAIL: getTimeZone(PST) = null");
614
615        // Bug 4096694
616        tz = TimeZone.getTimeZone("NON_EXISTENT");
617        if (tz == null)
618            errln("FAIL: getTimeZone(NON_EXISTENT) = null");
619        else if (!tz.getID().equals("GMT"))
620            errln("FAIL: getTimeZone(NON_EXISTENT) = " + tz.getID());
621    }
622
623    /**
624     * Bug 4107276
625     */
626    public void TestDSTSavings() {
627        // It might be better to find a way to integrate this test into the main TimeZone
628        // tests above, but I don't have time to figure out how to do this (or if it's
629        // even really a good idea).  Let's consider that a future.  --rtg 1/27/98
630        SimpleTimeZone tz = new SimpleTimeZone(-5 * millisPerHour, "dstSavingsTest",
631                                               Calendar.MARCH, 1, 0, 0, Calendar.SEPTEMBER, 1, 0, 0,
632                                               (int)(0.5 * millisPerHour));
633
634        if (tz.getRawOffset() != -5 * millisPerHour)
635            errln("Got back a raw offset of " + (tz.getRawOffset() / millisPerHour) +
636                  " hours instead of -5 hours.");
637        if (!tz.useDaylightTime())
638            errln("Test time zone should use DST but claims it doesn't.");
639        if (tz.getDSTSavings() != 0.5 * millisPerHour)
640            errln("Set DST offset to 0.5 hour, but got back " + (tz.getDSTSavings() /
641                                                                 millisPerHour) + " hours instead.");
642
643        int offset = tz.getOffset(GregorianCalendar.AD, 1998, Calendar.JANUARY, 1,
644                                  Calendar.THURSDAY, 10 * millisPerHour);
645        if (offset != -5 * millisPerHour)
646            errln("The offset for 10 AM, 1/1/98 should have been -5 hours, but we got "
647                  + (offset / millisPerHour) + " hours.");
648
649        offset = tz.getOffset(GregorianCalendar.AD, 1998, Calendar.JUNE, 1, Calendar.MONDAY,
650                              10 * millisPerHour);
651        if (offset != -4.5 * millisPerHour)
652            errln("The offset for 10 AM, 6/1/98 should have been -4.5 hours, but we got "
653                  + (offset / millisPerHour) + " hours.");
654
655        tz.setDSTSavings(millisPerHour);
656        offset = tz.getOffset(GregorianCalendar.AD, 1998, Calendar.JANUARY, 1,
657                              Calendar.THURSDAY, 10 * millisPerHour);
658        if (offset != -5 * millisPerHour)
659            errln("The offset for 10 AM, 1/1/98 should have been -5 hours, but we got "
660                  + (offset / millisPerHour) + " hours.");
661
662        offset = tz.getOffset(GregorianCalendar.AD, 1998, Calendar.JUNE, 1, Calendar.MONDAY,
663                              10 * millisPerHour);
664        if (offset != -4 * millisPerHour)
665            errln("The offset for 10 AM, 6/1/98 (with a 1-hour DST offset) should have been -4 hours, but we got "
666                  + (offset / millisPerHour) + " hours.");
667    }
668
669    /**
670     * Bug 4107570
671     */
672    public void TestAlternateRules() {
673        // Like TestDSTSavings, this test should probably be integrated somehow with the main
674        // test at the top of this class, but I didn't have time to figure out how to do that.
675        //                      --rtg 1/28/98
676
677        SimpleTimeZone tz = new SimpleTimeZone(-5 * millisPerHour, "alternateRuleTest");
678
679        // test the day-of-month API
680        tz.setStartRule(Calendar.MARCH, 10, 12 * millisPerHour);
681        tz.setEndRule(Calendar.OCTOBER, 20, 12 * millisPerHour);
682
683        int offset = tz.getOffset(GregorianCalendar.AD, 1998, Calendar.MARCH, 5,
684                                  Calendar.THURSDAY, 10 * millisPerHour);
685        if (offset != -5 * millisPerHour)
686            errln("The offset for 10AM, 3/5/98 should have been -5 hours, but we got "
687                  + (offset / millisPerHour) + " hours.");
688
689        offset = tz.getOffset(GregorianCalendar.AD, 1998, Calendar.MARCH, 15,
690                              Calendar.SUNDAY, 10 * millisPerHour);
691        if (offset != -4 * millisPerHour)
692            errln("The offset for 10AM, 3/15/98 should have been -4 hours, but we got "
693                  + (offset / millisPerHour) + " hours.");
694
695        offset = tz.getOffset(GregorianCalendar.AD, 1998, Calendar.OCTOBER, 15,
696                              Calendar.THURSDAY, 10 * millisPerHour);
697        if (offset != -4 * millisPerHour)
698            errln("The offset for 10AM, 10/15/98 should have been -4 hours, but we got "
699                  + (offset / millisPerHour) + " hours.");
700
701        offset = tz.getOffset(GregorianCalendar.AD, 1998, Calendar.OCTOBER, 25,
702                              Calendar.SUNDAY, 10 * millisPerHour);
703        if (offset != -5 * millisPerHour)
704            errln("The offset for 10AM, 10/25/98 should have been -5 hours, but we got "
705                  + (offset / millisPerHour) + " hours.");
706
707        // test the day-of-week-after-day-in-month API
708        tz.setStartRule(Calendar.MARCH, 10, Calendar.FRIDAY, 12 * millisPerHour, true);
709        tz.setEndRule(Calendar.OCTOBER, 20, Calendar.FRIDAY, 12 * millisPerHour, false);
710
711        offset = tz.getOffset(GregorianCalendar.AD, 1998, Calendar.MARCH, 11,
712                              Calendar.WEDNESDAY, 10 * millisPerHour);
713        if (offset != -5 * millisPerHour)
714            errln("The offset for 10AM, 3/11/98 should have been -5 hours, but we got "
715                  + (offset / millisPerHour) + " hours.");
716
717        offset = tz.getOffset(GregorianCalendar.AD, 1998, Calendar.MARCH, 14,
718                              Calendar.SATURDAY, 10 * millisPerHour);
719        if (offset != -4 * millisPerHour)
720            errln("The offset for 10AM, 3/14/98 should have been -4 hours, but we got "
721                  + (offset / millisPerHour) + " hours.");
722
723        offset = tz.getOffset(GregorianCalendar.AD, 1998, Calendar.OCTOBER, 15,
724                              Calendar.THURSDAY, 10 * millisPerHour);
725        if (offset != -4 * millisPerHour)
726            errln("The offset for 10AM, 10/15/98 should have been -4 hours, but we got "
727                  + (offset / millisPerHour) + " hours.");
728
729        offset = tz.getOffset(GregorianCalendar.AD, 1998, Calendar.OCTOBER, 17,
730                              Calendar.SATURDAY, 10 * millisPerHour);
731        if (offset != -5 * millisPerHour)
732            errln("The offset for 10AM, 10/17/98 should have been -5 hours, but we got "
733                  + (offset / millisPerHour) + " hours.");
734    }
735}
736
737//eof
738