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