1 /*
2  * Copyright (c) 2002, 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 4278609 4761696
27  * @library /java/text/testlib
28  * @summary Make sure to handle DST transition ending at 0:00 January 1.
29  */
30 
31 import java.text.SimpleDateFormat;
32 import java.util.Calendar;
33 import java.util.Date;
34 import java.util.GregorianCalendar;
35 import java.util.Locale;
36 import java.util.SimpleTimeZone;
37 import java.util.TimeZone;
38 
39 public class TransitionTest extends IntlTest {
40 
main(String[] args)41     public static void main(String[] args) throws Exception {
42         new TransitionTest().run(args);
43     }
44 
Test4278609()45     public void Test4278609() {
46         SimpleTimeZone tz = new SimpleTimeZone(0, "MyTimeZone",
47                                /* DST start day: August, 1, 0:00 */
48                                Calendar.AUGUST, 1, 0, 0,
49                                /* DST end day: January, 1, 0:00 (wall-clock)*/
50                                Calendar.JANUARY, 1, 0, 0,
51                                60 * 60 * 1000);
52 
53         Calendar cal = new GregorianCalendar(TimeZone.getTimeZone("GMT"));
54 
55         // setting a date using GMT zone just after the end rule of tz zone
56         cal.clear();
57         cal.set(Calendar.ERA, GregorianCalendar.AD);
58         cal.set(1998, Calendar.DECEMBER, 31, 23, 01, 00);
59 
60         Date date = cal.getTime();
61 
62         int millis = cal.get(Calendar.HOUR_OF_DAY) * 3600000
63                      + cal.get(Calendar.MINUTE) * 60000
64                      + cal.get(Calendar.SECOND) * 1000
65                      + cal.get(Calendar.MILLISECOND);
66         /* we must use standard local time */
67         millis += tz.getRawOffset();
68 
69         int offset = tz.getOffset(cal.get(Calendar.ERA),
70                                   cal.get(Calendar.YEAR),
71                                   cal.get(Calendar.MONTH),
72                                   cal.get(Calendar.DATE),
73                                   cal.get(Calendar.DAY_OF_WEEK),
74                                   millis);
75 
76         if (offset != 0) {
77             SimpleDateFormat format = new SimpleDateFormat("dd MMM HH:mm:ss zzz",
78                                                            Locale.US);
79             format.setTimeZone(tz);
80             errln("Wrong DST transition: " + tz
81                   + "\na date just after DST = " + format.format(date)
82                   + "\ngetOffset = " + offset);
83         }
84     }
85 
86     /*
87      * 4761696: Rewrite SimpleTimeZone to support correct DST transitions
88      *
89      * Derived from JCK test cases some of which specify wrong day of week values.
90      */
Test4761696()91     public void Test4761696() {
92         GregorianCalendar cal = new GregorianCalendar(TimeZone.getTimeZone("GMT"));
93 
94         // test#1
95         int rawOffset = -43200000;
96         int saving = 1800000;
97         int timeOfDay = 84600001;
98         SimpleTimeZone tz = new SimpleTimeZone(rawOffset, "stz",
99                                 Calendar.JULY, 1, 0, 0,
100                                 Calendar.JANUARY, 1, 0, 0,
101                                 saving);
102         int year = Integer.MIN_VALUE;
103         tz.setStartYear(year);
104         int offset = tz.getOffset(GregorianCalendar.AD,
105                               year,
106                               Calendar.DECEMBER,
107                               31,
108                               1, // should be SATURDAY
109                               timeOfDay);
110         int y = (int) mod((long)year, 28L); // 28-year cycle
111         cal.clear();
112         cal.set(cal.ERA, cal.AD);
113         cal.set(y, Calendar.DECEMBER, 31);
114         cal.set(cal.MILLISECOND, timeOfDay);
115         long localtime = cal.getTimeInMillis() + rawOffset; // local standard time
116 
117         cal.clear();
118         cal.set(cal.ERA, cal.AD);
119         cal.set(y + 1, Calendar.JANUARY, 1);
120         cal.set(cal.MILLISECOND, -saving);
121         long endTime = cal.getTimeInMillis() + rawOffset;
122         long expectedOffset = (localtime < endTime) ? rawOffset + saving : rawOffset;
123         if (offset != expectedOffset) {
124             errln("test#1: wrong offset: got "+offset+", expected="+expectedOffset);
125         }
126 
127         // test#2
128         saving = 1;
129         timeOfDay = 0;
130         tz = new SimpleTimeZone(rawOffset, "stz",
131                                 Calendar.JULY, 1, 0, 0,
132                                 Calendar.JANUARY, 1, 0, 0,
133                                 saving);
134         tz.setStartYear(year);
135         offset = tz.getOffset(GregorianCalendar.AD,
136                               year,
137                               Calendar.AUGUST,
138                               15,
139                               1, // should be MONDAY
140                               timeOfDay);
141         y = (int) mod((long)year, 28L); // 28-year cycle
142         cal.clear();
143         cal.set(y, Calendar.AUGUST, 15);
144         cal.set(cal.MILLISECOND, timeOfDay);
145         localtime = cal.getTimeInMillis() + rawOffset; // local standard time
146 
147         cal.clear();
148         cal.set(y + 1, Calendar.JANUARY, 1);
149         cal.set(cal.MILLISECOND, -saving);
150         endTime = cal.getTimeInMillis() + rawOffset;
151         expectedOffset = (localtime < endTime) ? rawOffset + saving : rawOffset;
152         if (offset != expectedOffset) {
153             errln("Wrong offset: got "+offset+", expected="+expectedOffset);
154         }
155 
156         rawOffset = 43200000;
157         saving = 1;
158         timeOfDay = 3599998;
159         tz = new SimpleTimeZone(rawOffset, "stz",
160                                 Calendar.JULY, 1, 0, 3600000,
161                                 Calendar.JANUARY, 1, 0, 3600000,
162                                 saving);
163         tz.setStartYear(year);
164         offset = tz.getOffset(GregorianCalendar.AD,
165                               year,
166                               Calendar.JANUARY,
167                               1,
168                               1,
169                               timeOfDay);
170         y = (int) mod((long)year, 28L); // 28-year cycle
171         cal.clear();
172         cal.set(y, Calendar.JANUARY, 1);
173         cal.set(cal.MILLISECOND, timeOfDay);
174         localtime = cal.getTimeInMillis() + rawOffset; // local standard time
175 
176         cal.clear();
177         cal.set(y + 1, Calendar.JANUARY, 1);
178         cal.set(cal.MILLISECOND, 3600000-saving);
179         endTime = cal.getTimeInMillis() + rawOffset;
180         expectedOffset = (localtime < endTime) ? rawOffset + saving : rawOffset;
181         if (offset != expectedOffset) {
182             errln("test#2: wrong offset: got "+offset+", expected="+expectedOffset);
183         }
184 
185         // test#3
186         rawOffset = -43200000;
187         saving = 1800000;
188         timeOfDay = 84600001;
189         tz = new SimpleTimeZone(rawOffset, "stz",
190                                 Calendar.SEPTEMBER, 1, 0, 0,
191                                 Calendar.MARCH, 1, 0, 0,
192                                 saving);
193         tz.setStartYear(year);
194         offset = tz.getOffset(GregorianCalendar.AD,
195                               year,
196                               Calendar.FEBRUARY,
197                               28,
198                               1,
199                               timeOfDay);
200         y = (int) mod((long)year, 28L); // 28-year cycle
201         cal.clear();
202         cal.set(y, Calendar.FEBRUARY, 28);
203         cal.set(cal.MILLISECOND, timeOfDay);
204         localtime = cal.getTimeInMillis() + rawOffset; // local standard time
205 
206         cal.clear();
207         cal.set(y, Calendar.MARCH, 1);
208         cal.set(cal.MILLISECOND, -saving);
209         endTime = cal.getTimeInMillis() + rawOffset;
210         expectedOffset = (localtime < endTime) ? rawOffset + saving : rawOffset;
211         if (offset != expectedOffset) {
212             errln("test#3: wrong offset: got "+offset+", expected="+expectedOffset);
213         }
214 
215         // test#4
216         rawOffset = -43200000;
217         saving = 1;
218         timeOfDay = 0;
219         tz = new SimpleTimeZone(rawOffset, "stz",
220                                 Calendar.JANUARY, -4, 1, 3600000,
221                                 Calendar.JULY, -4, 1, 3600000,
222                                 saving);
223         tz.setStartYear(year);
224         offset = tz.getOffset(GregorianCalendar.AD,
225                               year,
226                               Calendar.JANUARY,
227                               10,
228                               2, // should be 1 (SUNDAY)
229                               timeOfDay);
230         y = (int) mod((long)year, 28L); // 28-year cycle
231         cal.clear();
232         cal.set(y, Calendar.JANUARY, 10);
233         cal.set(cal.MILLISECOND, timeOfDay);
234         localtime = cal.getTimeInMillis() + rawOffset; // local standard time
235 
236         cal.clear();
237         cal.set(cal.YEAR, y);
238         cal.set(cal.MONTH, Calendar.JANUARY);
239         cal.set(cal.DAY_OF_MONTH, 8);
240         cal.set(cal.WEEK_OF_MONTH, cal.getActualMaximum(cal.WEEK_OF_MONTH)-4+1);
241         cal.set(cal.DAY_OF_WEEK, 1);
242         cal.set(cal.MILLISECOND, 3600000-saving);
243         long startTime = cal.getTimeInMillis() + rawOffset;
244         expectedOffset = (localtime >= startTime) ? rawOffset + saving : rawOffset;
245         if (offset != expectedOffset) {
246             errln("test#4: wrong offset: got "+offset+", expected="+expectedOffset);
247         }
248 
249         // test#5
250         rawOffset = 0;
251         saving = 3600000;
252         timeOfDay = 7200000;
253         year = 1982;
254         tz = new SimpleTimeZone(rawOffset, "stz",
255                                 Calendar.APRIL, 1, 0, 7200000,
256                                 Calendar.OCTOBER, 10, 0, 7200000,
257                                 saving);
258         offset = tz.getOffset(GregorianCalendar.AD,
259                               year,
260                               Calendar.OCTOBER,
261                               10,
262                               1,
263                               timeOfDay);
264         cal.clear();
265         cal.set(year, Calendar.OCTOBER, 10);
266         cal.set(cal.MILLISECOND, timeOfDay);
267         localtime = cal.getTimeInMillis() + rawOffset; // local standard time
268 
269         cal.clear();
270         cal.set(year, Calendar.OCTOBER, 10);
271         cal.set(cal.MILLISECOND, 7200000-saving);
272         endTime = cal.getTimeInMillis() + rawOffset;
273         expectedOffset = (localtime < endTime) ? rawOffset + saving : rawOffset;
274         if (offset != expectedOffset) {
275             errln("test#5: wrong offset: got "+offset+", expected="+expectedOffset);
276         }
277     }
278 
floorDivide(long n, long d)279     public static final long floorDivide(long n, long d) {
280         return ((n >= 0) ?
281                 (n / d) : (((n + 1L) / d) - 1L));
282     }
283 
mod(long x, long y)284     public static final long mod(long x, long y) {
285         return (x - y * floorDivide(x, y));
286     }
287 }
288