1 | /* java.util.SimpleTimeZone
|
---|
2 | Copyright (C) 1998, 1999, 2000 Free Software Foundation, Inc.
|
---|
3 |
|
---|
4 | This file is part of GNU Classpath.
|
---|
5 |
|
---|
6 | GNU Classpath is free software; you can redistribute it and/or modify
|
---|
7 | it under the terms of the GNU General Public License as published by
|
---|
8 | the Free Software Foundation; either version 2, or (at your option)
|
---|
9 | any later version.
|
---|
10 |
|
---|
11 | GNU Classpath is distributed in the hope that it will be useful, but
|
---|
12 | WITHOUT ANY WARRANTY; without even the implied warranty of
|
---|
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
---|
14 | General Public License for more details.
|
---|
15 |
|
---|
16 | You should have received a copy of the GNU General Public License
|
---|
17 | along with GNU Classpath; see the file COPYING. If not, write to the
|
---|
18 | Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
---|
19 | 02111-1307 USA.
|
---|
20 |
|
---|
21 | Linking this library statically or dynamically with other modules is
|
---|
22 | making a combined work based on this library. Thus, the terms and
|
---|
23 | conditions of the GNU General Public License cover the whole
|
---|
24 | combination.
|
---|
25 |
|
---|
26 | As a special exception, the copyright holders of this library give you
|
---|
27 | permission to link this library with independent modules to produce an
|
---|
28 | executable, regardless of the license terms of these independent
|
---|
29 | modules, and to copy and distribute the resulting executable under
|
---|
30 | terms of your choice, provided that you also meet, for each linked
|
---|
31 | independent module, the terms and conditions of the license of that
|
---|
32 | module. An independent module is a module which is not derived from
|
---|
33 | or based on this library. If you modify this library, you may extend
|
---|
34 | this exception to your version of the library, but you are not
|
---|
35 | obligated to do so. If you do not wish to do so, delete this
|
---|
36 | exception statement from your version. */
|
---|
37 |
|
---|
38 |
|
---|
39 | package java.util;
|
---|
40 |
|
---|
41 | import java.text.DateFormatSymbols;
|
---|
42 |
|
---|
43 | /**
|
---|
44 | * This class represents a simple time zone offset and handles
|
---|
45 | * daylight savings. It can only handle one daylight savings rule, so
|
---|
46 | * it can't represent historical changes.
|
---|
47 | *
|
---|
48 | * This object is tightly bound to the Gregorian calendar. It assumes
|
---|
49 | * a regular seven days week, and the month lengths are that of the
|
---|
50 | * Gregorian Calendar. It can only handle daylight savings for years
|
---|
51 | * lying in the AD era.
|
---|
52 | *
|
---|
53 | * @see Calendar
|
---|
54 | * @see GregorianCalender
|
---|
55 | * @author Jochen Hoenicke
|
---|
56 | */
|
---|
57 | public class SimpleTimeZone extends TimeZone
|
---|
58 | {
|
---|
59 | /**
|
---|
60 | * The raw time zone offset in milliseconds to GMT, ignoring
|
---|
61 | * daylight savings.
|
---|
62 | * @serial
|
---|
63 | */
|
---|
64 | private int rawOffset;
|
---|
65 |
|
---|
66 | /**
|
---|
67 | * True, if this timezone uses daylight savings, false otherwise.
|
---|
68 | * @serial
|
---|
69 | */
|
---|
70 | private boolean useDaylight;
|
---|
71 |
|
---|
72 | /**
|
---|
73 | * The daylight savings offset. This is a positive offset in
|
---|
74 | * milliseconds with respect to standard time. Typically this
|
---|
75 | * is one hour, but for some time zones this may be half an our.
|
---|
76 | * @serial
|
---|
77 | * @since JDK1.1.4
|
---|
78 | */
|
---|
79 | private int dstSavings = 60 * 60 * 1000;
|
---|
80 |
|
---|
81 | /**
|
---|
82 | * The first year, in which daylight savings rules applies.
|
---|
83 | * @serial
|
---|
84 | */
|
---|
85 | private int startYear;
|
---|
86 |
|
---|
87 | private static final int DOM_MODE = 1;
|
---|
88 | private static final int DOW_IN_MONTH_MODE = 2;
|
---|
89 | private static final int DOW_GE_DOM_MODE = 3;
|
---|
90 | private static final int DOW_LE_DOM_MODE = 4;
|
---|
91 | /**
|
---|
92 | * The mode of the start rule. This takes one of the following values:
|
---|
93 | * <dl>
|
---|
94 | * <dt>DOM_MODE (1)</dt>
|
---|
95 | * <dd> startDay contains the day in month of the start date,
|
---|
96 | * startDayOfWeek is unused. </dd>
|
---|
97 | * <dt>DOW_IN_MONTH_MODE (2)</dt>
|
---|
98 | * <dd> The startDay gives the day of week in month, and
|
---|
99 | * startDayOfWeek the day of week. For example startDay=2 and
|
---|
100 | * startDayOfWeek=Calender.SUNDAY specifies that the change is on
|
---|
101 | * the second sunday in that month. You must make sure, that this
|
---|
102 | * day always exists (ie. don't specify the 5th sunday).
|
---|
103 | * </dd>
|
---|
104 | * <dt>DOW_GE_DOM_MODE (3)</dt>
|
---|
105 | * <dd> The start is on the first startDayOfWeek on or after
|
---|
106 | * startDay. For example startDay=13 and
|
---|
107 | * startDayOfWeek=Calendar.FRIDAY specifies that the daylight
|
---|
108 | * savings start on the first FRIDAY on or after the 13th of that
|
---|
109 | * Month. Make sure that the change is always in the given month, or
|
---|
110 | * the result is undefined.
|
---|
111 | * </dd>
|
---|
112 | * <dt>DOW_LE_DOM_MONTH (4)</dt>
|
---|
113 | * <dd> The start is on the first startDayOfWeek on or before the
|
---|
114 | * startDay. Make sure that the change is always in the given
|
---|
115 | * month, or the result is undefined.
|
---|
116 | </dd>
|
---|
117 | * </dl>
|
---|
118 | * @serial */
|
---|
119 | private int startMode;
|
---|
120 |
|
---|
121 | /**
|
---|
122 | * The month in which daylight savings start. This is one of the
|
---|
123 | * constants Calendar.JANUARY, ..., Calendar.DECEMBER.
|
---|
124 | * @serial
|
---|
125 | */
|
---|
126 | private int startMonth;
|
---|
127 |
|
---|
128 | /**
|
---|
129 | * This variable can have different meanings. See startMode for details
|
---|
130 | * @see #startMode;
|
---|
131 | * @serial
|
---|
132 | */
|
---|
133 | private int startDay;
|
---|
134 |
|
---|
135 | /**
|
---|
136 | * This variable specifies the day of week the change takes place. If
|
---|
137 | * startMode == DOM_MODE, this is undefined.
|
---|
138 | * @serial
|
---|
139 | * @see #startMode;
|
---|
140 | */
|
---|
141 | private int startDayOfWeek;
|
---|
142 |
|
---|
143 | /**
|
---|
144 | * This variable specifies the time of change to daylight savings.
|
---|
145 | * This time is given in milliseconds after midnight local
|
---|
146 | * standard time.
|
---|
147 | * @serial
|
---|
148 | */
|
---|
149 | private int startTime;
|
---|
150 |
|
---|
151 | /**
|
---|
152 | * The month in which daylight savings ends. This is one of the
|
---|
153 | * constants Calendar.JANUARY, ..., Calendar.DECEMBER.
|
---|
154 | * @serial
|
---|
155 | */
|
---|
156 | private int endMonth;
|
---|
157 |
|
---|
158 | /**
|
---|
159 | * This variable gives the mode for the end of daylight savings rule.
|
---|
160 | * It can take the same values as startMode.
|
---|
161 | * @serial
|
---|
162 | * @see #startMode
|
---|
163 | */
|
---|
164 | private int endMode;
|
---|
165 |
|
---|
166 | /**
|
---|
167 | * This variable can have different meanings. See startMode for details
|
---|
168 | * @serial
|
---|
169 | * @see #startMode;
|
---|
170 | */
|
---|
171 | private int endDay;
|
---|
172 |
|
---|
173 | /**
|
---|
174 | * This variable specifies the day of week the change takes place. If
|
---|
175 | * endMode == DOM_MODE, this is undefined.
|
---|
176 | * @serial
|
---|
177 | * @see #startMode;
|
---|
178 | */
|
---|
179 | private int endDayOfWeek;
|
---|
180 |
|
---|
181 | /**
|
---|
182 | * This variable specifies the time of change back to standard time.
|
---|
183 | * This time is given in milliseconds after midnight local
|
---|
184 | * standard time.
|
---|
185 | * @serial
|
---|
186 | */
|
---|
187 | private int endTime;
|
---|
188 |
|
---|
189 | /**
|
---|
190 | * This variable points to a deprecated array from JDK 1.1. It is
|
---|
191 | * ignored in JDK 1.2 but streamed out for compatibility with JDK 1.1.
|
---|
192 | * The array contains the lengths of the months in the year and is
|
---|
193 | * assigned from a private static final field to avoid allocating
|
---|
194 | * the array for every instance of the object.
|
---|
195 | * Note that static final fields are not serialized.
|
---|
196 | * @serial
|
---|
197 | */
|
---|
198 | private byte[] monthLength = monthArr;
|
---|
199 | private static final byte[] monthArr =
|
---|
200 | {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
|
---|
201 |
|
---|
202 | /**
|
---|
203 | * The version of the serialized data on the stream.
|
---|
204 | * <dl>
|
---|
205 | * <dt>0 or not present on stream</dt>
|
---|
206 | * <dd> JDK 1.1.3 or earlier, only provides this fields:
|
---|
207 | * rawOffset, startDay, startDayOfWeek, startMonth, startTime,
|
---|
208 | * startYear, endDay, endDayOfWeek, endMonth, endTime
|
---|
209 | * </dd>
|
---|
210 | * <dd> JDK 1.1.4 or later. This includes three new fields, namely
|
---|
211 | * startMode, endMode and dstSavings. And there is a optional section
|
---|
212 | * as described in writeObject.
|
---|
213 | * </dd>
|
---|
214 | *
|
---|
215 | * XXX - JDK 1.2 Beta 4 docu states 1.1.4, but my 1.1.5 has the old
|
---|
216 | * version.
|
---|
217 | *
|
---|
218 | * When streaming out this class it is always written in the latest
|
---|
219 | * version.
|
---|
220 | * @serial
|
---|
221 | * @since JDK1.1.4
|
---|
222 | */
|
---|
223 | private int serialVersionOnStream = 1;
|
---|
224 |
|
---|
225 | private static final long serialVersionUID = -403250971215465050L;
|
---|
226 |
|
---|
227 | /**
|
---|
228 | * Create a <code>SimpleTimeZone</code> with the given time offset
|
---|
229 | * from GMT and without daylight savings.
|
---|
230 | * @param rawOffset the time offset from GMT in milliseconds.
|
---|
231 | * @param id The identifier of this time zone.
|
---|
232 | */
|
---|
233 | public SimpleTimeZone(int rawOffset, String id)
|
---|
234 | {
|
---|
235 | this.rawOffset = rawOffset;
|
---|
236 | setID(id);
|
---|
237 | useDaylight = false;
|
---|
238 | startYear = 0;
|
---|
239 | }
|
---|
240 |
|
---|
241 | /**
|
---|
242 | * Create a <code>SimpleTimeZone</code> with the given time offset
|
---|
243 | * from GMT and with daylight savings. The start/end parameters
|
---|
244 | * can have different meaning (replace WEEKDAY with a real day of
|
---|
245 | * week). Only the first two meanings were supported by earlier
|
---|
246 | * versions of jdk.
|
---|
247 | *
|
---|
248 | * <dl>
|
---|
249 | * <dt><code>day > 0, dayOfWeek = Calendar.WEEKDAY</code></dt>
|
---|
250 | * <dd>The start/end of daylight savings is on the <code>day</code>-th
|
---|
251 | * <code>WEEKDAY</code> in the given month. </dd>
|
---|
252 | * <dt><code>day < 0, dayOfWeek = Calendar.WEEKDAY</code></dt>
|
---|
253 | * <dd>The start/end of daylight savings is on the <code>-day</code>-th
|
---|
254 | * <code>WEEKDAY</code> counted from the <i>end</i> of the month. </dd>
|
---|
255 | * <dt><code>day > 0, dayOfWeek = 0</code></dt>
|
---|
256 | * <dd>The start/end of daylight is on the <code>day</code>-th day of
|
---|
257 | * the month. </dd>
|
---|
258 | * <dt><code>day > 0, dayOfWeek = -Calendar.WEEKDAY</code></dt>
|
---|
259 | * <dd>The start/end of daylight is on the first WEEKDAY on or after
|
---|
260 | * the <code>day</code>-th day of the month. You must make sure that
|
---|
261 | * this day lies in the same month. </dd>
|
---|
262 | * <dt><code>day < 0, dayOfWeek = -Calendar.WEEKDAY</code></dt>
|
---|
263 | * <dd>The start/end of daylight is on the first WEEKDAY on or
|
---|
264 | * <i>before</i> the <code>-day</code>-th day of the month. You
|
---|
265 | * must make sure that this day lies in the same month. </dd>
|
---|
266 | * </dl>
|
---|
267 | *
|
---|
268 | * If you give a non existing month, a day that is zero, or too big,
|
---|
269 | * or a dayOfWeek that is too big, the result is undefined.
|
---|
270 | *
|
---|
271 | * The start rule must have a different month than the end rule.
|
---|
272 | * This restriction shouldn't hurt for all possible time zones.
|
---|
273 | *
|
---|
274 | * @param rawOffset The time offset from GMT in milliseconds.
|
---|
275 | * @param id The identifier of this time zone.
|
---|
276 | * @param startMonth The start month of daylight savings; use the
|
---|
277 | * constants in Calendar.
|
---|
278 | * @param startday A day in month or a day of week number, as
|
---|
279 | * described above.
|
---|
280 | * @param startDayOfWeek The start rule day of week; see above.
|
---|
281 | * @param startTime A time in millis in standard time.
|
---|
282 | * @param endMonth The end month of daylight savings; use the
|
---|
283 | * constants in Calendar.
|
---|
284 | * @param endday A day in month or a day of week number, as
|
---|
285 | * described above.
|
---|
286 | * @param endDayOfWeek The end rule day of week; see above.
|
---|
287 | * @param endTime A time in millis in standard time. */
|
---|
288 | public SimpleTimeZone(int rawOffset, String id,
|
---|
289 | int startMonth, int startDayOfWeekInMonth,
|
---|
290 | int startDayOfWeek, int startTime,
|
---|
291 | int endMonth, int endDayOfWeekInMonth,
|
---|
292 | int endDayOfWeek, int endTime)
|
---|
293 | {
|
---|
294 | this.rawOffset = rawOffset;
|
---|
295 | setID(id);
|
---|
296 | useDaylight = true;
|
---|
297 |
|
---|
298 | setStartRule(startMonth, startDayOfWeekInMonth,
|
---|
299 | startDayOfWeek, startTime);
|
---|
300 | setEndRule(endMonth, endDayOfWeekInMonth, endDayOfWeek, endTime);
|
---|
301 | if (startMonth == endMonth)
|
---|
302 | throw new IllegalArgumentException
|
---|
303 | ("startMonth and endMonth must be different");
|
---|
304 | this.startYear = 0;
|
---|
305 | }
|
---|
306 |
|
---|
307 | /**
|
---|
308 | * This constructs a new SimpleTimeZone that supports a daylight savings
|
---|
309 | * rule. The parameter are the same as for the constructor above, except
|
---|
310 | * there is the additional dstSavaings parameter.
|
---|
311 | *
|
---|
312 | * @param dstSavings the amount of savings for daylight savings
|
---|
313 | * time in milliseconds. This must be positive.
|
---|
314 | */
|
---|
315 | public SimpleTimeZone(int rawOffset, String id,
|
---|
316 | int startMonth, int startDayOfWeekInMonth,
|
---|
317 | int startDayOfWeek, int startTime,
|
---|
318 | int endMonth, int endDayOfWeekInMonth,
|
---|
319 | int endDayOfWeek, int endTime, int dstSavings)
|
---|
320 | {
|
---|
321 | this(rawOffset, id,
|
---|
322 | startMonth, startDayOfWeekInMonth, startDayOfWeek, startTime,
|
---|
323 | endMonth, endDayOfWeekInMonth, endDayOfWeek, endTime);
|
---|
324 |
|
---|
325 | this.dstSavings = dstSavings;
|
---|
326 | }
|
---|
327 |
|
---|
328 | /**
|
---|
329 | * Sets the first year, where daylight savings applies. The daylight
|
---|
330 | * savings rule never apply for years in the BC era. Note that this
|
---|
331 | * is gregorian calendar specific.
|
---|
332 | * @param year the start year.
|
---|
333 | */
|
---|
334 | public void setStartYear(int year)
|
---|
335 | {
|
---|
336 | startYear = year;
|
---|
337 | useDaylight = true;
|
---|
338 | }
|
---|
339 |
|
---|
340 | /**
|
---|
341 | * Checks if the month, day, dayOfWeek arguments are in range and
|
---|
342 | * returns the mode of the rule.
|
---|
343 | * @param month the month parameter as in the constructor
|
---|
344 | * @param day the day parameter as in the constructor
|
---|
345 | * @param dayOfWeek the day of week parameter as in the constructor
|
---|
346 | * @return the mode of this rule see startMode.
|
---|
347 | * @exception IllegalArgumentException if parameters are out of range.
|
---|
348 | * @see #SimpleTimeZone(int, String, int, int, int, int, int, int, int, int)
|
---|
349 | * @see #startMode
|
---|
350 | */
|
---|
351 | private int checkRule(int month, int day, int dayOfWeek)
|
---|
352 | {
|
---|
353 | int daysInMonth = getDaysInMonth(month, 1);
|
---|
354 | if (dayOfWeek == 0)
|
---|
355 | {
|
---|
356 | if (day <= 0 || day > daysInMonth)
|
---|
357 | throw new IllegalArgumentException("day out of range");
|
---|
358 | return DOM_MODE;
|
---|
359 | }
|
---|
360 | else if (dayOfWeek > 0)
|
---|
361 | {
|
---|
362 | if (Math.abs(day) > (daysInMonth + 6) / 7)
|
---|
363 | throw new IllegalArgumentException("dayOfWeekInMonth out of range");
|
---|
364 | if (dayOfWeek > Calendar.SATURDAY)
|
---|
365 | throw new IllegalArgumentException("dayOfWeek out of range");
|
---|
366 | return DOW_IN_MONTH_MODE;
|
---|
367 | }
|
---|
368 | else
|
---|
369 | {
|
---|
370 | if (day == 0 || Math.abs(day) > daysInMonth)
|
---|
371 | throw new IllegalArgumentException("day out of range");
|
---|
372 | if (dayOfWeek < -Calendar.SATURDAY)
|
---|
373 | throw new IllegalArgumentException("dayOfWeek out of range");
|
---|
374 | if (day < 0)
|
---|
375 | return DOW_LE_DOM_MODE;
|
---|
376 | else
|
---|
377 | return DOW_GE_DOM_MODE;
|
---|
378 | }
|
---|
379 | }
|
---|
380 |
|
---|
381 |
|
---|
382 | /**
|
---|
383 | * Sets the daylight savings start rule. You must also set the
|
---|
384 | * end rule with <code>setEndRule</code> or the result of
|
---|
385 | * getOffset is undefined. For the parameters see the ten-argument
|
---|
386 | * constructor above.
|
---|
387 | *
|
---|
388 | * @param month The month where daylight savings start, zero
|
---|
389 | * based. You should use the constants in Calendar.
|
---|
390 | * @param day A day of month or day of week in month.
|
---|
391 | * @param dayOfWeek The day of week where daylight savings start.
|
---|
392 | * @param time The time in milliseconds standard time where daylight
|
---|
393 | * savings start.
|
---|
394 | * @see SimpleTimeZone */
|
---|
395 | public void setStartRule(int month, int day, int dayOfWeek, int time)
|
---|
396 | {
|
---|
397 | this.startMode = checkRule(month, day, dayOfWeek);
|
---|
398 | this.startMonth = month;
|
---|
399 | // FIXME: XXX: JDK 1.2 allows negative values and has 2 new variations
|
---|
400 | // of this method.
|
---|
401 | this.startDay = Math.abs(day);
|
---|
402 | this.startDayOfWeek = Math.abs(dayOfWeek);
|
---|
403 | this.startTime = time;
|
---|
404 | useDaylight = true;
|
---|
405 | }
|
---|
406 |
|
---|
407 | /**
|
---|
408 | * Sets the daylight savings end rule. You must also set the
|
---|
409 | * start rule with <code>setStartRule</code> or the result of
|
---|
410 | * getOffset is undefined. For the parameters see the ten-argument
|
---|
411 | * constructor above.
|
---|
412 | *
|
---|
413 | * @param rawOffset The time offset from GMT.
|
---|
414 | * @param id The identifier of this time zone.
|
---|
415 | * @param Month The end month of daylight savings.
|
---|
416 | * @param day A day in month, or a day of week in month.
|
---|
417 | * @param DayOfWeek A day of week, when daylight savings ends.
|
---|
418 | * @param Time A time in millis in standard time.
|
---|
419 | * @see #setStartRule */
|
---|
420 | public void setEndRule(int month, int day, int dayOfWeek, int time)
|
---|
421 | {
|
---|
422 | this.endMode = checkRule(month, day, dayOfWeek);
|
---|
423 | this.endMonth = month;
|
---|
424 | // FIXME: XXX: JDK 1.2 allows negative values and has 2 new variations
|
---|
425 | // of this method.
|
---|
426 | this.endDay = Math.abs(day);
|
---|
427 | this.endDayOfWeek = Math.abs(dayOfWeek);
|
---|
428 | this.endTime = time;
|
---|
429 | useDaylight = true;
|
---|
430 | }
|
---|
431 |
|
---|
432 | /**
|
---|
433 | * Gets the time zone offset, for current date, modified in case of
|
---|
434 | * daylight savings. This is the offset to add to UTC to get the local
|
---|
435 | * time.
|
---|
436 | *
|
---|
437 | * In the standard JDK the results given by this method may result in
|
---|
438 | * inaccurate results at the end of February or the beginning of March.
|
---|
439 | * To avoid this, you should use Calendar instead:
|
---|
440 | * <code>offset = cal.get(Calendar.ZONE_OFFSET)
|
---|
441 | * + cal.get(Calendar.DST_OFFSET);</code>
|
---|
442 | *
|
---|
443 | * You could also use in
|
---|
444 | *
|
---|
445 | * This version doesn't suffer this inaccuracy.
|
---|
446 | *
|
---|
447 | * @param era the era of the given date
|
---|
448 | * @param year the year of the given date
|
---|
449 | * @param month the month of the given date, 0 for January.
|
---|
450 | * @param day the day of month
|
---|
451 | * @param dayOfWeek the day of week; this must be matching the
|
---|
452 | * other fields.
|
---|
453 | * @param millis the millis in the day (in local standard time)
|
---|
454 | * @return the time zone offset in milliseconds. */
|
---|
455 | public int getOffset(int era, int year, int month,
|
---|
456 | int day, int dayOfWeek, int millis)
|
---|
457 | {
|
---|
458 | // This method is called by Calendar, so we mustn't use that class.
|
---|
459 | int daylightSavings = 0;
|
---|
460 | if (useDaylight && era == GregorianCalendar.AD && year >= startYear)
|
---|
461 | {
|
---|
462 | // This does only work for Gregorian calendars :-(
|
---|
463 | // This is mainly because setStartYear doesn't take an era.
|
---|
464 |
|
---|
465 | boolean afterStart = !isBefore(year, month, day, dayOfWeek, millis,
|
---|
466 | startMode, startMonth,
|
---|
467 | startDay, startDayOfWeek, startTime);
|
---|
468 | boolean beforeEnd = isBefore(year, month, day, dayOfWeek, millis,
|
---|
469 | endMode, endMonth,
|
---|
470 | endDay, endDayOfWeek, endTime);
|
---|
471 |
|
---|
472 | if (startMonth < endMonth)
|
---|
473 | {
|
---|
474 | // use daylight savings, if the date is after the start of
|
---|
475 | // savings, and before the end of savings.
|
---|
476 | daylightSavings = afterStart && beforeEnd ? dstSavings : 0;
|
---|
477 | }
|
---|
478 | else
|
---|
479 | {
|
---|
480 | // use daylight savings, if the date is before the end of
|
---|
481 | // savings, or after the start of savings.
|
---|
482 | daylightSavings = beforeEnd || afterStart ? dstSavings : 0;
|
---|
483 | }
|
---|
484 | }
|
---|
485 | return rawOffset + daylightSavings;
|
---|
486 | }
|
---|
487 |
|
---|
488 | /**
|
---|
489 | * Returns the time zone offset to GMT in milliseconds, ignoring
|
---|
490 | * day light savings.
|
---|
491 | * @return the time zone offset. */
|
---|
492 | public int getRawOffset()
|
---|
493 | {
|
---|
494 | return rawOffset;
|
---|
495 | }
|
---|
496 |
|
---|
497 | /**
|
---|
498 | * Sets the standard time zone offset to GMT.
|
---|
499 | * @param rawOffset The time offset from GMT in milliseconds.
|
---|
500 | */
|
---|
501 | public void setRawOffset(int rawOffset)
|
---|
502 | {
|
---|
503 | this.rawOffset = rawOffset;
|
---|
504 | }
|
---|
505 |
|
---|
506 | /**
|
---|
507 | * Gets the daylight savings offset. This is a positive offset in
|
---|
508 | * milliseconds with respect to standard time. Typically this
|
---|
509 | * is one hour, but for some time zones this may be half an our.
|
---|
510 | * @return the daylight savings offset in milliseconds.
|
---|
511 | * @since JDK1.1.4?
|
---|
512 | */
|
---|
513 | public int getDSTSavings()
|
---|
514 | {
|
---|
515 | return dstSavings;
|
---|
516 | }
|
---|
517 |
|
---|
518 | /**
|
---|
519 | * Returns if this time zone uses daylight savings time.
|
---|
520 | * @return true, if we use daylight savings time, false otherwise.
|
---|
521 | */
|
---|
522 | public boolean useDaylightTime()
|
---|
523 | {
|
---|
524 | return useDaylight;
|
---|
525 | }
|
---|
526 |
|
---|
527 | /**
|
---|
528 | * Returns the number of days in the given month. It does always
|
---|
529 | * use the Gregorian leap year rule.
|
---|
530 | * @param month The month, zero based; use one of the Calendar constants.
|
---|
531 | * @param year The year.
|
---|
532 | */
|
---|
533 | private int getDaysInMonth(int month, int year)
|
---|
534 | {
|
---|
535 | // Most of this is copied from GregorianCalendar.getActualMaximum()
|
---|
536 | if (month == Calendar.FEBRUARY)
|
---|
537 | {
|
---|
538 | return ((year & 3) == 0 && (year % 100 != 0 || year % 400 == 0))
|
---|
539 | ? 29 : 28;
|
---|
540 | }
|
---|
541 | else if (month < Calendar.AUGUST)
|
---|
542 | return 31 - (month & 1);
|
---|
543 | else
|
---|
544 | return 30 + (month & 1);
|
---|
545 | }
|
---|
546 |
|
---|
547 | /**
|
---|
548 | * Checks if the date given in calXXXX, is before the change between
|
---|
549 | * dst and standard time.
|
---|
550 | * @param calYear the year of the date to check (for leap day cheking).
|
---|
551 | * @param calMonth the month of the date to check.
|
---|
552 | * @param calDay the day of month of the date to check.
|
---|
553 | * @param calDayOfWeek the day of week of the date to check.
|
---|
554 | * @param calMillis the millis of day of the date to check (standard time).
|
---|
555 | * @param mode the change mode; same semantic as startMode.
|
---|
556 | * @param month the change month; same semantic as startMonth.
|
---|
557 | * @param day the change day; same semantic as startDay.
|
---|
558 | * @param dayOfWeek the change day of week;
|
---|
559 | * @param millis the change time in millis since midnight standard time.
|
---|
560 | * same semantic as startDayOfWeek.
|
---|
561 | * @return true, if cal is before the change, false if cal is on
|
---|
562 | * or after the change.
|
---|
563 | */
|
---|
564 | private boolean isBefore(int calYear,
|
---|
565 | int calMonth, int calDayOfMonth, int calDayOfWeek,
|
---|
566 | int calMillis, int mode, int month,
|
---|
567 | int day, int dayOfWeek, int millis)
|
---|
568 | {
|
---|
569 |
|
---|
570 | // This method is called by Calendar, so we mustn't use that class.
|
---|
571 | // We have to do all calculations by hand.
|
---|
572 |
|
---|
573 | // check the months:
|
---|
574 |
|
---|
575 | // XXX - this is not correct:
|
---|
576 | // for the DOW_GE_DOM and DOW_LE_DOM modes the change date may
|
---|
577 | // be in a different month.
|
---|
578 | if (calMonth != month)
|
---|
579 | return calMonth < month;
|
---|
580 |
|
---|
581 | // check the day:
|
---|
582 | switch (mode)
|
---|
583 | {
|
---|
584 | case DOM_MODE:
|
---|
585 | if (calDayOfMonth != day)
|
---|
586 | return calDayOfMonth < day;
|
---|
587 | break;
|
---|
588 | case DOW_IN_MONTH_MODE:
|
---|
589 | {
|
---|
590 | // This computes the day of month of the day of type
|
---|
591 | // "dayOfWeek" that lies in the same (sunday based) week as cal.
|
---|
592 | calDayOfMonth += (dayOfWeek - calDayOfWeek);
|
---|
593 |
|
---|
594 | // Now we convert it to 7 based number (to get a one based offset
|
---|
595 | // after dividing by 7). If we count from the end of the
|
---|
596 | // month, we get want a -7 based number counting the days from
|
---|
597 | // the end:
|
---|
598 |
|
---|
599 | if (day < 0)
|
---|
600 | calDayOfMonth -= getDaysInMonth(calMonth, calYear) + 7;
|
---|
601 | else
|
---|
602 | calDayOfMonth += 6;
|
---|
603 |
|
---|
604 | // day > 0 day < 0
|
---|
605 | // S M T W T F S S M T W T F S
|
---|
606 | // 7 8 9 10 11 12 -36-35-34-33-32-31
|
---|
607 | // 13 14 15 16 17 18 19 -30-29-28-27-26-25-24
|
---|
608 | // 20 21 22 23 24 25 26 -23-22-21-20-19-18-17
|
---|
609 | // 27 28 29 30 31 32 33 -16-15-14-13-12-11-10
|
---|
610 | // 34 35 36 -9 -8 -7
|
---|
611 |
|
---|
612 | // Now we calculate the day of week in month:
|
---|
613 | int week = calDayOfMonth / 7;
|
---|
614 | // day > 0 day < 0
|
---|
615 | // S M T W T F S S M T W T F S
|
---|
616 | // 1 1 1 1 1 1 -5 -5 -4 -4 -4 -4
|
---|
617 | // 1 2 2 2 2 2 2 -4 -4 -4 -3 -3 -3 -3
|
---|
618 | // 2 3 3 3 3 3 3 -3 -3 -3 -2 -2 -2 -2
|
---|
619 | // 3 4 4 4 4 4 4 -2 -2 -2 -1 -1 -1 -1
|
---|
620 | // 4 5 5 -1 -1 -1
|
---|
621 |
|
---|
622 | if (week != day)
|
---|
623 | return week < day;
|
---|
624 |
|
---|
625 | if (calDayOfWeek != dayOfWeek)
|
---|
626 | return calDayOfWeek < dayOfWeek;
|
---|
627 |
|
---|
628 | // daylight savings starts/ends on the given day.
|
---|
629 | break;
|
---|
630 | }
|
---|
631 |
|
---|
632 | case DOW_LE_DOM_MODE:
|
---|
633 | // The greatest sunday before or equal December, 12
|
---|
634 | // is the same as smallest sunday after or equal December, 6.
|
---|
635 | day -= 6;
|
---|
636 |
|
---|
637 | case DOW_GE_DOM_MODE:
|
---|
638 |
|
---|
639 | // Calculate the day of month of the day of type
|
---|
640 | // "dayOfWeek" that lies before (or on) the given date.
|
---|
641 | calDayOfMonth -= (calDayOfWeek < dayOfWeek ? 7 : 0)
|
---|
642 | + calDayOfWeek - dayOfWeek;
|
---|
643 | if (calDayOfMonth < day)
|
---|
644 | return true;
|
---|
645 | if (calDayOfWeek != dayOfWeek || calDayOfMonth >= day + 7)
|
---|
646 | return false;
|
---|
647 | // now we have the same day
|
---|
648 | break;
|
---|
649 | }
|
---|
650 | // the millis decides:
|
---|
651 | return (calMillis < millis);
|
---|
652 | }
|
---|
653 |
|
---|
654 | /**
|
---|
655 | * Determines if the given date is in daylight savings time.
|
---|
656 | * @return true, if it is in daylight savings time, false otherwise.
|
---|
657 | */
|
---|
658 | public boolean inDaylightTime(Date date)
|
---|
659 | {
|
---|
660 | Calendar cal = Calendar.getInstance(this);
|
---|
661 | cal.setTime(date);
|
---|
662 | return (cal.get(Calendar.DST_OFFSET) != 0);
|
---|
663 | }
|
---|
664 |
|
---|
665 | /**
|
---|
666 | * Generates the hashCode for the SimpleDateFormat object. It is
|
---|
667 | * the rawOffset, possibly, if useDaylightSavings is true, xored
|
---|
668 | * with startYear, startMonth, startDayOfWeekInMonth, ..., endTime.
|
---|
669 | */
|
---|
670 | public synchronized int hashCode()
|
---|
671 | {
|
---|
672 | return rawOffset ^
|
---|
673 | (useDaylight ?
|
---|
674 | startMonth ^ startDay ^ startDayOfWeek ^ startTime
|
---|
675 | ^ endMonth ^ endDay ^ endDayOfWeek ^ endTime : 0);
|
---|
676 | }
|
---|
677 |
|
---|
678 | public synchronized boolean equals(Object o)
|
---|
679 | {
|
---|
680 | if (this == o)
|
---|
681 | return true;
|
---|
682 | if (!(o instanceof SimpleTimeZone))
|
---|
683 | return false;
|
---|
684 | SimpleTimeZone zone = (SimpleTimeZone) o;
|
---|
685 | if (zone.hashCode() != hashCode()
|
---|
686 | || !getID().equals(zone.getID())
|
---|
687 | || rawOffset != zone.rawOffset || useDaylight != zone.useDaylight)
|
---|
688 | return false;
|
---|
689 | if (!useDaylight)
|
---|
690 | return true;
|
---|
691 | return (startYear == zone.startYear
|
---|
692 | && startMonth == zone.startMonth
|
---|
693 | && startDay == zone.startDay
|
---|
694 | && startDayOfWeek == zone.startDayOfWeek
|
---|
695 | && startTime == zone.startTime
|
---|
696 | && endMonth == zone.endMonth
|
---|
697 | && endDay == zone.endDay
|
---|
698 | && endDayOfWeek == zone.endDayOfWeek
|
---|
699 | && endTime == zone.endTime);
|
---|
700 | }
|
---|
701 |
|
---|
702 | /**
|
---|
703 | * Test if the other time zone uses the same rule and only
|
---|
704 | * possibly differs in ID. This implementation for this particular
|
---|
705 | * class will return true if the other object is a SimpleTimeZone,
|
---|
706 | * the raw offsets and useDaylight are identical and if useDaylight
|
---|
707 | * is true, also the start and end datas are identical.
|
---|
708 | * @return true if this zone uses the same rule.
|
---|
709 | */
|
---|
710 | public boolean hasSameRules(TimeZone other)
|
---|
711 | {
|
---|
712 | if (this == other)
|
---|
713 | return true;
|
---|
714 | if (!(other instanceof SimpleTimeZone))
|
---|
715 | return false;
|
---|
716 | SimpleTimeZone zone = (SimpleTimeZone) other;
|
---|
717 | if (zone.hashCode() != hashCode()
|
---|
718 | || rawOffset != zone.rawOffset || useDaylight != zone.useDaylight)
|
---|
719 | return false;
|
---|
720 | if (!useDaylight)
|
---|
721 | return true;
|
---|
722 | return (startYear == zone.startYear
|
---|
723 | && startMonth == zone.startMonth
|
---|
724 | && startDay == zone.startDay
|
---|
725 | && startDayOfWeek == zone.startDayOfWeek
|
---|
726 | && startTime == zone.startTime
|
---|
727 | && endMonth == zone.endMonth
|
---|
728 | && endDay == zone.endDay
|
---|
729 | && endDayOfWeek == zone.endDayOfWeek && endTime == zone.endTime);
|
---|
730 | }
|
---|
731 |
|
---|
732 | /**
|
---|
733 | * Returns a string representation of this SimpleTimeZone object.
|
---|
734 | * @return a string representation of this SimpleTimeZone object.
|
---|
735 | */
|
---|
736 | public String toString()
|
---|
737 | {
|
---|
738 | // the test for useDaylight is an incompatibility to jdk1.2, but
|
---|
739 | // I think this shouldn't hurt.
|
---|
740 | return getClass().getName() + "["
|
---|
741 | + "id=" + getID()
|
---|
742 | + ",offset=" + rawOffset
|
---|
743 | + ",dstSavings=" + dstSavings
|
---|
744 | + ",useDaylight=" + useDaylight
|
---|
745 | + (useDaylight ?
|
---|
746 | ",startYear=" + startYear
|
---|
747 | + ",startMode=" + startMode
|
---|
748 | + ",startMonth=" + startMonth
|
---|
749 | + ",startDay=" + startDay
|
---|
750 | + ",startDayOfWeek=" + startDayOfWeek
|
---|
751 | + ",startTime=" + startTime
|
---|
752 | + ",endMode=" + endMode
|
---|
753 | + ",endMonth=" + endMonth
|
---|
754 | + ",endDay=" + endDay
|
---|
755 | + ",endDayOfWeek=" + endDayOfWeek
|
---|
756 | + ",endTime=" + endTime : "") + "]";
|
---|
757 | }
|
---|
758 |
|
---|
759 | /**
|
---|
760 | * Reads a serialized simple time zone from stream.
|
---|
761 | * @see #writeObject
|
---|
762 | */
|
---|
763 | private void readObject(java.io.ObjectInputStream input)
|
---|
764 | throws java.io.IOException, ClassNotFoundException
|
---|
765 | {
|
---|
766 | input.defaultReadObject();
|
---|
767 | if (serialVersionOnStream == 0)
|
---|
768 | {
|
---|
769 | // initialize the new fields to default values.
|
---|
770 | dstSavings = 60 * 60 * 1000;
|
---|
771 | endMode = DOW_IN_MONTH_MODE;
|
---|
772 | startMode = DOW_IN_MONTH_MODE;
|
---|
773 | serialVersionOnStream = 1;
|
---|
774 | }
|
---|
775 | else
|
---|
776 | {
|
---|
777 | int length = input.readInt();
|
---|
778 | byte[] byteArray = new byte[length];
|
---|
779 | input.read(byteArray, 0, length);
|
---|
780 | if (length >= 4)
|
---|
781 | {
|
---|
782 | // Lets hope that Sun does extensions to the serialized
|
---|
783 | // form in a sane manner.
|
---|
784 | startDay = byteArray[0];
|
---|
785 | startDayOfWeek = byteArray[1];
|
---|
786 | endDay = byteArray[2];
|
---|
787 | endDayOfWeek = byteArray[3];
|
---|
788 | }
|
---|
789 | }
|
---|
790 | }
|
---|
791 |
|
---|
792 | /**
|
---|
793 | * Serializes this object to a stream. @serialdata The object is
|
---|
794 | * first written in the old JDK 1.1 format, so that it can be read
|
---|
795 | * by by the old classes. This means, that the
|
---|
796 | * <code>start/endDay(OfWeek)</code>-Fields are written in the
|
---|
797 | * DOW_IN_MONTH_MODE rule, since this was the only supported rule
|
---|
798 | * in 1.1.
|
---|
799 | *
|
---|
800 | * In the optional section, we write first the length of an byte
|
---|
801 | * array as int and afterwards the byte array itself. The byte
|
---|
802 | * array contains in this release four elements, namely the real
|
---|
803 | * startDay, startDayOfWeek endDay, endDayOfWeek in that Order.
|
---|
804 | * These fields are needed, because for compatibility reasons only
|
---|
805 | * approximative values are written to the required section, as
|
---|
806 | * described above.
|
---|
807 | */
|
---|
808 | private void writeObject(java.io.ObjectOutputStream output)
|
---|
809 | throws java.io.IOException
|
---|
810 | {
|
---|
811 | byte[] byteArray = new byte[]
|
---|
812 | {
|
---|
813 | (byte) startDay, (byte) startDayOfWeek,
|
---|
814 | (byte) endDay, (byte) endDayOfWeek};
|
---|
815 |
|
---|
816 | /* calculate the approximation for JDK 1.1 */
|
---|
817 | switch (startMode)
|
---|
818 | {
|
---|
819 | case DOM_MODE:
|
---|
820 | startDayOfWeek = Calendar.SUNDAY; // random day of week
|
---|
821 | // fall through
|
---|
822 | case DOW_GE_DOM_MODE:
|
---|
823 | case DOW_LE_DOM_MODE:
|
---|
824 | startDay = (startDay + 6) / 7;
|
---|
825 | }
|
---|
826 | switch (endMode)
|
---|
827 | {
|
---|
828 | case DOM_MODE:
|
---|
829 | endDayOfWeek = Calendar.SUNDAY;
|
---|
830 | // fall through
|
---|
831 | case DOW_GE_DOM_MODE:
|
---|
832 | case DOW_LE_DOM_MODE:
|
---|
833 | endDay = (endDay + 6) / 7;
|
---|
834 | }
|
---|
835 |
|
---|
836 | // the required part:
|
---|
837 | output.defaultWriteObject();
|
---|
838 | // the optional part:
|
---|
839 | output.writeInt(byteArray.length);
|
---|
840 | output.write(byteArray, 0, byteArray.length);
|
---|
841 | }
|
---|
842 | }
|
---|