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 package java.util; 19 20 import java.io.IOException; 21 import java.io.ObjectInputStream; 22 import java.io.ObjectOutputStream; 23 24 /** 25 * {@code GregorianCalendar} is a concrete subclass of {@link Calendar} 26 * and provides the standard calendar used by most of the world. 27 * 28 * <p> 29 * The standard (Gregorian) calendar has 2 eras, BC and AD. 30 * 31 * <p> 32 * This implementation handles a single discontinuity, which corresponds by 33 * default to the date the Gregorian calendar was instituted (October 15, 1582 34 * in some countries, later in others). The cutover date may be changed by the 35 * caller by calling {@code setGregorianChange()}. 36 * 37 * <p> 38 * Historically, in those countries which adopted the Gregorian calendar first, 39 * October 4, 1582 was thus followed by October 15, 1582. This calendar models 40 * this correctly. Before the Gregorian cutover, {@code GregorianCalendar} 41 * implements the Julian calendar. The only difference between the Gregorian and 42 * the Julian calendar is the leap year rule. The Julian calendar specifies leap 43 * years every four years, whereas the Gregorian calendar omits century years 44 * which are not divisible by 400. 45 * 46 * <p> 47 * {@code GregorianCalendar} implements <em>proleptic</em> Gregorian 48 * and Julian calendars. That is, dates are computed by extrapolating the 49 * current rules indefinitely far backward and forward in time. As a result, 50 * {@code GregorianCalendar} may be used for all years to generate 51 * meaningful and consistent results. However, dates obtained using 52 * {@code GregorianCalendar} are historically accurate only from March 1, 53 * 4 AD onward, when modern Julian calendar rules were adopted. Before this 54 * date, leap year rules were applied irregularly, and before 45 BC the Julian 55 * calendar did not even exist. 56 * 57 * <p> 58 * Prior to the institution of the Gregorian calendar, New Year's Day was March 59 * 25. To avoid confusion, this calendar always uses January 1. A manual 60 * adjustment may be made if desired for dates that are prior to the Gregorian 61 * changeover and which fall between January 1 and March 24. 62 * 63 * <p> 64 * Values calculated for the {@code WEEK_OF_YEAR} field range from 1 to 65 * 53. Week 1 for a year is the earliest seven day period starting on 66 * {@code getFirstDayOfWeek()} that contains at least 67 * {@code getMinimalDaysInFirstWeek()} days from that year. It thus 68 * depends on the values of {@code getMinimalDaysInFirstWeek()}, 69 * {@code getFirstDayOfWeek()}, and the day of the week of January 1. 70 * Weeks between week 1 of one year and week 1 of the following year are 71 * numbered sequentially from 2 to 52 or 53 (as needed). 72 * 73 * <p> 74 * For example, January 1, 1998 was a Thursday. If 75 * {@code getFirstDayOfWeek()} is {@code MONDAY} and 76 * {@code getMinimalDaysInFirstWeek()} is 4 (these are the values 77 * reflecting ISO 8601 and many national standards), then week 1 of 1998 starts 78 * on December 29, 1997, and ends on January 4, 1998. If, however, 79 * {@code getFirstDayOfWeek()} is {@code SUNDAY}, then week 1 of 80 * 1998 starts on January 4, 1998, and ends on January 10, 1998; the first three 81 * days of 1998 then are part of week 53 of 1997. 82 * 83 * <p> 84 * Values calculated for the {@code WEEK_OF_MONTH} field range from 0 or 85 * 1 to 4 or 5. Week 1 of a month (the days with <code>WEEK_OF_MONTH = 86 * 1</code>) 87 * is the earliest set of at least {@code getMinimalDaysInFirstWeek()} 88 * contiguous days in that month, ending on the day before 89 * {@code getFirstDayOfWeek()}. Unlike week 1 of a year, week 1 of a 90 * month may be shorter than 7 days, need not start on 91 * {@code getFirstDayOfWeek()}, and will not include days of the 92 * previous month. Days of a month before week 1 have a 93 * {@code WEEK_OF_MONTH} of 0. 94 * 95 * <p> 96 * For example, if {@code getFirstDayOfWeek()} is {@code SUNDAY} 97 * and {@code getMinimalDaysInFirstWeek()} is 4, then the first week of 98 * January 1998 is Sunday, January 4 through Saturday, January 10. These days 99 * have a {@code WEEK_OF_MONTH} of 1. Thursday, January 1 through 100 * Saturday, January 3 have a {@code WEEK_OF_MONTH} of 0. If 101 * {@code getMinimalDaysInFirstWeek()} is changed to 3, then January 1 102 * through January 3 have a {@code WEEK_OF_MONTH} of 1. 103 * 104 * <p> 105 * <strong>Example:</strong> <blockquote> 106 * 107 * <pre> 108 * // get the supported ids for GMT-08:00 (Pacific Standard Time) 109 * String[] ids = TimeZone.getAvailableIDs(-8 * 60 * 60 * 1000); 110 * // if no ids were returned, something is wrong. get out. 111 * if (ids.length == 0) 112 * System.exit(0); 113 * 114 * // begin output 115 * System.out.println("Current Time"); 116 * 117 * // create a Pacific Standard Time time zone 118 * SimpleTimeZone pdt = new SimpleTimeZone(-8 * 60 * 60 * 1000, ids[0]); 119 * 120 * // set up rules for daylight savings time 121 * pdt.setStartRule(Calendar.APRIL, 1, Calendar.SUNDAY, 2 * 60 * 60 * 1000); 122 * pdt.setEndRule(Calendar.OCTOBER, -1, Calendar.SUNDAY, 2 * 60 * 60 * 1000); 123 * 124 * // create a GregorianCalendar with the Pacific Daylight time zone 125 * // and the current date and time 126 * Calendar calendar = new GregorianCalendar(pdt); 127 * Date trialTime = new Date(); 128 * calendar.setTime(trialTime); 129 * 130 * // print out a bunch of interesting things 131 * System.out.println("ERA: " + calendar.get(Calendar.ERA)); 132 * System.out.println("YEAR: " + calendar.get(Calendar.YEAR)); 133 * System.out.println("MONTH: " + calendar.get(Calendar.MONTH)); 134 * System.out.println("WEEK_OF_YEAR: " + calendar.get(Calendar.WEEK_OF_YEAR)); 135 * System.out.println("WEEK_OF_MONTH: " + calendar.get(Calendar.WEEK_OF_MONTH)); 136 * System.out.println("DATE: " + calendar.get(Calendar.DATE)); 137 * System.out.println("DAY_OF_MONTH: " + calendar.get(Calendar.DAY_OF_MONTH)); 138 * System.out.println("DAY_OF_YEAR: " + calendar.get(Calendar.DAY_OF_YEAR)); 139 * System.out.println("DAY_OF_WEEK: " + calendar.get(Calendar.DAY_OF_WEEK)); 140 * System.out.println("DAY_OF_WEEK_IN_MONTH: " 141 * + calendar.get(Calendar.DAY_OF_WEEK_IN_MONTH)); 142 * System.out.println("AM_PM: " + calendar.get(Calendar.AM_PM)); 143 * System.out.println("HOUR: " + calendar.get(Calendar.HOUR)); 144 * System.out.println("HOUR_OF_DAY: " + calendar.get(Calendar.HOUR_OF_DAY)); 145 * System.out.println("MINUTE: " + calendar.get(Calendar.MINUTE)); 146 * System.out.println("SECOND: " + calendar.get(Calendar.SECOND)); 147 * System.out.println("MILLISECOND: " + calendar.get(Calendar.MILLISECOND)); 148 * System.out.println("ZONE_OFFSET: " 149 * + (calendar.get(Calendar.ZONE_OFFSET)/(60*60*1000))); 150 * System.out.println("DST_OFFSET: " 151 * + (calendar.get(Calendar.DST_OFFSET)/(60*60*1000))); 152 153 * System.out.println("Current Time, with hour reset to 3"); 154 * calendar.clear(Calendar.HOUR_OF_DAY); // so doesn't override 155 * calendar.set(Calendar.HOUR, 3); 156 * System.out.println("ERA: " + calendar.get(Calendar.ERA)); 157 * System.out.println("YEAR: " + calendar.get(Calendar.YEAR)); 158 * System.out.println("MONTH: " + calendar.get(Calendar.MONTH)); 159 * System.out.println("WEEK_OF_YEAR: " + calendar.get(Calendar.WEEK_OF_YEAR)); 160 * System.out.println("WEEK_OF_MONTH: " + calendar.get(Calendar.WEEK_OF_MONTH)); 161 * System.out.println("DATE: " + calendar.get(Calendar.DATE)); 162 * System.out.println("DAY_OF_MONTH: " + calendar.get(Calendar.DAY_OF_MONTH)); 163 * System.out.println("DAY_OF_YEAR: " + calendar.get(Calendar.DAY_OF_YEAR)); 164 * System.out.println("DAY_OF_WEEK: " + calendar.get(Calendar.DAY_OF_WEEK)); 165 * System.out.println("DAY_OF_WEEK_IN_MONTH: " 166 * + calendar.get(Calendar.DAY_OF_WEEK_IN_MONTH)); 167 * System.out.println("AM_PM: " + calendar.get(Calendar.AM_PM)); 168 * System.out.println("HOUR: " + calendar.get(Calendar.HOUR)); 169 * System.out.println("HOUR_OF_DAY: " + calendar.get(Calendar.HOUR_OF_DAY)); 170 * System.out.println("MINUTE: " + calendar.get(Calendar.MINUTE)); 171 * System.out.println("SECOND: " + calendar.get(Calendar.SECOND)); 172 * System.out.println("MILLISECOND: " + calendar.get(Calendar.MILLISECOND)); 173 * System.out.println("ZONE_OFFSET: " 174 * + (calendar.get(Calendar.ZONE_OFFSET)/(60*60*1000))); // in hours 175 * System.out.println("DST_OFFSET: " 176 * + (calendar.get(Calendar.DST_OFFSET)/(60*60*1000))); // in hours 177 * </pre> 178 * 179 * </blockquote> 180 * 181 * @see Calendar 182 * @see TimeZone 183 */ 184 public class GregorianCalendar extends Calendar { 185 186 private static final long serialVersionUID = -8125100834729963327L; 187 188 /** 189 * Value for the BC era. 190 */ 191 public static final int BC = 0; 192 193 /** 194 * Value for the AD era. 195 */ 196 public static final int AD = 1; 197 198 private static final long defaultGregorianCutover = -12219292800000l; 199 200 private long gregorianCutover = defaultGregorianCutover; 201 202 private transient int changeYear = 1582; 203 204 private transient int julianSkew = ((changeYear - 2000) / 400) 205 + julianError() - ((changeYear - 2000) / 100); 206 207 static byte[] DaysInMonth = new byte[] { 31, 28, 31, 30, 31, 30, 31, 31, 208 30, 31, 30, 31 }; 209 210 private static int[] DaysInYear = new int[] { 0, 31, 59, 90, 120, 151, 181, 211 212, 243, 273, 304, 334 }; 212 213 private static int[] maximums = new int[] { 1, 292278994, 11, 53, 6, 31, 214 366, 7, 6, 1, 11, 23, 59, 59, 999, 14 * 3600 * 1000, 7200000 }; 215 216 private static int[] minimums = new int[] { 0, 1, 0, 1, 0, 1, 1, 1, 1, 0, 217 0, 0, 0, 0, 0, -13 * 3600 * 1000, 0 }; 218 219 private static int[] leastMaximums = new int[] { 1, 292269054, 11, 50, 3, 220 28, 355, 7, 3, 1, 11, 23, 59, 59, 999, 50400000, 1200000 }; 221 222 private int currentYearSkew = 10; 223 224 private int lastYearSkew = 0; 225 226 /** 227 * Constructs a new {@code GregorianCalendar} initialized to the current date and 228 * time with the default {@code Locale} and {@code TimeZone}. 229 */ GregorianCalendar()230 public GregorianCalendar() { 231 this(TimeZone.getDefault(), Locale.getDefault()); 232 } 233 234 /** 235 * Constructs a new {@code GregorianCalendar} initialized to midnight in the default 236 * {@code TimeZone} and {@code Locale} on the specified date. 237 * 238 * @param year 239 * the year. 240 * @param month 241 * the month. 242 * @param day 243 * the day of the month. 244 */ GregorianCalendar(int year, int month, int day)245 public GregorianCalendar(int year, int month, int day) { 246 super(TimeZone.getDefault(), Locale.getDefault()); 247 set(year, month, day); 248 } 249 250 /** 251 * Constructs a new {@code GregorianCalendar} initialized to the specified date and 252 * time in the default {@code TimeZone} and {@code Locale}. 253 * 254 * @param year 255 * the year. 256 * @param month 257 * the month. 258 * @param day 259 * the day of the month. 260 * @param hour 261 * the hour. 262 * @param minute 263 * the minute. 264 */ GregorianCalendar(int year, int month, int day, int hour, int minute)265 public GregorianCalendar(int year, int month, int day, int hour, int minute) { 266 super(TimeZone.getDefault(), Locale.getDefault()); 267 set(year, month, day, hour, minute); 268 } 269 270 /** 271 * Constructs a new {@code GregorianCalendar} initialized to the specified date and 272 * time in the default {@code TimeZone} and {@code Locale}. 273 * 274 * @param year 275 * the year. 276 * @param month 277 * the month. 278 * @param day 279 * the day of the month. 280 * @param hour 281 * the hour. 282 * @param minute 283 * the minute. 284 * @param second 285 * the second. 286 */ GregorianCalendar(int year, int month, int day, int hour, int minute, int second)287 public GregorianCalendar(int year, int month, int day, int hour, 288 int minute, int second) { 289 super(TimeZone.getDefault(), Locale.getDefault()); 290 set(year, month, day, hour, minute, second); 291 } 292 GregorianCalendar(long milliseconds)293 GregorianCalendar(long milliseconds) { 294 this(false); 295 setTimeInMillis(milliseconds); 296 } 297 298 /** 299 * Constructs a new {@code GregorianCalendar} initialized to the current date and 300 * time and using the specified {@code Locale} and the default {@code TimeZone}. 301 * 302 * @param locale 303 * the {@code Locale}. 304 */ GregorianCalendar(Locale locale)305 public GregorianCalendar(Locale locale) { 306 this(TimeZone.getDefault(), locale); 307 } 308 309 /** 310 * Constructs a new {@code GregorianCalendar} initialized to the current date and 311 * time and using the specified {@code TimeZone} and the default {@code Locale}. 312 * 313 * @param timezone 314 * the {@code TimeZone}. 315 */ GregorianCalendar(TimeZone timezone)316 public GregorianCalendar(TimeZone timezone) { 317 this(timezone, Locale.getDefault()); 318 } 319 320 /** 321 * Constructs a new {@code GregorianCalendar} initialized to the current date and 322 * time and using the specified {@code TimeZone} and {@code Locale}. 323 * 324 * @param timezone 325 * the {@code TimeZone}. 326 * @param locale 327 * the {@code Locale}. 328 */ GregorianCalendar(TimeZone timezone, Locale locale)329 public GregorianCalendar(TimeZone timezone, Locale locale) { 330 super(timezone, locale); 331 setTimeInMillis(System.currentTimeMillis()); 332 } 333 334 /** 335 * A minimum-cost constructor that does not initialize the current time or perform any date 336 * calculations. For use internally when the time will be set later. Other constructors, such as 337 * {@link GregorianCalendar#GregorianCalendar()}, set the time to the current system clock 338 * and recalculate the fields incurring unnecessary cost when the time or fields will be set 339 * later. 340 * 341 * @hide used internally 342 */ GregorianCalendar(boolean ignored)343 public GregorianCalendar(boolean ignored) { 344 super(TimeZone.getDefault()); 345 setFirstDayOfWeek(SUNDAY); 346 setMinimalDaysInFirstWeek(1); 347 } 348 349 /** 350 * Adds the specified amount to a {@code Calendar} field. 351 * 352 * @param field 353 * the {@code Calendar} field to modify. 354 * @param value 355 * the amount to add to the field. 356 * 357 * @throws IllegalArgumentException 358 * if the specified field is DST_OFFSET or ZONE_OFFSET. 359 */ 360 @Override add(int field, int value)361 public void add(int field, int value) { 362 if (value == 0) { 363 return; 364 } 365 if (field < 0 || field >= ZONE_OFFSET) { 366 throw new IllegalArgumentException(); 367 } 368 369 if (field == ERA) { 370 complete(); 371 if (fields[ERA] == AD) { 372 if (value >= 0) { 373 return; 374 } 375 set(ERA, BC); 376 } else { 377 if (value <= 0) { 378 return; 379 } 380 set(ERA, AD); 381 } 382 complete(); 383 return; 384 } 385 386 if (field == YEAR || field == MONTH) { 387 complete(); 388 if (field == MONTH) { 389 int month = fields[MONTH] + value; 390 if (month < 0) { 391 value = (month - 11) / 12; 392 month = 12 + (month % 12); 393 } else { 394 value = month / 12; 395 } 396 set(MONTH, month % 12); 397 } 398 set(YEAR, fields[YEAR] + value); 399 int days = daysInMonth(isLeapYear(fields[YEAR]), fields[MONTH]); 400 if (fields[DATE] > days) { 401 set(DATE, days); 402 } 403 complete(); 404 return; 405 } 406 407 long multiplier = 0; 408 getTimeInMillis(); // Update the time 409 switch (field) { 410 case MILLISECOND: 411 time += value; 412 break; 413 case SECOND: 414 time += value * 1000L; 415 break; 416 case MINUTE: 417 time += value * 60000L; 418 break; 419 case HOUR: 420 case HOUR_OF_DAY: 421 time += value * 3600000L; 422 break; 423 case AM_PM: 424 multiplier = 43200000L; 425 break; 426 case DATE: 427 case DAY_OF_YEAR: 428 case DAY_OF_WEEK: 429 multiplier = 86400000L; 430 break; 431 case WEEK_OF_YEAR: 432 case WEEK_OF_MONTH: 433 case DAY_OF_WEEK_IN_MONTH: 434 multiplier = 604800000L; 435 break; 436 } 437 438 if (multiplier == 0) { 439 areFieldsSet = false; 440 complete(); 441 return; 442 } 443 444 long delta = value * multiplier; 445 446 /* 447 * Attempt to keep the hour and minute constant when we've crossed a DST 448 * boundary and the user's units are AM_PM or larger. The typical 449 * consequence is that calls to add(DATE, 1) will add 23, 24 or 25 hours 450 * depending on whether the DST goes forward, constant, or backward. 451 * 452 * We know we've crossed a DST boundary if the new time will have a 453 * different timezone offset. Adjust by adding the difference of the two 454 * offsets. We don't adjust when doing so prevents the change from 455 * crossing the boundary. 456 */ 457 int zoneOffset = getTimeZone().getRawOffset(); 458 int offsetBefore = getOffset(time + zoneOffset); 459 int offsetAfter = getOffset(time + zoneOffset + delta); 460 int dstDelta = offsetBefore - offsetAfter; 461 if (getOffset(time + zoneOffset + delta + dstDelta) == offsetAfter) { 462 delta += dstDelta; 463 } 464 465 time += delta; 466 areFieldsSet = false; 467 complete(); 468 } 469 fullFieldsCalc()470 private void fullFieldsCalc() { 471 int millis = (int) (time % 86400000); 472 long days = time / 86400000; 473 474 if (millis < 0) { 475 millis += 86400000; 476 days--; 477 } 478 // Adding fields[ZONE_OFFSET] to time might make it overflow, so we add 479 // it to millis (the number of milliseconds in the current day) instead. 480 millis += fields[ZONE_OFFSET]; 481 while (millis < 0) { 482 millis += 86400000; 483 days--; 484 } 485 while (millis >= 86400000) { 486 millis -= 86400000; 487 days++; 488 } 489 490 int dayOfYear = computeYearAndDay(days, time + fields[ZONE_OFFSET]); 491 fields[DAY_OF_YEAR] = dayOfYear; 492 if (fields[YEAR] == changeYear && gregorianCutover <= time + fields[ZONE_OFFSET]){ 493 dayOfYear += currentYearSkew; 494 } 495 int month = dayOfYear / 32; 496 boolean leapYear = isLeapYear(fields[YEAR]); 497 int date = dayOfYear - daysInYear(leapYear, month); 498 if (date > daysInMonth(leapYear, month)) { 499 date -= daysInMonth(leapYear, month); 500 month++; 501 } 502 fields[DAY_OF_WEEK] = mod7(days - 3) + 1; 503 int dstOffset = fields[YEAR] <= 0 ? 0 : getTimeZone().getOffset(AD, 504 fields[YEAR], month, date, fields[DAY_OF_WEEK], millis); 505 if (fields[YEAR] > 0) { 506 dstOffset -= fields[ZONE_OFFSET]; 507 } 508 fields[DST_OFFSET] = dstOffset; 509 if (dstOffset != 0) { 510 long oldDays = days; 511 millis += dstOffset; 512 if (millis < 0) { 513 millis += 86400000; 514 days--; 515 } else if (millis >= 86400000) { 516 millis -= 86400000; 517 days++; 518 } 519 if (oldDays != days) { 520 dayOfYear = computeYearAndDay(days, time - fields[ZONE_OFFSET] 521 + dstOffset); 522 fields[DAY_OF_YEAR] = dayOfYear; 523 if(fields[YEAR] == changeYear && gregorianCutover <= time - fields[ZONE_OFFSET] + dstOffset){ 524 dayOfYear += currentYearSkew; 525 } 526 month = dayOfYear / 32; 527 leapYear = isLeapYear(fields[YEAR]); 528 date = dayOfYear - daysInYear(leapYear, month); 529 if (date > daysInMonth(leapYear, month)) { 530 date -= daysInMonth(leapYear, month); 531 month++; 532 } 533 fields[DAY_OF_WEEK] = mod7(days - 3) + 1; 534 } 535 } 536 537 fields[MILLISECOND] = (millis % 1000); 538 millis /= 1000; 539 fields[SECOND] = (millis % 60); 540 millis /= 60; 541 fields[MINUTE] = (millis % 60); 542 millis /= 60; 543 fields[HOUR_OF_DAY] = (millis % 24); 544 fields[AM_PM] = fields[HOUR_OF_DAY] > 11 ? 1 : 0; 545 fields[HOUR] = fields[HOUR_OF_DAY] % 12; 546 547 if (fields[YEAR] <= 0) { 548 fields[ERA] = BC; 549 fields[YEAR] = -fields[YEAR] + 1; 550 } else { 551 fields[ERA] = AD; 552 } 553 fields[MONTH] = month; 554 fields[DATE] = date; 555 fields[DAY_OF_WEEK_IN_MONTH] = (date - 1) / 7 + 1; 556 fields[WEEK_OF_MONTH] = (date - 1 + mod7(days - date - 2 557 - (getFirstDayOfWeek() - 1))) / 7 + 1; 558 int daysFromStart = mod7(days - 3 - (fields[DAY_OF_YEAR] - 1) 559 - (getFirstDayOfWeek() - 1)); 560 int week = (fields[DAY_OF_YEAR] - 1 + daysFromStart) / 7 561 + (7 - daysFromStart >= getMinimalDaysInFirstWeek() ? 1 : 0); 562 if (week == 0) { 563 fields[WEEK_OF_YEAR] = 7 - mod7(daysFromStart 564 - (isLeapYear(fields[YEAR] - 1) ? 2 : 1)) >= getMinimalDaysInFirstWeek() ? 53 565 : 52; 566 } else if (fields[DAY_OF_YEAR] >= (leapYear ? 367 : 366) 567 - mod7(daysFromStart + (leapYear ? 2 : 1))) { 568 fields[WEEK_OF_YEAR] = 7 - mod7(daysFromStart + (leapYear ? 2 : 1)) >= getMinimalDaysInFirstWeek() ? 1 569 : week; 570 } else { 571 fields[WEEK_OF_YEAR] = week; 572 } 573 } 574 575 @Override computeFields()576 protected void computeFields() { 577 TimeZone timeZone = getTimeZone(); 578 int dstOffset = timeZone.inDaylightTime(new Date(time)) ? timeZone.getDSTSavings() : 0; 579 int zoneOffset = timeZone.getRawOffset(); 580 581 // We unconditionally overwrite DST_OFFSET and ZONE_OFFSET with 582 // values from the timezone that's currently in use. This gives us 583 // much more consistent behavior, and matches ICU4J behavior (though 584 // it is inconsistent with the RI). 585 // 586 // Anything callers can do with ZONE_OFFSET they can do by constructing 587 // a SimpleTimeZone with the required offset. 588 // 589 // DST_OFFSET is a bit of a WTF, given that it's dependent on the rest 590 // of the fields. There's no sensible reason we'd want to allow it to 591 // be set, nor can we implement consistent full-fields calculation after 592 // this field is set without maintaining a large deal of additional state. 593 // 594 // At the very least, we will need isSet to differentiate between fields 595 // set by the user and fields set by our internal field calculation. 596 fields[DST_OFFSET] = dstOffset; 597 fields[ZONE_OFFSET] = zoneOffset; 598 599 fullFieldsCalc(); 600 601 for (int i = 0; i < FIELD_COUNT; i++) { 602 isSet[i] = true; 603 } 604 } 605 606 @Override computeTime()607 protected void computeTime() { 608 if (!isLenient()) { 609 if (isSet[HOUR_OF_DAY]) { 610 if (fields[HOUR_OF_DAY] < 0 || fields[HOUR_OF_DAY] > 23) { 611 throw new IllegalArgumentException(); 612 } 613 } else if (isSet[HOUR] && (fields[HOUR] < 0 || fields[HOUR] > 11)) { 614 throw new IllegalArgumentException(); 615 } 616 if (isSet[MINUTE] && (fields[MINUTE] < 0 || fields[MINUTE] > 59)) { 617 throw new IllegalArgumentException(); 618 } 619 if (isSet[SECOND] && (fields[SECOND] < 0 || fields[SECOND] > 59)) { 620 throw new IllegalArgumentException(); 621 } 622 if (isSet[MILLISECOND] 623 && (fields[MILLISECOND] < 0 || fields[MILLISECOND] > 999)) { 624 throw new IllegalArgumentException(); 625 } 626 if (isSet[WEEK_OF_YEAR] 627 && (fields[WEEK_OF_YEAR] < 1 || fields[WEEK_OF_YEAR] > 53)) { 628 throw new IllegalArgumentException(); 629 } 630 if (isSet[DAY_OF_WEEK] 631 && (fields[DAY_OF_WEEK] < 1 || fields[DAY_OF_WEEK] > 7)) { 632 throw new IllegalArgumentException(); 633 } 634 if (isSet[DAY_OF_WEEK_IN_MONTH] 635 && (fields[DAY_OF_WEEK_IN_MONTH] < 1 || fields[DAY_OF_WEEK_IN_MONTH] > 6)) { 636 throw new IllegalArgumentException(); 637 } 638 if (isSet[WEEK_OF_MONTH] 639 && (fields[WEEK_OF_MONTH] < 1 || fields[WEEK_OF_MONTH] > 6)) { 640 throw new IllegalArgumentException(); 641 } 642 if (isSet[AM_PM] && fields[AM_PM] != AM && fields[AM_PM] != PM) { 643 throw new IllegalArgumentException(); 644 } 645 if (isSet[HOUR] && (fields[HOUR] < 0 || fields[HOUR] > 11)) { 646 throw new IllegalArgumentException(); 647 } 648 if (isSet[YEAR]) { 649 if (isSet[ERA] && fields[ERA] == BC 650 && (fields[YEAR] < 1 || fields[YEAR] > 292269054)) { 651 throw new IllegalArgumentException(); 652 } else if (fields[YEAR] < 1 || fields[YEAR] > 292278994) { 653 throw new IllegalArgumentException(); 654 } 655 } 656 if (isSet[MONTH] && (fields[MONTH] < 0 || fields[MONTH] > 11)) { 657 throw new IllegalArgumentException(); 658 } 659 } 660 661 long timeVal; 662 long hour = 0; 663 if (isSet[HOUR_OF_DAY] && lastTimeFieldSet != HOUR) { 664 hour = fields[HOUR_OF_DAY]; 665 } else if (isSet[HOUR]) { 666 hour = (fields[AM_PM] * 12) + fields[HOUR]; 667 } 668 timeVal = hour * 3600000; 669 670 if (isSet[MINUTE]) { 671 timeVal += ((long) fields[MINUTE]) * 60000; 672 } 673 if (isSet[SECOND]) { 674 timeVal += ((long) fields[SECOND]) * 1000; 675 } 676 if (isSet[MILLISECOND]) { 677 timeVal += fields[MILLISECOND]; 678 } 679 680 long days; 681 int year = isSet[YEAR] ? fields[YEAR] : 1970; 682 if (isSet[ERA]) { 683 // Always test for valid ERA, even if the Calendar is lenient 684 if (fields[ERA] != BC && fields[ERA] != AD) { 685 throw new IllegalArgumentException(); 686 } 687 if (fields[ERA] == BC) { 688 year = 1 - year; 689 } 690 } 691 692 boolean weekMonthSet = isSet[WEEK_OF_MONTH] 693 || isSet[DAY_OF_WEEK_IN_MONTH]; 694 boolean useMonth = (isSet[DATE] || isSet[MONTH] || weekMonthSet) 695 && lastDateFieldSet != DAY_OF_YEAR; 696 if (useMonth 697 && (lastDateFieldSet == DAY_OF_WEEK || lastDateFieldSet == WEEK_OF_YEAR)) { 698 if (isSet[WEEK_OF_YEAR] && isSet[DAY_OF_WEEK]) { 699 if (lastDateFieldSet == WEEK_OF_YEAR) { 700 useMonth = false; 701 } else if (lastDateFieldSet == DAY_OF_WEEK) { 702 // DAY_OF_WEEK belongs to both the Month + Week + Day and the 703 // WeekOfYear + Day combinations. We're supposed to use the most 704 // recent combination, as specified by the single set field. We can't 705 // know for sure in this case, so we always prefer the week-month-day 706 // combination if week-month is already set. 707 useMonth = weekMonthSet; 708 } 709 } else if (isSet[DAY_OF_YEAR]) { 710 useMonth = isSet[DATE] && isSet[MONTH]; 711 } 712 } 713 714 if (useMonth) { 715 int month = fields[MONTH]; 716 year += month / 12; 717 month %= 12; 718 if (month < 0) { 719 year--; 720 month += 12; 721 } 722 boolean leapYear = isLeapYear(year); 723 days = daysFromBaseYear(year) + daysInYear(leapYear, month); 724 boolean useDate = isSet[DATE]; 725 if (useDate 726 && (lastDateFieldSet == DAY_OF_WEEK 727 || lastDateFieldSet == WEEK_OF_MONTH || lastDateFieldSet == DAY_OF_WEEK_IN_MONTH)) { 728 useDate = !(isSet[DAY_OF_WEEK] && weekMonthSet); 729 } 730 if (useDate) { 731 if (!isLenient() 732 && (fields[DATE] < 1 || fields[DATE] > daysInMonth( 733 leapYear, month))) { 734 throw new IllegalArgumentException(); 735 } 736 days += fields[DATE] - 1; 737 } else { 738 int dayOfWeek; 739 if (isSet[DAY_OF_WEEK]) { 740 dayOfWeek = fields[DAY_OF_WEEK] - 1; 741 } else { 742 dayOfWeek = getFirstDayOfWeek() - 1; 743 } 744 if (isSet[WEEK_OF_MONTH] 745 && lastDateFieldSet != DAY_OF_WEEK_IN_MONTH) { 746 int skew = mod7(days - 3 - (getFirstDayOfWeek() - 1)); 747 days += (fields[WEEK_OF_MONTH] - 1) * 7 748 + mod7(skew + dayOfWeek - (days - 3)) - skew; 749 } else if (isSet[DAY_OF_WEEK_IN_MONTH]) { 750 if (fields[DAY_OF_WEEK_IN_MONTH] >= 0) { 751 days += mod7(dayOfWeek - (days - 3)) 752 + (fields[DAY_OF_WEEK_IN_MONTH] - 1) * 7; 753 } else { 754 days += daysInMonth(leapYear, month) 755 + mod7(dayOfWeek 756 - (days + daysInMonth(leapYear, month) - 3)) 757 + fields[DAY_OF_WEEK_IN_MONTH] * 7; 758 } 759 } else if (isSet[DAY_OF_WEEK]) { 760 int skew = mod7(days - 3 - (getFirstDayOfWeek() - 1)); 761 days += mod7(mod7(skew + dayOfWeek - (days - 3)) - skew); 762 } 763 } 764 } else { 765 boolean useWeekYear = isSet[WEEK_OF_YEAR] 766 && lastDateFieldSet != DAY_OF_YEAR; 767 if (useWeekYear && isSet[DAY_OF_YEAR]) { 768 useWeekYear = isSet[DAY_OF_WEEK]; 769 } 770 days = daysFromBaseYear(year); 771 if (useWeekYear) { 772 int dayOfWeek; 773 if (isSet[DAY_OF_WEEK]) { 774 dayOfWeek = fields[DAY_OF_WEEK] - 1; 775 } else { 776 dayOfWeek = getFirstDayOfWeek() - 1; 777 } 778 int skew = mod7(days - 3 - (getFirstDayOfWeek() - 1)); 779 days += (fields[WEEK_OF_YEAR] - 1) * 7 780 + mod7(skew + dayOfWeek - (days - 3)) - skew; 781 if (7 - skew < getMinimalDaysInFirstWeek()) { 782 days += 7; 783 } 784 } else if (isSet[DAY_OF_YEAR]) { 785 if (!isLenient() 786 && (fields[DAY_OF_YEAR] < 1 || fields[DAY_OF_YEAR] > (365 + (isLeapYear(year) ? 1 787 : 0)))) { 788 throw new IllegalArgumentException(); 789 } 790 days += fields[DAY_OF_YEAR] - 1; 791 } else if (isSet[DAY_OF_WEEK]) { 792 days += mod7(fields[DAY_OF_WEEK] - 1 - (days - 3)); 793 } 794 } 795 lastDateFieldSet = 0; 796 797 timeVal += days * 86400000; 798 // Use local time to compare with the gregorian change 799 if (year == changeYear 800 && timeVal >= gregorianCutover + julianError() * 86400000L) { 801 timeVal -= julianError() * 86400000L; 802 } 803 804 // It is not possible to simply subtract getOffset(timeVal) from timeVal 805 // to get UTC. 806 // The trick is needed for the moment when DST transition occurs, 807 // say 1:00 is a transition time when DST offset becomes +1 hour, 808 // then wall time in the interval 1:00 - 2:00 is invalid and is 809 // treated as UTC time. 810 long timeValWithoutDST = timeVal - getOffset(timeVal) 811 + getTimeZone().getRawOffset(); 812 timeVal -= getOffset(timeValWithoutDST); 813 // Need to update wall time in fields, since it was invalid due to DST 814 // transition 815 this.time = timeVal; 816 if (timeValWithoutDST != timeVal) { 817 computeFields(); 818 areFieldsSet = true; 819 } 820 } 821 computeYearAndDay(long dayCount, long localTime)822 private int computeYearAndDay(long dayCount, long localTime) { 823 int year = 1970; 824 long days = dayCount; 825 if (localTime < gregorianCutover) { 826 days -= julianSkew; 827 } 828 int approxYears; 829 830 while ((approxYears = (int) (days / 365)) != 0) { 831 year = year + approxYears; 832 days = dayCount - daysFromBaseYear(year); 833 } 834 if (days < 0) { 835 year = year - 1; 836 days = days + daysInYear(year); 837 } 838 fields[YEAR] = year; 839 return (int) days + 1; 840 } 841 daysFromBaseYear(long year)842 private long daysFromBaseYear(long year) { 843 if (year >= 1970) { 844 long days = (year - 1970) * 365 + ((year - 1969) / 4); 845 if (year > changeYear) { 846 days -= ((year - 1901) / 100) - ((year - 1601) / 400); 847 } else { 848 if (year == changeYear) { 849 days += currentYearSkew; 850 } else if (year == changeYear - 1) { 851 days += lastYearSkew; 852 } else { 853 days += julianSkew; 854 } 855 } 856 return days; 857 } else if (year <= changeYear) { 858 return (year - 1970) * 365 + ((year - 1972) / 4) + julianSkew; 859 } 860 return (year - 1970) * 365 + ((year - 1972) / 4) 861 - ((year - 2000) / 100) + ((year - 2000) / 400); 862 } 863 daysInMonth()864 private int daysInMonth() { 865 return daysInMonth(isLeapYear(fields[YEAR]), fields[MONTH]); 866 } 867 daysInMonth(boolean leapYear, int month)868 private int daysInMonth(boolean leapYear, int month) { 869 if (leapYear && month == FEBRUARY) { 870 return DaysInMonth[month] + 1; 871 } 872 873 return DaysInMonth[month]; 874 } 875 daysInYear(int year)876 private int daysInYear(int year) { 877 int daysInYear = isLeapYear(year) ? 366 : 365; 878 if (year == changeYear) { 879 daysInYear -= currentYearSkew; 880 } 881 if (year == changeYear - 1) { 882 daysInYear -= lastYearSkew; 883 } 884 return daysInYear; 885 } 886 daysInYear(boolean leapYear, int month)887 private int daysInYear(boolean leapYear, int month) { 888 if (leapYear && month > FEBRUARY) { 889 return DaysInYear[month] + 1; 890 } 891 892 return DaysInYear[month]; 893 } 894 895 /** 896 * Returns true if {@code object} is a GregorianCalendar with the same 897 * properties. 898 */ equals(Object object)899 @Override public boolean equals(Object object) { 900 if (!(object instanceof GregorianCalendar)) { 901 return false; 902 } 903 if (object == this) { 904 return true; 905 } 906 return super.equals(object) 907 && gregorianCutover == ((GregorianCalendar) object).gregorianCutover; 908 } 909 getActualMaximum(int field)910 @Override public int getActualMaximum(int field) { 911 int value; 912 if ((value = maximums[field]) == leastMaximums[field]) { 913 return value; 914 } 915 916 complete(); 917 long orgTime = time; 918 int result = 0; 919 switch (field) { 920 case WEEK_OF_YEAR: 921 set(DATE, 31); 922 set(MONTH, DECEMBER); 923 result = get(WEEK_OF_YEAR); 924 if (result == 1) { 925 set(DATE, 31 - 7); 926 result = get(WEEK_OF_YEAR); 927 } 928 areFieldsSet = false; 929 break; 930 case WEEK_OF_MONTH: 931 set(DATE, daysInMonth()); 932 result = get(WEEK_OF_MONTH); 933 areFieldsSet = false; 934 break; 935 case DATE: 936 return daysInMonth(); 937 case DAY_OF_YEAR: 938 return daysInYear(fields[YEAR]); 939 case DAY_OF_WEEK_IN_MONTH: 940 result = get(DAY_OF_WEEK_IN_MONTH) 941 + ((daysInMonth() - get(DATE)) / 7); 942 break; 943 case YEAR: 944 GregorianCalendar clone = (GregorianCalendar) clone(); 945 if (get(ERA) == AD) { 946 clone.setTimeInMillis(Long.MAX_VALUE); 947 } else { 948 clone.setTimeInMillis(Long.MIN_VALUE); 949 } 950 result = clone.get(YEAR); 951 clone.set(YEAR, get(YEAR)); 952 if (clone.before(this)) { 953 result--; 954 } 955 break; 956 case DST_OFFSET: 957 result = getMaximum(DST_OFFSET); 958 break; 959 } 960 time = orgTime; 961 return result; 962 } 963 964 /** 965 * Gets the minimum value of the specified field for the current date. For 966 * the gregorian calendar, this value is the same as 967 * {@code getMinimum()}. 968 * 969 * @param field 970 * the field. 971 * @return the minimum value of the specified field. 972 */ 973 @Override getActualMinimum(int field)974 public int getActualMinimum(int field) { 975 return getMinimum(field); 976 } 977 978 /** 979 * Gets the greatest minimum value of the specified field. For the gregorian 980 * calendar, this value is the same as {@code getMinimum()}. 981 * 982 * @param field 983 * the field. 984 * @return the greatest minimum value of the specified field. 985 */ 986 @Override getGreatestMinimum(int field)987 public int getGreatestMinimum(int field) { 988 return minimums[field]; 989 } 990 991 /** 992 * Returns the gregorian change date of this calendar. This is the date on 993 * which the gregorian calendar came into effect. 994 * 995 * @return a {@code Date} which represents the gregorian change date. 996 */ getGregorianChange()997 public final Date getGregorianChange() { 998 return new Date(gregorianCutover); 999 } 1000 1001 /** 1002 * Gets the smallest maximum value of the specified field. For example, 28 1003 * for the day of month field. 1004 * 1005 * @param field 1006 * the field. 1007 * @return the smallest maximum value of the specified field. 1008 */ 1009 @Override getLeastMaximum(int field)1010 public int getLeastMaximum(int field) { 1011 // return value for WEEK_OF_YEAR should make corresponding changes when 1012 // the gregorian change date have been reset. 1013 if (gregorianCutover != defaultGregorianCutover 1014 && field == WEEK_OF_YEAR) { 1015 long currentTimeInMillis = time; 1016 setTimeInMillis(gregorianCutover); 1017 int actual = getActualMaximum(field); 1018 setTimeInMillis(currentTimeInMillis); 1019 return actual; 1020 } 1021 return leastMaximums[field]; 1022 } 1023 1024 /** 1025 * Gets the greatest maximum value of the specified field. For example, 31 1026 * for the day of month field. 1027 * 1028 * @param field 1029 * the field. 1030 * @return the greatest maximum value of the specified field. 1031 */ 1032 @Override getMaximum(int field)1033 public int getMaximum(int field) { 1034 return maximums[field]; 1035 } 1036 1037 /** 1038 * Gets the smallest minimum value of the specified field. 1039 * 1040 * @param field 1041 * the field. 1042 * @return the smallest minimum value of the specified field. 1043 */ 1044 @Override getMinimum(int field)1045 public int getMinimum(int field) { 1046 return minimums[field]; 1047 } 1048 getOffset(long localTime)1049 private int getOffset(long localTime) { 1050 TimeZone timeZone = getTimeZone(); 1051 1052 long dayCount = localTime / 86400000; 1053 int millis = (int) (localTime % 86400000); 1054 if (millis < 0) { 1055 millis += 86400000; 1056 dayCount--; 1057 } 1058 1059 int year = 1970; 1060 long days = dayCount; 1061 if (localTime < gregorianCutover) { 1062 days -= julianSkew; 1063 } 1064 int approxYears; 1065 1066 while ((approxYears = (int) (days / 365)) != 0) { 1067 year = year + approxYears; 1068 days = dayCount - daysFromBaseYear(year); 1069 } 1070 if (days < 0) { 1071 year = year - 1; 1072 days = days + 365 + (isLeapYear(year) ? 1 : 0); 1073 if (year == changeYear && localTime < gregorianCutover) { 1074 days -= julianError(); 1075 } 1076 } 1077 if (year <= 0) { 1078 return timeZone.getRawOffset(); 1079 } 1080 int dayOfYear = (int) days + 1; 1081 1082 int month = dayOfYear / 32; 1083 boolean leapYear = isLeapYear(year); 1084 int date = dayOfYear - daysInYear(leapYear, month); 1085 if (date > daysInMonth(leapYear, month)) { 1086 date -= daysInMonth(leapYear, month); 1087 month++; 1088 } 1089 int dayOfWeek = mod7(dayCount - 3) + 1; 1090 return timeZone.getOffset(AD, year, month, date, dayOfWeek, millis); 1091 } 1092 hashCode()1093 @Override public int hashCode() { 1094 return super.hashCode() 1095 + ((int) (gregorianCutover >>> 32) ^ (int) gregorianCutover); 1096 } 1097 1098 /** 1099 * Returns true if {@code year} is a leap year. 1100 */ isLeapYear(int year)1101 public boolean isLeapYear(int year) { 1102 if (year > changeYear) { 1103 return year % 4 == 0 && (year % 100 != 0 || year % 400 == 0); 1104 } 1105 1106 return year % 4 == 0; 1107 } 1108 julianError()1109 private int julianError() { 1110 return changeYear / 100 - changeYear / 400 - 2; 1111 } 1112 mod(int value, int mod)1113 private int mod(int value, int mod) { 1114 int rem = value % mod; 1115 if (value < 0 && rem < 0) { 1116 return rem + mod; 1117 } 1118 return rem; 1119 } 1120 mod7(long num1)1121 private int mod7(long num1) { 1122 int rem = (int) (num1 % 7); 1123 if (num1 < 0 && rem < 0) { 1124 return rem + 7; 1125 } 1126 return rem; 1127 } 1128 1129 /** 1130 * Adds the specified amount the specified field and wraps the value of the 1131 * field when it goes beyond the maximum or minimum value for the current 1132 * date. Other fields will be adjusted as required to maintain a consistent 1133 * date. 1134 * 1135 * @param field 1136 * the field to roll. 1137 * @param value 1138 * the amount to add. 1139 * 1140 * @throws IllegalArgumentException 1141 * if an invalid field is specified. 1142 */ 1143 @Override roll(int field, int value)1144 public void roll(int field, int value) { 1145 if (value == 0) { 1146 return; 1147 } 1148 if (field < 0 || field >= ZONE_OFFSET) { 1149 throw new IllegalArgumentException(); 1150 } 1151 1152 complete(); 1153 int days, day, mod, maxWeeks, newWeek; 1154 int max = -1; 1155 switch (field) { 1156 case YEAR: 1157 max = maximums[field]; 1158 break; 1159 case WEEK_OF_YEAR: 1160 days = daysInYear(fields[YEAR]); 1161 day = DAY_OF_YEAR; 1162 mod = mod7(fields[DAY_OF_WEEK] - fields[day] 1163 - (getFirstDayOfWeek() - 1)); 1164 maxWeeks = (days - 1 + mod) / 7 + 1; 1165 newWeek = mod(fields[field] - 1 + value, maxWeeks) + 1; 1166 if (newWeek == maxWeeks) { 1167 int addDays = (newWeek - fields[field]) * 7; 1168 if (fields[day] > addDays && fields[day] + addDays > days) { 1169 set(field, 1); 1170 } else { 1171 set(field, newWeek - 1); 1172 } 1173 } else if (newWeek == 1) { 1174 int week = (fields[day] - ((fields[day] - 1) / 7 * 7) - 1 + mod) / 7 + 1; 1175 if (week > 1) { 1176 set(field, 1); 1177 } else { 1178 set(field, newWeek); 1179 } 1180 } else { 1181 set(field, newWeek); 1182 } 1183 break; 1184 case WEEK_OF_MONTH: 1185 days = daysInMonth(); 1186 day = DATE; 1187 mod = mod7(fields[DAY_OF_WEEK] - fields[day] 1188 - (getFirstDayOfWeek() - 1)); 1189 maxWeeks = (days - 1 + mod) / 7 + 1; 1190 newWeek = mod(fields[field] - 1 + value, maxWeeks) + 1; 1191 if (newWeek == maxWeeks) { 1192 if (fields[day] + (newWeek - fields[field]) * 7 > days) { 1193 set(day, days); 1194 } else { 1195 set(field, newWeek); 1196 } 1197 } else if (newWeek == 1) { 1198 int week = (fields[day] - ((fields[day] - 1) / 7 * 7) - 1 + mod) / 7 + 1; 1199 if (week > 1) { 1200 set(day, 1); 1201 } else { 1202 set(field, newWeek); 1203 } 1204 } else { 1205 set(field, newWeek); 1206 } 1207 break; 1208 case DATE: 1209 max = daysInMonth(); 1210 break; 1211 case DAY_OF_YEAR: 1212 max = daysInYear(fields[YEAR]); 1213 break; 1214 case DAY_OF_WEEK: 1215 max = maximums[field]; 1216 lastDateFieldSet = WEEK_OF_MONTH; 1217 break; 1218 case DAY_OF_WEEK_IN_MONTH: 1219 max = (fields[DATE] + ((daysInMonth() - fields[DATE]) / 7 * 7) - 1) / 7 + 1; 1220 break; 1221 1222 case ERA: 1223 case MONTH: 1224 case AM_PM: 1225 case HOUR: 1226 case HOUR_OF_DAY: 1227 case MINUTE: 1228 case SECOND: 1229 case MILLISECOND: 1230 set(field, mod(fields[field] + value, maximums[field] + 1)); 1231 if (field == MONTH && fields[DATE] > daysInMonth()) { 1232 set(DATE, daysInMonth()); 1233 } else if (field == AM_PM) { 1234 lastTimeFieldSet = HOUR; 1235 } 1236 break; 1237 } 1238 if (max != -1) { 1239 set(field, mod(fields[field] - 1 + value, max) + 1); 1240 } 1241 complete(); 1242 } 1243 1244 /** 1245 * Increments or decrements the specified field and wraps the value of the 1246 * field when it goes beyond the maximum or minimum value for the current 1247 * date. Other fields will be adjusted as required to maintain a consistent 1248 * date. For example, March 31 will roll to April 30 when rolling the month 1249 * field. 1250 * 1251 * @param field 1252 * the field to roll. 1253 * @param increment 1254 * {@code true} to increment the field, {@code false} to 1255 * decrement. 1256 * @throws IllegalArgumentException 1257 * if an invalid field is specified. 1258 */ 1259 @Override roll(int field, boolean increment)1260 public void roll(int field, boolean increment) { 1261 roll(field, increment ? 1 : -1); 1262 } 1263 1264 /** 1265 * Sets the gregorian change date of this calendar. 1266 */ setGregorianChange(Date date)1267 public void setGregorianChange(Date date) { 1268 gregorianCutover = date.getTime(); 1269 GregorianCalendar cal = new GregorianCalendar(TimeZone.getTimeZone("GMT")); 1270 cal.setTime(date); 1271 changeYear = cal.get(YEAR); 1272 if (cal.get(ERA) == BC) { 1273 changeYear = 1 - changeYear; 1274 } 1275 julianSkew = ((changeYear - 2000) / 400) + julianError() 1276 - ((changeYear - 2000) / 100); 1277 int dayOfYear = cal.get(DAY_OF_YEAR); 1278 if (dayOfYear < julianSkew) { 1279 currentYearSkew = dayOfYear-1; 1280 lastYearSkew = julianSkew - dayOfYear + 1; 1281 } else { 1282 lastYearSkew = 0; 1283 currentYearSkew = julianSkew; 1284 } 1285 } 1286 writeObject(ObjectOutputStream stream)1287 private void writeObject(ObjectOutputStream stream) throws IOException { 1288 stream.defaultWriteObject(); 1289 } 1290 readObject(ObjectInputStream stream)1291 private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { 1292 stream.defaultReadObject(); 1293 setGregorianChange(new Date(gregorianCutover)); 1294 } 1295 } 1296