1 /* 2 * Copyright (c) 1996, 2011, 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. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 /* 27 * (C) Copyright Taligent, Inc. 1996 - All Rights Reserved 28 * (C) Copyright IBM Corp. 1996 - All Rights Reserved 29 * 30 * The original version of this source code and documentation is copyrighted 31 * and owned by Taligent, Inc., a wholly-owned subsidiary of IBM. These 32 * materials are provided under terms of a License Agreement between Taligent 33 * and Sun. This technology is protected by multiple US and International 34 * patents. This notice and attribution to Taligent may not be removed. 35 * Taligent is a registered trademark of Taligent, Inc. 36 * 37 */ 38 39 package java.util; 40 41 import java.io.ObjectInputStream; 42 import java.io.ObjectOutputStream; 43 import java.io.IOException; 44 import sun.util.calendar.CalendarSystem; 45 import sun.util.calendar.CalendarUtils; 46 import sun.util.calendar.BaseCalendar; 47 import sun.util.calendar.Gregorian; 48 49 /** 50 * <code>SimpleTimeZone</code> is a concrete subclass of <code>TimeZone</code> 51 * that represents a time zone for use with a Gregorian calendar. 52 * The class holds an offset from GMT, called <em>raw offset</em>, and start 53 * and end rules for a daylight saving time schedule. Since it only holds 54 * single values for each, it cannot handle historical changes in the offset 55 * from GMT and the daylight saving schedule, except that the {@link 56 * #setStartYear setStartYear} method can specify the year when the daylight 57 * saving time schedule starts in effect. 58 * <p> 59 * To construct a <code>SimpleTimeZone</code> with a daylight saving time 60 * schedule, the schedule can be described with a set of rules, 61 * <em>start-rule</em> and <em>end-rule</em>. A day when daylight saving time 62 * starts or ends is specified by a combination of <em>month</em>, 63 * <em>day-of-month</em>, and <em>day-of-week</em> values. The <em>month</em> 64 * value is represented by a Calendar {@link Calendar#MONTH MONTH} field 65 * value, such as {@link Calendar#MARCH}. The <em>day-of-week</em> value is 66 * represented by a Calendar {@link Calendar#DAY_OF_WEEK DAY_OF_WEEK} value, 67 * such as {@link Calendar#SUNDAY SUNDAY}. The meanings of value combinations 68 * are as follows. 69 * 70 * <ul> 71 * <li><b>Exact day of month</b><br> 72 * To specify an exact day of month, set the <em>month</em> and 73 * <em>day-of-month</em> to an exact value, and <em>day-of-week</em> to zero. For 74 * example, to specify March 1, set the <em>month</em> to {@link Calendar#MARCH 75 * MARCH}, <em>day-of-month</em> to 1, and <em>day-of-week</em> to 0.</li> 76 * 77 * <li><b>Day of week on or after day of month</b><br> 78 * To specify a day of week on or after an exact day of month, set the 79 * <em>month</em> to an exact month value, <em>day-of-month</em> to the day on 80 * or after which the rule is applied, and <em>day-of-week</em> to a negative {@link 81 * Calendar#DAY_OF_WEEK DAY_OF_WEEK} field value. For example, to specify the 82 * second Sunday of April, set <em>month</em> to {@link Calendar#APRIL APRIL}, 83 * <em>day-of-month</em> to 8, and <em>day-of-week</em> to <code>-</code>{@link 84 * Calendar#SUNDAY SUNDAY}.</li> 85 * 86 * <li><b>Day of week on or before day of month</b><br> 87 * To specify a day of the week on or before an exact day of the month, set 88 * <em>day-of-month</em> and <em>day-of-week</em> to a negative value. For 89 * example, to specify the last Wednesday on or before the 21st of March, set 90 * <em>month</em> to {@link Calendar#MARCH MARCH}, <em>day-of-month</em> is -21 91 * and <em>day-of-week</em> is <code>-</code>{@link Calendar#WEDNESDAY WEDNESDAY}. </li> 92 * 93 * <li><b>Last day-of-week of month</b><br> 94 * To specify, the last day-of-week of the month, set <em>day-of-week</em> to a 95 * {@link Calendar#DAY_OF_WEEK DAY_OF_WEEK} value and <em>day-of-month</em> to 96 * -1. For example, to specify the last Sunday of October, set <em>month</em> 97 * to {@link Calendar#OCTOBER OCTOBER}, <em>day-of-week</em> to {@link 98 * Calendar#SUNDAY SUNDAY} and <em>day-of-month</em> to -1. </li> 99 * 100 * </ul> 101 * The time of the day at which daylight saving time starts or ends is 102 * specified by a millisecond value within the day. There are three kinds of 103 * <em>mode</em>s to specify the time: {@link #WALL_TIME}, {@link 104 * #STANDARD_TIME} and {@link #UTC_TIME}. For example, if daylight 105 * saving time ends 106 * at 2:00 am in the wall clock time, it can be specified by 7200000 107 * milliseconds in the {@link #WALL_TIME} mode. In this case, the wall clock time 108 * for an <em>end-rule</em> means the same thing as the daylight time. 109 * <p> 110 * The following are examples of parameters for constructing time zone objects. 111 * <pre><code> 112 * // Base GMT offset: -8:00 113 * // DST starts: at 2:00am in standard time 114 * // on the first Sunday in April 115 * // DST ends: at 2:00am in daylight time 116 * // on the last Sunday in October 117 * // Save: 1 hour 118 * SimpleTimeZone(-28800000, 119 * "America/Los_Angeles", 120 * Calendar.APRIL, 1, -Calendar.SUNDAY, 121 * 7200000, 122 * Calendar.OCTOBER, -1, Calendar.SUNDAY, 123 * 7200000, 124 * 3600000) 125 * 126 * // Base GMT offset: +1:00 127 * // DST starts: at 1:00am in UTC time 128 * // on the last Sunday in March 129 * // DST ends: at 1:00am in UTC time 130 * // on the last Sunday in October 131 * // Save: 1 hour 132 * SimpleTimeZone(3600000, 133 * "Europe/Paris", 134 * Calendar.MARCH, -1, Calendar.SUNDAY, 135 * 3600000, SimpleTimeZone.UTC_TIME, 136 * Calendar.OCTOBER, -1, Calendar.SUNDAY, 137 * 3600000, SimpleTimeZone.UTC_TIME, 138 * 3600000) 139 * </code></pre> 140 * These parameter rules are also applicable to the set rule methods, such as 141 * <code>setStartRule</code>. 142 * 143 * @since 1.1 144 * @see Calendar 145 * @see GregorianCalendar 146 * @see TimeZone 147 * @author David Goldsmith, Mark Davis, Chen-Lieh Huang, Alan Liu 148 */ 149 150 public class SimpleTimeZone extends TimeZone { 151 /** 152 * Constructs a SimpleTimeZone with the given base time zone offset from GMT 153 * and time zone ID with no daylight saving time schedule. 154 * 155 * @param rawOffset The base time zone offset in milliseconds to GMT. 156 * @param ID The time zone name that is given to this instance. 157 */ SimpleTimeZone(int rawOffset, String ID)158 public SimpleTimeZone(int rawOffset, String ID) 159 { 160 this.rawOffset = rawOffset; 161 setID (ID); 162 dstSavings = millisPerHour; // In case user sets rules later 163 } 164 165 /** 166 * Constructs a SimpleTimeZone with the given base time zone offset from 167 * GMT, time zone ID, and rules for starting and ending the daylight 168 * time. 169 * Both <code>startTime</code> and <code>endTime</code> are specified to be 170 * represented in the wall clock time. The amount of daylight saving is 171 * assumed to be 3600000 milliseconds (i.e., one hour). This constructor is 172 * equivalent to: 173 * <pre><code> 174 * SimpleTimeZone(rawOffset, 175 * ID, 176 * startMonth, 177 * startDay, 178 * startDayOfWeek, 179 * startTime, 180 * SimpleTimeZone.{@link #WALL_TIME}, 181 * endMonth, 182 * endDay, 183 * endDayOfWeek, 184 * endTime, 185 * SimpleTimeZone.{@link #WALL_TIME}, 186 * 3600000) 187 * </code></pre> 188 * 189 * @param rawOffset The given base time zone offset from GMT. 190 * @param ID The time zone ID which is given to this object. 191 * @param startMonth The daylight saving time starting month. Month is 192 * a {@link Calendar#MONTH MONTH} field value (0-based. e.g., 0 193 * for January). 194 * @param startDay The day of the month on which the daylight saving time starts. 195 * See the class description for the special cases of this parameter. 196 * @param startDayOfWeek The daylight saving time starting day-of-week. 197 * See the class description for the special cases of this parameter. 198 * @param startTime The daylight saving time starting time in local wall clock 199 * time (in milliseconds within the day), which is local 200 * standard time in this case. 201 * @param endMonth The daylight saving time ending month. Month is 202 * a {@link Calendar#MONTH MONTH} field 203 * value (0-based. e.g., 9 for October). 204 * @param endDay The day of the month on which the daylight saving time ends. 205 * See the class description for the special cases of this parameter. 206 * @param endDayOfWeek The daylight saving time ending day-of-week. 207 * See the class description for the special cases of this parameter. 208 * @param endTime The daylight saving ending time in local wall clock time, 209 * (in milliseconds within the day) which is local daylight 210 * time in this case. 211 * @exception IllegalArgumentException if the month, day, dayOfWeek, or time 212 * parameters are out of range for the start or end rule 213 */ SimpleTimeZone(int rawOffset, String ID, int startMonth, int startDay, int startDayOfWeek, int startTime, int endMonth, int endDay, int endDayOfWeek, int endTime)214 public SimpleTimeZone(int rawOffset, String ID, 215 int startMonth, int startDay, int startDayOfWeek, int startTime, 216 int endMonth, int endDay, int endDayOfWeek, int endTime) 217 { 218 this(rawOffset, ID, 219 startMonth, startDay, startDayOfWeek, startTime, WALL_TIME, 220 endMonth, endDay, endDayOfWeek, endTime, WALL_TIME, 221 millisPerHour); 222 } 223 224 /** 225 * Constructs a SimpleTimeZone with the given base time zone offset from 226 * GMT, time zone ID, and rules for starting and ending the daylight 227 * time. 228 * Both <code>startTime</code> and <code>endTime</code> are assumed to be 229 * represented in the wall clock time. This constructor is equivalent to: 230 * <pre><code> 231 * SimpleTimeZone(rawOffset, 232 * ID, 233 * startMonth, 234 * startDay, 235 * startDayOfWeek, 236 * startTime, 237 * SimpleTimeZone.{@link #WALL_TIME}, 238 * endMonth, 239 * endDay, 240 * endDayOfWeek, 241 * endTime, 242 * SimpleTimeZone.{@link #WALL_TIME}, 243 * dstSavings) 244 * </code></pre> 245 * 246 * @param rawOffset The given base time zone offset from GMT. 247 * @param ID The time zone ID which is given to this object. 248 * @param startMonth The daylight saving time starting month. Month is 249 * a {@link Calendar#MONTH MONTH} field 250 * value (0-based. e.g., 0 for January). 251 * @param startDay The day of the month on which the daylight saving time starts. 252 * See the class description for the special cases of this parameter. 253 * @param startDayOfWeek The daylight saving time starting day-of-week. 254 * See the class description for the special cases of this parameter. 255 * @param startTime The daylight saving time starting time in local wall clock 256 * time, which is local standard time in this case. 257 * @param endMonth The daylight saving time ending month. Month is 258 * a {@link Calendar#MONTH MONTH} field 259 * value (0-based. e.g., 9 for October). 260 * @param endDay The day of the month on which the daylight saving time ends. 261 * See the class description for the special cases of this parameter. 262 * @param endDayOfWeek The daylight saving time ending day-of-week. 263 * See the class description for the special cases of this parameter. 264 * @param endTime The daylight saving ending time in local wall clock time, 265 * which is local daylight time in this case. 266 * @param dstSavings The amount of time in milliseconds saved during 267 * daylight saving time. 268 * @exception IllegalArgumentException if the month, day, dayOfWeek, or time 269 * parameters are out of range for the start or end rule 270 * @since 1.2 271 */ SimpleTimeZone(int rawOffset, String ID, int startMonth, int startDay, int startDayOfWeek, int startTime, int endMonth, int endDay, int endDayOfWeek, int endTime, int dstSavings)272 public SimpleTimeZone(int rawOffset, String ID, 273 int startMonth, int startDay, int startDayOfWeek, int startTime, 274 int endMonth, int endDay, int endDayOfWeek, int endTime, 275 int dstSavings) 276 { 277 this(rawOffset, ID, 278 startMonth, startDay, startDayOfWeek, startTime, WALL_TIME, 279 endMonth, endDay, endDayOfWeek, endTime, WALL_TIME, 280 dstSavings); 281 } 282 283 /** 284 * Constructs a SimpleTimeZone with the given base time zone offset from 285 * GMT, time zone ID, and rules for starting and ending the daylight 286 * time. 287 * This constructor takes the full set of the start and end rules 288 * parameters, including modes of <code>startTime</code> and 289 * <code>endTime</code>. The mode specifies either {@link #WALL_TIME wall 290 * time} or {@link #STANDARD_TIME standard time} or {@link #UTC_TIME UTC 291 * time}. 292 * 293 * @param rawOffset The given base time zone offset from GMT. 294 * @param ID The time zone ID which is given to this object. 295 * @param startMonth The daylight saving time starting month. Month is 296 * a {@link Calendar#MONTH MONTH} field 297 * value (0-based. e.g., 0 for January). 298 * @param startDay The day of the month on which the daylight saving time starts. 299 * See the class description for the special cases of this parameter. 300 * @param startDayOfWeek The daylight saving time starting day-of-week. 301 * See the class description for the special cases of this parameter. 302 * @param startTime The daylight saving time starting time in the time mode 303 * specified by <code>startTimeMode</code>. 304 * @param startTimeMode The mode of the start time specified by startTime. 305 * @param endMonth The daylight saving time ending month. Month is 306 * a {@link Calendar#MONTH MONTH} field 307 * value (0-based. e.g., 9 for October). 308 * @param endDay The day of the month on which the daylight saving time ends. 309 * See the class description for the special cases of this parameter. 310 * @param endDayOfWeek The daylight saving time ending day-of-week. 311 * See the class description for the special cases of this parameter. 312 * @param endTime The daylight saving ending time in time time mode 313 * specified by <code>endTimeMode</code>. 314 * @param endTimeMode The mode of the end time specified by endTime 315 * @param dstSavings The amount of time in milliseconds saved during 316 * daylight saving time. 317 * 318 * @exception IllegalArgumentException if the month, day, dayOfWeek, time more, or 319 * time parameters are out of range for the start or end rule, or if a time mode 320 * value is invalid. 321 * 322 * @see #WALL_TIME 323 * @see #STANDARD_TIME 324 * @see #UTC_TIME 325 * 326 * @since 1.4 327 */ SimpleTimeZone(int rawOffset, String ID, int startMonth, int startDay, int startDayOfWeek, int startTime, int startTimeMode, int endMonth, int endDay, int endDayOfWeek, int endTime, int endTimeMode, int dstSavings)328 public SimpleTimeZone(int rawOffset, String ID, 329 int startMonth, int startDay, int startDayOfWeek, 330 int startTime, int startTimeMode, 331 int endMonth, int endDay, int endDayOfWeek, 332 int endTime, int endTimeMode, 333 int dstSavings) { 334 335 setID(ID); 336 this.rawOffset = rawOffset; 337 this.startMonth = startMonth; 338 this.startDay = startDay; 339 this.startDayOfWeek = startDayOfWeek; 340 this.startTime = startTime; 341 this.startTimeMode = startTimeMode; 342 this.endMonth = endMonth; 343 this.endDay = endDay; 344 this.endDayOfWeek = endDayOfWeek; 345 this.endTime = endTime; 346 this.endTimeMode = endTimeMode; 347 this.dstSavings = dstSavings; 348 349 // this.useDaylight is set by decodeRules 350 decodeRules(); 351 if (dstSavings <= 0) { 352 throw new IllegalArgumentException("Illegal daylight saving value: " + dstSavings); 353 } 354 } 355 356 /** 357 * Sets the daylight saving time starting year. 358 * 359 * @param year The daylight saving starting year. 360 */ setStartYear(int year)361 public void setStartYear(int year) 362 { 363 startYear = year; 364 invalidateCache(); 365 } 366 367 /** 368 * Sets the daylight saving time start rule. For example, if daylight saving 369 * time starts on the first Sunday in April at 2 am in local wall clock 370 * time, you can set the start rule by calling: 371 * <pre><code>setStartRule(Calendar.APRIL, 1, Calendar.SUNDAY, 2*60*60*1000);</code></pre> 372 * 373 * @param startMonth The daylight saving time starting month. Month is 374 * a {@link Calendar#MONTH MONTH} field 375 * value (0-based. e.g., 0 for January). 376 * @param startDay The day of the month on which the daylight saving time starts. 377 * See the class description for the special cases of this parameter. 378 * @param startDayOfWeek The daylight saving time starting day-of-week. 379 * See the class description for the special cases of this parameter. 380 * @param startTime The daylight saving time starting time in local wall clock 381 * time, which is local standard time in this case. 382 * @exception IllegalArgumentException if the <code>startMonth</code>, <code>startDay</code>, 383 * <code>startDayOfWeek</code>, or <code>startTime</code> parameters are out of range 384 */ setStartRule(int startMonth, int startDay, int startDayOfWeek, int startTime)385 public void setStartRule(int startMonth, int startDay, int startDayOfWeek, int startTime) 386 { 387 this.startMonth = startMonth; 388 this.startDay = startDay; 389 this.startDayOfWeek = startDayOfWeek; 390 this.startTime = startTime; 391 startTimeMode = WALL_TIME; 392 decodeStartRule(); 393 invalidateCache(); 394 } 395 396 /** 397 * Sets the daylight saving time start rule to a fixed date within a month. 398 * This method is equivalent to: 399 * <pre><code>setStartRule(startMonth, startDay, 0, startTime)</code></pre> 400 * 401 * @param startMonth The daylight saving time starting month. Month is 402 * a {@link Calendar#MONTH MONTH} field 403 * value (0-based. e.g., 0 for January). 404 * @param startDay The day of the month on which the daylight saving time starts. 405 * @param startTime The daylight saving time starting time in local wall clock 406 * time, which is local standard time in this case. 407 * See the class description for the special cases of this parameter. 408 * @exception IllegalArgumentException if the <code>startMonth</code>, 409 * <code>startDayOfMonth</code>, or <code>startTime</code> parameters are out of range 410 * @since 1.2 411 */ setStartRule(int startMonth, int startDay, int startTime)412 public void setStartRule(int startMonth, int startDay, int startTime) { 413 setStartRule(startMonth, startDay, 0, startTime); 414 } 415 416 /** 417 * Sets the daylight saving time start rule to a weekday before or after the given date within 418 * a month, e.g., the first Monday on or after the 8th. 419 * 420 * @param startMonth The daylight saving time starting month. Month is 421 * a {@link Calendar#MONTH MONTH} field 422 * value (0-based. e.g., 0 for January). 423 * @param startDay The day of the month on which the daylight saving time starts. 424 * @param startDayOfWeek The daylight saving time starting day-of-week. 425 * @param startTime The daylight saving time starting time in local wall clock 426 * time, which is local standard time in this case. 427 * @param after If true, this rule selects the first <code>dayOfWeek</code> on or 428 * <em>after</em> <code>dayOfMonth</code>. If false, this rule 429 * selects the last <code>dayOfWeek</code> on or <em>before</em> 430 * <code>dayOfMonth</code>. 431 * @exception IllegalArgumentException if the <code>startMonth</code>, <code>startDay</code>, 432 * <code>startDayOfWeek</code>, or <code>startTime</code> parameters are out of range 433 * @since 1.2 434 */ setStartRule(int startMonth, int startDay, int startDayOfWeek, int startTime, boolean after)435 public void setStartRule(int startMonth, int startDay, int startDayOfWeek, 436 int startTime, boolean after) 437 { 438 // TODO: this method doesn't check the initial values of dayOfMonth or dayOfWeek. 439 if (after) { 440 setStartRule(startMonth, startDay, -startDayOfWeek, startTime); 441 } else { 442 setStartRule(startMonth, -startDay, -startDayOfWeek, startTime); 443 } 444 } 445 446 /** 447 * Sets the daylight saving time end rule. For example, if daylight saving time 448 * ends on the last Sunday in October at 2 am in wall clock time, 449 * you can set the end rule by calling: 450 * <code>setEndRule(Calendar.OCTOBER, -1, Calendar.SUNDAY, 2*60*60*1000);</code> 451 * 452 * @param endMonth The daylight saving time ending month. Month is 453 * a {@link Calendar#MONTH MONTH} field 454 * value (0-based. e.g., 9 for October). 455 * @param endDay The day of the month on which the daylight saving time ends. 456 * See the class description for the special cases of this parameter. 457 * @param endDayOfWeek The daylight saving time ending day-of-week. 458 * See the class description for the special cases of this parameter. 459 * @param endTime The daylight saving ending time in local wall clock time, 460 * (in milliseconds within the day) which is local daylight 461 * time in this case. 462 * @exception IllegalArgumentException if the <code>endMonth</code>, <code>endDay</code>, 463 * <code>endDayOfWeek</code>, or <code>endTime</code> parameters are out of range 464 */ setEndRule(int endMonth, int endDay, int endDayOfWeek, int endTime)465 public void setEndRule(int endMonth, int endDay, int endDayOfWeek, 466 int endTime) 467 { 468 this.endMonth = endMonth; 469 this.endDay = endDay; 470 this.endDayOfWeek = endDayOfWeek; 471 this.endTime = endTime; 472 this.endTimeMode = WALL_TIME; 473 decodeEndRule(); 474 invalidateCache(); 475 } 476 477 /** 478 * Sets the daylight saving time end rule to a fixed date within a month. 479 * This method is equivalent to: 480 * <pre><code>setEndRule(endMonth, endDay, 0, endTime)</code></pre> 481 * 482 * @param endMonth The daylight saving time ending month. Month is 483 * a {@link Calendar#MONTH MONTH} field 484 * value (0-based. e.g., 9 for October). 485 * @param endDay The day of the month on which the daylight saving time ends. 486 * @param endTime The daylight saving ending time in local wall clock time, 487 * (in milliseconds within the day) which is local daylight 488 * time in this case. 489 * @exception IllegalArgumentException the <code>endMonth</code>, <code>endDay</code>, 490 * or <code>endTime</code> parameters are out of range 491 * @since 1.2 492 */ setEndRule(int endMonth, int endDay, int endTime)493 public void setEndRule(int endMonth, int endDay, int endTime) 494 { 495 setEndRule(endMonth, endDay, 0, endTime); 496 } 497 498 /** 499 * Sets the daylight saving time end rule to a weekday before or after the given date within 500 * a month, e.g., the first Monday on or after the 8th. 501 * 502 * @param endMonth The daylight saving time ending month. Month is 503 * a {@link Calendar#MONTH MONTH} field 504 * value (0-based. e.g., 9 for October). 505 * @param endDay The day of the month on which the daylight saving time ends. 506 * @param endDayOfWeek The daylight saving time ending day-of-week. 507 * @param endTime The daylight saving ending time in local wall clock time, 508 * (in milliseconds within the day) which is local daylight 509 * time in this case. 510 * @param after If true, this rule selects the first <code>endDayOfWeek</code> on 511 * or <em>after</em> <code>endDay</code>. If false, this rule 512 * selects the last <code>endDayOfWeek</code> on or before 513 * <code>endDay</code> of the month. 514 * @exception IllegalArgumentException the <code>endMonth</code>, <code>endDay</code>, 515 * <code>endDayOfWeek</code>, or <code>endTime</code> parameters are out of range 516 * @since 1.2 517 */ setEndRule(int endMonth, int endDay, int endDayOfWeek, int endTime, boolean after)518 public void setEndRule(int endMonth, int endDay, int endDayOfWeek, int endTime, boolean after) 519 { 520 if (after) { 521 setEndRule(endMonth, endDay, -endDayOfWeek, endTime); 522 } else { 523 setEndRule(endMonth, -endDay, -endDayOfWeek, endTime); 524 } 525 } 526 527 /** 528 * Returns the offset of this time zone from UTC at the given 529 * time. If daylight saving time is in effect at the given time, 530 * the offset value is adjusted with the amount of daylight 531 * saving. 532 * 533 * @param date the time at which the time zone offset is found 534 * @return the amount of time in milliseconds to add to UTC to get 535 * local time. 536 * @since 1.4 537 */ getOffset(long date)538 public int getOffset(long date) { 539 return getOffsets(date, null); 540 } 541 542 /** 543 * @see TimeZone#getOffsets 544 */ getOffsets(long date, int[] offsets)545 int getOffsets(long date, int[] offsets) { 546 int offset = rawOffset; 547 548 computeOffset: 549 if (useDaylight) { 550 synchronized (this) { 551 if (cacheStart != 0) { 552 if (date >= cacheStart && date < cacheEnd) { 553 offset += dstSavings; 554 break computeOffset; 555 } 556 } 557 } 558 BaseCalendar cal = date >= GregorianCalendar.DEFAULT_GREGORIAN_CUTOVER ? 559 gcal : (BaseCalendar) CalendarSystem.forName("julian"); 560 BaseCalendar.Date cdate = (BaseCalendar.Date) cal.newCalendarDate(TimeZone.NO_TIMEZONE); 561 // Get the year in local time 562 cal.getCalendarDate(date + rawOffset, cdate); 563 int year = cdate.getNormalizedYear(); 564 if (year >= startYear) { 565 // Clear time elements for the transition calculations 566 cdate.setTimeOfDay(0, 0, 0, 0); 567 offset = getOffset(cal, cdate, year, date); 568 } 569 } 570 571 if (offsets != null) { 572 offsets[0] = rawOffset; 573 offsets[1] = offset - rawOffset; 574 } 575 return offset; 576 } 577 578 /** 579 * Returns the difference in milliseconds between local time and 580 * UTC, taking into account both the raw offset and the effect of 581 * daylight saving, for the specified date and time. This method 582 * assumes that the start and end month are distinct. It also 583 * uses a default {@link GregorianCalendar} object as its 584 * underlying calendar, such as for determining leap years. Do 585 * not use the result of this method with a calendar other than a 586 * default <code>GregorianCalendar</code>. 587 * 588 * <p><em>Note: In general, clients should use 589 * <code>Calendar.get(ZONE_OFFSET) + Calendar.get(DST_OFFSET)</code> 590 * instead of calling this method.</em> 591 * 592 * @param era The era of the given date. 593 * @param year The year in the given date. 594 * @param month The month in the given date. Month is 0-based. e.g., 595 * 0 for January. 596 * @param day The day-in-month of the given date. 597 * @param dayOfWeek The day-of-week of the given date. 598 * @param millis The milliseconds in day in <em>standard</em> local time. 599 * @return The milliseconds to add to UTC to get local time. 600 * @exception IllegalArgumentException the <code>era</code>, 601 * <code>month</code>, <code>day</code>, <code>dayOfWeek</code>, 602 * or <code>millis</code> parameters are out of range 603 */ getOffset(int era, int year, int month, int day, int dayOfWeek, int millis)604 public int getOffset(int era, int year, int month, int day, int dayOfWeek, 605 int millis) 606 { 607 if (era != GregorianCalendar.AD && era != GregorianCalendar.BC) { 608 throw new IllegalArgumentException("Illegal era " + era); 609 } 610 611 int y = year; 612 if (era == GregorianCalendar.BC) { 613 // adjust y with the GregorianCalendar-style year numbering. 614 y = 1 - y; 615 } 616 617 // If the year isn't representable with the 64-bit long 618 // integer in milliseconds, convert the year to an 619 // equivalent year. This is required to pass some JCK test cases 620 // which are actually useless though because the specified years 621 // can't be supported by the Java time system. 622 if (y >= 292278994) { 623 y = 2800 + y % 2800; 624 } else if (y <= -292269054) { 625 // y %= 28 also produces an equivalent year, but positive 626 // year numbers would be convenient to use the UNIX cal 627 // command. 628 y = (int) CalendarUtils.mod((long) y, 28); 629 } 630 631 // convert year to its 1-based month value 632 int m = month + 1; 633 634 // First, calculate time as a Gregorian date. 635 BaseCalendar cal = gcal; 636 BaseCalendar.Date cdate = (BaseCalendar.Date) cal.newCalendarDate(TimeZone.NO_TIMEZONE); 637 cdate.setDate(y, m, day); 638 long time = cal.getTime(cdate); // normalize cdate 639 time += millis - rawOffset; // UTC time 640 641 // If the time value represents a time before the default 642 // Gregorian cutover, recalculate time using the Julian 643 // calendar system. For the Julian calendar system, the 644 // normalized year numbering is ..., -2 (BCE 2), -1 (BCE 1), 645 // 1, 2 ... which is different from the GregorianCalendar 646 // style year numbering (..., -1, 0 (BCE 1), 1, 2, ...). 647 if (time < GregorianCalendar.DEFAULT_GREGORIAN_CUTOVER) { 648 cal = (BaseCalendar) CalendarSystem.forName("julian"); 649 cdate = (BaseCalendar.Date) cal.newCalendarDate(TimeZone.NO_TIMEZONE); 650 cdate.setNormalizedDate(y, m, day); 651 time = cal.getTime(cdate) + millis - rawOffset; 652 } 653 654 if ((cdate.getNormalizedYear() != y) 655 || (cdate.getMonth() != m) 656 || (cdate.getDayOfMonth() != day) 657 // The validation should be cdate.getDayOfWeek() == 658 // dayOfWeek. However, we don't check dayOfWeek for 659 // compatibility. 660 || (dayOfWeek < Calendar.SUNDAY || dayOfWeek > Calendar.SATURDAY) 661 || (millis < 0 || millis >= (24*60*60*1000))) { 662 throw new IllegalArgumentException(); 663 } 664 665 if (!useDaylight || year < startYear || era != GregorianCalendar.CE) { 666 return rawOffset; 667 } 668 669 return getOffset(cal, cdate, y, time); 670 } 671 getOffset(BaseCalendar cal, BaseCalendar.Date cdate, int year, long time)672 private int getOffset(BaseCalendar cal, BaseCalendar.Date cdate, int year, long time) { 673 synchronized (this) { 674 if (cacheStart != 0) { 675 if (time >= cacheStart && time < cacheEnd) { 676 return rawOffset + dstSavings; 677 } 678 if (year == cacheYear) { 679 return rawOffset; 680 } 681 } 682 } 683 684 long start = getStart(cal, cdate, year); 685 long end = getEnd(cal, cdate, year); 686 int offset = rawOffset; 687 if (start <= end) { 688 if (time >= start && time < end) { 689 offset += dstSavings; 690 } 691 synchronized (this) { 692 cacheYear = year; 693 cacheStart = start; 694 cacheEnd = end; 695 } 696 } else { 697 if (time < end) { 698 // TODO: support Gregorian cutover. The previous year 699 // may be in the other calendar system. 700 start = getStart(cal, cdate, year - 1); 701 if (time >= start) { 702 offset += dstSavings; 703 } 704 } else if (time >= start) { 705 // TODO: support Gregorian cutover. The next year 706 // may be in the other calendar system. 707 end = getEnd(cal, cdate, year + 1); 708 if (time < end) { 709 offset += dstSavings; 710 } 711 } 712 if (start <= end) { 713 synchronized (this) { 714 // The start and end transitions are in multiple years. 715 cacheYear = (long) startYear - 1; 716 cacheStart = start; 717 cacheEnd = end; 718 } 719 } 720 } 721 return offset; 722 } 723 getStart(BaseCalendar cal, BaseCalendar.Date cdate, int year)724 private long getStart(BaseCalendar cal, BaseCalendar.Date cdate, int year) { 725 int time = startTime; 726 if (startTimeMode != UTC_TIME) { 727 time -= rawOffset; 728 } 729 return getTransition(cal, cdate, startMode, year, startMonth, startDay, 730 startDayOfWeek, time); 731 } 732 getEnd(BaseCalendar cal, BaseCalendar.Date cdate, int year)733 private long getEnd(BaseCalendar cal, BaseCalendar.Date cdate, int year) { 734 int time = endTime; 735 if (endTimeMode != UTC_TIME) { 736 time -= rawOffset; 737 } 738 if (endTimeMode == WALL_TIME) { 739 time -= dstSavings; 740 } 741 return getTransition(cal, cdate, endMode, year, endMonth, endDay, 742 endDayOfWeek, time); 743 } 744 getTransition(BaseCalendar cal, BaseCalendar.Date cdate, int mode, int year, int month, int dayOfMonth, int dayOfWeek, int timeOfDay)745 private long getTransition(BaseCalendar cal, BaseCalendar.Date cdate, 746 int mode, int year, int month, int dayOfMonth, 747 int dayOfWeek, int timeOfDay) { 748 cdate.setNormalizedYear(year); 749 cdate.setMonth(month + 1); 750 switch (mode) { 751 case DOM_MODE: 752 cdate.setDayOfMonth(dayOfMonth); 753 break; 754 755 case DOW_IN_MONTH_MODE: 756 cdate.setDayOfMonth(1); 757 if (dayOfMonth < 0) { 758 cdate.setDayOfMonth(cal.getMonthLength(cdate)); 759 } 760 cdate = (BaseCalendar.Date) cal.getNthDayOfWeek(dayOfMonth, dayOfWeek, cdate); 761 break; 762 763 case DOW_GE_DOM_MODE: 764 cdate.setDayOfMonth(dayOfMonth); 765 cdate = (BaseCalendar.Date) cal.getNthDayOfWeek(1, dayOfWeek, cdate); 766 break; 767 768 case DOW_LE_DOM_MODE: 769 cdate.setDayOfMonth(dayOfMonth); 770 cdate = (BaseCalendar.Date) cal.getNthDayOfWeek(-1, dayOfWeek, cdate); 771 break; 772 } 773 return cal.getTime(cdate) + timeOfDay; 774 } 775 776 /** 777 * Gets the GMT offset for this time zone. 778 * @return the GMT offset value in milliseconds 779 * @see #setRawOffset 780 */ getRawOffset()781 public int getRawOffset() 782 { 783 // The given date will be taken into account while 784 // we have the historical time zone data in place. 785 return rawOffset; 786 } 787 788 /** 789 * Sets the base time zone offset to GMT. 790 * This is the offset to add to UTC to get local time. 791 * @see #getRawOffset 792 */ setRawOffset(int offsetMillis)793 public void setRawOffset(int offsetMillis) 794 { 795 this.rawOffset = offsetMillis; 796 } 797 798 /** 799 * Sets the amount of time in milliseconds that the clock is advanced 800 * during daylight saving time. 801 * @param millisSavedDuringDST the number of milliseconds the time is 802 * advanced with respect to standard time when the daylight saving time rules 803 * are in effect. A positive number, typically one hour (3600000). 804 * @see #getDSTSavings 805 * @since 1.2 806 */ setDSTSavings(int millisSavedDuringDST)807 public void setDSTSavings(int millisSavedDuringDST) { 808 if (millisSavedDuringDST <= 0) { 809 throw new IllegalArgumentException("Illegal daylight saving value: " 810 + millisSavedDuringDST); 811 } 812 dstSavings = millisSavedDuringDST; 813 } 814 815 /** 816 * Returns the amount of time in milliseconds that the clock is 817 * advanced during daylight saving time. 818 * 819 * @return the number of milliseconds the time is advanced with 820 * respect to standard time when the daylight saving rules are in 821 * effect, or 0 (zero) if this time zone doesn't observe daylight 822 * saving time. 823 * 824 * @see #setDSTSavings 825 * @since 1.2 826 */ getDSTSavings()827 public int getDSTSavings() { 828 return useDaylight ? dstSavings : 0; 829 } 830 831 /** 832 * Queries if this time zone uses daylight saving time. 833 * @return true if this time zone uses daylight saving time; 834 * false otherwise. 835 */ useDaylightTime()836 public boolean useDaylightTime() 837 { 838 return useDaylight; 839 } 840 841 /** 842 * Returns {@code true} if this {@code SimpleTimeZone} observes 843 * Daylight Saving Time. This method is equivalent to {@link 844 * #useDaylightTime()}. 845 * 846 * @return {@code true} if this {@code SimpleTimeZone} observes 847 * Daylight Saving Time; {@code false} otherwise. 848 * @since 1.7 849 */ 850 @Override observesDaylightTime()851 public boolean observesDaylightTime() { 852 return useDaylightTime(); 853 } 854 855 /** 856 * Queries if the given date is in daylight saving time. 857 * @return true if daylight saving time is in effective at the 858 * given date; false otherwise. 859 */ inDaylightTime(Date date)860 public boolean inDaylightTime(Date date) 861 { 862 return (getOffset(date.getTime()) != rawOffset); 863 } 864 865 /** 866 * Returns a clone of this <code>SimpleTimeZone</code> instance. 867 * @return a clone of this instance. 868 */ clone()869 public Object clone() 870 { 871 return super.clone(); 872 } 873 874 /** 875 * Generates the hash code for the SimpleDateFormat object. 876 * @return the hash code for this object 877 */ hashCode()878 public synchronized int hashCode() 879 { 880 return startMonth ^ startDay ^ startDayOfWeek ^ startTime ^ 881 endMonth ^ endDay ^ endDayOfWeek ^ endTime ^ rawOffset; 882 } 883 884 /** 885 * Compares the equality of two <code>SimpleTimeZone</code> objects. 886 * 887 * @param obj The <code>SimpleTimeZone</code> object to be compared with. 888 * @return True if the given <code>obj</code> is the same as this 889 * <code>SimpleTimeZone</code> object; false otherwise. 890 */ equals(Object obj)891 public boolean equals(Object obj) 892 { 893 if (this == obj) { 894 return true; 895 } 896 if (!(obj instanceof SimpleTimeZone)) { 897 return false; 898 } 899 900 SimpleTimeZone that = (SimpleTimeZone) obj; 901 902 return getID().equals(that.getID()) && 903 hasSameRules(that); 904 } 905 906 /** 907 * Returns <code>true</code> if this zone has the same rules and offset as another zone. 908 * @param other the TimeZone object to be compared with 909 * @return <code>true</code> if the given zone is a SimpleTimeZone and has the 910 * same rules and offset as this one 911 * @since 1.2 912 */ hasSameRules(TimeZone other)913 public boolean hasSameRules(TimeZone other) { 914 if (this == other) { 915 return true; 916 } 917 if (!(other instanceof SimpleTimeZone)) { 918 return false; 919 } 920 SimpleTimeZone that = (SimpleTimeZone) other; 921 return rawOffset == that.rawOffset && 922 useDaylight == that.useDaylight && 923 (!useDaylight 924 // Only check rules if using DST 925 || (dstSavings == that.dstSavings && 926 startMode == that.startMode && 927 startMonth == that.startMonth && 928 startDay == that.startDay && 929 startDayOfWeek == that.startDayOfWeek && 930 startTime == that.startTime && 931 startTimeMode == that.startTimeMode && 932 endMode == that.endMode && 933 endMonth == that.endMonth && 934 endDay == that.endDay && 935 endDayOfWeek == that.endDayOfWeek && 936 endTime == that.endTime && 937 endTimeMode == that.endTimeMode && 938 startYear == that.startYear)); 939 } 940 941 /** 942 * Returns a string representation of this time zone. 943 * @return a string representation of this time zone. 944 */ toString()945 public String toString() { 946 return getClass().getName() + 947 "[id=" + getID() + 948 ",offset=" + rawOffset + 949 ",dstSavings=" + dstSavings + 950 ",useDaylight=" + useDaylight + 951 ",startYear=" + startYear + 952 ",startMode=" + startMode + 953 ",startMonth=" + startMonth + 954 ",startDay=" + startDay + 955 ",startDayOfWeek=" + startDayOfWeek + 956 ",startTime=" + startTime + 957 ",startTimeMode=" + startTimeMode + 958 ",endMode=" + endMode + 959 ",endMonth=" + endMonth + 960 ",endDay=" + endDay + 961 ",endDayOfWeek=" + endDayOfWeek + 962 ",endTime=" + endTime + 963 ",endTimeMode=" + endTimeMode + ']'; 964 } 965 966 // =======================privates=============================== 967 968 /** 969 * The month in which daylight saving time starts. This value must be 970 * between <code>Calendar.JANUARY</code> and 971 * <code>Calendar.DECEMBER</code> inclusive. This value must not equal 972 * <code>endMonth</code>. 973 * <p>If <code>useDaylight</code> is false, this value is ignored. 974 * @serial 975 */ 976 private int startMonth; 977 978 /** 979 * This field has two possible interpretations: 980 * <dl> 981 * <dt><code>startMode == DOW_IN_MONTH</code></dt> 982 * <dd> 983 * <code>startDay</code> indicates the day of the month of 984 * <code>startMonth</code> on which daylight 985 * saving time starts, from 1 to 28, 30, or 31, depending on the 986 * <code>startMonth</code>. 987 * </dd> 988 * <dt><code>startMode != DOW_IN_MONTH</code></dt> 989 * <dd> 990 * <code>startDay</code> indicates which <code>startDayOfWeek</code> in the 991 * month <code>startMonth</code> daylight 992 * saving time starts on. For example, a value of +1 and a 993 * <code>startDayOfWeek</code> of <code>Calendar.SUNDAY</code> indicates the 994 * first Sunday of <code>startMonth</code>. Likewise, +2 would indicate the 995 * second Sunday, and -1 the last Sunday. A value of 0 is illegal. 996 * </dd> 997 * </dl> 998 * <p>If <code>useDaylight</code> is false, this value is ignored. 999 * @serial 1000 */ 1001 private int startDay; 1002 1003 /** 1004 * The day of the week on which daylight saving time starts. This value 1005 * must be between <code>Calendar.SUNDAY</code> and 1006 * <code>Calendar.SATURDAY</code> inclusive. 1007 * <p>If <code>useDaylight</code> is false or 1008 * <code>startMode == DAY_OF_MONTH</code>, this value is ignored. 1009 * @serial 1010 */ 1011 private int startDayOfWeek; 1012 1013 /** 1014 * The time in milliseconds after midnight at which daylight saving 1015 * time starts. This value is expressed as wall time, standard time, 1016 * or UTC time, depending on the setting of <code>startTimeMode</code>. 1017 * <p>If <code>useDaylight</code> is false, this value is ignored. 1018 * @serial 1019 */ 1020 private int startTime; 1021 1022 /** 1023 * The format of startTime, either WALL_TIME, STANDARD_TIME, or UTC_TIME. 1024 * @serial 1025 * @since 1.3 1026 */ 1027 private int startTimeMode; 1028 1029 /** 1030 * The month in which daylight saving time ends. This value must be 1031 * between <code>Calendar.JANUARY</code> and 1032 * <code>Calendar.UNDECIMBER</code>. This value must not equal 1033 * <code>startMonth</code>. 1034 * <p>If <code>useDaylight</code> is false, this value is ignored. 1035 * @serial 1036 */ 1037 private int endMonth; 1038 1039 /** 1040 * This field has two possible interpretations: 1041 * <dl> 1042 * <dt><code>endMode == DOW_IN_MONTH</code></dt> 1043 * <dd> 1044 * <code>endDay</code> indicates the day of the month of 1045 * <code>endMonth</code> on which daylight 1046 * saving time ends, from 1 to 28, 30, or 31, depending on the 1047 * <code>endMonth</code>. 1048 * </dd> 1049 * <dt><code>endMode != DOW_IN_MONTH</code></dt> 1050 * <dd> 1051 * <code>endDay</code> indicates which <code>endDayOfWeek</code> in th 1052 * month <code>endMonth</code> daylight 1053 * saving time ends on. For example, a value of +1 and a 1054 * <code>endDayOfWeek</code> of <code>Calendar.SUNDAY</code> indicates the 1055 * first Sunday of <code>endMonth</code>. Likewise, +2 would indicate the 1056 * second Sunday, and -1 the last Sunday. A value of 0 is illegal. 1057 * </dd> 1058 * </dl> 1059 * <p>If <code>useDaylight</code> is false, this value is ignored. 1060 * @serial 1061 */ 1062 private int endDay; 1063 1064 /** 1065 * The day of the week on which daylight saving time ends. This value 1066 * must be between <code>Calendar.SUNDAY</code> and 1067 * <code>Calendar.SATURDAY</code> inclusive. 1068 * <p>If <code>useDaylight</code> is false or 1069 * <code>endMode == DAY_OF_MONTH</code>, this value is ignored. 1070 * @serial 1071 */ 1072 private int endDayOfWeek; 1073 1074 /** 1075 * The time in milliseconds after midnight at which daylight saving 1076 * time ends. This value is expressed as wall time, standard time, 1077 * or UTC time, depending on the setting of <code>endTimeMode</code>. 1078 * <p>If <code>useDaylight</code> is false, this value is ignored. 1079 * @serial 1080 */ 1081 private int endTime; 1082 1083 /** 1084 * The format of endTime, either <code>WALL_TIME</code>, 1085 * <code>STANDARD_TIME</code>, or <code>UTC_TIME</code>. 1086 * @serial 1087 * @since 1.3 1088 */ 1089 private int endTimeMode; 1090 1091 /** 1092 * The year in which daylight saving time is first observed. This is an {@link GregorianCalendar#AD AD} 1093 * value. If this value is less than 1 then daylight saving time is observed 1094 * for all <code>AD</code> years. 1095 * <p>If <code>useDaylight</code> is false, this value is ignored. 1096 * @serial 1097 */ 1098 private int startYear; 1099 1100 /** 1101 * The offset in milliseconds between this zone and GMT. Negative offsets 1102 * are to the west of Greenwich. To obtain local <em>standard</em> time, 1103 * add the offset to GMT time. To obtain local wall time it may also be 1104 * necessary to add <code>dstSavings</code>. 1105 * @serial 1106 */ 1107 private int rawOffset; 1108 1109 /** 1110 * A boolean value which is true if and only if this zone uses daylight 1111 * saving time. If this value is false, several other fields are ignored. 1112 * @serial 1113 */ 1114 private boolean useDaylight=false; // indicate if this time zone uses DST 1115 1116 private static final int millisPerHour = 60*60*1000; 1117 private static final int millisPerDay = 24*millisPerHour; 1118 1119 /** 1120 * This field was serialized in JDK 1.1, so we have to keep it that way 1121 * to maintain serialization compatibility. However, there's no need to 1122 * recreate the array each time we create a new time zone. 1123 * @serial An array of bytes containing the values {31, 28, 31, 30, 31, 30, 1124 * 31, 31, 30, 31, 30, 31}. This is ignored as of the Java 2 platform v1.2, however, it must 1125 * be streamed out for compatibility with JDK 1.1. 1126 */ 1127 private final byte monthLength[] = staticMonthLength; 1128 private final static byte staticMonthLength[] = {31,28,31,30,31,30,31,31,30,31,30,31}; 1129 private final static byte staticLeapMonthLength[] = {31,29,31,30,31,30,31,31,30,31,30,31}; 1130 1131 /** 1132 * Variables specifying the mode of the start rule. Takes the following 1133 * values: 1134 * <dl> 1135 * <dt><code>DOM_MODE</code></dt> 1136 * <dd> 1137 * Exact day of week; e.g., March 1. 1138 * </dd> 1139 * <dt><code>DOW_IN_MONTH_MODE</code></dt> 1140 * <dd> 1141 * Day of week in month; e.g., last Sunday in March. 1142 * </dd> 1143 * <dt><code>DOW_GE_DOM_MODE</code></dt> 1144 * <dd> 1145 * Day of week after day of month; e.g., Sunday on or after March 15. 1146 * </dd> 1147 * <dt><code>DOW_LE_DOM_MODE</code></dt> 1148 * <dd> 1149 * Day of week before day of month; e.g., Sunday on or before March 15. 1150 * </dd> 1151 * </dl> 1152 * The setting of this field affects the interpretation of the 1153 * <code>startDay</code> field. 1154 * <p>If <code>useDaylight</code> is false, this value is ignored. 1155 * @serial 1156 * @since 1.1.4 1157 */ 1158 private int startMode; 1159 1160 /** 1161 * Variables specifying the mode of the end rule. Takes the following 1162 * values: 1163 * <dl> 1164 * <dt><code>DOM_MODE</code></dt> 1165 * <dd> 1166 * Exact day of week; e.g., March 1. 1167 * </dd> 1168 * <dt><code>DOW_IN_MONTH_MODE</code></dt> 1169 * <dd> 1170 * Day of week in month; e.g., last Sunday in March. 1171 * </dd> 1172 * <dt><code>DOW_GE_DOM_MODE</code></dt> 1173 * <dd> 1174 * Day of week after day of month; e.g., Sunday on or after March 15. 1175 * </dd> 1176 * <dt><code>DOW_LE_DOM_MODE</code></dt> 1177 * <dd> 1178 * Day of week before day of month; e.g., Sunday on or before March 15. 1179 * </dd> 1180 * </dl> 1181 * The setting of this field affects the interpretation of the 1182 * <code>endDay</code> field. 1183 * <p>If <code>useDaylight</code> is false, this value is ignored. 1184 * @serial 1185 * @since 1.1.4 1186 */ 1187 private int endMode; 1188 1189 /** 1190 * A positive value indicating the amount of time saved during DST in 1191 * milliseconds. 1192 * Typically one hour (3600000); sometimes 30 minutes (1800000). 1193 * <p>If <code>useDaylight</code> is false, this value is ignored. 1194 * @serial 1195 * @since 1.1.4 1196 */ 1197 private int dstSavings; 1198 1199 private static final Gregorian gcal = CalendarSystem.getGregorianCalendar(); 1200 1201 /** 1202 * Cache values representing a single period of daylight saving 1203 * time. When the cache values are valid, cacheStart is the start 1204 * time (inclusive) of daylight saving time and cacheEnd is the 1205 * end time (exclusive). 1206 * 1207 * cacheYear has a year value if both cacheStart and cacheEnd are 1208 * in the same year. cacheYear is set to startYear - 1 if 1209 * cacheStart and cacheEnd are in different years. cacheStart is 0 1210 * if the cache values are void. cacheYear is a long to support 1211 * Integer.MIN_VALUE - 1 (JCK requirement). 1212 */ 1213 private transient long cacheYear; 1214 private transient long cacheStart; 1215 private transient long cacheEnd; 1216 1217 /** 1218 * Constants specifying values of startMode and endMode. 1219 */ 1220 private static final int DOM_MODE = 1; // Exact day of month, "Mar 1" 1221 private static final int DOW_IN_MONTH_MODE = 2; // Day of week in month, "lastSun" 1222 private static final int DOW_GE_DOM_MODE = 3; // Day of week after day of month, "Sun>=15" 1223 private static final int DOW_LE_DOM_MODE = 4; // Day of week before day of month, "Sun<=21" 1224 1225 /** 1226 * Constant for a mode of start or end time specified as wall clock 1227 * time. Wall clock time is standard time for the onset rule, and 1228 * daylight time for the end rule. 1229 * @since 1.4 1230 */ 1231 public static final int WALL_TIME = 0; // Zero for backward compatibility 1232 1233 /** 1234 * Constant for a mode of start or end time specified as standard time. 1235 * @since 1.4 1236 */ 1237 public static final int STANDARD_TIME = 1; 1238 1239 /** 1240 * Constant for a mode of start or end time specified as UTC. European 1241 * Union rules are specified as UTC time, for example. 1242 * @since 1.4 1243 */ 1244 public static final int UTC_TIME = 2; 1245 1246 // Proclaim compatibility with 1.1 1247 static final long serialVersionUID = -403250971215465050L; 1248 1249 // the internal serial version which says which version was written 1250 // - 0 (default) for version up to JDK 1.1.3 1251 // - 1 for version from JDK 1.1.4, which includes 3 new fields 1252 // - 2 for JDK 1.3, which includes 2 new fields 1253 static final int currentSerialVersion = 2; 1254 1255 /** 1256 * The version of the serialized data on the stream. Possible values: 1257 * <dl> 1258 * <dt><b>0</b> or not present on stream</dt> 1259 * <dd> 1260 * JDK 1.1.3 or earlier. 1261 * </dd> 1262 * <dt><b>1</b></dt> 1263 * <dd> 1264 * JDK 1.1.4 or later. Includes three new fields: <code>startMode</code>, 1265 * <code>endMode</code>, and <code>dstSavings</code>. 1266 * </dd> 1267 * <dt><b>2</b></dt> 1268 * <dd> 1269 * JDK 1.3 or later. Includes two new fields: <code>startTimeMode</code> 1270 * and <code>endTimeMode</code>. 1271 * </dd> 1272 * </dl> 1273 * When streaming out this class, the most recent format 1274 * and the highest allowable <code>serialVersionOnStream</code> 1275 * is written. 1276 * @serial 1277 * @since 1.1.4 1278 */ 1279 private int serialVersionOnStream = currentSerialVersion; 1280 invalidateCache()1281 synchronized private void invalidateCache() { 1282 cacheYear = startYear - 1; 1283 cacheStart = cacheEnd = 0; 1284 } 1285 1286 //---------------------------------------------------------------------- 1287 // Rule representation 1288 // 1289 // We represent the following flavors of rules: 1290 // 5 the fifth of the month 1291 // lastSun the last Sunday in the month 1292 // lastMon the last Monday in the month 1293 // Sun>=8 first Sunday on or after the eighth 1294 // Sun<=25 last Sunday on or before the 25th 1295 // This is further complicated by the fact that we need to remain 1296 // backward compatible with the 1.1 FCS. Finally, we need to minimize 1297 // API changes. In order to satisfy these requirements, we support 1298 // three representation systems, and we translate between them. 1299 // 1300 // INTERNAL REPRESENTATION 1301 // This is the format SimpleTimeZone objects take after construction or 1302 // streaming in is complete. Rules are represented directly, using an 1303 // unencoded format. We will discuss the start rule only below; the end 1304 // rule is analogous. 1305 // startMode Takes on enumerated values DAY_OF_MONTH, 1306 // DOW_IN_MONTH, DOW_AFTER_DOM, or DOW_BEFORE_DOM. 1307 // startDay The day of the month, or for DOW_IN_MONTH mode, a 1308 // value indicating which DOW, such as +1 for first, 1309 // +2 for second, -1 for last, etc. 1310 // startDayOfWeek The day of the week. Ignored for DAY_OF_MONTH. 1311 // 1312 // ENCODED REPRESENTATION 1313 // This is the format accepted by the constructor and by setStartRule() 1314 // and setEndRule(). It uses various combinations of positive, negative, 1315 // and zero values to encode the different rules. This representation 1316 // allows us to specify all the different rule flavors without altering 1317 // the API. 1318 // MODE startMonth startDay startDayOfWeek 1319 // DOW_IN_MONTH_MODE >=0 !=0 >0 1320 // DOM_MODE >=0 >0 ==0 1321 // DOW_GE_DOM_MODE >=0 >0 <0 1322 // DOW_LE_DOM_MODE >=0 <0 <0 1323 // (no DST) don't care ==0 don't care 1324 // 1325 // STREAMED REPRESENTATION 1326 // We must retain binary compatibility with the 1.1 FCS. The 1.1 code only 1327 // handles DOW_IN_MONTH_MODE and non-DST mode, the latter indicated by the 1328 // flag useDaylight. When we stream an object out, we translate into an 1329 // approximate DOW_IN_MONTH_MODE representation so the object can be parsed 1330 // and used by 1.1 code. Following that, we write out the full 1331 // representation separately so that contemporary code can recognize and 1332 // parse it. The full representation is written in a "packed" format, 1333 // consisting of a version number, a length, and an array of bytes. Future 1334 // versions of this class may specify different versions. If they wish to 1335 // include additional data, they should do so by storing them after the 1336 // packed representation below. 1337 //---------------------------------------------------------------------- 1338 1339 /** 1340 * Given a set of encoded rules in startDay and startDayOfMonth, decode 1341 * them and set the startMode appropriately. Do the same for endDay and 1342 * endDayOfMonth. Upon entry, the day of week variables may be zero or 1343 * negative, in order to indicate special modes. The day of month 1344 * variables may also be negative. Upon exit, the mode variables will be 1345 * set, and the day of week and day of month variables will be positive. 1346 * This method also recognizes a startDay or endDay of zero as indicating 1347 * no DST. 1348 */ decodeRules()1349 private void decodeRules() 1350 { 1351 decodeStartRule(); 1352 decodeEndRule(); 1353 } 1354 1355 /** 1356 * Decode the start rule and validate the parameters. The parameters are 1357 * expected to be in encoded form, which represents the various rule modes 1358 * by negating or zeroing certain values. Representation formats are: 1359 * <p> 1360 * <pre> 1361 * DOW_IN_MONTH DOM DOW>=DOM DOW<=DOM no DST 1362 * ------------ ----- -------- -------- ---------- 1363 * month 0..11 same same same don't care 1364 * day -5..5 1..31 1..31 -1..-31 0 1365 * dayOfWeek 1..7 0 -1..-7 -1..-7 don't care 1366 * time 0..ONEDAY same same same don't care 1367 * </pre> 1368 * The range for month does not include UNDECIMBER since this class is 1369 * really specific to GregorianCalendar, which does not use that month. 1370 * The range for time includes ONEDAY (vs. ending at ONEDAY-1) because the 1371 * end rule is an exclusive limit point. That is, the range of times that 1372 * are in DST include those >= the start and < the end. For this reason, 1373 * it should be possible to specify an end of ONEDAY in order to include the 1374 * entire day. Although this is equivalent to time 0 of the following day, 1375 * it's not always possible to specify that, for example, on December 31. 1376 * While arguably the start range should still be 0..ONEDAY-1, we keep 1377 * the start and end ranges the same for consistency. 1378 */ decodeStartRule()1379 private void decodeStartRule() { 1380 useDaylight = (startDay != 0) && (endDay != 0); 1381 if (startDay != 0) { 1382 if (startMonth < Calendar.JANUARY || startMonth > Calendar.DECEMBER) { 1383 throw new IllegalArgumentException( 1384 "Illegal start month " + startMonth); 1385 } 1386 if (startTime < 0 || startTime > millisPerDay) { 1387 throw new IllegalArgumentException( 1388 "Illegal start time " + startTime); 1389 } 1390 if (startDayOfWeek == 0) { 1391 startMode = DOM_MODE; 1392 } else { 1393 if (startDayOfWeek > 0) { 1394 startMode = DOW_IN_MONTH_MODE; 1395 } else { 1396 startDayOfWeek = -startDayOfWeek; 1397 if (startDay > 0) { 1398 startMode = DOW_GE_DOM_MODE; 1399 } else { 1400 startDay = -startDay; 1401 startMode = DOW_LE_DOM_MODE; 1402 } 1403 } 1404 if (startDayOfWeek > Calendar.SATURDAY) { 1405 throw new IllegalArgumentException( 1406 "Illegal start day of week " + startDayOfWeek); 1407 } 1408 } 1409 if (startMode == DOW_IN_MONTH_MODE) { 1410 if (startDay < -5 || startDay > 5) { 1411 throw new IllegalArgumentException( 1412 "Illegal start day of week in month " + startDay); 1413 } 1414 } else if (startDay < 1 || startDay > staticMonthLength[startMonth]) { 1415 throw new IllegalArgumentException( 1416 "Illegal start day " + startDay); 1417 } 1418 } 1419 } 1420 1421 /** 1422 * Decode the end rule and validate the parameters. This method is exactly 1423 * analogous to decodeStartRule(). 1424 * @see decodeStartRule 1425 */ decodeEndRule()1426 private void decodeEndRule() { 1427 useDaylight = (startDay != 0) && (endDay != 0); 1428 if (endDay != 0) { 1429 if (endMonth < Calendar.JANUARY || endMonth > Calendar.DECEMBER) { 1430 throw new IllegalArgumentException( 1431 "Illegal end month " + endMonth); 1432 } 1433 if (endTime < 0 || endTime > millisPerDay) { 1434 throw new IllegalArgumentException( 1435 "Illegal end time " + endTime); 1436 } 1437 if (endDayOfWeek == 0) { 1438 endMode = DOM_MODE; 1439 } else { 1440 if (endDayOfWeek > 0) { 1441 endMode = DOW_IN_MONTH_MODE; 1442 } else { 1443 endDayOfWeek = -endDayOfWeek; 1444 if (endDay > 0) { 1445 endMode = DOW_GE_DOM_MODE; 1446 } else { 1447 endDay = -endDay; 1448 endMode = DOW_LE_DOM_MODE; 1449 } 1450 } 1451 if (endDayOfWeek > Calendar.SATURDAY) { 1452 throw new IllegalArgumentException( 1453 "Illegal end day of week " + endDayOfWeek); 1454 } 1455 } 1456 if (endMode == DOW_IN_MONTH_MODE) { 1457 if (endDay < -5 || endDay > 5) { 1458 throw new IllegalArgumentException( 1459 "Illegal end day of week in month " + endDay); 1460 } 1461 } else if (endDay < 1 || endDay > staticMonthLength[endMonth]) { 1462 throw new IllegalArgumentException( 1463 "Illegal end day " + endDay); 1464 } 1465 } 1466 } 1467 1468 /** 1469 * Make rules compatible to 1.1 FCS code. Since 1.1 FCS code only understands 1470 * day-of-week-in-month rules, we must modify other modes of rules to their 1471 * approximate equivalent in 1.1 FCS terms. This method is used when streaming 1472 * out objects of this class. After it is called, the rules will be modified, 1473 * with a possible loss of information. startMode and endMode will NOT be 1474 * altered, even though semantically they should be set to DOW_IN_MONTH_MODE, 1475 * since the rule modification is only intended to be temporary. 1476 */ makeRulesCompatible()1477 private void makeRulesCompatible() 1478 { 1479 switch (startMode) { 1480 case DOM_MODE: 1481 startDay = 1 + (startDay / 7); 1482 startDayOfWeek = Calendar.SUNDAY; 1483 break; 1484 1485 case DOW_GE_DOM_MODE: 1486 // A day-of-month of 1 is equivalent to DOW_IN_MONTH_MODE 1487 // that is, Sun>=1 == firstSun. 1488 if (startDay != 1) { 1489 startDay = 1 + (startDay / 7); 1490 } 1491 break; 1492 1493 case DOW_LE_DOM_MODE: 1494 if (startDay >= 30) { 1495 startDay = -1; 1496 } else { 1497 startDay = 1 + (startDay / 7); 1498 } 1499 break; 1500 } 1501 1502 switch (endMode) { 1503 case DOM_MODE: 1504 endDay = 1 + (endDay / 7); 1505 endDayOfWeek = Calendar.SUNDAY; 1506 break; 1507 1508 case DOW_GE_DOM_MODE: 1509 // A day-of-month of 1 is equivalent to DOW_IN_MONTH_MODE 1510 // that is, Sun>=1 == firstSun. 1511 if (endDay != 1) { 1512 endDay = 1 + (endDay / 7); 1513 } 1514 break; 1515 1516 case DOW_LE_DOM_MODE: 1517 if (endDay >= 30) { 1518 endDay = -1; 1519 } else { 1520 endDay = 1 + (endDay / 7); 1521 } 1522 break; 1523 } 1524 1525 /* 1526 * Adjust the start and end times to wall time. This works perfectly 1527 * well unless it pushes into the next or previous day. If that 1528 * happens, we attempt to adjust the day rule somewhat crudely. The day 1529 * rules have been forced into DOW_IN_MONTH mode already, so we change 1530 * the day of week to move forward or back by a day. It's possible to 1531 * make a more refined adjustment of the original rules first, but in 1532 * most cases this extra effort will go to waste once we adjust the day 1533 * rules anyway. 1534 */ 1535 switch (startTimeMode) { 1536 case UTC_TIME: 1537 startTime += rawOffset; 1538 break; 1539 } 1540 while (startTime < 0) { 1541 startTime += millisPerDay; 1542 startDayOfWeek = 1 + ((startDayOfWeek+5) % 7); // Back 1 day 1543 } 1544 while (startTime >= millisPerDay) { 1545 startTime -= millisPerDay; 1546 startDayOfWeek = 1 + (startDayOfWeek % 7); // Forward 1 day 1547 } 1548 1549 switch (endTimeMode) { 1550 case UTC_TIME: 1551 endTime += rawOffset + dstSavings; 1552 break; 1553 case STANDARD_TIME: 1554 endTime += dstSavings; 1555 } 1556 while (endTime < 0) { 1557 endTime += millisPerDay; 1558 endDayOfWeek = 1 + ((endDayOfWeek+5) % 7); // Back 1 day 1559 } 1560 while (endTime >= millisPerDay) { 1561 endTime -= millisPerDay; 1562 endDayOfWeek = 1 + (endDayOfWeek % 7); // Forward 1 day 1563 } 1564 } 1565 1566 /** 1567 * Pack the start and end rules into an array of bytes. Only pack 1568 * data which is not preserved by makeRulesCompatible. 1569 */ packRules()1570 private byte[] packRules() 1571 { 1572 byte[] rules = new byte[6]; 1573 rules[0] = (byte)startDay; 1574 rules[1] = (byte)startDayOfWeek; 1575 rules[2] = (byte)endDay; 1576 rules[3] = (byte)endDayOfWeek; 1577 1578 // As of serial version 2, include time modes 1579 rules[4] = (byte)startTimeMode; 1580 rules[5] = (byte)endTimeMode; 1581 1582 return rules; 1583 } 1584 1585 /** 1586 * Given an array of bytes produced by packRules, interpret them 1587 * as the start and end rules. 1588 */ unpackRules(byte[] rules)1589 private void unpackRules(byte[] rules) 1590 { 1591 startDay = rules[0]; 1592 startDayOfWeek = rules[1]; 1593 endDay = rules[2]; 1594 endDayOfWeek = rules[3]; 1595 1596 // As of serial version 2, include time modes 1597 if (rules.length >= 6) { 1598 startTimeMode = rules[4]; 1599 endTimeMode = rules[5]; 1600 } 1601 } 1602 1603 /** 1604 * Pack the start and end times into an array of bytes. This is required 1605 * as of serial version 2. 1606 */ packTimes()1607 private int[] packTimes() { 1608 int[] times = new int[2]; 1609 times[0] = startTime; 1610 times[1] = endTime; 1611 return times; 1612 } 1613 1614 /** 1615 * Unpack the start and end times from an array of bytes. This is required 1616 * as of serial version 2. 1617 */ unpackTimes(int[] times)1618 private void unpackTimes(int[] times) { 1619 startTime = times[0]; 1620 endTime = times[1]; 1621 } 1622 1623 /** 1624 * Save the state of this object to a stream (i.e., serialize it). 1625 * 1626 * @serialData We write out two formats, a JDK 1.1 compatible format, using 1627 * <code>DOW_IN_MONTH_MODE</code> rules, in the required section, followed 1628 * by the full rules, in packed format, in the optional section. The 1629 * optional section will be ignored by JDK 1.1 code upon stream in. 1630 * <p> Contents of the optional section: The length of a byte array is 1631 * emitted (int); this is 4 as of this release. The byte array of the given 1632 * length is emitted. The contents of the byte array are the true values of 1633 * the fields <code>startDay</code>, <code>startDayOfWeek</code>, 1634 * <code>endDay</code>, and <code>endDayOfWeek</code>. The values of these 1635 * fields in the required section are approximate values suited to the rule 1636 * mode <code>DOW_IN_MONTH_MODE</code>, which is the only mode recognized by 1637 * JDK 1.1. 1638 */ writeObject(ObjectOutputStream stream)1639 private void writeObject(ObjectOutputStream stream) 1640 throws IOException 1641 { 1642 // Construct a binary rule 1643 byte[] rules = packRules(); 1644 int[] times = packTimes(); 1645 1646 // Convert to 1.1 FCS rules. This step may cause us to lose information. 1647 makeRulesCompatible(); 1648 1649 // Write out the 1.1 FCS rules 1650 stream.defaultWriteObject(); 1651 1652 // Write out the binary rules in the optional data area of the stream. 1653 stream.writeInt(rules.length); 1654 stream.write(rules); 1655 stream.writeObject(times); 1656 1657 // Recover the original rules. This recovers the information lost 1658 // by makeRulesCompatible. 1659 unpackRules(rules); 1660 unpackTimes(times); 1661 } 1662 1663 /** 1664 * Reconstitute this object from a stream (i.e., deserialize it). 1665 * 1666 * We handle both JDK 1.1 1667 * binary formats and full formats with a packed byte array. 1668 */ readObject(ObjectInputStream stream)1669 private void readObject(ObjectInputStream stream) 1670 throws IOException, ClassNotFoundException 1671 { 1672 stream.defaultReadObject(); 1673 1674 if (serialVersionOnStream < 1) { 1675 // Fix a bug in the 1.1 SimpleTimeZone code -- namely, 1676 // startDayOfWeek and endDayOfWeek were usually uninitialized. We can't do 1677 // too much, so we assume SUNDAY, which actually works most of the time. 1678 if (startDayOfWeek == 0) { 1679 startDayOfWeek = Calendar.SUNDAY; 1680 } 1681 if (endDayOfWeek == 0) { 1682 endDayOfWeek = Calendar.SUNDAY; 1683 } 1684 1685 // The variables dstSavings, startMode, and endMode are post-1.1, so they 1686 // won't be present if we're reading from a 1.1 stream. Fix them up. 1687 startMode = endMode = DOW_IN_MONTH_MODE; 1688 dstSavings = millisPerHour; 1689 } else { 1690 // For 1.1.4, in addition to the 3 new instance variables, we also 1691 // store the actual rules (which have not be made compatible with 1.1) 1692 // in the optional area. Read them in here and parse them. 1693 int length = stream.readInt(); 1694 byte[] rules = new byte[length]; 1695 stream.readFully(rules); 1696 unpackRules(rules); 1697 } 1698 1699 if (serialVersionOnStream >= 2) { 1700 int[] times = (int[]) stream.readObject(); 1701 unpackTimes(times); 1702 } 1703 1704 serialVersionOnStream = currentSerialVersion; 1705 } 1706 } 1707