1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18 // BEGIN android-note 19 // This implementation is based on an old version of Apache Harmony. The current 20 // Harmony uses ICU4J, which makes it much simpler. We should consider updating 21 // this implementation to leverage ICU4JNI. 22 // END android-note 23 24 package java.util; 25 26 import java.io.IOException; 27 import java.io.ObjectInputStream; 28 import java.io.ObjectOutputStream; 29 import java.io.ObjectStreamField; 30 // BEGIN android-removed 31 // import java.security.AccessController; 32 // import java.security.PrivilegedAction; 33 // END android-removed 34 35 import org.apache.harmony.luni.util.Msg; 36 37 /** 38 * {@code SimpleTimeZone} is a concrete subclass of {@code TimeZone} 39 * that represents a time zone for use with a Gregorian calendar. This class 40 * does not handle historical changes. 41 * <p> 42 * Use a negative value for {@code dayOfWeekInMonth} to indicate that 43 * {@code SimpleTimeZone} should count from the end of the month 44 * backwards. For example, Daylight Savings Time ends at the last 45 * (dayOfWeekInMonth = -1) Sunday in October, at 2 AM in standard time. 46 * 47 * @see Calendar 48 * @see GregorianCalendar 49 * @see TimeZone 50 */ 51 public class SimpleTimeZone extends TimeZone { 52 53 private static final long serialVersionUID = -403250971215465050L; 54 55 // BEGIN android-removed 56 // private static com.ibm.icu.util.TimeZone getICUTimeZone(final String name){ 57 // return AccessController.doPrivileged(new PrivilegedAction<com.ibm.icu.util.TimeZone>(){ 58 // public com.ibm.icu.util.TimeZone run() { 59 // return com.ibm.icu.util.TimeZone.getTimeZone(name); 60 // } 61 // }); 62 // } 63 // END android-removed 64 65 private int rawOffset; 66 67 private int startYear, startMonth, startDay, startDayOfWeek, startTime; 68 69 private int endMonth, endDay, endDayOfWeek, endTime; 70 71 private int startMode, endMode; 72 73 private static final int DOM_MODE = 1, DOW_IN_MONTH_MODE = 2, 74 DOW_GE_DOM_MODE = 3, DOW_LE_DOM_MODE = 4; 75 76 /** 77 * The constant for representing a start or end time in GMT time mode. 78 */ 79 public static final int UTC_TIME = 2; 80 81 /** 82 * The constant for representing a start or end time in standard local time mode, 83 * based on timezone's raw offset from GMT; does not include Daylight 84 * savings. 85 */ 86 public static final int STANDARD_TIME = 1; 87 88 /** 89 * The constant for representing a start or end time in local wall clock time 90 * mode, based on timezone's adjusted offset from GMT; includes 91 * Daylight savings. 92 */ 93 public static final int WALL_TIME = 0; 94 95 private boolean useDaylight; 96 97 private GregorianCalendar daylightSavings; 98 99 private int dstSavings = 3600000; 100 101 // BEGIN android-removed 102 // private final transient com.ibm.icu.util.TimeZone icuTZ; 103 // 104 // private final transient boolean isSimple; 105 // END android-removed 106 107 /** 108 * Constructs a {@code SimpleTimeZone} with the given base time zone offset from GMT 109 * and time zone ID. Timezone IDs can be obtained from 110 * {@code TimeZone.getAvailableIDs}. Normally you should use {@code TimeZone.getDefault} to 111 * construct a {@code TimeZone}. 112 * 113 * @param offset 114 * the given base time zone offset to GMT. 115 * @param name 116 * the time zone ID which is obtained from 117 * {@code TimeZone.getAvailableIDs}. 118 */ 119 public SimpleTimeZone(int offset, final String name) { 120 setID(name); 121 rawOffset = offset; 122 // BEGIN android-removed 123 // icuTZ = getICUTimeZone(name); 124 // if (icuTZ instanceof com.ibm.icu.util.SimpleTimeZone) { 125 // isSimple = true; 126 // icuTZ.setRawOffset(offset); 127 // } else { 128 // isSimple = false; 129 // } 130 // useDaylight = icuTZ.useDaylightTime(); 131 // END android-removed 132 } 133 134 /** 135 * Constructs a {@code SimpleTimeZone} with the given base time zone offset from GMT, 136 * time zone ID, and times to start and end the daylight savings time. Timezone IDs can 137 * be obtained from {@code TimeZone.getAvailableIDs}. Normally you should use 138 * {@code TimeZone.getDefault} to create a {@code TimeZone}. For a time zone that does not 139 * use daylight saving time, do not use this constructor; instead you should 140 * use {@code SimpleTimeZone(rawOffset, ID)}. 141 * <p> 142 * By default, this constructor specifies day-of-week-in-month rules. That 143 * is, if the {@code startDay} is 1, and the {@code startDayOfWeek} is {@code SUNDAY}, then this 144 * indicates the first Sunday in the {@code startMonth}. A {@code startDay} of -1 likewise 145 * indicates the last Sunday. However, by using negative or zero values for 146 * certain parameters, other types of rules can be specified. 147 * <p> 148 * Day of month: To specify an exact day of the month, such as March 1, set 149 * {@code startDayOfWeek} to zero. 150 * <p> 151 * Day of week after day of month: To specify the first day of the week 152 * occurring on or after an exact day of the month, make the day of the week 153 * negative. For example, if {@code startDay} is 5 and {@code startDayOfWeek} is {@code -MONDAY}, 154 * this indicates the first Monday on or after the 5th day of the 155 * {@code startMonth}. 156 * <p> 157 * Day of week before day of month: To specify the last day of the week 158 * occurring on or before an exact day of the month, make the day of the 159 * week and the day of the month negative. For example, if {@code startDay} is {@code -21} 160 * and {@code startDayOfWeek} is {@code -WEDNESDAY}, this indicates the last Wednesday on or 161 * before the 21st of the {@code startMonth}. 162 * <p> 163 * The above examples refer to the {@code startMonth}, {@code startDay}, and {@code startDayOfWeek}; 164 * the same applies for the {@code endMonth}, {@code endDay}, and {@code endDayOfWeek}. 165 * <p> 166 * The daylight savings time difference is set to the default value: one hour. 167 * 168 * @param offset 169 * the given base time zone offset to GMT. 170 * @param name 171 * the time zone ID which is obtained from 172 * {@code TimeZone.getAvailableIDs}. 173 * @param startMonth 174 * the daylight savings starting month. The month indexing is 0-based. eg, 0 175 * for January. 176 * @param startDay 177 * the daylight savings starting day-of-week-in-month. Please see 178 * the member description for an example. 179 * @param startDayOfWeek 180 * the daylight savings starting day-of-week. Please see the 181 * member description for an example. 182 * @param startTime 183 * the daylight savings starting time in local wall time, which 184 * is standard time in this case. Please see the member 185 * description for an example. 186 * @param endMonth 187 * the daylight savings ending month. The month indexing is 0-based. eg, 0 for 188 * January. 189 * @param endDay 190 * the daylight savings ending day-of-week-in-month. Please see 191 * the member description for an example. 192 * @param endDayOfWeek 193 * the daylight savings ending day-of-week. Please see the member 194 * description for an example. 195 * @param endTime 196 * the daylight savings ending time in local wall time, which is 197 * daylight time in this case. Please see the member description 198 * for an example. 199 * @throws IllegalArgumentException 200 * if the month, day, dayOfWeek, or time parameters are out of 201 * range for the start or end rule. 202 */ 203 public SimpleTimeZone(int offset, String name, int startMonth, 204 int startDay, int startDayOfWeek, int startTime, int endMonth, 205 int endDay, int endDayOfWeek, int endTime) { 206 this(offset, name, startMonth, startDay, startDayOfWeek, startTime, 207 endMonth, endDay, endDayOfWeek, endTime, 3600000); 208 } 209 210 /** 211 * Constructs a {@code SimpleTimeZone} with the given base time zone offset from GMT, 212 * time zone ID, times to start and end the daylight savings time, and 213 * the daylight savings time difference in milliseconds. 214 * 215 * @param offset 216 * the given base time zone offset to GMT. 217 * @param name 218 * the time zone ID which is obtained from 219 * {@code TimeZone.getAvailableIDs}. 220 * @param startMonth 221 * the daylight savings starting month. Month is 0-based. eg, 0 222 * for January. 223 * @param startDay 224 * the daylight savings starting day-of-week-in-month. Please see 225 * the description of {@link #SimpleTimeZone(int, String, int, int, int, int, int, int, int, int)} for an example. 226 * @param startDayOfWeek 227 * the daylight savings starting day-of-week. Please see the 228 * description of {@link #SimpleTimeZone(int, String, int, int, int, int, int, int, int, int)} for an example. 229 * @param startTime 230 * The daylight savings starting time in local wall time, which 231 * is standard time in this case. Please see the description of 232 * {@link #SimpleTimeZone(int, String, int, int, int, int, int, int, int, int)} for an example. 233 * @param endMonth 234 * the daylight savings ending month. Month is 0-based. eg, 0 for 235 * January. 236 * @param endDay 237 * the daylight savings ending day-of-week-in-month. Please see 238 * the description of {@link #SimpleTimeZone(int, String, int, int, int, int, int, int, int, int)} for an example. 239 * @param endDayOfWeek 240 * the daylight savings ending day-of-week. Please see the description of 241 * {@link #SimpleTimeZone(int, String, int, int, int, int, int, int, int, int)} for an example. 242 * @param endTime 243 * the daylight savings ending time in local wall time, which is 244 * daylight time in this case. Please see the description of {@link #SimpleTimeZone(int, String, int, int, int, int, int, int, int, int)} 245 * for an example. 246 * @param daylightSavings 247 * the daylight savings time difference in milliseconds. 248 * @throws IllegalArgumentException 249 * if the month, day, dayOfWeek, or time parameters are out of 250 * range for the start or end rule. 251 */ 252 public SimpleTimeZone(int offset, String name, int startMonth, 253 int startDay, int startDayOfWeek, int startTime, int endMonth, 254 int endDay, int endDayOfWeek, int endTime, int daylightSavings) { 255 // BEGIN android-changed 256 // icuTZ = getICUTimeZone(name); 257 // if (icuTZ instanceof com.ibm.icu.util.SimpleTimeZone) { 258 // isSimple = true; 259 // com.ibm.icu.util.SimpleTimeZone tz = (com.ibm.icu.util.SimpleTimeZone)icuTZ; 260 // tz.setRawOffset(offset); 261 // tz.setStartRule(startMonth, startDay, startDayOfWeek, startTime); 262 // tz.setEndRule(endMonth, endDay, endDayOfWeek, endTime); 263 // tz.setDSTSavings(daylightSavings); 264 // } else { 265 // isSimple = false; 266 // } 267 // setID(name); 268 // rawOffset = offset; 269 this(offset, name); 270 // END android-changed 271 if (daylightSavings <= 0) { 272 throw new IllegalArgumentException(Msg.getString( 273 "K00e9", daylightSavings)); //$NON-NLS-1$ 274 } 275 dstSavings = daylightSavings; 276 277 setStartRule(startMonth, startDay, startDayOfWeek, startTime); 278 setEndRule(endMonth, endDay, endDayOfWeek, endTime); 279 280 // BEGIN android-removed 281 // useDaylight = daylightSavings > 0 || icuTZ.useDaylightTime(); 282 // END android-removed 283 } 284 285 /** 286 * Construct a {@code SimpleTimeZone} with the given base time zone offset from GMT, 287 * time zone ID, times to start and end the daylight savings time including a 288 * mode specifier, the daylight savings time difference in milliseconds. 289 * The mode specifies either {@link #WALL_TIME}, {@link #STANDARD_TIME}, or 290 * {@link #UTC_TIME}. 291 * 292 * @param offset 293 * the given base time zone offset to GMT. 294 * @param name 295 * the time zone ID which is obtained from 296 * {@code TimeZone.getAvailableIDs}. 297 * @param startMonth 298 * the daylight savings starting month. The month indexing is 0-based. eg, 0 299 * for January. 300 * @param startDay 301 * the daylight savings starting day-of-week-in-month. Please see 302 * the description of {@link #SimpleTimeZone(int, String, int, int, int, int, int, int, int, int)} for an example. 303 * @param startDayOfWeek 304 * the daylight savings starting day-of-week. Please see the 305 * description of {@link #SimpleTimeZone(int, String, int, int, int, int, int, int, int, int)} for an example. 306 * @param startTime 307 * the time of day in milliseconds on which daylight savings 308 * time starts, based on the {@code startTimeMode}. 309 * @param startTimeMode 310 * the mode (UTC, standard, or wall time) of the start time 311 * value. 312 * @param endDay 313 * the day of the week on which daylight savings time ends. 314 * @param endMonth 315 * the daylight savings ending month. The month indexing is 0-based. eg, 0 for 316 * January. 317 * @param endDayOfWeek 318 * the daylight savings ending day-of-week. Please see the description of 319 * {@link #SimpleTimeZone(int, String, int, int, int, int, int, int, int, int)} for an example. 320 * @param endTime 321 * the time of day in milliseconds on which daylight savings 322 * time ends, based on the {@code endTimeMode}. 323 * @param endTimeMode 324 * the mode (UTC, standard, or wall time) of the end time value. 325 * @param daylightSavings 326 * the daylight savings time difference in milliseconds. 327 * @throws IllegalArgumentException 328 * if the month, day, dayOfWeek, or time parameters are out of 329 * range for the start or end rule. 330 */ 331 public SimpleTimeZone(int offset, String name, int startMonth, 332 int startDay, int startDayOfWeek, int startTime, int startTimeMode, 333 int endMonth, int endDay, int endDayOfWeek, int endTime, 334 int endTimeMode, int daylightSavings) { 335 336 this(offset, name, startMonth, startDay, startDayOfWeek, startTime, 337 endMonth, endDay, endDayOfWeek, endTime, daylightSavings); 338 startMode = startTimeMode; 339 endMode = endTimeMode; 340 } 341 342 /** 343 * Returns a new {@code SimpleTimeZone} with the same ID, {@code rawOffset} and daylight 344 * savings time rules as this SimpleTimeZone. 345 * 346 * @return a shallow copy of this {@code SimpleTimeZone}. 347 * @see java.lang.Cloneable 348 */ 349 @Override 350 public Object clone() { 351 SimpleTimeZone zone = (SimpleTimeZone) super.clone(); 352 if (daylightSavings != null) { 353 zone.daylightSavings = (GregorianCalendar) daylightSavings.clone(); 354 } 355 return zone; 356 } 357 358 /** 359 * Compares the specified object to this {@code SimpleTimeZone} and returns whether they 360 * are equal. The object must be an instance of {@code SimpleTimeZone} and have the 361 * same internal data. 362 * 363 * @param object 364 * the object to compare with this object. 365 * @return {@code true} if the specified object is equal to this 366 * {@code SimpleTimeZone}, {@code false} otherwise. 367 * @see #hashCode 368 */ 369 @Override 370 public boolean equals(Object object) { 371 if (!(object instanceof SimpleTimeZone)) { 372 return false; 373 } 374 SimpleTimeZone tz = (SimpleTimeZone) object; 375 return getID().equals(tz.getID()) 376 && rawOffset == tz.rawOffset 377 && useDaylight == tz.useDaylight 378 && (!useDaylight || (startYear == tz.startYear 379 && startMonth == tz.startMonth 380 && startDay == tz.startDay && startMode == tz.startMode 381 && startDayOfWeek == tz.startDayOfWeek 382 && startTime == tz.startTime && endMonth == tz.endMonth 383 && endDay == tz.endDay 384 && endDayOfWeek == tz.endDayOfWeek 385 && endTime == tz.endTime && endMode == tz.endMode && dstSavings == tz.dstSavings)); 386 } 387 388 @Override 389 public int getDSTSavings() { 390 if (!useDaylight) { 391 return 0; 392 } 393 return dstSavings; 394 } 395 396 @Override 397 public int getOffset(int era, int year, int month, int day, int dayOfWeek, 398 int time) { 399 if (era != GregorianCalendar.BC && era != GregorianCalendar.AD) { 400 throw new IllegalArgumentException(Msg.getString("K00ea", era)); //$NON-NLS-1$ 401 } 402 checkRange(month, dayOfWeek, time); 403 if (month != Calendar.FEBRUARY || day != 29 || !isLeapYear(year)) { 404 checkDay(month, day); 405 } 406 407 // BEGIN android-changed 408 // return icuTZ.getOffset(era, year, month, day, dayOfWeek, time); 409 if (!useDaylightTime() || era != GregorianCalendar.AD 410 || year < startYear) { 411 return rawOffset; 412 } 413 if (endMonth < startMonth) { 414 if (month > endMonth && month < startMonth) { 415 return rawOffset; 416 } 417 } else { 418 if (month < startMonth || month > endMonth) { 419 return rawOffset; 420 } 421 } 422 423 int ruleDay = 0, daysInMonth, firstDayOfMonth = mod7(dayOfWeek - day); 424 if (month == startMonth) { 425 switch (startMode) { 426 case DOM_MODE: 427 ruleDay = startDay; 428 break; 429 case DOW_IN_MONTH_MODE: 430 if (startDay >= 0) { 431 ruleDay = mod7(startDayOfWeek - firstDayOfMonth) + 1 432 + (startDay - 1) * 7; 433 } else { 434 daysInMonth = GregorianCalendar.DaysInMonth[startMonth]; 435 if (startMonth == Calendar.FEBRUARY && isLeapYear( 436 year)) { 437 daysInMonth += 1; 438 } 439 ruleDay = daysInMonth 440 + 1 441 + mod7(startDayOfWeek 442 - (firstDayOfMonth + daysInMonth)) 443 + startDay * 7; 444 } 445 break; 446 case DOW_GE_DOM_MODE: 447 ruleDay = startDay 448 + mod7(startDayOfWeek 449 - (firstDayOfMonth + startDay - 1)); 450 break; 451 case DOW_LE_DOM_MODE: 452 ruleDay = startDay 453 + mod7(startDayOfWeek 454 - (firstDayOfMonth + startDay - 1)); 455 if (ruleDay != startDay) { 456 ruleDay -= 7; 457 } 458 break; 459 } 460 if (ruleDay > day || ruleDay == day && time < startTime) { 461 return rawOffset; 462 } 463 } 464 465 int ruleTime = endTime - dstSavings; 466 int nextMonth = (month + 1) % 12; 467 if (month == endMonth || (ruleTime < 0 && nextMonth == endMonth)) { 468 switch (endMode) { 469 case DOM_MODE: 470 ruleDay = endDay; 471 break; 472 case DOW_IN_MONTH_MODE: 473 if (endDay >= 0) { 474 ruleDay = mod7(endDayOfWeek - firstDayOfMonth) + 1 475 + (endDay - 1) * 7; 476 } else { 477 daysInMonth = GregorianCalendar.DaysInMonth[endMonth]; 478 if (endMonth == Calendar.FEBRUARY && isLeapYear(year)) { 479 daysInMonth++; 480 } 481 ruleDay = daysInMonth 482 + 1 483 + mod7(endDayOfWeek 484 - (firstDayOfMonth + daysInMonth)) + endDay 485 * 7; 486 } 487 break; 488 case DOW_GE_DOM_MODE: 489 ruleDay = endDay 490 + mod7( 491 endDayOfWeek - (firstDayOfMonth + endDay - 1)); 492 break; 493 case DOW_LE_DOM_MODE: 494 ruleDay = endDay 495 + mod7( 496 endDayOfWeek - (firstDayOfMonth + endDay - 1)); 497 if (ruleDay != endDay) { 498 ruleDay -= 7; 499 } 500 break; 501 } 502 503 int ruleMonth = endMonth; 504 if (ruleTime < 0) { 505 int changeDays = 1 - (ruleTime / 86400000); 506 ruleTime = (ruleTime % 86400000) + 86400000; 507 ruleDay -= changeDays; 508 if (ruleDay <= 0) { 509 if (--ruleMonth < Calendar.JANUARY) { 510 ruleMonth = Calendar.DECEMBER; 511 } 512 ruleDay += GregorianCalendar.DaysInMonth[ruleMonth]; 513 if (ruleMonth == Calendar.FEBRUARY && isLeapYear(year)) { 514 ruleDay++; 515 } 516 } 517 } 518 519 if (month == ruleMonth) { 520 if (ruleDay < day || ruleDay == day && time >= ruleTime) { 521 return rawOffset; 522 } 523 } else if (nextMonth != ruleMonth) { 524 return rawOffset; 525 } 526 } 527 return rawOffset + dstSavings; 528 // END android-changed 529 } 530 531 @Override 532 public int getOffset(long time) { 533 // BEGIN android-changed 534 // return icuTZ.getOffset(time); 535 if (!useDaylightTime()) { 536 return rawOffset; 537 } 538 if (daylightSavings == null) { 539 daylightSavings = new GregorianCalendar(this); 540 } 541 return daylightSavings.getOffset(time + rawOffset); 542 // END android-changed 543 } 544 545 @Override 546 public int getRawOffset() { 547 return rawOffset; 548 } 549 550 /** 551 * Returns an integer hash code for the receiver. Objects which are equal 552 * return the same value for this method. 553 * 554 * @return the receiver's hash. 555 * @see #equals 556 */ 557 @Override 558 public synchronized int hashCode() { 559 int hashCode = getID().hashCode() + rawOffset; 560 if (useDaylight) { 561 hashCode += startYear + startMonth + startDay + startDayOfWeek 562 + startTime + startMode + endMonth + endDay + endDayOfWeek 563 + endTime + endMode + dstSavings; 564 } 565 return hashCode; 566 } 567 568 @Override 569 public boolean hasSameRules(TimeZone zone) { 570 if (!(zone instanceof SimpleTimeZone)) { 571 return false; 572 } 573 SimpleTimeZone tz = (SimpleTimeZone) zone; 574 if (useDaylight != tz.useDaylight) { 575 return false; 576 } 577 if (!useDaylight) { 578 return rawOffset == tz.rawOffset; 579 } 580 return rawOffset == tz.rawOffset && dstSavings == tz.dstSavings 581 && startYear == tz.startYear && startMonth == tz.startMonth 582 && startDay == tz.startDay && startMode == tz.startMode 583 && startDayOfWeek == tz.startDayOfWeek 584 && startTime == tz.startTime && endMonth == tz.endMonth 585 && endDay == tz.endDay && endDayOfWeek == tz.endDayOfWeek 586 && endTime == tz.endTime && endMode == tz.endMode; 587 } 588 589 @Override 590 public boolean inDaylightTime(Date time) { 591 // BEGIN android-changed 592 // return icuTZ.inDaylightTime(time); 593 // check for null pointer 594 long millis = time.getTime(); 595 if (!useDaylightTime()) { 596 return false; 597 } 598 if (daylightSavings == null) { 599 daylightSavings = new GregorianCalendar(this); 600 } 601 return daylightSavings.getOffset(millis + rawOffset) != rawOffset; 602 // END android-changed 603 } 604 605 private boolean isLeapYear(int year) { 606 if (year > 1582) { 607 return year % 4 == 0 && (year % 100 != 0 || year % 400 == 0); 608 } 609 return year % 4 == 0; 610 } 611 612 // BEGIN android-added 613 private int mod7(int num1) { 614 int rem = num1 % 7; 615 return (num1 < 0 && rem < 0) ? 7 + rem : rem; 616 } 617 // END android-added 618 619 /** 620 * Sets the daylight savings offset in milliseconds for this {@code SimpleTimeZone}. 621 * 622 * @param milliseconds 623 * the daylight savings offset in milliseconds. 624 */ 625 public void setDSTSavings(int milliseconds) { 626 if (milliseconds > 0) { 627 dstSavings = milliseconds; 628 } else { 629 throw new IllegalArgumentException(); 630 } 631 } 632 633 private void checkRange(int month, int dayOfWeek, int time) { 634 if (month < Calendar.JANUARY || month > Calendar.DECEMBER) { 635 throw new IllegalArgumentException(Msg.getString("K00e5", month)); //$NON-NLS-1$ 636 } 637 if (dayOfWeek < Calendar.SUNDAY || dayOfWeek > Calendar.SATURDAY) { 638 throw new IllegalArgumentException(Msg 639 .getString("K00e7", dayOfWeek)); //$NON-NLS-1$ 640 } 641 if (time < 0 || time >= 24 * 3600000) { 642 throw new IllegalArgumentException(Msg.getString("K00e8", time)); //$NON-NLS-1$ 643 } 644 } 645 646 private void checkDay(int month, int day) { 647 if (day <= 0 || day > GregorianCalendar.DaysInMonth[month]) { 648 throw new IllegalArgumentException(Msg.getString("K00e6", day)); //$NON-NLS-1$ 649 } 650 } 651 652 private void setEndMode() { 653 if (endDayOfWeek == 0) { 654 endMode = DOM_MODE; 655 } else if (endDayOfWeek < 0) { 656 endDayOfWeek = -endDayOfWeek; 657 if (endDay < 0) { 658 endDay = -endDay; 659 endMode = DOW_LE_DOM_MODE; 660 } else { 661 endMode = DOW_GE_DOM_MODE; 662 } 663 } else { 664 endMode = DOW_IN_MONTH_MODE; 665 } 666 useDaylight = startDay != 0 && endDay != 0; 667 if (endDay != 0) { 668 checkRange(endMonth, endMode == DOM_MODE ? 1 : endDayOfWeek, 669 endTime); 670 if (endMode != DOW_IN_MONTH_MODE) { 671 checkDay(endMonth, endDay); 672 } else { 673 if (endDay < -5 || endDay > 5) { 674 throw new IllegalArgumentException(Msg.getString( 675 "K00f8", endDay)); //$NON-NLS-1$ 676 } 677 } 678 } 679 if (endMode != DOM_MODE) { 680 endDayOfWeek--; 681 } 682 } 683 684 /** 685 * Sets the rule which specifies the end of daylight savings time. 686 * 687 * @param month 688 * the {@code Calendar} month in which daylight savings time ends. 689 * @param dayOfMonth 690 * the {@code Calendar} day of the month on which daylight savings time 691 * ends. 692 * @param time 693 * the time of day in milliseconds standard time on which 694 * daylight savings time ends. 695 */ 696 public void setEndRule(int month, int dayOfMonth, int time) { 697 endMonth = month; 698 endDay = dayOfMonth; 699 endDayOfWeek = 0; // Initialize this value for hasSameRules() 700 endTime = time; 701 setEndMode(); 702 // BEGIN android-removed 703 // if (isSimple) { 704 // ((com.ibm.icu.util.SimpleTimeZone) icuTZ).setEndRule(month, 705 // dayOfMonth, time); 706 // } 707 // END android-removed 708 } 709 710 /** 711 * Sets the rule which specifies the end of daylight savings time. 712 * 713 * @param month 714 * the {@code Calendar} month in which daylight savings time ends. 715 * @param day 716 * the occurrence of the day of the week on which daylight 717 * savings time ends. 718 * @param dayOfWeek 719 * the {@code Calendar} day of the week on which daylight savings time 720 * ends. 721 * @param time 722 * the time of day in milliseconds standard time on which 723 * daylight savings time ends. 724 */ 725 public void setEndRule(int month, int day, int dayOfWeek, int time) { 726 endMonth = month; 727 endDay = day; 728 endDayOfWeek = dayOfWeek; 729 endTime = time; 730 setEndMode(); 731 // BEGIN android-removed 732 // if (isSimple) { 733 // ((com.ibm.icu.util.SimpleTimeZone) icuTZ).setEndRule(month, day, 734 // dayOfWeek, time); 735 // } 736 // END android-removed 737 } 738 739 /** 740 * Sets the rule which specifies the end of daylight savings time. 741 * 742 * @param month 743 * the {@code Calendar} month in which daylight savings time ends. 744 * @param day 745 * the {@code Calendar} day of the month. 746 * @param dayOfWeek 747 * the {@code Calendar} day of the week on which daylight savings time 748 * ends. 749 * @param time 750 * the time of day in milliseconds on which daylight savings time 751 * ends. 752 * @param after 753 * selects the day after or before the day of month. 754 */ 755 public void setEndRule(int month, int day, int dayOfWeek, int time, 756 boolean after) { 757 endMonth = month; 758 endDay = after ? day : -day; 759 endDayOfWeek = -dayOfWeek; 760 endTime = time; 761 setEndMode(); 762 // BEGIN android-removed 763 // if (isSimple) { 764 // ((com.ibm.icu.util.SimpleTimeZone) icuTZ).setEndRule(month, day, 765 // dayOfWeek, time, after); 766 // } 767 // END android-removed 768 } 769 770 /** 771 * Sets the offset for standard time from GMT for this {@code SimpleTimeZone}. 772 * 773 * @param offset 774 * the offset from GMT of standard time in milliseconds. 775 */ 776 @Override 777 public void setRawOffset(int offset) { 778 rawOffset = offset; 779 // BEGIN android-removed 780 // icuTZ.setRawOffset(offset); 781 // END android-removed 782 } 783 784 private void setStartMode() { 785 if (startDayOfWeek == 0) { 786 startMode = DOM_MODE; 787 } else if (startDayOfWeek < 0) { 788 startDayOfWeek = -startDayOfWeek; 789 if (startDay < 0) { 790 startDay = -startDay; 791 startMode = DOW_LE_DOM_MODE; 792 } else { 793 startMode = DOW_GE_DOM_MODE; 794 } 795 } else { 796 startMode = DOW_IN_MONTH_MODE; 797 } 798 useDaylight = startDay != 0 && endDay != 0; 799 if (startDay != 0) { 800 checkRange(startMonth, startMode == DOM_MODE ? 1 : startDayOfWeek, 801 startTime); 802 if (startMode != DOW_IN_MONTH_MODE) { 803 checkDay(startMonth, startDay); 804 } else { 805 if (startDay < -5 || startDay > 5) { 806 throw new IllegalArgumentException(Msg.getString( 807 "K00f8", startDay)); //$NON-NLS-1$ 808 } 809 } 810 } 811 if (startMode != DOM_MODE) { 812 startDayOfWeek--; 813 } 814 } 815 816 /** 817 * Sets the rule which specifies the start of daylight savings time. 818 * 819 * @param month 820 * the {@code Calendar} month in which daylight savings time starts. 821 * @param dayOfMonth 822 * the {@code Calendar} day of the month on which daylight savings time 823 * starts. 824 * @param time 825 * the time of day in milliseconds on which daylight savings time 826 * starts. 827 */ 828 public void setStartRule(int month, int dayOfMonth, int time) { 829 startMonth = month; 830 startDay = dayOfMonth; 831 startDayOfWeek = 0; // Initialize this value for hasSameRules() 832 startTime = time; 833 setStartMode(); 834 // BEGIN android-removed 835 // if (isSimple) { 836 // ((com.ibm.icu.util.SimpleTimeZone) icuTZ).setStartRule(month, 837 // dayOfMonth, time); 838 // } 839 // END android-removed 840 } 841 842 /** 843 * Sets the rule which specifies the start of daylight savings time. 844 * 845 * @param month 846 * the {@code Calendar} month in which daylight savings time starts. 847 * @param day 848 * the occurrence of the day of the week on which daylight 849 * savings time starts. 850 * @param dayOfWeek 851 * the {@code Calendar} day of the week on which daylight savings time 852 * starts. 853 * @param time 854 * the time of day in milliseconds on which daylight savings time 855 * starts. 856 */ 857 public void setStartRule(int month, int day, int dayOfWeek, int time) { 858 startMonth = month; 859 startDay = day; 860 startDayOfWeek = dayOfWeek; 861 startTime = time; 862 setStartMode(); 863 // BEGIN android-removed 864 // if (isSimple) { 865 // ((com.ibm.icu.util.SimpleTimeZone) icuTZ).setStartRule(month, day, 866 // dayOfWeek, time); 867 // } 868 // END android-removed 869 } 870 871 /** 872 * Sets the rule which specifies the start of daylight savings time. 873 * 874 * @param month 875 * the {@code Calendar} month in which daylight savings time starts. 876 * @param day 877 * the {@code Calendar} day of the month. 878 * @param dayOfWeek 879 * the {@code Calendar} day of the week on which daylight savings time 880 * starts. 881 * @param time 882 * the time of day in milliseconds on which daylight savings time 883 * starts. 884 * @param after 885 * selects the day after or before the day of month. 886 */ 887 public void setStartRule(int month, int day, int dayOfWeek, int time, 888 boolean after) { 889 startMonth = month; 890 startDay = after ? day : -day; 891 startDayOfWeek = -dayOfWeek; 892 startTime = time; 893 setStartMode(); 894 // BEGIN android-removed 895 // if (isSimple) { 896 // ((com.ibm.icu.util.SimpleTimeZone) icuTZ).setStartRule(month, day, 897 // dayOfWeek, time, after); 898 // } 899 // END android-removed 900 } 901 902 /** 903 * Sets the starting year for daylight savings time in this {@code SimpleTimeZone}. 904 * Years before this start year will always be in standard time. 905 * 906 * @param year 907 * the starting year. 908 */ 909 public void setStartYear(int year) { 910 startYear = year; 911 useDaylight = true; 912 } 913 914 /** 915 * Returns the string representation of this {@code SimpleTimeZone}. 916 * 917 * @return the string representation of this {@code SimpleTimeZone}. 918 */ 919 @Override 920 public String toString() { 921 return getClass().getName() 922 + "[id=" //$NON-NLS-1$ 923 + getID() 924 + ",offset=" //$NON-NLS-1$ 925 + rawOffset 926 + ",dstSavings=" //$NON-NLS-1$ 927 + dstSavings 928 + ",useDaylight=" //$NON-NLS-1$ 929 + useDaylight 930 + ",startYear=" //$NON-NLS-1$ 931 + startYear 932 + ",startMode=" //$NON-NLS-1$ 933 + startMode 934 + ",startMonth=" //$NON-NLS-1$ 935 + startMonth 936 + ",startDay=" //$NON-NLS-1$ 937 + startDay 938 + ",startDayOfWeek=" //$NON-NLS-1$ 939 + (useDaylight && (startMode != DOM_MODE) ? startDayOfWeek + 1 940 : 0) + ",startTime=" + startTime + ",endMode=" //$NON-NLS-1$ //$NON-NLS-2$ 941 + endMode + ",endMonth=" + endMonth + ",endDay=" + endDay //$NON-NLS-1$ //$NON-NLS-2$ 942 + ",endDayOfWeek=" //$NON-NLS-1$ 943 + (useDaylight && (endMode != DOM_MODE) ? endDayOfWeek + 1 : 0) 944 + ",endTime=" + endTime + "]"; //$NON-NLS-1$//$NON-NLS-2$ 945 } 946 947 @Override 948 public boolean useDaylightTime() { 949 return useDaylight; 950 } 951 952 private static final ObjectStreamField[] serialPersistentFields = { 953 new ObjectStreamField("dstSavings", Integer.TYPE), //$NON-NLS-1$ 954 new ObjectStreamField("endDay", Integer.TYPE), //$NON-NLS-1$ 955 new ObjectStreamField("endDayOfWeek", Integer.TYPE), //$NON-NLS-1$ 956 new ObjectStreamField("endMode", Integer.TYPE), //$NON-NLS-1$ 957 new ObjectStreamField("endMonth", Integer.TYPE), //$NON-NLS-1$ 958 new ObjectStreamField("endTime", Integer.TYPE), //$NON-NLS-1$ 959 new ObjectStreamField("monthLength", byte[].class), //$NON-NLS-1$ 960 new ObjectStreamField("rawOffset", Integer.TYPE), //$NON-NLS-1$ 961 new ObjectStreamField("serialVersionOnStream", Integer.TYPE), //$NON-NLS-1$ 962 new ObjectStreamField("startDay", Integer.TYPE), //$NON-NLS-1$ 963 new ObjectStreamField("startDayOfWeek", Integer.TYPE), //$NON-NLS-1$ 964 new ObjectStreamField("startMode", Integer.TYPE), //$NON-NLS-1$ 965 new ObjectStreamField("startMonth", Integer.TYPE), //$NON-NLS-1$ 966 new ObjectStreamField("startTime", Integer.TYPE), //$NON-NLS-1$ 967 new ObjectStreamField("startYear", Integer.TYPE), //$NON-NLS-1$ 968 new ObjectStreamField("useDaylight", Boolean.TYPE), }; //$NON-NLS-1$ 969 970 private void writeObject(ObjectOutputStream stream) throws IOException { 971 int sEndDay = endDay, sEndDayOfWeek = endDayOfWeek + 1, sStartDay = startDay, sStartDayOfWeek = startDayOfWeek + 1; 972 if (useDaylight 973 && (startMode != DOW_IN_MONTH_MODE || endMode != DOW_IN_MONTH_MODE)) { 974 Calendar cal = new GregorianCalendar(this); 975 if (endMode != DOW_IN_MONTH_MODE) { 976 cal.set(Calendar.MONTH, endMonth); 977 cal.set(Calendar.DATE, endDay); 978 sEndDay = cal.get(Calendar.DAY_OF_WEEK_IN_MONTH); 979 if (endMode == DOM_MODE) { 980 sEndDayOfWeek = cal.getFirstDayOfWeek(); 981 } 982 } 983 if (startMode != DOW_IN_MONTH_MODE) { 984 cal.set(Calendar.MONTH, startMonth); 985 cal.set(Calendar.DATE, startDay); 986 sStartDay = cal.get(Calendar.DAY_OF_WEEK_IN_MONTH); 987 if (startMode == DOM_MODE) { 988 sStartDayOfWeek = cal.getFirstDayOfWeek(); 989 } 990 } 991 } 992 ObjectOutputStream.PutField fields = stream.putFields(); 993 fields.put("dstSavings", dstSavings); //$NON-NLS-1$ 994 fields.put("endDay", sEndDay); //$NON-NLS-1$ 995 fields.put("endDayOfWeek", sEndDayOfWeek); //$NON-NLS-1$ 996 fields.put("endMode", endMode); //$NON-NLS-1$ 997 fields.put("endMonth", endMonth); //$NON-NLS-1$ 998 fields.put("endTime", endTime); //$NON-NLS-1$ 999 fields.put("monthLength", GregorianCalendar.DaysInMonth); //$NON-NLS-1$ 1000 fields.put("rawOffset", rawOffset); //$NON-NLS-1$ 1001 fields.put("serialVersionOnStream", 1); //$NON-NLS-1$ 1002 fields.put("startDay", sStartDay); //$NON-NLS-1$ 1003 fields.put("startDayOfWeek", sStartDayOfWeek); //$NON-NLS-1$ 1004 fields.put("startMode", startMode); //$NON-NLS-1$ 1005 fields.put("startMonth", startMonth); //$NON-NLS-1$ 1006 fields.put("startTime", startTime); //$NON-NLS-1$ 1007 fields.put("startYear", startYear); //$NON-NLS-1$ 1008 fields.put("useDaylight", useDaylight); //$NON-NLS-1$ 1009 stream.writeFields(); 1010 stream.writeInt(4); 1011 byte[] values = new byte[4]; 1012 values[0] = (byte) startDay; 1013 values[1] = (byte) (startMode == DOM_MODE ? 0 : startDayOfWeek + 1); 1014 values[2] = (byte) endDay; 1015 values[3] = (byte) (endMode == DOM_MODE ? 0 : endDayOfWeek + 1); 1016 stream.write(values); 1017 } 1018 1019 private void readObject(ObjectInputStream stream) throws IOException, 1020 ClassNotFoundException { 1021 ObjectInputStream.GetField fields = stream.readFields(); 1022 rawOffset = fields.get("rawOffset", 0); //$NON-NLS-1$ 1023 useDaylight = fields.get("useDaylight", false); //$NON-NLS-1$ 1024 if (useDaylight) { 1025 endMonth = fields.get("endMonth", 0); //$NON-NLS-1$ 1026 endTime = fields.get("endTime", 0); //$NON-NLS-1$ 1027 startMonth = fields.get("startMonth", 0); //$NON-NLS-1$ 1028 startTime = fields.get("startTime", 0); //$NON-NLS-1$ 1029 startYear = fields.get("startYear", 0); //$NON-NLS-1$ 1030 } 1031 if (fields.get("serialVersionOnStream", 0) == 0) { //$NON-NLS-1$ 1032 if (useDaylight) { 1033 startMode = endMode = DOW_IN_MONTH_MODE; 1034 endDay = fields.get("endDay", 0); //$NON-NLS-1$ 1035 endDayOfWeek = fields.get("endDayOfWeek", 0) - 1; //$NON-NLS-1$ 1036 startDay = fields.get("startDay", 0); //$NON-NLS-1$ 1037 startDayOfWeek = fields.get("startDayOfWeek", 0) - 1; //$NON-NLS-1$ 1038 } 1039 } else { 1040 dstSavings = fields.get("dstSavings", 0); //$NON-NLS-1$ 1041 if (useDaylight) { 1042 endMode = fields.get("endMode", 0); //$NON-NLS-1$ 1043 startMode = fields.get("startMode", 0); //$NON-NLS-1$ 1044 int length = stream.readInt(); 1045 byte[] values = new byte[length]; 1046 stream.readFully(values); 1047 if (length >= 4) { 1048 startDay = values[0]; 1049 startDayOfWeek = values[1]; 1050 if (startMode != DOM_MODE) { 1051 startDayOfWeek--; 1052 } 1053 endDay = values[2]; 1054 endDayOfWeek = values[3]; 1055 if (endMode != DOM_MODE) { 1056 endDayOfWeek--; 1057 } 1058 } 1059 } 1060 } 1061 } 1062 1063 } 1064