1// © 2016 and later: Unicode, Inc. and others.
2// License & terms of use: http://www.unicode.org/copyright.html#License
3/**
4 *******************************************************************************
5 * Copyright (C) 2000-2014, International Business Machines Corporation and    *
6 * others. All Rights Reserved.                                                *
7 *******************************************************************************
8 */
9
10/**
11 * @test 1.18 99/09/21
12 * @bug 4052967 4073209 4073215 4084933 4096952 4109314 4126678 4151406 4151429
13 * @bug 4154525 4154537 4154542 4154650 4159922 4162593 4173604 4176686 4184229 4208960
14 */
15
16package com.ibm.icu.dev.test.timezone;
17import java.io.ByteArrayInputStream;
18import java.io.ByteArrayOutputStream;
19import java.io.IOException;
20import java.io.ObjectInputStream;
21import java.io.ObjectOutputStream;
22import java.security.AccessControlException;
23import java.util.Date;
24import java.util.Locale;
25
26import org.junit.Test;
27
28import com.ibm.icu.dev.test.TestFmwk;
29import com.ibm.icu.text.DateFormat;
30import com.ibm.icu.text.SimpleDateFormat;
31import com.ibm.icu.util.Calendar;
32import com.ibm.icu.util.GregorianCalendar;
33import com.ibm.icu.util.SimpleTimeZone;
34import com.ibm.icu.util.TimeZone;
35import com.ibm.icu.util.ULocale;
36
37public class TimeZoneRegressionTest extends TestFmwk {
38    @Test
39    public void Test4052967() {
40        logln("*** CHECK TIMEZONE AGAINST HOST OS SETTING ***");
41        String id = TimeZone.getDefault().getID();
42        try {
43            try {
44                logln("user.timezone: " + System.getProperty("user.timezone", "<not set>"));
45            } catch (AccessControlException e) {
46                // user.timezone is a protected system property - ignore
47            }
48            logln("TimeZone.getDefault().getID(): " + id);
49            logln(new Date().toString());
50            logln("*** THE RESULTS OF THIS TEST MUST BE VERIFIED MANUALLY ***");
51        }
52        catch (SecurityException e) {
53            warnln("security exception: " + e.toString());
54        }
55    }
56
57    @Test
58    public void Test4073209() {
59        TimeZone z1 = TimeZone.getTimeZone("PST");
60        TimeZone z2 = TimeZone.getTimeZone("PST");
61        if (z1 == z2) errln("Fail: TimeZone should return clones");
62    }
63
64    @Test
65    public void Test4073215() {
66        SimpleTimeZone z = new SimpleTimeZone(0, "GMT");
67        if (z.useDaylightTime())
68            errln("Fail: Fix test to start with non-DST zone");
69        z.setStartRule(Calendar.FEBRUARY, 1, Calendar.SUNDAY, 0);
70        z.setEndRule(Calendar.MARCH, -1, Calendar.SUNDAY, 0);
71        if (!z.useDaylightTime())
72            errln("Fail: DST not active");
73        Calendar tempcal = Calendar.getInstance();
74        tempcal.clear();
75        tempcal.setTimeZone(z);
76        tempcal.set(1997, Calendar.JANUARY, 31);
77        Date d1 = tempcal.getTime();
78        if (z.inDaylightTime(d1)) {
79            errln("Fail: DST not working as expected");
80        }
81
82        tempcal.set(1997, Calendar.MARCH, 1);
83        Date d2 = tempcal.getTime();
84        if (!z.inDaylightTime(d2)) {
85            errln("Fail: DST not working as expected");
86        }
87        tempcal.clear();
88        tempcal.set(1997, Calendar.MARCH, 31);
89        Date d3 = tempcal.getTime();
90        if (z.inDaylightTime(d3)) {
91            errln("Fail: DST not working as expected");
92        }
93    }
94
95    /**
96     * The expected behavior of TimeZone around the boundaries is:
97     * (Assume transition time of 2:00 AM)
98     *    day of onset 1:59 AM STD  = display name 1:59 AM ST
99     *                 2:00 AM STD  = display name 3:00 AM DT
100     *    day of end   0:59 AM STD  = display name 1:59 AM DT
101     *                 1:00 AM STD  = display name 1:00 AM ST
102     */
103    @Test
104    public void Test4084933() {
105        TimeZone tz = TimeZone.getTimeZone("PST");
106
107        long offset1 = tz.getOffset(1,
108            1997, Calendar.OCTOBER, 26, Calendar.SUNDAY, (2*60*60*1000));
109        long offset2 = tz.getOffset(1,
110            1997, Calendar.OCTOBER, 26, Calendar.SUNDAY, (2*60*60*1000)-1);
111
112        long offset3 = tz.getOffset(1,
113            1997, Calendar.OCTOBER, 26, Calendar.SUNDAY, (1*60*60*1000));
114        long offset4 = tz.getOffset(1,
115            1997, Calendar.OCTOBER, 26, Calendar.SUNDAY, (1*60*60*1000)-1);
116
117        /*
118         *  The following was added just for consistency.  It shows that going *to* Daylight
119         *  Savings Time (PDT) does work at 2am.
120         */
121
122        long offset5 = tz.getOffset(1,
123            1997, Calendar.APRIL, 6, Calendar.SUNDAY, (2*60*60*1000));
124        long offset6 = tz.getOffset(1,
125            1997, Calendar.APRIL, 6, Calendar.SUNDAY, (2*60*60*1000)-1);
126
127        long offset7 = tz.getOffset(1,
128            1997, Calendar.APRIL, 6, Calendar.SUNDAY, (1*60*60*1000));
129        long offset8 = tz.getOffset(1,
130            1997, Calendar.APRIL, 6, Calendar.SUNDAY, (1*60*60*1000)-1);
131
132        long SToffset = -8 * 60*60*1000L;
133        long DToffset = -7 * 60*60*1000L;
134        if (offset1 != SToffset || offset2 != SToffset ||
135            offset3 != SToffset || offset4 != DToffset ||
136            offset5 != DToffset || offset6 != SToffset ||
137            offset7 != SToffset || offset8 != SToffset)
138            warnln("Fail: TimeZone misbehaving");
139    }
140
141    @Test
142    public void Test4096952() {
143        String[] ZONES = { "GMT", "MET", "IST" };
144        boolean pass = true;
145        try {
146            for (int i=0; i<ZONES.length; ++i) {
147                TimeZone zone = TimeZone.getTimeZone(ZONES[i]);
148                if (!zone.getID().equals(ZONES[i]))
149                    warnln("Fail: Test broken; zones not instantiating");
150
151                ByteArrayOutputStream baos;
152                ObjectOutputStream ostream =
153                    new ObjectOutputStream(baos = new
154                                           ByteArrayOutputStream());
155                ostream.writeObject(zone);
156                ostream.close();
157                baos.close();
158                ObjectInputStream istream =
159                    new ObjectInputStream(new
160                                          ByteArrayInputStream(baos.toByteArray()));
161                TimeZone frankenZone = (TimeZone) istream.readObject();
162                //logln("Zone:        " + zone);
163                //logln("FrankenZone: " + frankenZone);
164                if (!zone.equals(frankenZone)) {
165                    logln("TimeZone " + zone.getID() +
166                          " not equal to serialized/deserialized one");
167                    pass = false;
168                }
169            }
170            if (!pass) errln("Fail: TimeZone serialization/equality bug");
171        }
172        catch (IOException e) {
173            errln("Fail: " + e);
174            e.printStackTrace();
175        }
176        catch (ClassNotFoundException e) {
177            errln("Fail: " + e);
178            e.printStackTrace();
179        }
180    }
181
182    @Test
183    public void Test4109314() {
184        GregorianCalendar testCal = (GregorianCalendar)Calendar.getInstance();
185        TimeZone PST = TimeZone.getTimeZone("PST");
186        java.util.Calendar tempcal = java.util.Calendar.getInstance();
187        tempcal.clear();
188        tempcal.set(1998,Calendar.APRIL,4,22,0);
189        Date d1 = tempcal.getTime();
190        tempcal.set(1998,Calendar.APRIL,5,6,0);
191        Date d2 = tempcal.getTime();
192        tempcal.set(1998,Calendar.OCTOBER,24,22,0);
193        Date d3 = tempcal.getTime();
194        tempcal.set(1998,Calendar.OCTOBER,25,6,0);
195        Date d4 = tempcal.getTime();
196        Object[] testData = {
197            PST, d1, d2,
198            PST, d3, d4,
199        };
200        boolean pass=true;
201        for (int i=0; i<testData.length; i+=3) {
202            testCal.setTimeZone((TimeZone) testData[i]);
203            long t = ((Date)testData[i+1]).getTime();
204            Date end = (Date) testData[i+2];
205            while (t < end.getTime()) {
206                testCal.setTime(new Date(t));
207                if (!checkCalendar314(testCal, (TimeZone) testData[i]))
208                    pass = false;
209                t += 60*60*1000L;
210            }
211        }
212        if (!pass) errln("Fail: TZ API inconsistent");
213    }
214
215    boolean checkCalendar314(GregorianCalendar testCal, TimeZone testTZ) {
216        // GregorianCalendar testCal = (GregorianCalendar)aCal.clone();
217
218        final int ONE_DAY = 24*60*60*1000;
219
220        int tzOffset, tzRawOffset;
221        Float tzOffsetFloat,tzRawOffsetFloat;
222        // Here is where the user made an error.  They were passing in the value of
223        // the MILLSECOND field; you need to pass in the millis in the day in STANDARD
224        // time.
225        int millis = testCal.get(Calendar.MILLISECOND) +
226            1000 * (testCal.get(Calendar.SECOND) +
227                    60 * (testCal.get(Calendar.MINUTE) +
228                          60 * (testCal.get(Calendar.HOUR_OF_DAY)))) -
229            testCal.get(Calendar.DST_OFFSET);
230
231        /* Fix up millis to be in range.  ASSUME THAT WE ARE NOT AT THE
232         * BEGINNING OR END OF A MONTH.  We must add this code because
233         * getOffset() has been changed to be more strict about the parameters
234         * it receives -- it turns out that this test was passing in illegal
235         * values. */
236        int date = testCal.get(Calendar.DATE);
237        int dow  = testCal.get(Calendar.DAY_OF_WEEK);
238        while (millis < 0) {
239            millis += ONE_DAY;
240            --date;
241            dow = Calendar.SUNDAY + ((dow - Calendar.SUNDAY + 6) % 7);
242        }
243        while (millis >= ONE_DAY) {
244            millis -= ONE_DAY;
245            ++date;
246            dow = Calendar.SUNDAY + ((dow - Calendar.SUNDAY + 1) % 7);
247        }
248
249        tzOffset = testTZ.getOffset(testCal.get(Calendar.ERA),
250                                    testCal.get(Calendar.YEAR),
251                                    testCal.get(Calendar.MONTH),
252                                    date,
253                                    dow,
254                                    millis);
255        tzRawOffset = testTZ.getRawOffset();
256        tzOffsetFloat = new Float((float)tzOffset/(float)3600000);
257        tzRawOffsetFloat = new Float((float)tzRawOffset/(float)3600000);
258
259        Date testDate = testCal.getTime();
260
261        boolean inDaylightTime = testTZ.inDaylightTime(testDate);
262        SimpleDateFormat sdf = new SimpleDateFormat("MM/dd/yyyy HH:mm");
263        sdf.setCalendar(testCal);
264        String inDaylightTimeString;
265
266        boolean passed;
267
268        if (inDaylightTime)
269        {
270            inDaylightTimeString = " DST ";
271            passed = (tzOffset == (tzRawOffset + 3600000));
272        }
273        else
274        {
275            inDaylightTimeString = "     ";
276            passed = (tzOffset == tzRawOffset);
277        }
278
279        String output = testTZ.getID() + " " + sdf.format(testDate) +
280            " Offset(" + tzOffsetFloat + ")" +
281            " RawOffset(" + tzRawOffsetFloat + ")" +
282            " " + millis/(float)3600000 + " " +
283            inDaylightTimeString;
284
285        if (passed)
286            output += "     ";
287        else
288            output += "ERROR";
289
290        if (passed) logln(output); else errln(output);
291        return passed;
292    }
293
294    /**
295     * CANNOT REPRODUDE
296     *
297     * Yet another _alleged_ bug in TimeZone.getOffset(), a method that never
298     * should have been made public.  It's simply too hard to use correctly.
299     *
300     * The original test code failed to do the following:
301     * (1) Call Calendar.setTime() before getting the fields!
302     * (2) Use the right millis (as usual) for getOffset(); they were passing
303     *     in the MILLIS field, instead of the STANDARD MILLIS IN DAY.
304     * When you fix these two problems, the test passes, as expected.
305     */
306    @Test
307    public void Test4126678() {
308    // Note: this test depends on the PST time zone.
309    TimeZone initialZone = TimeZone.getDefault();
310        Calendar cal = Calendar.getInstance();
311        TimeZone tz = TimeZone.getTimeZone("PST");
312    TimeZone.setDefault(tz);
313        cal.setTimeZone(tz);
314
315        java.util.Calendar tempcal = java.util.Calendar.getInstance();
316        tempcal.clear();
317        tempcal.set(1998, Calendar.APRIL, 5, 10, 0);
318        Date dt = tempcal.getTime();
319    // the dt value is local time in PST.
320        if (!tz.inDaylightTime(dt))
321            errln("We're not in Daylight Savings Time and we should be.\n");
322
323        cal.setTime(dt);
324        int era = cal.get(Calendar.ERA);
325        int year = cal.get(Calendar.YEAR);
326        int month = cal.get(Calendar.MONTH);
327        int day = cal.get(Calendar.DATE);
328        int dayOfWeek = cal.get(Calendar.DAY_OF_WEEK);
329        int millis = cal.get(Calendar.MILLISECOND) +
330            (cal.get(Calendar.SECOND) +
331             (cal.get(Calendar.MINUTE) +
332              (cal.get(Calendar.HOUR) * 60) * 60) * 1000) -
333            cal.get(Calendar.DST_OFFSET);
334
335        long offset = tz.getOffset(era, year, month, day, dayOfWeek, millis);
336        long raw_offset = tz.getRawOffset();
337        if (offset == raw_offset)
338            errln("Offsets should not match when in DST");
339
340    // restore the initial time zone so that this test case
341    // doesn't affect the others.
342    TimeZone.setDefault(initialZone);
343    }
344
345    /**
346     * TimeZone.getAvailableIDs(int) throws exception for certain values,
347     * due to a faulty constant in TimeZone.java.
348     */
349    @Test
350    public void Test4151406() {
351        int max = 0;
352        for (int h=-28; h<=30; ++h) {
353            // h is in half-hours from GMT; rawoffset is in millis
354            int rawoffset = h * 1800000;
355            int hh = (h<0) ? -h : h;
356            String hname = ((h<0) ? "GMT-" : "GMT+") +
357                ((hh/2 < 10) ? "0" : "") +
358                (hh/2) + ':' +
359                ((hh%2==0) ? "00" : "30");
360            try {
361                String[] ids = TimeZone.getAvailableIDs(rawoffset);
362                if (ids.length > max) max = ids.length;
363                logln(hname + ' ' + ids.length +
364                      ((ids.length > 0) ? (" e.g. " + ids[0]) : ""));
365            } catch (Exception e) {
366                errln(hname + ' ' + "Fail: " + e);
367            }
368        }
369        logln("Maximum zones per offset = " + max);
370    }
371
372    @Test
373    public void Test4151429() {
374        try {
375            TimeZone tz = TimeZone.getTimeZone("GMT");
376            /*String name =*/ tz.getDisplayName(true, Integer.MAX_VALUE,
377                                            Locale.getDefault());
378            errln("IllegalArgumentException not thrown by TimeZone.getDisplayName()");
379        } catch(IllegalArgumentException e) {
380            System.out.print("");
381        }
382    }
383
384    /**
385     * SimpleTimeZone accepts illegal DST savings values.  These values
386     * must be non-zero.  There is no upper limit at this time.
387     */
388    @Test
389    public void Test4154525() {
390        final int GOOD = 1, BAD = 0;
391        int[] DATA = {
392            1, GOOD,
393            0, BAD,
394            -1, BAD,
395            60*60*1000, GOOD,
396            Integer.MIN_VALUE, BAD,
397            // Integer.MAX_VALUE, ?, // no upper limit on DST savings at this time
398        };
399        for (int i=0; i<DATA.length; i+=2) {
400            int savings = DATA[i];
401            boolean valid = DATA[i+1] == GOOD;
402            String method = null;
403            for (int j=0; j<2; ++j) {
404                try {
405                    switch (j) {
406                    case 0:
407                        method = "constructor";
408                        SimpleTimeZone z = new SimpleTimeZone(0, "id",
409                            Calendar.JANUARY, 1, 0, 0,
410                            Calendar.MARCH, 1, 0, 0,
411                            savings); // <- what we're interested in
412                        break;
413                    case 1:
414                        method = "setDSTSavings()";
415                        z = new SimpleTimeZone(0, "GMT");
416                        z.setDSTSavings(savings);
417                        break;
418                    }
419                    if (valid) {
420                        logln("Pass: DST savings of " + savings + " accepted by " + method);
421                    } else {
422                        errln("Fail: DST savings of " + savings + " accepted by " + method);
423                    }
424                } catch (IllegalArgumentException e) {
425                    if (valid) {
426                        errln("Fail: DST savings of " + savings + " to " + method + " gave " + e);
427                    } else {
428                        logln("Pass: DST savings of " + savings + " to " + method + " gave " + e);
429                    }
430                }
431            }
432        }
433    }
434
435    /**
436     * SimpleTimeZone.hasSameRules() doesn't work for zones with no DST
437     * and different DST parameters.
438     */
439    @Test
440    public void Test4154537() {
441        // tz1 and tz2 have no DST and different rule parameters
442        SimpleTimeZone tz1 = new SimpleTimeZone(0, "1", 0, 0, 0, 0, 2, 0, 0, 0);
443        SimpleTimeZone tz2 = new SimpleTimeZone(0, "2", 1, 0, 0, 0, 3, 0, 0, 0);
444        // tza and tzA have the same rule params
445        SimpleTimeZone tza = new SimpleTimeZone(0, "a", 0, 1, 0, 0, 3, 2, 0, 0);
446        SimpleTimeZone tzA = new SimpleTimeZone(0, "A", 0, 1, 0, 0, 3, 2, 0, 0);
447        // tzb differs from tza
448        SimpleTimeZone tzb = new SimpleTimeZone(0, "b", 0, 1, 0, 0, 3, 1, 0, 0);
449        if (tz1.useDaylightTime() || tz2.useDaylightTime() ||
450            !tza.useDaylightTime() || !tzA.useDaylightTime() ||
451            !tzb.useDaylightTime()) {
452            errln("Test is broken -- rewrite it");
453        }
454        if (!tza.hasSameRules(tzA) || tza.hasSameRules(tzb)) {
455            errln("Fail: hasSameRules() broken for zones with rules");
456        }
457        if (!tz1.hasSameRules(tz2)) {
458            errln("Fail: hasSameRules() returns false for zones without rules");
459            errln("zone 1 = " + tz1);
460            errln("zone 2 = " + tz2);
461        }
462    }
463
464    /**
465     * SimpleTimeZone constructors, setStartRule(), and setEndRule() don't
466     * check for out-of-range arguments.
467     */
468    @Test
469    public void Test4154542() {
470        final int GOOD = 1;
471        final int BAD  = 0;
472
473        final int GOOD_MONTH       = Calendar.JANUARY;
474        final int GOOD_DAY         = 1;
475        final int GOOD_DAY_OF_WEEK = Calendar.SUNDAY;
476        final int GOOD_TIME        = 0;
477
478        int[] DATA = {
479            GOOD, Integer.MIN_VALUE,    0,  Integer.MAX_VALUE,   Integer.MIN_VALUE,
480            GOOD, Calendar.JANUARY,    -5,  Calendar.SUNDAY,     0,
481            GOOD, Calendar.DECEMBER,    5,  Calendar.SATURDAY,   24*60*60*1000-1,
482            BAD,  Calendar.DECEMBER,    5,  Calendar.SATURDAY,   24*60*60*1000+1,
483            BAD,  Calendar.DECEMBER,    5,  Calendar.SATURDAY,  -1,
484            BAD,  Calendar.JANUARY,    -6,  Calendar.SUNDAY,     0,
485            BAD,  Calendar.DECEMBER,    6,  Calendar.SATURDAY,   24*60*60*1000,
486            GOOD, Calendar.DECEMBER,    1,  0,                   0,
487            GOOD, Calendar.DECEMBER,   31,  0,                   0,
488            BAD,  Calendar.APRIL,      31,  0,                   0,
489            BAD,  Calendar.DECEMBER,   32,  0,                   0,
490            BAD,  Calendar.JANUARY-1,   1,  Calendar.SUNDAY,     0,
491            BAD,  Calendar.DECEMBER+1,  1,  Calendar.SUNDAY,     0,
492            GOOD, Calendar.DECEMBER,   31, -Calendar.SUNDAY,     0,
493            GOOD, Calendar.DECEMBER,   31, -Calendar.SATURDAY,   0,
494            BAD,  Calendar.DECEMBER,   32, -Calendar.SATURDAY,   0,
495            BAD,  Calendar.DECEMBER,  -32, -Calendar.SATURDAY,   0,
496            BAD,  Calendar.DECEMBER,   31, -Calendar.SATURDAY-1, 0,
497        };
498        SimpleTimeZone zone = new SimpleTimeZone(0, "Z");
499        for (int i=0; i<DATA.length; i+=5) {
500            boolean shouldBeGood = (DATA[i] == GOOD);
501            int month     = DATA[i+1];
502            int day       = DATA[i+2];
503            int dayOfWeek = DATA[i+3];
504            int time      = DATA[i+4];
505
506            Exception ex = null;
507            try {
508                zone.setStartRule(month, day, dayOfWeek, time);
509            } catch (IllegalArgumentException e) {
510                ex = e;
511            }
512            if ((ex == null) != shouldBeGood) {
513                errln("setStartRule(month=" + month + ", day=" + day +
514                      ", dayOfWeek=" + dayOfWeek + ", time=" + time +
515                      (shouldBeGood ? (") should work but throws " + ex)
516                       : ") should fail but doesn't"));
517            }
518
519            ex = null;
520            try {
521                zone.setEndRule(month, day, dayOfWeek, time);
522            } catch (IllegalArgumentException e) {
523                ex = e;
524            }
525            if ((ex == null) != shouldBeGood) {
526                errln("setEndRule(month=" + month + ", day=" + day +
527                      ", dayOfWeek=" + dayOfWeek + ", time=" + time +
528                      (shouldBeGood ? (") should work but throws " + ex)
529                       : ") should fail but doesn't"));
530            }
531
532            ex = null;
533            try {
534                /*SimpleTimeZone temp =*/ new SimpleTimeZone(0, "Z",
535                        month, day, dayOfWeek, time,
536                        GOOD_MONTH, GOOD_DAY, GOOD_DAY_OF_WEEK, GOOD_TIME);
537            } catch (IllegalArgumentException e) {
538                ex = e;
539            }
540            if ((ex == null) != shouldBeGood) {
541                errln("SimpleTimeZone(month=" + month + ", day=" + day +
542                      ", dayOfWeek=" + dayOfWeek + ", time=" + time +
543                      (shouldBeGood ? (", <end>) should work but throws " + ex)
544                       : ", <end>) should fail but doesn't"));
545            }
546
547            ex = null;
548            try {
549                /*SimpleTimeZone temp = */new SimpleTimeZone(0, "Z",
550                        GOOD_MONTH, GOOD_DAY, GOOD_DAY_OF_WEEK, GOOD_TIME,
551                        month, day, dayOfWeek, time);
552               // temp = null;
553            } catch (IllegalArgumentException e) {
554                ex = e;
555            }
556            if ((ex == null) != shouldBeGood) {
557                errln("SimpleTimeZone(<start>, month=" + month + ", day=" + day +
558                      ", dayOfWeek=" + dayOfWeek + ", time=" + time +
559                      (shouldBeGood ? (") should work but throws " + ex)
560                       : ") should fail but doesn't"));
561            }
562        }
563    }
564
565    /**
566     * SimpleTimeZone.getOffset accepts illegal arguments.
567     */
568    @Test
569    public void Test4154650() {
570        final int GOOD=1, BAD=0;
571        final int GOOD_ERA=GregorianCalendar.AD, GOOD_YEAR=1998, GOOD_MONTH=Calendar.AUGUST;
572        final int GOOD_DAY=2, GOOD_DOW=Calendar.SUNDAY, GOOD_TIME=16*3600000;
573        int[] DATA = {
574            GOOD, GOOD_ERA, GOOD_YEAR, GOOD_MONTH, GOOD_DAY, GOOD_DOW, GOOD_TIME,
575
576            GOOD, GregorianCalendar.BC, GOOD_YEAR, GOOD_MONTH, GOOD_DAY, GOOD_DOW, GOOD_TIME,
577            GOOD, GregorianCalendar.AD, GOOD_YEAR, GOOD_MONTH, GOOD_DAY, GOOD_DOW, GOOD_TIME,
578            BAD,  GregorianCalendar.BC-1, GOOD_YEAR, GOOD_MONTH, GOOD_DAY, GOOD_DOW, GOOD_TIME,
579            BAD,  GregorianCalendar.AD+1, GOOD_YEAR, GOOD_MONTH, GOOD_DAY, GOOD_DOW, GOOD_TIME,
580
581            GOOD, GOOD_ERA, GOOD_YEAR, Calendar.JANUARY, GOOD_DAY, GOOD_DOW, GOOD_TIME,
582            GOOD, GOOD_ERA, GOOD_YEAR, Calendar.DECEMBER, GOOD_DAY, GOOD_DOW, GOOD_TIME,
583            BAD,  GOOD_ERA, GOOD_YEAR, Calendar.JANUARY-1, GOOD_DAY, GOOD_DOW, GOOD_TIME,
584            BAD,  GOOD_ERA, GOOD_YEAR, Calendar.DECEMBER+1, GOOD_DAY, GOOD_DOW, GOOD_TIME,
585
586            GOOD, GOOD_ERA, GOOD_YEAR, Calendar.JANUARY, 1, GOOD_DOW, GOOD_TIME,
587            GOOD, GOOD_ERA, GOOD_YEAR, Calendar.JANUARY, 31, GOOD_DOW, GOOD_TIME,
588            BAD,  GOOD_ERA, GOOD_YEAR, Calendar.JANUARY, 0, GOOD_DOW, GOOD_TIME,
589            BAD,  GOOD_ERA, GOOD_YEAR, Calendar.JANUARY, 32, GOOD_DOW, GOOD_TIME,
590
591            GOOD, GOOD_ERA, GOOD_YEAR, GOOD_MONTH, GOOD_DAY, Calendar.SUNDAY, GOOD_TIME,
592            GOOD, GOOD_ERA, GOOD_YEAR, GOOD_MONTH, GOOD_DAY, Calendar.SATURDAY, GOOD_TIME,
593            BAD,  GOOD_ERA, GOOD_YEAR, GOOD_MONTH, GOOD_DAY, Calendar.SUNDAY-1, GOOD_TIME,
594            BAD,  GOOD_ERA, GOOD_YEAR, GOOD_MONTH, GOOD_DAY, Calendar.SATURDAY+1, GOOD_TIME,
595
596            GOOD, GOOD_ERA, GOOD_YEAR, GOOD_MONTH, GOOD_DAY, GOOD_DOW, 0,
597            GOOD, GOOD_ERA, GOOD_YEAR, GOOD_MONTH, GOOD_DAY, GOOD_DOW, 24*3600000-1,
598            BAD,  GOOD_ERA, GOOD_YEAR, GOOD_MONTH, GOOD_DAY, GOOD_DOW, -1,
599            BAD,  GOOD_ERA, GOOD_YEAR, GOOD_MONTH, GOOD_DAY, GOOD_DOW, 24*3600000,
600        };
601
602        TimeZone tz = TimeZone.getDefault();
603        for (int i=0; i<DATA.length; i+=7) {
604            boolean good = DATA[i] == GOOD;
605            IllegalArgumentException e = null;
606            try {
607                /*int offset =*/ tz.getOffset(DATA[i+1], DATA[i+2], DATA[i+3],
608                                          DATA[i+4], DATA[i+5], DATA[i+6]);
609                //offset = 0;
610           } catch (IllegalArgumentException ex) {
611                e = ex;
612            }
613            if (good != (e == null)) {
614                errln("Fail: getOffset(" +
615                      DATA[i+1] + ", " + DATA[i+2] + ", " + DATA[i+3] + ", " +
616                      DATA[i+4] + ", " + DATA[i+5] + ", " + DATA[i+6] +
617                      (good ? (") threw " + e) : ") accepts invalid args"));
618            }
619        }
620    }
621
622    /**
623     * TimeZone constructors allow null IDs.
624     */
625    @Test
626    public void Test4159922() {
627        TimeZone z = null;
628
629        // TimeZone API.  Only hasSameRules() and setDefault() should
630        // allow null.
631        try {
632            z = TimeZone.getTimeZone(null);
633            errln("FAIL: Null allowed in getTimeZone");
634        } catch (NullPointerException e) {
635            System.out.print("");
636        }
637        z = TimeZone.getTimeZone("GMT");
638        try {
639        // {dlf} requiring cast for disambiguation is ok for compatibility since null
640        // is not a valid argument to this API
641            z.getDisplayName(false, TimeZone.SHORT, (ULocale)null);
642            errln("FAIL: Null allowed in getDisplayName(3)");
643        } catch (NullPointerException e) {
644            System.out.print("");
645        }
646        try {
647        // {dlf} see above
648            z.getDisplayName((ULocale)null);
649            errln("FAIL: Null allowed in getDisplayName(1)");
650        } catch (NullPointerException e) {
651            System.out.print("");
652        }
653        try {
654            if (z.hasSameRules(null)) {
655                errln("FAIL: hasSameRules returned true");
656            }
657        } catch (NullPointerException e) {
658            errln("FAIL: Null NOT allowed in hasSameRules");
659        }
660        try {
661            z.inDaylightTime(null);
662            errln("FAIL: Null allowed in inDaylightTime");
663        } catch (NullPointerException e) {
664            System.out.print("");
665        }
666        try {
667            z.setID(null);
668            errln("FAIL: Null allowed in setID");
669        } catch (NullPointerException e) {
670            System.out.print("");
671        }
672
673        TimeZone save = TimeZone.getDefault();
674        try {
675            TimeZone.setDefault(null);
676        } catch (NullPointerException e) {
677            errln("FAIL: Null NOT allowed in setDefault");
678        } finally {
679            TimeZone.setDefault(save);
680        }
681
682        // SimpleTimeZone API
683        SimpleTimeZone s = null;
684        try {
685            s = new SimpleTimeZone(0, null);
686            errln("FAIL: Null allowed in SimpleTimeZone(2)");
687        } catch (NullPointerException e) {
688            System.out.print("");
689        }
690        try {
691            s = new SimpleTimeZone(0, null, 0, 1, 0, 0, 0, 1, 0, 0);
692            errln("FAIL: Null allowed in SimpleTimeZone(10)");
693        } catch (NullPointerException e) {
694            System.out.print("");
695        }
696        try {
697            s = new SimpleTimeZone(0, null, 0, 1, 0, 0, 0, 1, 0, 0, 1000);
698            errln("FAIL: Null allowed in SimpleTimeZone(11)");
699        } catch (NullPointerException e) {
700            System.out.print("");
701        }
702        if(s!=null){
703            errln("FAIL: Did not get the expected Exception");
704        }
705    }
706
707    /**
708     * TimeZone broken at midnight.  The TimeZone code fails to handle
709     * transitions at midnight correctly.
710     */
711    @Test
712    public void Test4162593() {
713        SimpleDateFormat fmt = new SimpleDateFormat("z", Locale.US);
714        final int ONE_HOUR = 60*60*1000;
715        final float H = (float) ONE_HOUR;
716        TimeZone initialZone = TimeZone.getDefault();
717        SimpleDateFormat sdf = new SimpleDateFormat("MMM dd yyyy HH:mm z");
718
719        SimpleTimeZone asuncion = new SimpleTimeZone(-4*ONE_HOUR, "America/Asuncion" /*PY%sT*/,
720            Calendar.OCTOBER, 1, 0 /*DOM*/, 0*ONE_HOUR,
721            Calendar.MARCH, 1, 0 /*DOM*/, 0*ONE_HOUR, 1*ONE_HOUR);
722
723        /* Zone
724         * Starting time
725         * Transition expected between start+1H and start+2H
726         */
727        Object[] DATA = {
728            new SimpleTimeZone(2*ONE_HOUR, "Asia/Damascus" /*EE%sT*/,
729                Calendar.APRIL, 1, 0 /*DOM*/, 0*ONE_HOUR,
730                Calendar.OCTOBER, 1, 0 /*DOM*/, 0*ONE_HOUR, 1*ONE_HOUR),
731            new int[] {1998, Calendar.SEPTEMBER, 30, 22, 0},
732            Boolean.TRUE,
733
734            asuncion,
735            new int[] {2000, Calendar.FEBRUARY, 28, 22, 0},
736            Boolean.FALSE,
737
738            asuncion,
739            new int[] {2000, Calendar.FEBRUARY, 29, 22, 0},
740            Boolean.TRUE,
741        };
742
743        String[] zone = new String[4];
744
745        for (int j=0; j<DATA.length; j+=3) {
746            TimeZone tz = (TimeZone)DATA[j];
747            TimeZone.setDefault(tz);
748            fmt.setTimeZone(tz);
749            sdf.setTimeZone(tz);
750
751            // Must construct the Date object AFTER setting the default zone
752            int[] p = (int[])DATA[j+1];
753            Calendar cal = Calendar.getInstance();
754            cal.clear();
755            cal.set(p[0], p[1], p[2], p[3], p[4]);
756            long start = cal.getTime().getTime();
757            boolean transitionExpected = ((Boolean)DATA[j+2]).booleanValue();
758
759            logln(tz.getID() + ":");
760            for (int i=0; i<4; ++i) {
761                Date d = new Date(start + i*ONE_HOUR);
762                zone[i] = fmt.format(d);
763                logln("" + i + ": " + sdf.format(d) + " => " + zone[i] +
764                      " (" + d.getTime()/H + ")");
765            }
766            cal.set(p[0], p[1], p[2], 0, 0);
767            for (int i=0; i<4; ++i) {
768                int h = 22+i;
769                int dom = p[2]+(h>=24?1:0);
770                h %= 24;
771                int ms = h*ONE_HOUR;
772                cal.clear();
773                cal.set(p[0], p[1], dom, 0, 0);
774                int off = tz.getOffset(GregorianCalendar.AD,
775                                       cal.get(Calendar.YEAR),
776                                       cal.get(Calendar.MONTH),
777                                       cal.get(Calendar.DATE),
778                                       cal.get(Calendar.DAY_OF_WEEK),
779                                       ms);
780                cal.add(Calendar.HOUR, h);
781                int dstOffset = cal.get(Calendar.DST_OFFSET);
782                logln("h=" + h + "; dom=" + dom +
783                      "; ZONE_OFFSET=" + cal.get(Calendar.ZONE_OFFSET)/H +
784                      "; DST_OFFSET=" + dstOffset/H +
785                      "; getOffset()=" + off/H +
786                      " (" + cal.getTime().getTime()/H + ")");
787            }
788            if (zone[0].equals(zone[1]) &&
789                (zone[1].equals(zone[2]) != transitionExpected) &&
790                zone[2].equals(zone[3])) {
791                logln("Ok: transition " + transitionExpected);
792            } else {
793                errln("FAIL: expected " +
794                      (transitionExpected?"transition":"no transition"));
795            }
796        }
797
798    // restore the initial time zone so that this test case
799    // doesn't affect the others.
800    TimeZone.setDefault(initialZone);
801    }
802
803    /**
804     * TimeZone broken in last hour of year
805     */
806    @Test
807    public void Test4173604() {
808        TimeZone pst = TimeZone.getTimeZone("PST");
809        int o22 = pst.getOffset(1, 1998, 11, 31, Calendar.THURSDAY, 22*60*60*1000);
810        int o23 = pst.getOffset(1, 1998, 11, 31, Calendar.THURSDAY, 23*60*60*1000);
811        int o00 = pst.getOffset(1, 1999, 0, 1, Calendar.FRIDAY, 0);
812        if (o22 != o23 || o22 != o00) {
813            errln("Offsets should be the same (for PST), but got: " +
814                  "12/31 22:00 " + o22 +
815                  ", 12/31 23:00 " + o23 +
816                  ", 01/01 00:00 " + o00);
817        }
818
819        GregorianCalendar cal = new GregorianCalendar();
820        cal.setTimeZone(pst);
821        cal.clear();
822        cal.set(1998, Calendar.JANUARY, 1);
823        int lastDST = cal.get(Calendar.DST_OFFSET);
824        int transitions = 0;
825        int delta = 5;
826        while (cal.get(Calendar.YEAR) < 2000) {
827            cal.add(Calendar.MINUTE, delta);
828            if (cal.get(Calendar.DST_OFFSET) != lastDST) {
829                ++transitions;
830                Calendar t = (Calendar)cal.clone();
831                t.add(Calendar.MINUTE, -delta);
832                logln(t.getTime() + "  " + t.get(Calendar.DST_OFFSET));
833                logln(cal.getTime() + "  " + (lastDST=cal.get(Calendar.DST_OFFSET)));
834            }
835        }
836        if (transitions != 4) {
837            errln("Saw " + transitions + " transitions; should have seen 4");
838        }
839    }
840
841    /**
842     * getDisplayName doesn't work with unusual savings/offsets.
843     */
844    @Test
845    public void Test4176686() {
846        // Construct a zone that does not observe DST but
847        // that does have a DST savings (which should be ignored).
848        int offset = 90 * 60000; // 1:30
849        SimpleTimeZone z1 = new SimpleTimeZone(offset, "_std_zone_");
850        z1.setDSTSavings(45 * 60000); // 0:45
851
852        // Construct a zone that observes DST for the first 6 months.
853        SimpleTimeZone z2 = new SimpleTimeZone(offset, "_dst_zone_");
854        z2.setDSTSavings(45 * 60000); // 0:45
855        z2.setStartRule(Calendar.JANUARY, 1, 0);
856        z2.setEndRule(Calendar.JULY, 1, 0);
857
858        // Also check DateFormat
859        DateFormat fmt1 = new SimpleDateFormat("z");
860        fmt1.setTimeZone(z1); // Format uses standard zone
861        DateFormat fmt2 = new SimpleDateFormat("z");
862        fmt2.setTimeZone(z2); // Format uses DST zone
863        java.util.Calendar tempcal = java.util.Calendar.getInstance();
864        tempcal.clear();
865        tempcal.set(1970, Calendar.FEBRUARY, 1);
866        Date dst = tempcal.getTime(); // Time in DST
867        tempcal.set(1970, Calendar.AUGUST, 1);
868        Date std = tempcal.getTime(); // Time in standard
869
870        // Description, Result, Expected Result
871        String[] DATA = {
872            "getDisplayName(false, SHORT)/std zone",
873            z1.getDisplayName(false, TimeZone.SHORT), "GMT+1:30",
874            "getDisplayName(false, LONG)/std zone",
875            z1.getDisplayName(false, TimeZone.LONG ), "GMT+01:30",
876            "getDisplayName(true, SHORT)/std zone",
877            z1.getDisplayName(true, TimeZone.SHORT), "GMT+1:30",
878            "getDisplayName(true, LONG)/std zone",
879            z1.getDisplayName(true, TimeZone.LONG ), "GMT+01:30",
880            "getDisplayName(false, SHORT)/dst zone",
881            z2.getDisplayName(false, TimeZone.SHORT), "GMT+1:30",
882            "getDisplayName(false, LONG)/dst zone",
883            z2.getDisplayName(false, TimeZone.LONG ), "GMT+01:30",
884            "getDisplayName(true, SHORT)/dst zone",
885            z2.getDisplayName(true, TimeZone.SHORT), "GMT+2:15",
886            "getDisplayName(true, LONG)/dst zone",
887            z2.getDisplayName(true, TimeZone.LONG ), "GMT+02:15",
888            "DateFormat.format(std)/std zone", fmt1.format(std), "GMT+1:30",
889            "DateFormat.format(dst)/std zone", fmt1.format(dst), "GMT+1:30",
890            "DateFormat.format(std)/dst zone", fmt2.format(std), "GMT+1:30",
891            "DateFormat.format(dst)/dst zone", fmt2.format(dst), "GMT+2:15",
892        };
893
894        for (int i=0; i<DATA.length; i+=3) {
895            if (!DATA[i+1].equals(DATA[i+2])) {
896                errln("FAIL: " + DATA[i] + " -> " + DATA[i+1] + ", exp " + DATA[i+2]);
897            }
898        }
899    }
900
901    /**
902     * SimpleTimeZone allows invalid DOM values.
903     */
904    // Current orgnaization of data in zoneinfor.res allows negative
905    // values from DOM so comment these tests out
906
907    @Test
908    public void Test4184229() {
909        SimpleTimeZone zone = null;
910        try {
911            zone = new SimpleTimeZone(0, "A", 0, -1, 0, 0, 0, 0, 0, 0);
912            errln("Failed. No exception has been thrown for DOM -1 startDay");
913        } catch(IllegalArgumentException e) {
914            logln("(a) " + e.getMessage());
915        }
916        try {
917            zone = new SimpleTimeZone(0, "A", 0, 0, 0, 0, 0, -1, 0, 0);
918            errln("Failed. No exception has been thrown for DOM -1 endDay");
919        } catch(IllegalArgumentException e) {
920            logln("(b) " + e.getMessage());
921        }
922        try {
923            zone = new SimpleTimeZone(0, "A", 0, -1, 0, 0, 0, 0, 0, 0, 1000);
924            errln("Failed. No exception has been thrown for DOM -1 startDay +savings");
925        } catch(IllegalArgumentException e) {
926            logln("(c) " + e.getMessage());
927        }
928        try {
929            zone = new SimpleTimeZone(0, "A", 0, 0, 0, 0, 0, -1, 0, 0, 1000);
930            errln("Failed. No exception has been thrown for DOM -1 endDay +savings");
931        } catch(IllegalArgumentException e) {
932            logln("(d) " + e.getMessage());
933        }
934        // Make a valid constructor call for subsequent tests.
935
936        zone = new SimpleTimeZone(0, "A", 0, 1, 0, 0, 0, 1, 0, 0);
937
938        try {
939            zone.setStartRule(0, -1, 0, 0);
940            errln("Failed. No exception has been thrown for DOM -1 setStartRule +savings");
941        } catch(IllegalArgumentException e) {
942            logln("(e) " + e.getMessage());
943        }
944        try {
945            zone.setStartRule(0, -1, 0);
946            errln("Failed. No exception has been thrown for DOM -1 setStartRule");
947        } catch(IllegalArgumentException e) {
948            logln("(f) " + e.getMessage());
949        }
950        try {
951            zone.setEndRule(0, -1, 0, 0);
952            errln("Failed. No exception has been thrown for DOM -1 setEndRule +savings");
953        } catch(IllegalArgumentException e) {
954            logln("(g) " + e.getMessage());
955        }
956        try {
957            zone.setEndRule(0, -1, 0);
958            errln("Failed. No exception has been thrown for DOM -1 setEndRule");
959        } catch(IllegalArgumentException e) {
960            logln("(h) " + e.getMessage());
961        }
962
963    }
964
965    /**
966     * SimpleTimeZone.getOffset() throws IllegalArgumentException when to get
967     * of 2/29/1996 (leap day).
968     */
969    @Test
970    public void Test4208960 () {
971    TimeZone tz = TimeZone.getTimeZone("PST");
972    try {
973        /*int offset =*/ tz.getOffset(GregorianCalendar.AD, 1996, Calendar.FEBRUARY, 29,
974                      Calendar.THURSDAY, 0);
975        //offset = 0;
976    } catch (IllegalArgumentException e) {
977        errln("FAILED: to get TimeZone.getOffset(2/29/96)");
978    }
979    try {
980        /*int offset =*/ tz.getOffset(GregorianCalendar.AD, 1997, Calendar.FEBRUARY, 29,
981                      Calendar.THURSDAY, 0);
982        //offset = 0;
983        warnln("FAILED: TimeZone.getOffset(2/29/97) expected to throw Exception.");
984    } catch (IllegalArgumentException e) {
985        logln("got IllegalArgumentException");
986    }
987    }
988
989    /**
990     * Test to see if DateFormat understands zone equivalency groups.  It
991     * might seem that this should be a DateFormat test, but it's really a
992     * TimeZone test -- the changes to DateFormat are minor.
993     *
994     * We use two known, zones that are equivalent, where one zone has
995     * localized name data, and the other doesn't, in some locale.
996     */
997    @Test
998    public void TestJ449() {
999        // not used String str;
1000
1001        // Modify the following three as necessary.  The two IDs must
1002        // specify two zones in the same equivalency group.  One must have
1003        // locale data in 'loc'; the other must not.
1004        String idWithLocaleData = "America/Los_Angeles";
1005        String idWithoutLocaleData = "PST"; // "US/Pacific";
1006        Locale loc = new Locale("en", "", "");
1007
1008        TimeZone zoneWith = TimeZone.getTimeZone(idWithLocaleData);
1009        TimeZone zoneWithout = TimeZone.getTimeZone(idWithoutLocaleData);
1010        // Make sure we got valid zones
1011        if (!(zoneWith.getID().equals(idWithLocaleData) &&
1012              zoneWithout.getID().equals(idWithoutLocaleData))) {
1013            warnln("Fail: Unable to create zones");
1014        } else {
1015            GregorianCalendar calWith = new GregorianCalendar(zoneWith);
1016            GregorianCalendar calWithout = new GregorianCalendar(zoneWithout);
1017            SimpleDateFormat fmt =
1018                new SimpleDateFormat("MMM d yyyy hh:mm a zzz", loc);
1019            Date date = new Date(0L);
1020            fmt.setCalendar(calWith);
1021            String strWith = fmt.format(date);
1022            fmt.setCalendar(calWithout);
1023            String strWithout = fmt.format(date);
1024            if (strWith.equals(strWithout)) {
1025                logln("Ok: " + idWithLocaleData + " -> " +
1026                      strWith + "; " + idWithoutLocaleData + " -> " +
1027                      strWithout);
1028            } else {
1029                errln("FAIL: " + idWithLocaleData + " -> " +
1030                      strWith + "; " + idWithoutLocaleData + " -> " +
1031                      strWithout);
1032            }
1033        }
1034    }
1035
1036    /**
1037     * getOffset returns wrong offset for days in early 20th century
1038     */
1039    @Test
1040    public void TestJ5134() {
1041        GregorianCalendar testCal = (GregorianCalendar)Calendar.getInstance();
1042        TimeZone icuEastern = TimeZone.getTimeZone("America/New_York");
1043        testCal.setTimeZone(icuEastern);
1044        testCal.set(1900, Calendar.JANUARY, 1, 0, 0, 0);
1045        long time = testCal.getTimeInMillis();
1046
1047        int offset = icuEastern.getOffset(time);
1048        if (offset != -18000000) {
1049            errln("FAIL: UTC offset in time zone America/New_York on Jan 1, 1900 -> " + offset);
1050        }
1051        boolean isDst = icuEastern.inDaylightTime(new Date(time));
1052        if (isDst) {
1053            errln("FAIL: DST is observed in time zone America/New_York on Jan 1, 1900");
1054        }
1055
1056        if (System.getProperty("java.vendor", "").startsWith("IBM") &&
1057            System.getProperty("java.version", "").equals("1.4.1")) {
1058            // IBM JDK 1.4.1 has a bug and fails to run this test case.
1059            return;
1060        }
1061        java.util.TimeZone jdkEastern = java.util.TimeZone.getTimeZone("America/New_York");
1062        // Compare offset and DST observation with JDK and ICU for 50 years since 1900
1063        testCal.add(Calendar.YEAR, 50);
1064        long endTime = testCal.getTimeInMillis();
1065        int jdkOffset;
1066        boolean isDstJdk;
1067        while (time < endTime) {
1068            offset = icuEastern.getOffset(time);
1069            jdkOffset = jdkEastern.getOffset(time);
1070            if (offset != jdkOffset) {
1071                errln("FAIL: Incompatible UTC offset -> JDK:" + jdkOffset + "/ICU:" + offset + " [" + time + "]");
1072            }
1073            Date d = new Date(time);
1074            isDst = icuEastern.inDaylightTime(d);
1075            isDstJdk = jdkEastern.inDaylightTime(d);
1076            if (isDst != isDstJdk) {
1077                errln("FAIL: Incompatible DST -> JDK:" + isDstJdk + "/ICU:" + isDst + " [" + time + "]");
1078            }
1079            time += 24*60*60*1000L; // increment 1 day
1080        }
1081    }
1082
1083    /**
1084     * Test setRawOffset works OK with system timezone
1085     */
1086    @Test
1087    public void TestT5280() {
1088        boolean isJdkZone = (TimeZone.getDefaultTimeZoneType() == TimeZone.TIMEZONE_JDK);
1089        String[] tzids = TimeZone.getAvailableIDs();
1090        for (int i = 0; i < tzids.length; i++) {
1091            TimeZone tz = TimeZone.getTimeZone(tzids[i]);
1092            boolean useDst = tz.useDaylightTime();
1093
1094            // Increase offset for 30 minutes
1095            int newRawOffset = tz.getRawOffset() + 30*60*1000;
1096            try {
1097                tz.setRawOffset(newRawOffset);
1098            } catch (Exception e) {
1099                errln("FAIL: setRawOffset throws an exception");
1100            }
1101            int offset = tz.getRawOffset();
1102            if (offset != newRawOffset) {
1103                if (isJdkZone) {
1104                    // JDK TimeZone#setRawOffset() only update the last rule, and getRawOffset() returns
1105                    // the current raw offset. Therefore, they might be different.
1106                    logln("Modified zone(" + tz.getID() + ") - getRawOffset returns " + offset + "/ Expected: " + newRawOffset);
1107                } else {
1108                    errln("FAIL: Modified zone(" + tz.getID() + ") - getRawOffset returns " + offset + "/ Expected: " + newRawOffset);
1109                }
1110            }
1111            // Ticket#5917
1112            // Check if DST observation status is not unexpectedly changed.
1113            boolean newDst = tz.useDaylightTime();
1114            if (useDst != newDst) {
1115                errln("FAIL: Modified zone(" + tz.getID() + ") - useDaylightTime has changed from " + useDst + " to " + newDst);
1116            }
1117            // Make sure the offset is preserved in a clone
1118            TimeZone tzClone = (TimeZone)tz.clone();
1119            int offsetC = tzClone.getRawOffset();
1120            if (offsetC != newRawOffset) {
1121                if (isJdkZone) {
1122                    logln("Cloned modified zone(" + tz.getID() + ") - getRawOffset returns " + offsetC + "/ Expected: " + newRawOffset);
1123                } else {
1124                    errln("FAIL: Cloned modified zone(" + tz.getID() + ") - getRawOffset returns " + offsetC + "/ Expected: " + newRawOffset);
1125                }
1126            }
1127
1128            if (offset != offsetC) {
1129                errln("FAIL: Different raw offset - Original:" + offset + ", Cloned:" + offsetC);
1130            }
1131        }
1132    }
1133
1134    /*
1135     * Zone ID is not set by a SimpleTimeZone constructor
1136     */
1137    @Test
1138    public void TestT5432() {
1139        String tzid = "MyZone";
1140        SimpleTimeZone stz;
1141
1142        // 2-arg constructor
1143        stz = new SimpleTimeZone(0, tzid);
1144        if (!tzid.equals(stz.getID())) {
1145            errln("FAIL: Bad zone id (" + stz.getID() + ") is returned - expected ("
1146                    + tzid + ") [2-arg constructor]");
1147        }
1148
1149        // 10-arg constructor
1150        stz = new SimpleTimeZone(0, tzid, 3, -1, 1, 3600000, 9, -1, 1, 3600000);
1151        if (!tzid.equals(stz.getID())) {
1152            errln("FAIL: Bad zone id (" + stz.getID() + ") is returned - expected ("
1153                    + tzid + ") [10-arg constructor]");
1154        }
1155
1156        // 11-arg constructor
1157        stz = new SimpleTimeZone(0, tzid, 3, -1, 1, 3600000, 9, -1, 1, 3600000, 3600000);
1158        if (!tzid.equals(stz.getID())) {
1159            errln("FAIL: Bad zone id (" + stz.getID() + ") is returned - expected ("
1160                    + tzid + ") [11-arg constructor]");
1161        }
1162
1163        // 13-arg constructor - this version had a problem reported by trac#5432
1164        stz = new SimpleTimeZone(0, tzid, 3, -1, 1, 3600000, SimpleTimeZone.WALL_TIME,
1165                9, -1, 1, 3600000, SimpleTimeZone.WALL_TIME, 3600000);
1166        if (!tzid.equals(stz.getID())) {
1167            errln("FAIL: Bad zone id (" + stz.getID() + ") is returned - expected ("
1168                    + tzid + ") [13-arg constructor]");
1169        }
1170    }
1171
1172    // test bug #4265
1173    @Test
1174    public void TestJohannesburg() {
1175        String j_id="Africa/Johannesburg";
1176        TimeZone johannesburg = TimeZone.getTimeZone(j_id);
1177        final int ONE_HOUR = 60*60*1000;
1178        int expectedOffset = ONE_HOUR*2;  // GMT+2 - NO DST
1179        int offset = johannesburg.getOffset(GregorianCalendar.AD,2007,Calendar.JULY,5,Calendar.THURSDAY,0);
1180
1181        if(offset != expectedOffset) {
1182            errln("FAIL: zone " + j_id +" returned offset in July " + offset +", expected "+expectedOffset);
1183        } else {
1184            logln("OK: zone " + j_id +" returned offset in July: " + offset);
1185        }
1186
1187        int offset2 = johannesburg.getOffset(GregorianCalendar.AD,2007,Calendar.DECEMBER,12,Calendar.WEDNESDAY,0);
1188
1189        if(offset2 != expectedOffset) {
1190            errln("FAIL: zone " + j_id +" returned offset in December " + offset2 +", expected "+expectedOffset);
1191        } else {
1192            logln("OK: zone " + j_id +" returned offset in December: " + offset2);
1193        }
1194    }
1195
1196    @Test
1197    public void TestT7107() {
1198        Thread[] workers = new Thread[20];
1199        for (int i = 0 ; i < workers.length; i++) {
1200            workers[i] = new Thread(new Runnable() {
1201                public void run() {
1202                    for (int j = 0; j < 10000; j++) {
1203                        try {
1204                            com.ibm.icu.util.TimeZone.getTimeZone("GMT").getDisplayName();
1205                        } catch (Exception e) {
1206                            errln("FAIL: Caught an exception " + e);
1207                        }
1208                    }
1209                }
1210            });
1211        }
1212        for (Thread wk : workers) {
1213            wk.start();
1214        }
1215        for (Thread wk : workers) {
1216            try {
1217                wk.join();
1218            } catch (InterruptedException ie) {
1219
1220            }
1221        }
1222    }
1223}
1224
1225//eof
1226