1/*
2 * Copyright (c) 2005, 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 6231602
27 * @summary Make sure that ZONE_OFFSET and/or DST_OFFSET setting is
28 * taken into account for time calculations.
29 */
30
31import java.util.Calendar;
32import java.util.Date;
33import java.util.GregorianCalendar;
34import java.util.Locale;
35import java.util.TimeZone;
36
37import static java.util.GregorianCalendar.*;
38
39public class ZoneOffsets {
40
41    // This TimeZone always returns the dstOffset value.
42    @SuppressWarnings("serial")
43    private static class TestTimeZone extends TimeZone {
44
45        private int gmtOffset;
46        private int dstOffset;
47
48        TestTimeZone(int gmtOffset, String id, int dstOffset) {
49            this.gmtOffset = gmtOffset;
50            setID(id);
51            this.dstOffset = dstOffset;
52        }
53
54        public int getOffset(int era, int year, int month, int day,
55                int dayOfWeek, int milliseconds) {
56            return gmtOffset + dstOffset;
57        }
58
59        public int getOffset(long date) {
60            return gmtOffset + dstOffset;
61        }
62
63        public void setRawOffset(int offsetMillis) {
64            gmtOffset = offsetMillis;
65        }
66
67        public int getRawOffset() {
68            return gmtOffset;
69        }
70
71        public int getDSTSavings() {
72            return dstOffset;
73        }
74
75        public boolean useDaylightTime() {
76            return dstOffset != 0;
77        }
78
79        public boolean inDaylightTime(Date date) {
80            return dstOffset != 0;
81        }
82
83        public String toString() {
84            return "TestTimeZone[" + getID() + ", " + gmtOffset + ", " + dstOffset + "]";
85        }
86    }
87
88    private static Locale[] locales = {
89        Locale.getDefault(),
90        new Locale("th", "TH"),
91        new Locale("ja", "JP", "JP")};
92
93    private static final int HOUR = 60 * 60 * 1000;
94
95    private static int[][] offsets = {
96        {0, 0},
97        {0, HOUR},
98        {0, 2 * HOUR},
99        {-8 * HOUR, 0},
100        {-8 * HOUR, HOUR},
101        {-8 * HOUR, 2 * HOUR},
102        {9 * HOUR, 0},
103        {9 * HOUR, HOUR},
104        {9 * HOUR, 2 * HOUR}};
105
106    public static void main(String[] args) {
107        for (int l = 0; l < locales.length; l++) {
108            Locale loc = locales[l];
109            for (int i = 0; i < offsets.length; i++) {
110                test(loc, offsets[i][0], offsets[i][1]);
111            }
112        }
113
114        // The test case in the bug report.
115        GregorianCalendar cal = new GregorianCalendar();
116        cal.setLenient(false);
117        cal.setGregorianChange(new Date(Long.MIN_VALUE));
118        cal.clear();
119        cal.set(ZONE_OFFSET, 0);
120        cal.set(DST_OFFSET, 0);
121        cal.set(ERA, AD);
122        cal.set(2004, FEBRUARY, 3, 0, 0, 0);
123        cal.set(MILLISECOND, 0);
124        // The following line should not throw an IllegalArgumentException.
125        cal.getTime();
126    }
127
128    private static void test(Locale loc, int gmtOffset, int dstOffset) {
129        TimeZone tz1 = new TestTimeZone(gmtOffset,
130                "GMT" + (gmtOffset / HOUR) + "." + (dstOffset / HOUR),
131                dstOffset);
132        int someDifferentOffset = gmtOffset + 2 * HOUR;
133        TimeZone tz2 = new TestTimeZone(someDifferentOffset,
134                "GMT" + (someDifferentOffset / HOUR) + "." + (dstOffset / HOUR),
135                dstOffset);
136
137        int someDifferentDSTOffset = dstOffset == 2 * HOUR ? HOUR : dstOffset + HOUR;
138        TimeZone tz3 = new TestTimeZone(gmtOffset,
139                "GMT" + (gmtOffset / HOUR) + "." + (someDifferentDSTOffset / HOUR),
140                someDifferentDSTOffset);
141
142        // cal1 is the base line.
143        Calendar cal1 = Calendar.getInstance(tz1, loc);
144        cal1.clear();
145        cal1.set(2005, MARCH, 11);
146        long t1 = cal1.getTime().getTime();
147        int gmt = cal1.get(ZONE_OFFSET);
148        int dst = cal1.get(DST_OFFSET);
149
150        // Test 8 cases with cal2.
151        Calendar cal2 = Calendar.getInstance(tz2, loc);
152        cal2.clear();
153        cal2.set(2005, MARCH, 11);
154        // test1: set only ZONE_OFFSET
155        cal2.set(ZONE_OFFSET, gmtOffset);
156        if (t1 != cal2.getTime().getTime() || dst != cal2.get(DST_OFFSET)) {
157            error("Test1", loc, cal2, gmtOffset, dstOffset, t1);
158        }
159
160        cal2.setTimeZone(tz3);
161        cal2.clear();
162        cal2.set(2005, MARCH, 11);
163        // test2: set only DST_OFFSET
164        cal2.set(DST_OFFSET, dstOffset);
165        if (t1 != cal2.getTime().getTime() || gmt != cal2.get(ZONE_OFFSET)) {
166            error("Test2", loc, cal2, gmtOffset, dstOffset, t1);
167        }
168
169        cal2.setTimeZone(tz2);
170        cal2.clear();
171        cal2.set(2005, MARCH, 11);
172        // test3: set both ZONE_OFFSET and DST_OFFSET
173        cal2.set(ZONE_OFFSET, gmtOffset);
174        cal2.set(DST_OFFSET, dstOffset);
175        if (t1 != cal2.getTime().getTime()) {
176            error("Test3", loc, cal2, gmtOffset, dstOffset, t1);
177        }
178
179        cal2.setTimeZone(tz3);
180        cal2.clear();
181        cal2.set(2005, MARCH, 11);
182        // test4: set both ZONE_OFFSET and DST_OFFSET
183        cal2.set(ZONE_OFFSET, gmtOffset);
184        cal2.set(DST_OFFSET, dstOffset);
185        if (t1 != cal2.getTime().getTime()) {
186            error("Test4", loc, cal2, gmtOffset, dstOffset, t1);
187        }
188
189        // Test the same thing in non-lenient
190        cal2.setLenient(false);
191
192        cal2.setTimeZone(tz2);
193        cal2.clear();
194        cal2.set(2005, MARCH, 11);
195        // test5: set only ZONE_OFFSET in non-lenient
196        cal2.set(ZONE_OFFSET, gmtOffset);
197        if (t1 != cal2.getTime().getTime() || dst != cal2.get(DST_OFFSET)) {
198            error("Test5", loc, cal2, gmtOffset, dstOffset, t1);
199        }
200
201        cal2.setTimeZone(tz3);
202        cal2.clear();
203        cal2.set(2005, MARCH, 11);
204        // test6: set only DST_OFFSET in non-lenient
205        cal2.set(DST_OFFSET, dstOffset);
206        if (t1 != cal2.getTime().getTime() || gmt != cal2.get(ZONE_OFFSET)) {
207            error("Test6", loc, cal2, gmtOffset, dstOffset, t1);
208        }
209
210        cal2.setTimeZone(tz2);
211        cal2.clear();
212        cal2.set(2005, MARCH, 11);
213        // test7: set both ZONE_OFFSET and DST_OFFSET in non-lenient
214        cal2.set(ZONE_OFFSET, gmtOffset);
215        cal2.set(DST_OFFSET, dstOffset);
216        if (t1 != cal2.getTime().getTime()) {
217            error("Test7", loc, cal2, gmtOffset, dstOffset, t1);
218        }
219
220        cal2.setTimeZone(tz3);
221        cal2.clear();
222        cal2.set(2005, MARCH, 11);
223        // test8: set both ZONE_OFFSET and DST_OFFSET in non-lenient
224        cal2.set(ZONE_OFFSET, gmtOffset);
225        cal2.set(DST_OFFSET, dstOffset);
226        if (t1 != cal2.getTime().getTime()) {
227            error("Test8", loc, cal2, gmtOffset, dstOffset, t1);
228        }
229    }
230
231    private static void error(String msg, Locale loc, Calendar cal2, int gmtOffset, int dstOffset, long t1) {
232        System.err.println(cal2);
233        throw new RuntimeException(msg + ": Locale=" + loc
234                + ", gmtOffset=" + gmtOffset + ", dstOffset=" + dstOffset
235                + ", cal1 time=" + t1 + ", cal2 time=" + cal2.getTime().getTime());
236    }
237}
238