1// © 2016 and later: Unicode, Inc. and others. 2// License & terms of use: http://www.unicode.org/copyright.html#License 3 /* 4* Copyright (C) 1996-2016, International Business Machines 5* Corporation and others. All Rights Reserved. 6*/ 7 8package com.ibm.icu.util; 9 10import java.io.IOException; 11import java.util.Date; 12 13import com.ibm.icu.impl.Grego; 14 15/** 16 * {@icuenhanced java.util.SimpleTimeZone}.{@icu _usage_} 17 * 18 * <p><code>SimpleTimeZone</code> is a concrete subclass of <code>TimeZone</code> 19 * that represents a time zone for use with a Gregorian calendar. This 20 * class does not handle historical changes. 21 * 22 * <p>Use a negative value for <code>dayOfWeekInMonth</code> to indicate that 23 * <code>SimpleTimeZone</code> should count from the end of the month backwards. For 24 * example, if Daylight Savings Time starts or ends at the last Sunday in a month, use 25 * <code>dayOfWeekInMonth = -1</code> along with <code>dayOfWeek = Calendar.SUNDAY</code> 26 * to specify the rule. 27 * 28 * @see Calendar 29 * @see GregorianCalendar 30 * @see TimeZone 31 * @author Deborah Goldsmith, Mark Davis, Chen-Lieh Huang, Alan Liu 32 * @stable ICU 2.0 33 */ 34public class SimpleTimeZone extends BasicTimeZone { 35 private static final long serialVersionUID = -7034676239311322769L; 36 37 /** 38 * Constant for a mode of start or end time specified as local wall time. 39 * @stable ICU 3.8 40 */ 41 public static final int WALL_TIME = 0; 42 43 /** 44 * Constant for a mode of start or end time specified as local standard time. 45 * @stable ICU 3.8 46 */ 47 public static final int STANDARD_TIME = 1; 48 49 /** 50 * Constant for a mode of start or end time specified as UTC. 51 * @stable ICU 3.8 52 */ 53 public static final int UTC_TIME = 2; 54 55 /** 56 * Constructs a SimpleTimeZone with the given base time zone offset from GMT 57 * and time zone ID. Timezone IDs can be obtained from 58 * TimeZone.getAvailableIDs. Normally you should use TimeZone.getDefault to 59 * construct a TimeZone. 60 * 61 * @param rawOffset The given base time zone offset to GMT. 62 * @param ID The time zone ID which is obtained from 63 * TimeZone.getAvailableIDs. 64 * @stable ICU 2.0 65 */ 66 public SimpleTimeZone(int rawOffset, String ID) { 67 super(ID); 68 construct(rawOffset, 0, 0, 0, 69 0, WALL_TIME, 70 0, 0, 0, 71 0, WALL_TIME, 72 Grego.MILLIS_PER_HOUR); 73 } 74 75 /** 76 * Constructs a SimpleTimeZone with the given base time zone offset from 77 * GMT, time zone ID, time to start and end the daylight time. Timezone IDs 78 * can be obtained from TimeZone.getAvailableIDs. Normally you should use 79 * TimeZone.getDefault to create a TimeZone. For a time zone that does not 80 * use daylight saving time, do not use this constructor; instead you should 81 * use SimpleTimeZone(rawOffset, ID). 82 * 83 * By default, this constructor specifies day-of-week-in-month rules. That 84 * is, if the startDay is 1, and the startDayOfWeek is SUNDAY, then this 85 * indicates the first Sunday in the startMonth. A startDay of -1 likewise 86 * indicates the last Sunday. However, by using negative or zero values for 87 * certain parameters, other types of rules can be specified. 88 * 89 * Day of month. To specify an exact day of the month, such as March 1, set 90 * startDayOfWeek to zero. 91 * 92 * Day of week after day of month. To specify the first day of the week 93 * occurring on or after an exact day of the month, make the day of the week 94 * negative. For example, if startDay is 5 and startDayOfWeek is -MONDAY, 95 * this indicates the first Monday on or after the 5th day of the 96 * startMonth. 97 * 98 * Day of week before day of month. To specify the last day of the week 99 * occurring on or before an exact day of the month, make the day of the 100 * week and the day of the month negative. For example, if startDay is -21 101 * and startDayOfWeek is -WEDNESDAY, this indicates the last Wednesday on or 102 * before the 21st of the startMonth. 103 * 104 * The above examples refer to the startMonth, startDay, and startDayOfWeek; 105 * the same applies for the endMonth, endDay, and endDayOfWeek. 106 * 107 * @param rawOffset The given base time zone offset to GMT. 108 * @param ID The time zone ID which is obtained from 109 * TimeZone.getAvailableIDs. 110 * @param startMonth The daylight savings starting month. Month is 111 * 0-based. eg, 0 for January. 112 * @param startDay The daylight savings starting 113 * day-of-week-in-month. Please see the member 114 * description for an example. 115 * @param startDayOfWeek The daylight savings starting day-of-week. Please 116 * see the member description for an example. 117 * @param startTime The daylight savings starting time in local wall 118 * time, which is standard time in this case. Please see the 119 * member description for an example. 120 * @param endMonth The daylight savings ending month. Month is 121 * 0-based. eg, 0 for January. 122 * @param endDay The daylight savings ending day-of-week-in-month. 123 * Please see the member description for an example. 124 * @param endDayOfWeek The daylight savings ending day-of-week. Please 125 * see the member description for an example. 126 * @param endTime The daylight savings ending time in local wall time, 127 * which is daylight time in this case. Please see the 128 * member description for an example. 129 * @throws IllegalArgumentException the month, day, dayOfWeek, or time 130 * parameters are out of range for the start or end rule 131 * @stable ICU 2.0 132 */ 133 public SimpleTimeZone(int rawOffset, String ID, 134 int startMonth, int startDay, int startDayOfWeek, int startTime, 135 int endMonth, int endDay, int endDayOfWeek, int endTime) { 136 super(ID); 137 construct(rawOffset, 138 startMonth, startDay, startDayOfWeek, 139 startTime, WALL_TIME, 140 endMonth, endDay, endDayOfWeek, 141 endTime, WALL_TIME, 142 Grego.MILLIS_PER_HOUR); 143 } 144 145 /** 146 * Constructs a SimpleTimeZone with the given base time zone offset from 147 * GMT, time zone ID, time and its mode to start and end the daylight time. 148 * The mode specifies either {@link #WALL_TIME} or {@link #STANDARD_TIME} 149 * or {@link #UTC_TIME}. 150 * 151 * @param rawOffset The given base time zone offset to GMT. 152 * @param ID The time zone ID which is obtained from 153 * TimeZone.getAvailableIDs. 154 * @param startMonth The daylight savings starting month. Month is 155 * 0-based. eg, 0 for January. 156 * @param startDay The daylight savings starting 157 * day-of-week-in-month. Please see the member 158 * description for an example. 159 * @param startDayOfWeek The daylight savings starting day-of-week. Please 160 * see the member description for an example. 161 * @param startTime The daylight savings starting time in local wall 162 * time, which is standard time in this case. Please see the 163 * member description for an example. 164 * @param startTimeMode The mode of the start time specified by startTime. 165 * @param endMonth The daylight savings ending month. Month is 166 * 0-based. eg, 0 for January. 167 * @param endDay The daylight savings ending day-of-week-in-month. 168 * Please see the member description for an example. 169 * @param endDayOfWeek The daylight savings ending day-of-week. Please 170 * see the member description for an example. 171 * @param endTime The daylight savings ending time in local wall time, 172 * which is daylight time in this case. Please see the 173 * member description for an example. 174 * @param endTimeMode The mode of the end time specified by endTime. 175 * @param dstSavings The amount of time in ms saved during DST. 176 * @throws IllegalArgumentException the month, day, dayOfWeek, or time 177 * parameters are out of range for the start or end rule 178 * @stable ICU 3.8 179 */ 180 public SimpleTimeZone(int rawOffset, String ID, 181 int startMonth, int startDay, 182 int startDayOfWeek, int startTime, 183 int startTimeMode, 184 int endMonth, int endDay, 185 int endDayOfWeek, int endTime, 186 int endTimeMode,int dstSavings){ 187 super(ID); 188 construct(rawOffset, 189 startMonth, startDay, startDayOfWeek, 190 startTime, startTimeMode, 191 endMonth, endDay, endDayOfWeek, 192 endTime, endTimeMode, 193 dstSavings); 194 } 195 196 /** 197 * Constructor. This constructor is identical to the 10-argument 198 * constructor, but also takes a dstSavings parameter. 199 * @param rawOffset The given base time zone offset to GMT. 200 * @param ID The time zone ID which is obtained from 201 * TimeZone.getAvailableIDs. 202 * @param startMonth The daylight savings starting month. Month is 203 * 0-based. eg, 0 for January. 204 * @param startDay The daylight savings starting 205 * day-of-week-in-month. Please see the member 206 * description for an example. 207 * @param startDayOfWeek The daylight savings starting day-of-week. Please 208 * see the member description for an example. 209 * @param startTime The daylight savings starting time in local wall 210 * time, which is standard time in this case. Please see the 211 * member description for an example. 212 * @param endMonth The daylight savings ending month. Month is 213 * 0-based. eg, 0 for January. 214 * @param endDay The daylight savings ending day-of-week-in-month. 215 * Please see the member description for an example. 216 * @param endDayOfWeek The daylight savings ending day-of-week. Please 217 * see the member description for an example. 218 * @param endTime The daylight savings ending time in local wall time, 219 * which is daylight time in this case. Please see the 220 * member description for an example. 221 * @param dstSavings The amount of time in ms saved during DST. 222 * @throws IllegalArgumentException the month, day, dayOfWeek, or time 223 * parameters are out of range for the start or end rule 224 * @stable ICU 2.0 225 */ 226 public SimpleTimeZone(int rawOffset, String ID, 227 int startMonth, int startDay, int startDayOfWeek, int startTime, 228 int endMonth, int endDay, int endDayOfWeek, int endTime, 229 int dstSavings) { 230 super(ID); 231 construct(rawOffset, 232 startMonth, startDay, startDayOfWeek, 233 startTime, WALL_TIME, 234 endMonth, endDay, endDayOfWeek, 235 endTime, WALL_TIME, 236 dstSavings); 237 } 238 239 /** 240 * {@inheritDoc} 241 * 242 * @stable ICU 3.8 243 */ 244 @Override 245 public void setID(String ID) { 246 if (isFrozen()) { 247 throw new UnsupportedOperationException("Attempt to modify a frozen SimpleTimeZone instance."); 248 } 249 super.setID(ID); 250 transitionRulesInitialized = false; 251 } 252 253 /** 254 * Overrides TimeZone 255 * Sets the base time zone offset to GMT. 256 * This is the offset to add "to" UTC to get local time. 257 * @param offsetMillis the raw offset of the time zone 258 * @stable ICU 2.0 259 */ 260 @Override 261 public void setRawOffset(int offsetMillis) { 262 if (isFrozen()) { 263 throw new UnsupportedOperationException("Attempt to modify a frozen SimpleTimeZone instance."); 264 } 265 266 raw = offsetMillis; 267 transitionRulesInitialized = false; 268 } 269 270 /** 271 * Overrides TimeZone 272 * Gets the GMT offset for this time zone. 273 * @return the raw offset 274 * @stable ICU 2.0 275 */ 276 @Override 277 public int getRawOffset() { 278 return raw; 279 } 280 281 /** 282 * Sets the daylight savings starting year. 283 * 284 * @param year The daylight savings starting year. 285 * @stable ICU 2.0 286 */ 287 public void setStartYear(int year) { 288 if (isFrozen()) { 289 throw new UnsupportedOperationException("Attempt to modify a frozen SimpleTimeZone instance."); 290 } 291 292 getSTZInfo().sy = year; 293 this.startYear = year; 294 transitionRulesInitialized = false; 295 } 296 297 /** 298 * Sets the daylight savings starting rule. For example, Daylight Savings 299 * Time starts at the second Sunday in March, at 2 AM in standard time. 300 * Therefore, you can set the start rule by calling: 301 * setStartRule(Calendar.MARCH, 2, Calendar.SUNDAY, 2*60*60*1000); 302 * 303 * @param month The daylight savings starting month. Month is 304 * 0-based. eg, 0 for January. 305 * @param dayOfWeekInMonth The daylight savings starting 306 * day-of-week-in-month. Please see the member 307 * description for an example. 308 * @param dayOfWeek The daylight savings starting day-of-week. 309 * Please see the member description for an 310 * example. 311 * @param time The daylight savings starting time in local wall 312 * time, which is standard time in this case. Please see 313 * the member description for an example. 314 * @throws IllegalArgumentException the month, dayOfWeekInMonth, 315 * dayOfWeek, or time parameters are out of range 316 * @stable ICU 2.0 317 */ 318 public void setStartRule(int month, int dayOfWeekInMonth, int dayOfWeek, 319 int time) { 320 if (isFrozen()) { 321 throw new UnsupportedOperationException("Attempt to modify a frozen SimpleTimeZone instance."); 322 } 323 324 getSTZInfo().setStart(month, dayOfWeekInMonth, dayOfWeek, time, -1, false); 325 setStartRule(month, dayOfWeekInMonth, dayOfWeek, time, WALL_TIME); 326 } 327 328 /** 329 * Sets the daylight savings starting rule. For example, in the U.S., Daylight Savings 330 * Time starts at the second Sunday in March, at 2 AM in standard time. 331 * Therefore, you can set the start rule by calling: 332 * <code>setStartRule(Calendar.MARCH, 2, Calendar.SUNDAY, 2*60*60*1000);</code> 333 * The dayOfWeekInMonth and dayOfWeek parameters together specify how to calculate 334 * the exact starting date. Their exact meaning depend on their respective signs, 335 * allowing various types of rules to be constructed, as follows:<ul> 336 * <li>If both dayOfWeekInMonth and dayOfWeek are positive, they specify the 337 * day of week in the month (e.g., (2, WEDNESDAY) is the second Wednesday 338 * of the month). 339 * <li>If dayOfWeek is positive and dayOfWeekInMonth is negative, they specify 340 * the day of week in the month counting backward from the end of the month. 341 * (e.g., (-1, MONDAY) is the last Monday in the month) 342 * <li>If dayOfWeek is zero and dayOfWeekInMonth is positive, dayOfWeekInMonth 343 * specifies the day of the month, regardless of what day of the week it is. 344 * (e.g., (10, 0) is the tenth day of the month) 345 * <li>If dayOfWeek is zero and dayOfWeekInMonth is negative, dayOfWeekInMonth 346 * specifies the day of the month counting backward from the end of the 347 * month, regardless of what day of the week it is (e.g., (-2, 0) is the 348 * next-to-last day of the month). 349 * <li>If dayOfWeek is negative and dayOfWeekInMonth is positive, they specify the 350 * first specified day of the week on or after the specfied day of the month. 351 * (e.g., (15, -SUNDAY) is the first Sunday after the 15th of the month 352 * [or the 15th itself if the 15th is a Sunday].) 353 * <li>If dayOfWeek and DayOfWeekInMonth are both negative, they specify the 354 * last specified day of the week on or before the specified day of the month. 355 * (e.g., (-20, -TUESDAY) is the last Tuesday before the 20th of the month 356 * [or the 20th itself if the 20th is a Tuesday].)</ul> 357 * @param month the daylight savings starting month. Month is 0-based. 358 * eg, 0 for January. 359 * @param dayOfWeekInMonth the daylight savings starting 360 * day-of-week-in-month. Please see the member description for an example. 361 * @param dayOfWeek the daylight savings starting day-of-week. Please see 362 * the member description for an example. 363 * @param time the daylight savings starting time. Please see the member 364 * description for an example. 365 */ 366 private void setStartRule(int month, int dayOfWeekInMonth, int dayOfWeek, int time, int mode) { 367 assert (!isFrozen()); 368 369 startMonth = month; 370 startDay = dayOfWeekInMonth; 371 startDayOfWeek = dayOfWeek; 372 startTime = time; 373 startTimeMode = mode; 374 decodeStartRule(); 375 376 transitionRulesInitialized = false; 377 } 378 379 /** 380 * Sets the DST start rule to a fixed date within a month. 381 * 382 * @param month The month in which this rule occurs (0-based). 383 * @param dayOfMonth The date in that month (1-based). 384 * @param time The time of that day (number of millis after midnight) 385 * when DST takes effect in local wall time, which is 386 * standard time in this case. 387 * @throws IllegalArgumentException the month, 388 * dayOfMonth, or time parameters are out of range 389 * @stable ICU 2.0 390 */ 391 public void setStartRule(int month, int dayOfMonth, int time) { 392 if (isFrozen()) { 393 throw new UnsupportedOperationException("Attempt to modify a frozen SimpleTimeZone instance."); 394 } 395 396 getSTZInfo().setStart(month, -1, -1, time, dayOfMonth, false); 397 setStartRule(month, dayOfMonth, 0, time, WALL_TIME); 398 } 399 400 /** 401 * Sets the DST start rule to a weekday before or after a give date within 402 * a month, e.g., the first Monday on or after the 8th. 403 * 404 * @param month The month in which this rule occurs (0-based). 405 * @param dayOfMonth A date within that month (1-based). 406 * @param dayOfWeek The day of the week on which this rule occurs. 407 * @param time The time of that day (number of millis after midnight) 408 * when DST takes effect in local wall time, which is 409 * standard time in this case. 410 * @param after If true, this rule selects the first dayOfWeek on 411 * or after dayOfMonth. If false, this rule selects 412 * the last dayOfWeek on or before dayOfMonth. 413 * @throws IllegalArgumentException the month, dayOfMonth, 414 * dayOfWeek, or time parameters are out of range 415 * @stable ICU 2.0 416 */ 417 public void setStartRule(int month, int dayOfMonth, int dayOfWeek, int time, boolean after) { 418 if (isFrozen()) { 419 throw new UnsupportedOperationException("Attempt to modify a frozen SimpleTimeZone instance."); 420 } 421 422 getSTZInfo().setStart(month, -1, dayOfWeek, time, dayOfMonth, after); 423 setStartRule(month, after ? dayOfMonth : -dayOfMonth, 424 -dayOfWeek, time, WALL_TIME); 425 } 426 427 /** 428 * Sets the daylight savings ending rule. For example, if Daylight Savings Time 429 * ends at the last (-1) Sunday in October, at 2 AM in standard time, 430 * you can set the end rule by calling: 431 * <code>setEndRule(Calendar.OCTOBER, -1, Calendar.SUNDAY, 2*60*60*1000);</code> 432 * 433 * @param month The daylight savings ending month. Month is 434 * 0-based. eg, 0 for January. 435 * @param dayOfWeekInMonth The daylight savings ending 436 * day-of-week-in-month. Please see the member 437 * description for an example. 438 * @param dayOfWeek The daylight savings ending day-of-week. Please 439 * see the member description for an example. 440 * @param time The daylight savings ending time in local wall time, 441 * which is daylight time in this case. Please see the 442 * member description for an example. 443 * @throws IllegalArgumentException the month, dayOfWeekInMonth, 444 * dayOfWeek, or time parameters are out of range 445 * @stable ICU 2.0 446 */ 447 public void setEndRule(int month, int dayOfWeekInMonth, int dayOfWeek, int time) { 448 if (isFrozen()) { 449 throw new UnsupportedOperationException("Attempt to modify a frozen SimpleTimeZone instance."); 450 } 451 452 getSTZInfo().setEnd(month, dayOfWeekInMonth, dayOfWeek, time, -1, false); 453 setEndRule(month, dayOfWeekInMonth, dayOfWeek, time, WALL_TIME); 454 } 455 456 /** 457 * Sets the DST end rule to a fixed date within a month. 458 * 459 * @param month The month in which this rule occurs (0-based). 460 * @param dayOfMonth The date in that month (1-based). 461 * @param time The time of that day (number of millis after midnight) 462 * when DST ends in local wall time, which is daylight 463 * time in this case. 464 * @throws IllegalArgumentException the month, 465 * dayOfMonth, or time parameters are out of range 466 * @stable ICU 2.0 467 */ 468 public void setEndRule(int month, int dayOfMonth, int time) { 469 if (isFrozen()) { 470 throw new UnsupportedOperationException("Attempt to modify a frozen SimpleTimeZone instance."); 471 } 472 473 getSTZInfo().setEnd(month, -1, -1, time, dayOfMonth, false); 474 setEndRule(month, dayOfMonth, WALL_TIME, time); 475 } 476 477 /** 478 * Sets the DST end rule to a weekday before or after a give date within 479 * a month, e.g., the first Monday on or after the 8th. 480 * 481 * @param month The month in which this rule occurs (0-based). 482 * @param dayOfMonth A date within that month (1-based). 483 * @param dayOfWeek The day of the week on which this rule occurs. 484 * @param time The time of that day (number of millis after midnight) 485 * when DST ends in local wall time, which is daylight 486 * time in this case. 487 * @param after If true, this rule selects the first dayOfWeek on 488 * or after dayOfMonth. If false, this rule selects 489 * the last dayOfWeek on or before dayOfMonth. 490 * @throws IllegalArgumentException the month, dayOfMonth, 491 * dayOfWeek, or time parameters are out of range 492 * @stable ICU 2.0 493 */ 494 public void setEndRule(int month, int dayOfMonth, int dayOfWeek, int time, boolean after) { 495 if (isFrozen()) { 496 throw new UnsupportedOperationException("Attempt to modify a frozen SimpleTimeZone instance."); 497 } 498 499 getSTZInfo().setEnd(month, -1, dayOfWeek, time, dayOfMonth, after); 500 setEndRule(month, dayOfMonth, dayOfWeek, time, WALL_TIME, after); 501 } 502 503 private void setEndRule(int month, int dayOfMonth, int dayOfWeek, 504 int time, int mode, boolean after){ 505 assert (!isFrozen()); 506 setEndRule(month, after ? dayOfMonth : -dayOfMonth, -dayOfWeek, time, mode); 507 } 508 509 /** 510 * Sets the daylight savings ending rule. For example, in the U.S., Daylight 511 * Savings Time ends at the first Sunday in November, at 2 AM in standard time. 512 * Therefore, you can set the end rule by calling: 513 * setEndRule(Calendar.NOVEMBER, 1, Calendar.SUNDAY, 2*60*60*1000); 514 * Various other types of rules can be specified by manipulating the dayOfWeek 515 * and dayOfWeekInMonth parameters. For complete details, see the documentation 516 * for setStartRule(). 517 * @param month the daylight savings ending month. Month is 0-based. 518 * eg, 0 for January. 519 * @param dayOfWeekInMonth the daylight savings ending 520 * day-of-week-in-month. See setStartRule() for a complete explanation. 521 * @param dayOfWeek the daylight savings ending day-of-week. See setStartRule() 522 * for a complete explanation. 523 * @param time the daylight savings ending time. Please see the member 524 * description for an example. 525 */ 526 private void setEndRule(int month, int dayOfWeekInMonth, int dayOfWeek, int time, int mode){ 527 assert (!isFrozen()); 528 529 endMonth = month; 530 endDay = dayOfWeekInMonth; 531 endDayOfWeek = dayOfWeek; 532 endTime = time; 533 endTimeMode = mode; 534 decodeEndRule(); 535 536 transitionRulesInitialized = false; 537 } 538 539 /** 540 * Sets the amount of time in ms that the clock is advanced during DST. 541 * @param millisSavedDuringDST the number of milliseconds the time is 542 * advanced with respect to standard time when the daylight savings rules 543 * are in effect. A positive number, typically one hour (3600000). 544 * @stable ICU 2.0 545 */ 546 public void setDSTSavings(int millisSavedDuringDST) { 547 if (isFrozen()) { 548 throw new UnsupportedOperationException("Attempt to modify a frozen SimpleTimeZone instance."); 549 } 550 551 if (millisSavedDuringDST <= 0) { 552 throw new IllegalArgumentException(); 553 } 554 dst = millisSavedDuringDST; 555 556 transitionRulesInitialized = false; 557 } 558 559 /** 560 * Returns the amount of time in ms that the clock is advanced during DST. 561 * @return the number of milliseconds the time is 562 * advanced with respect to standard time when the daylight savings rules 563 * are in effect. A positive number, typically one hour (3600000). 564 * @stable ICU 2.0 565 */ 566 @Override 567 public int getDSTSavings() { 568 return dst; 569 } 570 571 /** 572 * Returns the java.util.SimpleTimeZone that this class wraps. 573 * 574 java.util.SimpleTimeZone unwrapSTZ() { 575 return (java.util.SimpleTimeZone) unwrap(); 576 } 577 */ 578 579 // on JDK 1.4 and later, can't deserialize a SimpleTimeZone as a SimpleTimeZone... 580 private void readObject(java.io.ObjectInputStream in) throws IOException, 581 ClassNotFoundException { 582 in.defaultReadObject(); 583 /* 584 String id = getID(); 585 if (id!=null && !(zone instanceof java.util.SimpleTimeZone && zone.getID().equals(id))) { 586 // System.out.println("*** readjust " + zone.getClass().getName() + 587 // " " + zone.getID() + " ***"); 588 java.util.SimpleTimeZone stz = 589 new java.util.SimpleTimeZone(raw, id); 590 if (dst != 0) { 591 stz.setDSTSavings(dst); 592 // if it is 0, then there shouldn't be start/end rules and the default 593 // behavior should be no dst 594 } 595 596 if (xinfo != null) { 597 xinfo.applyTo(stz); 598 } 599 zoneJDK = stz; 600 } 601 */ 602 /* set all instance variables in this object 603 * to the values in zone 604 */ 605 if (xinfo != null) { 606 xinfo.applyTo(this); 607 } 608 } 609 610 /** 611 * Returns a string representation of this object. 612 * @return a string representation of this object 613 * @stable ICU 3.6 614 */ 615 @Override 616 public String toString() { 617 return "SimpleTimeZone: " + getID(); 618 } 619 620 private STZInfo getSTZInfo() { 621 if (xinfo == null) { 622 xinfo = new STZInfo(); 623 } 624 return xinfo; 625 } 626 627 // Use only for decodeStartRule() and decodeEndRule() where the year is not 628 // available. Set February to 29 days to accomodate rules with that date 629 // and day-of-week-on-or-before-that-date mode (DOW_LE_DOM_MODE). 630 // The compareToRule() method adjusts to February 28 in non-leap years. 631 // 632 // For actual getOffset() calculations, use TimeZone::monthLength() and 633 // TimeZone::previousMonthLength() which take leap years into account. 634 // We handle leap years assuming always 635 // Gregorian, since we know they didn't have daylight time when 636 // Gregorian calendar started. 637 private final static byte staticMonthLength[] = {31,29,31,30,31,30,31,31,30,31,30,31}; 638 639 /** 640 * {@inheritDoc} 641 * @stable ICU 2.0 642 */ 643 @Override 644 public int getOffset(int era, int year, int month, int day, 645 int dayOfWeek, int millis) 646 { 647 // Check the month before calling Grego.monthLength(). This 648 // duplicates the test that occurs in the 7-argument getOffset(), 649 // however, this is unavoidable. We don't mind because this method, in 650 // fact, should not be called; internal code should always call the 651 // 7-argument getOffset(), and outside code should use Calendar.get(int 652 // field) with fields ZONE_OFFSET and DST_OFFSET. We can't get rid of 653 // this method because it's public API. - liu 8/10/98 654 if(month < Calendar.JANUARY || month > Calendar.DECEMBER) { 655 throw new IllegalArgumentException(); 656 } 657 658 return getOffset(era, year, month, day, dayOfWeek, millis, Grego.monthLength(year, month)); 659 } 660 661 /** 662 * @internal 663 * @deprecated This API is ICU internal only. 664 */ 665 @Deprecated 666 public int getOffset(int era, int year, int month, int day, 667 int dayOfWeek, int millis, 668 int monthLength) { 669 // Check the month before calling Grego.monthLength(). This 670 // duplicates a test that occurs in the 9-argument getOffset(), 671 // however, this is unavoidable. We don't mind because this method, in 672 // fact, should not be called; internal code should always call the 673 // 9-argument getOffset(), and outside code should use Calendar.get(int 674 // field) with fields ZONE_OFFSET and DST_OFFSET. We can't get rid of 675 // this method because it's public API. - liu 8/10/98 676 if(month < Calendar.JANUARY || month > Calendar.DECEMBER) { 677 throw new IllegalArgumentException(); 678 } 679 680 return getOffset(era, year, month, day, dayOfWeek, millis, 681 Grego.monthLength(year, month), Grego.previousMonthLength(year, month)); 682 } 683 684 private int getOffset(int era, int year, int month, int day, 685 int dayOfWeek, int millis, 686 int monthLength, int prevMonthLength ){ 687 688 if (true) { 689 /* Use this parameter checking code for normal operation. Only one 690 * of these two blocks should actually get compiled into the class 691 * file. */ 692 if ((era != GregorianCalendar.AD && era != GregorianCalendar.BC) 693 || month < Calendar.JANUARY 694 || month > Calendar.DECEMBER 695 || day < 1 696 || day > monthLength 697 || dayOfWeek < Calendar.SUNDAY 698 || dayOfWeek > Calendar.SATURDAY 699 || millis < 0 700 || millis >= Grego.MILLIS_PER_DAY 701 || monthLength < 28 702 || monthLength > 31 703 || prevMonthLength < 28 704 || prevMonthLength > 31) { 705 throw new IllegalArgumentException(); 706 } 707 } 708 //Eclipse stated the following is "dead code" 709 /*else { 710 // This parameter checking code is better for debugging, but 711 // overkill for normal operation. Only one of these two blocks 712 // should actually get compiled into the class file. 713 if (era != GregorianCalendar.AD && era != GregorianCalendar.BC) { 714 throw new IllegalArgumentException("Illegal era " + era); 715 } 716 if (month < Calendar.JANUARY 717 || month > Calendar.DECEMBER) { 718 throw new IllegalArgumentException("Illegal month " + month); 719 } 720 if (day < 1 721 || day > monthLength) { 722 throw new IllegalArgumentException("Illegal day " + day+" max month len: "+monthLength); 723 } 724 if (dayOfWeek < Calendar.SUNDAY 725 || dayOfWeek > Calendar.SATURDAY) { 726 throw new IllegalArgumentException("Illegal day of week " + dayOfWeek); 727 } 728 if (millis < 0 729 || millis >= Grego.MILLIS_PER_DAY) { 730 throw new IllegalArgumentException("Illegal millis " + millis); 731 } 732 if (monthLength < 28 733 || monthLength > 31) { 734 throw new IllegalArgumentException("Illegal month length " + monthLength); 735 } 736 if (prevMonthLength < 28 737 || prevMonthLength > 31) { 738 throw new IllegalArgumentException("Illegal previous month length " + prevMonthLength); 739 } 740 }*/ 741 742 int result = raw; 743 744 // Bail out if we are before the onset of daylight savings time 745 if (!useDaylight || year < startYear || era != GregorianCalendar.AD) return result; 746 747 // Check for southern hemisphere. We assume that the start and end 748 // month are different. 749 boolean southern = (startMonth > endMonth); 750 751 // Compare the date to the starting and ending rules.+1 = date>rule, -1 752 // = date<rule, 0 = date==rule. 753 int startCompare = compareToRule(month, monthLength, prevMonthLength, 754 day, dayOfWeek, millis, 755 startTimeMode == UTC_TIME ? -raw : 0, 756 startMode, startMonth, startDayOfWeek, 757 startDay, startTime); 758 int endCompare = 0; 759 760 /* We don't always have to compute endCompare. For many instances, 761 * startCompare is enough to determine if we are in DST or not. In the 762 * northern hemisphere, if we are before the start rule, we can't have 763 * DST. In the southern hemisphere, if we are after the start rule, we 764 * must have DST. This is reflected in the way the next if statement 765 * (not the one immediately following) short circuits. */ 766 if (southern != (startCompare >= 0)) { 767 /* For the ending rule comparison, we add the dstSavings to the millis 768 * passed in to convert them from standard to wall time. We then must 769 * normalize the millis to the range 0..millisPerDay-1. */ 770 endCompare = compareToRule(month, monthLength, prevMonthLength, 771 day, dayOfWeek, millis, 772 endTimeMode == WALL_TIME ? dst : 773 (endTimeMode == UTC_TIME ? -raw : 0), 774 endMode, endMonth, endDayOfWeek, 775 endDay, endTime); 776 } 777 778 // Check for both the northern and southern hemisphere cases. We 779 // assume that in the northern hemisphere, the start rule is before the 780 // end rule within the calendar year, and vice versa for the southern 781 // hemisphere. 782 if ((!southern && (startCompare >= 0 && endCompare < 0)) || 783 (southern && (startCompare >= 0 || endCompare < 0))) 784 result += dst; 785 786 return result; 787 } 788 789 /** 790 * {@inheritDoc} 791 * @internal 792 * @deprecated This API is ICU internal only. 793 */ 794 @Override 795 @Deprecated 796 public void getOffsetFromLocal(long date, 797 int nonExistingTimeOpt, int duplicatedTimeOpt, int[] offsets) { 798 offsets[0] = getRawOffset(); 799 int fields[] = new int[6]; 800 Grego.timeToFields(date, fields); 801 offsets[1] = getOffset(GregorianCalendar.AD, 802 fields[0], fields[1], fields[2], 803 fields[3], fields[5]) - offsets[0]; 804 805 boolean recalc = false; 806 807 // Now, we need some adjustment 808 if (offsets[1] > 0) { 809 if ((nonExistingTimeOpt & STD_DST_MASK) == LOCAL_STD 810 || (nonExistingTimeOpt & STD_DST_MASK) != LOCAL_DST 811 && (nonExistingTimeOpt & FORMER_LATTER_MASK) != LOCAL_LATTER) { 812 date -= getDSTSavings(); 813 recalc = true; 814 } 815 } else { 816 if ((duplicatedTimeOpt & STD_DST_MASK) == LOCAL_DST 817 || (duplicatedTimeOpt & STD_DST_MASK) != LOCAL_STD 818 && (duplicatedTimeOpt & FORMER_LATTER_MASK) == LOCAL_FORMER) { 819 date -= getDSTSavings(); 820 recalc = true; 821 } 822 } 823 824 if (recalc) { 825 Grego.timeToFields(date, fields); 826 offsets[1] = getOffset(GregorianCalendar.AD, 827 fields[0], fields[1], fields[2], 828 fields[3], fields[5]) - offsets[0]; 829 } 830 } 831 832 private static final int 833 DOM_MODE = 1, 834 DOW_IN_MONTH_MODE=2, 835 DOW_GE_DOM_MODE=3, 836 DOW_LE_DOM_MODE=4; 837 838 /** 839 * Compare a given date in the year to a rule. Return 1, 0, or -1, depending 840 * on whether the date is after, equal to, or before the rule date. The 841 * millis are compared directly against the ruleMillis, so any 842 * standard-daylight adjustments must be handled by the caller. 843 * 844 * @return 1 if the date is after the rule date, -1 if the date is before 845 * the rule date, or 0 if the date is equal to the rule date. 846 */ 847 private int compareToRule(int month, int monthLen, int prevMonthLen, 848 int dayOfMonth, 849 int dayOfWeek, int millis, int millisDelta, 850 int ruleMode, int ruleMonth, int ruleDayOfWeek, 851 int ruleDay, int ruleMillis) 852 { 853 // Make adjustments for startTimeMode and endTimeMode 854 855 millis += millisDelta; 856 857 while (millis >= Grego.MILLIS_PER_DAY) { 858 millis -= Grego.MILLIS_PER_DAY; 859 ++dayOfMonth; 860 dayOfWeek = 1 + (dayOfWeek % 7); // dayOfWeek is one-based 861 if (dayOfMonth > monthLen) { 862 dayOfMonth = 1; 863 /* When incrementing the month, it is desirable to overflow 864 * from DECEMBER to DECEMBER+1, since we use the result to 865 * compare against a real month. Wraparound of the value 866 * leads to bug 4173604. */ 867 ++month; 868 } 869 } 870 /* 871 * For some reasons, Sun Java 6 on Solaris/Linux has a problem with 872 * the while loop below (at least Java 6 up to build 1.6.0_02-b08). 873 * It looks the JRE messes up the variable 'millis' while executing 874 * the code in the while block. The problem is not reproduced with 875 * JVM option -Xint, that is, it is likely a bug of the HotSpot 876 * adaptive compiler. Moving 'millis += Grego.MILLIS_PER_DAY' 877 * to the end of this while block seems to resolve the problem. 878 * See ticket#5887 about the problem in detail. 879 */ 880 while (millis < 0) { 881 //millis += Grego.MILLIS_PER_DAY; 882 --dayOfMonth; 883 dayOfWeek = 1 + ((dayOfWeek+5) % 7); // dayOfWeek is one-based 884 if (dayOfMonth < 1) { 885 dayOfMonth = prevMonthLen; 886 --month; 887 } 888 millis += Grego.MILLIS_PER_DAY; 889 } 890 891 if (month < ruleMonth) return -1; 892 else if (month > ruleMonth) return 1; 893 894 int ruleDayOfMonth = 0; 895 896 // Adjust the ruleDay to the monthLen, for non-leap year February 29 rule days. 897 if (ruleDay > monthLen) { 898 ruleDay = monthLen; 899 } 900 901 switch (ruleMode) 902 { 903 case DOM_MODE: 904 ruleDayOfMonth = ruleDay; 905 break; 906 case DOW_IN_MONTH_MODE: 907 // In this case ruleDay is the day-of-week-in-month 908 if (ruleDay > 0) 909 ruleDayOfMonth = 1 + (ruleDay - 1) * 7 + 910 (7 + ruleDayOfWeek - (dayOfWeek - dayOfMonth + 1)) % 7; 911 else // Assume ruleDay < 0 here 912 { 913 ruleDayOfMonth = monthLen + (ruleDay + 1) * 7 - 914 (7 + (dayOfWeek + monthLen - dayOfMonth) - ruleDayOfWeek) % 7; 915 } 916 break; 917 case DOW_GE_DOM_MODE: 918 ruleDayOfMonth = ruleDay + 919 (49 + ruleDayOfWeek - ruleDay - dayOfWeek + dayOfMonth) % 7; 920 break; 921 case DOW_LE_DOM_MODE: 922 ruleDayOfMonth = ruleDay - 923 (49 - ruleDayOfWeek + ruleDay + dayOfWeek - dayOfMonth) % 7; 924 // Note at this point ruleDayOfMonth may be <1, although it will 925 // be >=1 for well-formed rules. 926 break; 927 } 928 929 if (dayOfMonth < ruleDayOfMonth) return -1; 930 else if (dayOfMonth > ruleDayOfMonth) return 1; 931 932 if (millis < ruleMillis){ 933 return -1; 934 }else if (millis > ruleMillis){ 935 return 1; 936 }else{ 937 return 0; 938 } 939 } 940 941 // data needed for streaming mutated SimpleTimeZones in JDK14 942 private int raw;// the TimeZone's raw GMT offset 943 private int dst = 3600000; 944 private STZInfo xinfo = null; 945 private int startMonth, startDay, startDayOfWeek; // the month, day, DOW, and time DST starts 946 private int startTime; 947 private int startTimeMode, endTimeMode; // Mode for startTime, endTime; see TimeMode 948 private int endMonth, endDay, endDayOfWeek; // the month, day, DOW, and time DST ends 949 private int endTime; 950 private int startYear; // the year these DST rules took effect 951 private boolean useDaylight; // flag indicating whether this TimeZone uses DST 952 private int startMode, endMode; // flags indicating what kind of rules the DST rules are 953 954 /** 955 * Overrides TimeZone 956 * Queries if this time zone uses Daylight Saving Time. 957 * @stable ICU 2.0 958 */ 959 @Override 960 public boolean useDaylightTime(){ 961 return useDaylight; 962 } 963 964 /** 965 * {@inheritDoc} 966 * @stable ICU 49 967 */ 968 @Override 969 public boolean observesDaylightTime() { 970 return useDaylight; 971 } 972 973 /** 974 * Overrides TimeZone 975 * Queries if the give date is in Daylight Saving Time. 976 * @stable ICU 2.0 977 */ 978 @Override 979 public boolean inDaylightTime(Date date){ 980 GregorianCalendar gc = new GregorianCalendar(this); 981 gc.setTime(date); 982 return gc.inDaylightTime(); 983 } 984 985 /** 986 * Internal construction method. 987 */ 988 private void construct(int _raw, 989 int _startMonth, 990 int _startDay, 991 int _startDayOfWeek, 992 int _startTime, 993 int _startTimeMode, 994 int _endMonth, 995 int _endDay, 996 int _endDayOfWeek, 997 int _endTime, 998 int _endTimeMode, 999 int _dst) { 1000 raw = _raw; 1001 startMonth = _startMonth; 1002 startDay = _startDay; 1003 startDayOfWeek = _startDayOfWeek; 1004 startTime = _startTime; 1005 startTimeMode = _startTimeMode; 1006 endMonth = _endMonth; 1007 endDay = _endDay; 1008 endDayOfWeek = _endDayOfWeek; 1009 endTime = _endTime; 1010 endTimeMode = _endTimeMode; 1011 dst = _dst; 1012 startYear = 0; 1013 startMode = DOM_MODE; 1014 endMode = DOM_MODE; 1015 1016 decodeRules(); 1017 1018 if (_dst <= 0) { 1019 throw new IllegalArgumentException(); 1020 } 1021 } 1022 private void decodeRules(){ 1023 decodeStartRule(); 1024 decodeEndRule(); 1025 } 1026 1027 /** 1028 * Decode the start rule and validate the parameters. The parameters are 1029 * expected to be in encoded form, which represents the various rule modes 1030 * by negating or zeroing certain values. Representation formats are: 1031 * <p> 1032 * <pre> 1033 * DOW_IN_MONTH DOM DOW>=DOM DOW<=DOM no DST 1034 * ------------ ----- -------- -------- ---------- 1035 * month 0..11 same same same don't care 1036 * day -5..5 1..31 1..31 -1..-31 0 1037 * dayOfWeek 1..7 0 -1..-7 -1..-7 don't care 1038 * time 0..ONEDAY same same same don't care 1039 * </pre> 1040 * The range for month does not include UNDECIMBER since this class is 1041 * really specific to GregorianCalendar, which does not use that month. 1042 * The range for time includes ONEDAY (vs. ending at ONEDAY-1) because the 1043 * end rule is an exclusive limit point. That is, the range of times that 1044 * are in DST include those >= the start and < the end. For this reason, 1045 * it should be possible to specify an end of ONEDAY in order to include the 1046 * entire day. Although this is equivalent to time 0 of the following day, 1047 * it's not always possible to specify that, for example, on December 31. 1048 * While arguably the start range should still be 0..ONEDAY-1, we keep 1049 * the start and end ranges the same for consistency. 1050 */ 1051 private void decodeStartRule() { 1052 useDaylight = (startDay != 0) && (endDay != 0); 1053 if (useDaylight && dst == 0) { 1054 dst = Grego.MILLIS_PER_DAY; 1055 } 1056 if (startDay != 0) { 1057 if (startMonth < Calendar.JANUARY || startMonth > Calendar.DECEMBER) { 1058 throw new IllegalArgumentException(); 1059 } 1060 if (startTime < 0 || startTime > Grego.MILLIS_PER_DAY || 1061 startTimeMode < WALL_TIME || startTimeMode > UTC_TIME) { 1062 throw new IllegalArgumentException(); 1063 } 1064 if (startDayOfWeek == 0) { 1065 startMode = DOM_MODE; 1066 } else { 1067 if (startDayOfWeek > 0) { 1068 startMode = DOW_IN_MONTH_MODE; 1069 } else { 1070 startDayOfWeek = -startDayOfWeek; 1071 if (startDay > 0) { 1072 startMode = DOW_GE_DOM_MODE; 1073 } else { 1074 startDay = -startDay; 1075 startMode = DOW_LE_DOM_MODE; 1076 } 1077 } 1078 if (startDayOfWeek > Calendar.SATURDAY) { 1079 throw new IllegalArgumentException(); 1080 } 1081 } 1082 if (startMode == DOW_IN_MONTH_MODE) { 1083 if (startDay < -5 || startDay > 5) { 1084 throw new IllegalArgumentException(); 1085 } 1086 } else if (startDay < 1 || startDay > staticMonthLength[startMonth]) { 1087 throw new IllegalArgumentException(); 1088 } 1089 } 1090 } 1091 1092 /** 1093 * Decode the end rule and validate the parameters. This method is exactly 1094 * analogous to decodeStartRule(). 1095 * @see #decodeStartRule 1096 */ 1097 private void decodeEndRule() { 1098 useDaylight = (startDay != 0) && (endDay != 0); 1099 if (useDaylight && dst == 0) { 1100 dst = Grego.MILLIS_PER_DAY; 1101 } 1102 if (endDay != 0) { 1103 if (endMonth < Calendar.JANUARY || endMonth > Calendar.DECEMBER) { 1104 throw new IllegalArgumentException(); 1105 } 1106 if (endTime < 0 || endTime > Grego.MILLIS_PER_DAY || 1107 endTimeMode < WALL_TIME || endTimeMode > UTC_TIME) { 1108 throw new IllegalArgumentException(); 1109 } 1110 if (endDayOfWeek == 0) { 1111 endMode = DOM_MODE; 1112 } else { 1113 if (endDayOfWeek > 0) { 1114 endMode = DOW_IN_MONTH_MODE; 1115 } else { 1116 endDayOfWeek = -endDayOfWeek; 1117 if (endDay > 0) { 1118 endMode = DOW_GE_DOM_MODE; 1119 } else { 1120 endDay = -endDay; 1121 endMode = DOW_LE_DOM_MODE; 1122 } 1123 } 1124 if (endDayOfWeek > Calendar.SATURDAY) { 1125 throw new IllegalArgumentException(); 1126 } 1127 } 1128 if (endMode == DOW_IN_MONTH_MODE) { 1129 if (endDay < -5 || endDay > 5) { 1130 throw new IllegalArgumentException(); 1131 } 1132 } else if (endDay<1 || endDay > staticMonthLength[endMonth]) { 1133 throw new IllegalArgumentException(); 1134 } 1135 } 1136 } 1137 1138 /** 1139 * Overrides equals. 1140 * @return true if obj is a SimpleTimeZone equivalent to this 1141 * @stable ICU 3.6 1142 */ 1143 @Override 1144 public boolean equals(Object obj){ 1145 if (this == obj) return true; 1146 if (obj == null || getClass() != obj.getClass()) return false; 1147 SimpleTimeZone that = (SimpleTimeZone) obj; 1148 return raw == that.raw && 1149 useDaylight == that.useDaylight && 1150 idEquals(getID(),that.getID()) && 1151 (!useDaylight 1152 // Only check rules if using DST 1153 || (dst == that.dst && 1154 startMode == that.startMode && 1155 startMonth == that.startMonth && 1156 startDay == that.startDay && 1157 startDayOfWeek == that.startDayOfWeek && 1158 startTime == that.startTime && 1159 startTimeMode == that.startTimeMode && 1160 endMode == that.endMode && 1161 endMonth == that.endMonth && 1162 endDay == that.endDay && 1163 endDayOfWeek == that.endDayOfWeek && 1164 endTime == that.endTime && 1165 endTimeMode == that.endTimeMode && 1166 startYear == that.startYear )); 1167 1168 } 1169 private boolean idEquals(String id1, String id2){ 1170 if(id1==null && id2==null){ 1171 return true; 1172 } 1173 if(id1!=null && id2!=null){ 1174 return id1.equals(id2); 1175 } 1176 return false; 1177 } 1178 1179 /** 1180 * Overrides hashCode. 1181 * @stable ICU 3.6 1182 */ 1183 @Override 1184 public int hashCode(){ 1185 int ret = super.hashCode() 1186 + raw ^ (raw >>> 8) 1187 + (useDaylight ? 0 : 1); 1188 if(!useDaylight){ 1189 ret += dst ^ (dst >>> 10) + 1190 startMode ^ (startMode>>>11) + 1191 startMonth ^ (startMonth>>>12) + 1192 startDay ^ (startDay>>>13) + 1193 startDayOfWeek ^ (startDayOfWeek>>>14) + 1194 startTime ^ (startTime>>>15) + 1195 startTimeMode ^ (startTimeMode>>>16) + 1196 endMode ^ (endMode>>>17) + 1197 endMonth ^ (endMonth>>>18) + 1198 endDay ^ (endDay>>>19) + 1199 endDayOfWeek ^ (endDayOfWeek>>>20) + 1200 endTime ^ (endTime>>>21) + 1201 endTimeMode ^ (endTimeMode>>>22) + 1202 startYear ^ (startYear>>>23); 1203 } 1204 return ret; 1205 } 1206 1207 /** 1208 * Overrides clone. 1209 * @stable ICU 3.6 1210 */ 1211 @Override 1212 public Object clone() { 1213 if (isFrozen()) { 1214 return this; 1215 } 1216 return cloneAsThawed(); 1217 } 1218 1219 /** 1220 * Returns true if this zone has the same rules and offset as another zone. 1221 * @param othr the TimeZone object to be compared with 1222 * @return true if the given zone has the same rules and offset as this one 1223 * @stable ICU 2.0 1224 */ 1225 @Override 1226 public boolean hasSameRules(TimeZone othr) { 1227 if (this == othr) { 1228 return true; 1229 } 1230 if(!(othr instanceof SimpleTimeZone)){ 1231 return false; 1232 } 1233 SimpleTimeZone other = (SimpleTimeZone)othr; 1234 return other != null && 1235 raw == other.raw && 1236 useDaylight == other.useDaylight && 1237 (!useDaylight 1238 // Only check rules if using DST 1239 || (dst == other.dst && 1240 startMode == other.startMode && 1241 startMonth == other.startMonth && 1242 startDay == other.startDay && 1243 startDayOfWeek == other.startDayOfWeek && 1244 startTime == other.startTime && 1245 startTimeMode == other.startTimeMode && 1246 endMode == other.endMode && 1247 endMonth == other.endMonth && 1248 endDay == other.endDay && 1249 endDayOfWeek == other.endDayOfWeek && 1250 endTime == other.endTime && 1251 endTimeMode == other.endTimeMode && 1252 startYear == other.startYear)); 1253 } 1254 1255 // BasicTimeZone methods 1256 1257 /** 1258 * {@inheritDoc} 1259 * @stable ICU 3.8 1260 */ 1261 @Override 1262 public TimeZoneTransition getNextTransition(long base, boolean inclusive) { 1263 if (!useDaylight) { 1264 return null; 1265 } 1266 1267 initTransitionRules(); 1268 long firstTransitionTime = firstTransition.getTime(); 1269 if (base < firstTransitionTime || (inclusive && base == firstTransitionTime)) { 1270 return firstTransition; 1271 } 1272 Date stdDate = stdRule.getNextStart(base, dstRule.getRawOffset(), dstRule.getDSTSavings(), 1273 inclusive); 1274 Date dstDate = dstRule.getNextStart(base, stdRule.getRawOffset(), stdRule.getDSTSavings(), 1275 inclusive); 1276 if (stdDate != null && (dstDate == null || stdDate.before(dstDate))) { 1277 return new TimeZoneTransition(stdDate.getTime(), dstRule, stdRule); 1278 } 1279 if (dstDate != null && (stdDate == null || dstDate.before(stdDate))) { 1280 return new TimeZoneTransition(dstDate.getTime(), stdRule, dstRule); 1281 } 1282 return null; 1283 } 1284 1285 /** 1286 * {@inheritDoc} 1287 * @stable ICU 3.8 1288 */ 1289 @Override 1290 public TimeZoneTransition getPreviousTransition(long base, boolean inclusive) { 1291 if (!useDaylight) { 1292 return null; 1293 } 1294 1295 initTransitionRules(); 1296 long firstTransitionTime = firstTransition.getTime(); 1297 if (base < firstTransitionTime || (!inclusive && base == firstTransitionTime)) { 1298 return null; 1299 } 1300 Date stdDate = stdRule.getPreviousStart(base, dstRule.getRawOffset(), 1301 dstRule.getDSTSavings(), inclusive); 1302 Date dstDate = dstRule.getPreviousStart(base, stdRule.getRawOffset(), 1303 stdRule.getDSTSavings(), inclusive); 1304 if (stdDate != null && (dstDate == null || stdDate.after(dstDate))) { 1305 return new TimeZoneTransition(stdDate.getTime(), dstRule, stdRule); 1306 } 1307 if (dstDate != null && (stdDate == null || dstDate.after(stdDate))) { 1308 return new TimeZoneTransition(dstDate.getTime(), stdRule, dstRule); 1309 } 1310 return null; 1311 } 1312 1313 /** 1314 * {@inheritDoc} 1315 * @stable ICU 3.8 1316 */ 1317 @Override 1318 public TimeZoneRule[] getTimeZoneRules() { 1319 initTransitionRules(); 1320 1321 int size = useDaylight ? 3 : 1; 1322 TimeZoneRule[] rules = new TimeZoneRule[size]; 1323 rules[0] = initialRule; 1324 if (useDaylight) { 1325 rules[1] = stdRule; 1326 rules[2] = dstRule; 1327 } 1328 return rules; 1329 } 1330 1331 private transient boolean transitionRulesInitialized; 1332 private transient InitialTimeZoneRule initialRule; 1333 private transient TimeZoneTransition firstTransition; 1334 private transient AnnualTimeZoneRule stdRule; 1335 private transient AnnualTimeZoneRule dstRule; 1336 1337 private synchronized void initTransitionRules() { 1338 if (transitionRulesInitialized) { 1339 return; 1340 } 1341 if (useDaylight) { 1342 DateTimeRule dtRule = null; 1343 int timeRuleType; 1344 long firstStdStart, firstDstStart; 1345 1346 // Create a TimeZoneRule for daylight saving time 1347 timeRuleType = (startTimeMode == STANDARD_TIME) ? DateTimeRule.STANDARD_TIME : 1348 ((startTimeMode == UTC_TIME) ? DateTimeRule.UTC_TIME : DateTimeRule.WALL_TIME); 1349 switch (startMode) { 1350 case DOM_MODE: 1351 dtRule = new DateTimeRule(startMonth, startDay, startTime, timeRuleType); 1352 break; 1353 case DOW_IN_MONTH_MODE: 1354 dtRule = new DateTimeRule(startMonth, startDay, startDayOfWeek, startTime, 1355 timeRuleType); 1356 break; 1357 case DOW_GE_DOM_MODE: 1358 dtRule = new DateTimeRule(startMonth, startDay, startDayOfWeek, true, startTime, 1359 timeRuleType); 1360 break; 1361 case DOW_LE_DOM_MODE: 1362 dtRule = new DateTimeRule(startMonth, startDay, startDayOfWeek, false, startTime, 1363 timeRuleType); 1364 break; 1365 } 1366 // For now, use ID + "(DST)" as the name 1367 dstRule = new AnnualTimeZoneRule(getID() + "(DST)", getRawOffset(), getDSTSavings(), 1368 dtRule, startYear, AnnualTimeZoneRule.MAX_YEAR); 1369 1370 // Calculate the first DST start time 1371 firstDstStart = dstRule.getFirstStart(getRawOffset(), 0).getTime(); 1372 1373 // Create a TimeZoneRule for standard time 1374 timeRuleType = (endTimeMode == STANDARD_TIME) ? DateTimeRule.STANDARD_TIME : 1375 ((endTimeMode == UTC_TIME) ? DateTimeRule.UTC_TIME : DateTimeRule.WALL_TIME); 1376 switch (endMode) { 1377 case DOM_MODE: 1378 dtRule = new DateTimeRule(endMonth, endDay, endTime, timeRuleType); 1379 break; 1380 case DOW_IN_MONTH_MODE: 1381 dtRule = new DateTimeRule(endMonth, endDay, endDayOfWeek, endTime, timeRuleType); 1382 break; 1383 case DOW_GE_DOM_MODE: 1384 dtRule = new DateTimeRule(endMonth, endDay, endDayOfWeek, true, endTime, 1385 timeRuleType); 1386 break; 1387 case DOW_LE_DOM_MODE: 1388 dtRule = new DateTimeRule(endMonth, endDay, endDayOfWeek, false, endTime, 1389 timeRuleType); 1390 break; 1391 } 1392 // For now, use ID + "(STD)" as the name 1393 stdRule = new AnnualTimeZoneRule(getID() + "(STD)", getRawOffset(), 0, 1394 dtRule, startYear, AnnualTimeZoneRule.MAX_YEAR); 1395 1396 // Calculate the first STD start time 1397 firstStdStart = stdRule.getFirstStart(getRawOffset(), dstRule.getDSTSavings()).getTime(); 1398 1399 // Create a TimeZoneRule for initial time 1400 if (firstStdStart < firstDstStart) { 1401 initialRule = new InitialTimeZoneRule(getID() + "(DST)", getRawOffset(), 1402 dstRule.getDSTSavings()); 1403 firstTransition = new TimeZoneTransition(firstStdStart, initialRule, stdRule); 1404 } else { 1405 initialRule = new InitialTimeZoneRule(getID() + "(STD)", getRawOffset(), 0); 1406 firstTransition = new TimeZoneTransition(firstDstStart, initialRule, dstRule); 1407 } 1408 1409 } else { 1410 // Create a TimeZoneRule for initial time 1411 initialRule = new InitialTimeZoneRule(getID(), getRawOffset(), 0); 1412 } 1413 transitionRulesInitialized = true; 1414 } 1415 1416 // Freezable stuffs 1417 private volatile transient boolean isFrozen = false; 1418 1419 /** 1420 * {@inheritDoc} 1421 * @stable ICU 49 1422 */ 1423 @Override 1424 public boolean isFrozen() { 1425 return isFrozen; 1426 } 1427 1428 /** 1429 * {@inheritDoc} 1430 * @stable ICU 49 1431 */ 1432 @Override 1433 public TimeZone freeze() { 1434 isFrozen = true; 1435 return this; 1436 } 1437 1438 /** 1439 * {@inheritDoc} 1440 * @stable ICU 49 1441 */ 1442 @Override 1443 public TimeZone cloneAsThawed() { 1444 SimpleTimeZone tz = (SimpleTimeZone)super.cloneAsThawed(); 1445 tz.isFrozen = false; 1446 return tz; 1447 } 1448} 1449