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 */ 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 */ 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 */ 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 */ 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 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 */ 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 */ 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 */ 329 public GregorianCalendar(TimeZone timezone, Locale locale) { 330 super(timezone, locale); 331 setTimeInMillis(System.currentTimeMillis()); 332 } 333 334 GregorianCalendar(boolean ignored) { 335 super(TimeZone.getDefault()); 336 setFirstDayOfWeek(SUNDAY); 337 setMinimalDaysInFirstWeek(1); 338 } 339 340 /** 341 * Adds the specified amount to a {@code Calendar} field. 342 * 343 * @param field 344 * the {@code Calendar} field to modify. 345 * @param value 346 * the amount to add to the field. 347 * 348 * @throws IllegalArgumentException 349 * if the specified field is DST_OFFSET or ZONE_OFFSET. 350 */ 351 @Override 352 public void add(int field, int value) { 353 if (value == 0) { 354 return; 355 } 356 if (field < 0 || field >= ZONE_OFFSET) { 357 throw new IllegalArgumentException(); 358 } 359 360 if (field == ERA) { 361 complete(); 362 if (fields[ERA] == AD) { 363 if (value >= 0) { 364 return; 365 } 366 set(ERA, BC); 367 } else { 368 if (value <= 0) { 369 return; 370 } 371 set(ERA, AD); 372 } 373 complete(); 374 return; 375 } 376 377 if (field == YEAR || field == MONTH) { 378 complete(); 379 if (field == MONTH) { 380 int month = fields[MONTH] + value; 381 if (month < 0) { 382 value = (month - 11) / 12; 383 month = 12 + (month % 12); 384 } else { 385 value = month / 12; 386 } 387 set(MONTH, month % 12); 388 } 389 set(YEAR, fields[YEAR] + value); 390 int days = daysInMonth(isLeapYear(fields[YEAR]), fields[MONTH]); 391 if (fields[DATE] > days) { 392 set(DATE, days); 393 } 394 complete(); 395 return; 396 } 397 398 long multiplier = 0; 399 getTimeInMillis(); // Update the time 400 switch (field) { 401 case MILLISECOND: 402 time += value; 403 break; 404 case SECOND: 405 time += value * 1000L; 406 break; 407 case MINUTE: 408 time += value * 60000L; 409 break; 410 case HOUR: 411 case HOUR_OF_DAY: 412 time += value * 3600000L; 413 break; 414 case AM_PM: 415 multiplier = 43200000L; 416 break; 417 case DATE: 418 case DAY_OF_YEAR: 419 case DAY_OF_WEEK: 420 multiplier = 86400000L; 421 break; 422 case WEEK_OF_YEAR: 423 case WEEK_OF_MONTH: 424 case DAY_OF_WEEK_IN_MONTH: 425 multiplier = 604800000L; 426 break; 427 } 428 429 if (multiplier == 0) { 430 areFieldsSet = false; 431 complete(); 432 return; 433 } 434 435 long delta = value * multiplier; 436 437 /* 438 * Attempt to keep the hour and minute constant when we've crossed a DST 439 * boundary and the user's units are AM_PM or larger. The typical 440 * consequence is that calls to add(DATE, 1) will add 23, 24 or 25 hours 441 * depending on whether the DST goes forward, constant, or backward. 442 * 443 * We know we've crossed a DST boundary if the new time will have a 444 * different timezone offset. Adjust by adding the difference of the two 445 * offsets. We don't adjust when doing so prevents the change from 446 * crossing the boundary. 447 */ 448 int zoneOffset = getTimeZone().getRawOffset(); 449 int offsetBefore = getOffset(time + zoneOffset); 450 int offsetAfter = getOffset(time + zoneOffset + delta); 451 int dstDelta = offsetBefore - offsetAfter; 452 if (getOffset(time + zoneOffset + delta + dstDelta) == offsetAfter) { 453 delta += dstDelta; 454 } 455 456 time += delta; 457 areFieldsSet = false; 458 complete(); 459 } 460 461 private void fullFieldsCalc(long timeVal, int zoneOffset) { 462 int millis = (int) (time % 86400000); 463 long days = timeVal / 86400000; 464 465 if (millis < 0) { 466 millis += 86400000; 467 days--; 468 } 469 // Cannot add ZONE_OFFSET to time as it might overflow 470 millis += zoneOffset; 471 while (millis < 0) { 472 millis += 86400000; 473 days--; 474 } 475 while (millis >= 86400000) { 476 millis -= 86400000; 477 days++; 478 } 479 480 int dayOfYear = computeYearAndDay(days, timeVal + zoneOffset); 481 fields[DAY_OF_YEAR] = dayOfYear; 482 if(fields[YEAR] == changeYear && gregorianCutover <= timeVal + zoneOffset){ 483 dayOfYear += currentYearSkew; 484 } 485 int month = dayOfYear / 32; 486 boolean leapYear = isLeapYear(fields[YEAR]); 487 int date = dayOfYear - daysInYear(leapYear, month); 488 if (date > daysInMonth(leapYear, month)) { 489 date -= daysInMonth(leapYear, month); 490 month++; 491 } 492 fields[DAY_OF_WEEK] = mod7(days - 3) + 1; 493 int dstOffset = fields[YEAR] <= 0 ? 0 : getTimeZone().getOffset(AD, 494 fields[YEAR], month, date, fields[DAY_OF_WEEK], millis); 495 if (fields[YEAR] > 0) { 496 dstOffset -= zoneOffset; 497 } 498 fields[DST_OFFSET] = dstOffset; 499 if (dstOffset != 0) { 500 long oldDays = days; 501 millis += dstOffset; 502 if (millis < 0) { 503 millis += 86400000; 504 days--; 505 } else if (millis >= 86400000) { 506 millis -= 86400000; 507 days++; 508 } 509 if (oldDays != days) { 510 dayOfYear = computeYearAndDay(days, timeVal - zoneOffset 511 + dstOffset); 512 fields[DAY_OF_YEAR] = dayOfYear; 513 if(fields[YEAR] == changeYear && gregorianCutover <= timeVal - zoneOffset + dstOffset){ 514 dayOfYear += currentYearSkew; 515 } 516 month = dayOfYear / 32; 517 leapYear = isLeapYear(fields[YEAR]); 518 date = dayOfYear - daysInYear(leapYear, month); 519 if (date > daysInMonth(leapYear, month)) { 520 date -= daysInMonth(leapYear, month); 521 month++; 522 } 523 fields[DAY_OF_WEEK] = mod7(days - 3) + 1; 524 } 525 } 526 527 fields[MILLISECOND] = (millis % 1000); 528 millis /= 1000; 529 fields[SECOND] = (millis % 60); 530 millis /= 60; 531 fields[MINUTE] = (millis % 60); 532 millis /= 60; 533 fields[HOUR_OF_DAY] = (millis % 24); 534 fields[AM_PM] = fields[HOUR_OF_DAY] > 11 ? 1 : 0; 535 fields[HOUR] = fields[HOUR_OF_DAY] % 12; 536 537 if (fields[YEAR] <= 0) { 538 fields[ERA] = BC; 539 fields[YEAR] = -fields[YEAR] + 1; 540 } else { 541 fields[ERA] = AD; 542 } 543 fields[MONTH] = month; 544 fields[DATE] = date; 545 fields[DAY_OF_WEEK_IN_MONTH] = (date - 1) / 7 + 1; 546 fields[WEEK_OF_MONTH] = (date - 1 + mod7(days - date - 2 547 - (getFirstDayOfWeek() - 1))) / 7 + 1; 548 int daysFromStart = mod7(days - 3 - (fields[DAY_OF_YEAR] - 1) 549 - (getFirstDayOfWeek() - 1)); 550 int week = (fields[DAY_OF_YEAR] - 1 + daysFromStart) / 7 551 + (7 - daysFromStart >= getMinimalDaysInFirstWeek() ? 1 : 0); 552 if (week == 0) { 553 fields[WEEK_OF_YEAR] = 7 - mod7(daysFromStart 554 - (isLeapYear(fields[YEAR] - 1) ? 2 : 1)) >= getMinimalDaysInFirstWeek() ? 53 555 : 52; 556 } else if (fields[DAY_OF_YEAR] >= (leapYear ? 367 : 366) 557 - mod7(daysFromStart + (leapYear ? 2 : 1))) { 558 fields[WEEK_OF_YEAR] = 7 - mod7(daysFromStart + (leapYear ? 2 : 1)) >= getMinimalDaysInFirstWeek() ? 1 559 : week; 560 } else { 561 fields[WEEK_OF_YEAR] = week; 562 } 563 } 564 565 @Override 566 protected void computeFields() { 567 TimeZone timeZone = getTimeZone(); 568 int dstOffset = timeZone.inDaylightTime(new Date(time)) ? timeZone.getDSTSavings() : 0; 569 int zoneOffset = timeZone.getRawOffset(); 570 fields[DST_OFFSET] = dstOffset; 571 fields[ZONE_OFFSET] = zoneOffset; 572 573 fullFieldsCalc(time, zoneOffset); 574 575 for (int i = 0; i < FIELD_COUNT; i++) { 576 isSet[i] = true; 577 } 578 } 579 580 @Override 581 protected void computeTime() { 582 if (!isLenient()) { 583 if (isSet[HOUR_OF_DAY]) { 584 if (fields[HOUR_OF_DAY] < 0 || fields[HOUR_OF_DAY] > 23) { 585 throw new IllegalArgumentException(); 586 } 587 } else if (isSet[HOUR] && (fields[HOUR] < 0 || fields[HOUR] > 11)) { 588 throw new IllegalArgumentException(); 589 } 590 if (isSet[MINUTE] && (fields[MINUTE] < 0 || fields[MINUTE] > 59)) { 591 throw new IllegalArgumentException(); 592 } 593 if (isSet[SECOND] && (fields[SECOND] < 0 || fields[SECOND] > 59)) { 594 throw new IllegalArgumentException(); 595 } 596 if (isSet[MILLISECOND] 597 && (fields[MILLISECOND] < 0 || fields[MILLISECOND] > 999)) { 598 throw new IllegalArgumentException(); 599 } 600 if (isSet[WEEK_OF_YEAR] 601 && (fields[WEEK_OF_YEAR] < 1 || fields[WEEK_OF_YEAR] > 53)) { 602 throw new IllegalArgumentException(); 603 } 604 if (isSet[DAY_OF_WEEK] 605 && (fields[DAY_OF_WEEK] < 1 || fields[DAY_OF_WEEK] > 7)) { 606 throw new IllegalArgumentException(); 607 } 608 if (isSet[DAY_OF_WEEK_IN_MONTH] 609 && (fields[DAY_OF_WEEK_IN_MONTH] < 1 || fields[DAY_OF_WEEK_IN_MONTH] > 6)) { 610 throw new IllegalArgumentException(); 611 } 612 if (isSet[WEEK_OF_MONTH] 613 && (fields[WEEK_OF_MONTH] < 1 || fields[WEEK_OF_MONTH] > 6)) { 614 throw new IllegalArgumentException(); 615 } 616 if (isSet[AM_PM] && fields[AM_PM] != AM && fields[AM_PM] != PM) { 617 throw new IllegalArgumentException(); 618 } 619 if (isSet[HOUR] && (fields[HOUR] < 0 || fields[HOUR] > 11)) { 620 throw new IllegalArgumentException(); 621 } 622 if (isSet[YEAR]) { 623 if (isSet[ERA] && fields[ERA] == BC 624 && (fields[YEAR] < 1 || fields[YEAR] > 292269054)) { 625 throw new IllegalArgumentException(); 626 } else if (fields[YEAR] < 1 || fields[YEAR] > 292278994) { 627 throw new IllegalArgumentException(); 628 } 629 } 630 if (isSet[MONTH] && (fields[MONTH] < 0 || fields[MONTH] > 11)) { 631 throw new IllegalArgumentException(); 632 } 633 } 634 635 long timeVal; 636 long hour = 0; 637 if (isSet[HOUR_OF_DAY] && lastTimeFieldSet != HOUR) { 638 hour = fields[HOUR_OF_DAY]; 639 } else if (isSet[HOUR]) { 640 hour = (fields[AM_PM] * 12) + fields[HOUR]; 641 } 642 timeVal = hour * 3600000; 643 644 if (isSet[MINUTE]) { 645 timeVal += ((long) fields[MINUTE]) * 60000; 646 } 647 if (isSet[SECOND]) { 648 timeVal += ((long) fields[SECOND]) * 1000; 649 } 650 if (isSet[MILLISECOND]) { 651 timeVal += fields[MILLISECOND]; 652 } 653 654 long days; 655 int year = isSet[YEAR] ? fields[YEAR] : 1970; 656 if (isSet[ERA]) { 657 // Always test for valid ERA, even if the Calendar is lenient 658 if (fields[ERA] != BC && fields[ERA] != AD) { 659 throw new IllegalArgumentException(); 660 } 661 if (fields[ERA] == BC) { 662 year = 1 - year; 663 } 664 } 665 666 boolean weekMonthSet = isSet[WEEK_OF_MONTH] 667 || isSet[DAY_OF_WEEK_IN_MONTH]; 668 boolean useMonth = (isSet[DATE] || isSet[MONTH] || weekMonthSet) 669 && lastDateFieldSet != DAY_OF_YEAR; 670 if (useMonth 671 && (lastDateFieldSet == DAY_OF_WEEK || lastDateFieldSet == WEEK_OF_YEAR)) { 672 if (isSet[WEEK_OF_YEAR] && isSet[DAY_OF_WEEK]) { 673 useMonth = lastDateFieldSet != WEEK_OF_YEAR && weekMonthSet 674 && isSet[DAY_OF_WEEK]; 675 } else if (isSet[DAY_OF_YEAR]) { 676 useMonth = isSet[DATE] && isSet[MONTH]; 677 } 678 } 679 680 if (useMonth) { 681 int month = fields[MONTH]; 682 year += month / 12; 683 month %= 12; 684 if (month < 0) { 685 year--; 686 month += 12; 687 } 688 boolean leapYear = isLeapYear(year); 689 days = daysFromBaseYear(year) + daysInYear(leapYear, month); 690 boolean useDate = isSet[DATE]; 691 if (useDate 692 && (lastDateFieldSet == DAY_OF_WEEK 693 || lastDateFieldSet == WEEK_OF_MONTH || lastDateFieldSet == DAY_OF_WEEK_IN_MONTH)) { 694 useDate = !(isSet[DAY_OF_WEEK] && weekMonthSet); 695 } 696 if (useDate) { 697 if (!isLenient() 698 && (fields[DATE] < 1 || fields[DATE] > daysInMonth( 699 leapYear, month))) { 700 throw new IllegalArgumentException(); 701 } 702 days += fields[DATE] - 1; 703 } else { 704 int dayOfWeek; 705 if (isSet[DAY_OF_WEEK]) { 706 dayOfWeek = fields[DAY_OF_WEEK] - 1; 707 } else { 708 dayOfWeek = getFirstDayOfWeek() - 1; 709 } 710 if (isSet[WEEK_OF_MONTH] 711 && lastDateFieldSet != DAY_OF_WEEK_IN_MONTH) { 712 int skew = mod7(days - 3 - (getFirstDayOfWeek() - 1)); 713 days += (fields[WEEK_OF_MONTH] - 1) * 7 714 + mod7(skew + dayOfWeek - (days - 3)) - skew; 715 } else if (isSet[DAY_OF_WEEK_IN_MONTH]) { 716 if (fields[DAY_OF_WEEK_IN_MONTH] >= 0) { 717 days += mod7(dayOfWeek - (days - 3)) 718 + (fields[DAY_OF_WEEK_IN_MONTH] - 1) * 7; 719 } else { 720 days += daysInMonth(leapYear, month) 721 + mod7(dayOfWeek 722 - (days + daysInMonth(leapYear, month) - 3)) 723 + fields[DAY_OF_WEEK_IN_MONTH] * 7; 724 } 725 } else if (isSet[DAY_OF_WEEK]) { 726 int skew = mod7(days - 3 - (getFirstDayOfWeek() - 1)); 727 days += mod7(mod7(skew + dayOfWeek - (days - 3)) - skew); 728 } 729 } 730 } else { 731 boolean useWeekYear = isSet[WEEK_OF_YEAR] 732 && lastDateFieldSet != DAY_OF_YEAR; 733 if (useWeekYear && isSet[DAY_OF_YEAR]) { 734 useWeekYear = isSet[DAY_OF_WEEK]; 735 } 736 days = daysFromBaseYear(year); 737 if (useWeekYear) { 738 int dayOfWeek; 739 if (isSet[DAY_OF_WEEK]) { 740 dayOfWeek = fields[DAY_OF_WEEK] - 1; 741 } else { 742 dayOfWeek = getFirstDayOfWeek() - 1; 743 } 744 int skew = mod7(days - 3 - (getFirstDayOfWeek() - 1)); 745 days += (fields[WEEK_OF_YEAR] - 1) * 7 746 + mod7(skew + dayOfWeek - (days - 3)) - skew; 747 if (7 - skew < getMinimalDaysInFirstWeek()) { 748 days += 7; 749 } 750 } else if (isSet[DAY_OF_YEAR]) { 751 if (!isLenient() 752 && (fields[DAY_OF_YEAR] < 1 || fields[DAY_OF_YEAR] > (365 + (isLeapYear(year) ? 1 753 : 0)))) { 754 throw new IllegalArgumentException(); 755 } 756 days += fields[DAY_OF_YEAR] - 1; 757 } else if (isSet[DAY_OF_WEEK]) { 758 days += mod7(fields[DAY_OF_WEEK] - 1 - (days - 3)); 759 } 760 } 761 lastDateFieldSet = 0; 762 763 timeVal += days * 86400000; 764 // Use local time to compare with the gregorian change 765 if (year == changeYear 766 && timeVal >= gregorianCutover + julianError() * 86400000L) { 767 timeVal -= julianError() * 86400000L; 768 } 769 770 // It is not possible to simply subtract getOffset(timeVal) from timeVal 771 // to get UTC. 772 // The trick is needed for the moment when DST transition occurs, 773 // say 1:00 is a transition time when DST offset becomes +1 hour, 774 // then wall time in the interval 1:00 - 2:00 is invalid and is 775 // treated as UTC time. 776 long timeValWithoutDST = timeVal - getOffset(timeVal) 777 + getTimeZone().getRawOffset(); 778 timeVal -= getOffset(timeValWithoutDST); 779 // Need to update wall time in fields, since it was invalid due to DST 780 // transition 781 this.time = timeVal; 782 if (timeValWithoutDST != timeVal) { 783 computeFields(); 784 areFieldsSet = true; 785 } 786 } 787 788 private int computeYearAndDay(long dayCount, long localTime) { 789 int year = 1970; 790 long days = dayCount; 791 if (localTime < gregorianCutover) { 792 days -= julianSkew; 793 } 794 int approxYears; 795 796 while ((approxYears = (int) (days / 365)) != 0) { 797 year = year + approxYears; 798 days = dayCount - daysFromBaseYear(year); 799 } 800 if (days < 0) { 801 year = year - 1; 802 days = days + daysInYear(year); 803 } 804 fields[YEAR] = year; 805 return (int) days + 1; 806 } 807 808 private long daysFromBaseYear(long year) { 809 if (year >= 1970) { 810 long days = (year - 1970) * 365 + ((year - 1969) / 4); 811 if (year > changeYear) { 812 days -= ((year - 1901) / 100) - ((year - 1601) / 400); 813 } else { 814 if (year == changeYear) { 815 days += currentYearSkew; 816 } else if (year == changeYear - 1) { 817 days += lastYearSkew; 818 } else { 819 days += julianSkew; 820 } 821 } 822 return days; 823 } else if (year <= changeYear) { 824 return (year - 1970) * 365 + ((year - 1972) / 4) + julianSkew; 825 } 826 return (year - 1970) * 365 + ((year - 1972) / 4) 827 - ((year - 2000) / 100) + ((year - 2000) / 400); 828 } 829 830 private int daysInMonth() { 831 return daysInMonth(isLeapYear(fields[YEAR]), fields[MONTH]); 832 } 833 834 private int daysInMonth(boolean leapYear, int month) { 835 if (leapYear && month == FEBRUARY) { 836 return DaysInMonth[month] + 1; 837 } 838 839 return DaysInMonth[month]; 840 } 841 842 private int daysInYear(int year) { 843 int daysInYear = isLeapYear(year) ? 366 : 365; 844 if (year == changeYear) { 845 daysInYear -= currentYearSkew; 846 } 847 if (year == changeYear - 1) { 848 daysInYear -= lastYearSkew; 849 } 850 return daysInYear; 851 } 852 853 private int daysInYear(boolean leapYear, int month) { 854 if (leapYear && month > FEBRUARY) { 855 return DaysInYear[month] + 1; 856 } 857 858 return DaysInYear[month]; 859 } 860 861 /** 862 * Returns true if {@code object} is a GregorianCalendar with the same 863 * properties. 864 */ 865 @Override public boolean equals(Object object) { 866 if (!(object instanceof GregorianCalendar)) { 867 return false; 868 } 869 if (object == this) { 870 return true; 871 } 872 return super.equals(object) 873 && gregorianCutover == ((GregorianCalendar) object).gregorianCutover; 874 } 875 876 @Override public int getActualMaximum(int field) { 877 int value; 878 if ((value = maximums[field]) == leastMaximums[field]) { 879 return value; 880 } 881 882 complete(); 883 long orgTime = time; 884 int result = 0; 885 switch (field) { 886 case WEEK_OF_YEAR: 887 set(DATE, 31); 888 set(MONTH, DECEMBER); 889 result = get(WEEK_OF_YEAR); 890 if (result == 1) { 891 set(DATE, 31 - 7); 892 result = get(WEEK_OF_YEAR); 893 } 894 areFieldsSet = false; 895 break; 896 case WEEK_OF_MONTH: 897 set(DATE, daysInMonth()); 898 result = get(WEEK_OF_MONTH); 899 areFieldsSet = false; 900 break; 901 case DATE: 902 return daysInMonth(); 903 case DAY_OF_YEAR: 904 return daysInYear(fields[YEAR]); 905 case DAY_OF_WEEK_IN_MONTH: 906 result = get(DAY_OF_WEEK_IN_MONTH) 907 + ((daysInMonth() - get(DATE)) / 7); 908 break; 909 case YEAR: 910 GregorianCalendar clone = (GregorianCalendar) clone(); 911 if (get(ERA) == AD) { 912 clone.setTimeInMillis(Long.MAX_VALUE); 913 } else { 914 clone.setTimeInMillis(Long.MIN_VALUE); 915 } 916 result = clone.get(YEAR); 917 clone.set(YEAR, get(YEAR)); 918 if (clone.before(this)) { 919 result--; 920 } 921 break; 922 case DST_OFFSET: 923 result = getMaximum(DST_OFFSET); 924 break; 925 } 926 time = orgTime; 927 return result; 928 } 929 930 /** 931 * Gets the minimum value of the specified field for the current date. For 932 * the gregorian calendar, this value is the same as 933 * {@code getMinimum()}. 934 * 935 * @param field 936 * the field. 937 * @return the minimum value of the specified field. 938 */ 939 @Override 940 public int getActualMinimum(int field) { 941 return getMinimum(field); 942 } 943 944 /** 945 * Gets the greatest minimum value of the specified field. For the gregorian 946 * calendar, this value is the same as {@code getMinimum()}. 947 * 948 * @param field 949 * the field. 950 * @return the greatest minimum value of the specified field. 951 */ 952 @Override 953 public int getGreatestMinimum(int field) { 954 return minimums[field]; 955 } 956 957 /** 958 * Returns the gregorian change date of this calendar. This is the date on 959 * which the gregorian calendar came into effect. 960 * 961 * @return a {@code Date} which represents the gregorian change date. 962 */ 963 public final Date getGregorianChange() { 964 return new Date(gregorianCutover); 965 } 966 967 /** 968 * Gets the smallest maximum value of the specified field. For example, 28 969 * for the day of month field. 970 * 971 * @param field 972 * the field. 973 * @return the smallest maximum value of the specified field. 974 */ 975 @Override 976 public int getLeastMaximum(int field) { 977 // return value for WEEK_OF_YEAR should make corresponding changes when 978 // the gregorian change date have been reset. 979 if (gregorianCutover != defaultGregorianCutover 980 && field == WEEK_OF_YEAR) { 981 long currentTimeInMillis = time; 982 setTimeInMillis(gregorianCutover); 983 int actual = getActualMaximum(field); 984 setTimeInMillis(currentTimeInMillis); 985 return actual; 986 } 987 return leastMaximums[field]; 988 } 989 990 /** 991 * Gets the greatest maximum value of the specified field. For example, 31 992 * for the day of month field. 993 * 994 * @param field 995 * the field. 996 * @return the greatest maximum value of the specified field. 997 */ 998 @Override 999 public int getMaximum(int field) { 1000 return maximums[field]; 1001 } 1002 1003 /** 1004 * Gets the smallest minimum value of the specified field. 1005 * 1006 * @param field 1007 * the field. 1008 * @return the smallest minimum value of the specified field. 1009 */ 1010 @Override 1011 public int getMinimum(int field) { 1012 return minimums[field]; 1013 } 1014 1015 private int getOffset(long localTime) { 1016 TimeZone timeZone = getTimeZone(); 1017 1018 long dayCount = localTime / 86400000; 1019 int millis = (int) (localTime % 86400000); 1020 if (millis < 0) { 1021 millis += 86400000; 1022 dayCount--; 1023 } 1024 1025 int year = 1970; 1026 long days = dayCount; 1027 if (localTime < gregorianCutover) { 1028 days -= julianSkew; 1029 } 1030 int approxYears; 1031 1032 while ((approxYears = (int) (days / 365)) != 0) { 1033 year = year + approxYears; 1034 days = dayCount - daysFromBaseYear(year); 1035 } 1036 if (days < 0) { 1037 year = year - 1; 1038 days = days + 365 + (isLeapYear(year) ? 1 : 0); 1039 if (year == changeYear && localTime < gregorianCutover) { 1040 days -= julianError(); 1041 } 1042 } 1043 if (year <= 0) { 1044 return timeZone.getRawOffset(); 1045 } 1046 int dayOfYear = (int) days + 1; 1047 1048 int month = dayOfYear / 32; 1049 boolean leapYear = isLeapYear(year); 1050 int date = dayOfYear - daysInYear(leapYear, month); 1051 if (date > daysInMonth(leapYear, month)) { 1052 date -= daysInMonth(leapYear, month); 1053 month++; 1054 } 1055 int dayOfWeek = mod7(dayCount - 3) + 1; 1056 return timeZone.getOffset(AD, year, month, date, dayOfWeek, millis); 1057 } 1058 1059 @Override public int hashCode() { 1060 return super.hashCode() 1061 + ((int) (gregorianCutover >>> 32) ^ (int) gregorianCutover); 1062 } 1063 1064 /** 1065 * Returns true if {@code year} is a leap year. 1066 */ 1067 public boolean isLeapYear(int year) { 1068 if (year > changeYear) { 1069 return year % 4 == 0 && (year % 100 != 0 || year % 400 == 0); 1070 } 1071 1072 return year % 4 == 0; 1073 } 1074 1075 private int julianError() { 1076 return changeYear / 100 - changeYear / 400 - 2; 1077 } 1078 1079 private int mod(int value, int mod) { 1080 int rem = value % mod; 1081 if (value < 0 && rem < 0) { 1082 return rem + mod; 1083 } 1084 return rem; 1085 } 1086 1087 private int mod7(long num1) { 1088 int rem = (int) (num1 % 7); 1089 if (num1 < 0 && rem < 0) { 1090 return rem + 7; 1091 } 1092 return rem; 1093 } 1094 1095 /** 1096 * Adds the specified amount the specified field and wraps the value of the 1097 * field when it goes beyond the maximum or minimum value for the current 1098 * date. Other fields will be adjusted as required to maintain a consistent 1099 * date. 1100 * 1101 * @param field 1102 * the field to roll. 1103 * @param value 1104 * the amount to add. 1105 * 1106 * @throws IllegalArgumentException 1107 * if an invalid field is specified. 1108 */ 1109 @Override 1110 public void roll(int field, int value) { 1111 if (value == 0) { 1112 return; 1113 } 1114 if (field < 0 || field >= ZONE_OFFSET) { 1115 throw new IllegalArgumentException(); 1116 } 1117 1118 complete(); 1119 int days, day, mod, maxWeeks, newWeek; 1120 int max = -1; 1121 switch (field) { 1122 case YEAR: 1123 max = maximums[field]; 1124 break; 1125 case WEEK_OF_YEAR: 1126 days = daysInYear(fields[YEAR]); 1127 day = DAY_OF_YEAR; 1128 mod = mod7(fields[DAY_OF_WEEK] - fields[day] 1129 - (getFirstDayOfWeek() - 1)); 1130 maxWeeks = (days - 1 + mod) / 7 + 1; 1131 newWeek = mod(fields[field] - 1 + value, maxWeeks) + 1; 1132 if (newWeek == maxWeeks) { 1133 int addDays = (newWeek - fields[field]) * 7; 1134 if (fields[day] > addDays && fields[day] + addDays > days) { 1135 set(field, 1); 1136 } else { 1137 set(field, newWeek - 1); 1138 } 1139 } else if (newWeek == 1) { 1140 int week = (fields[day] - ((fields[day] - 1) / 7 * 7) - 1 + mod) / 7 + 1; 1141 if (week > 1) { 1142 set(field, 1); 1143 } else { 1144 set(field, newWeek); 1145 } 1146 } else { 1147 set(field, newWeek); 1148 } 1149 break; 1150 case WEEK_OF_MONTH: 1151 days = daysInMonth(); 1152 day = DATE; 1153 mod = mod7(fields[DAY_OF_WEEK] - fields[day] 1154 - (getFirstDayOfWeek() - 1)); 1155 maxWeeks = (days - 1 + mod) / 7 + 1; 1156 newWeek = mod(fields[field] - 1 + value, maxWeeks) + 1; 1157 if (newWeek == maxWeeks) { 1158 if (fields[day] + (newWeek - fields[field]) * 7 > days) { 1159 set(day, days); 1160 } else { 1161 set(field, newWeek); 1162 } 1163 } else if (newWeek == 1) { 1164 int week = (fields[day] - ((fields[day] - 1) / 7 * 7) - 1 + mod) / 7 + 1; 1165 if (week > 1) { 1166 set(day, 1); 1167 } else { 1168 set(field, newWeek); 1169 } 1170 } else { 1171 set(field, newWeek); 1172 } 1173 break; 1174 case DATE: 1175 max = daysInMonth(); 1176 break; 1177 case DAY_OF_YEAR: 1178 max = daysInYear(fields[YEAR]); 1179 break; 1180 case DAY_OF_WEEK: 1181 max = maximums[field]; 1182 lastDateFieldSet = WEEK_OF_MONTH; 1183 break; 1184 case DAY_OF_WEEK_IN_MONTH: 1185 max = (fields[DATE] + ((daysInMonth() - fields[DATE]) / 7 * 7) - 1) / 7 + 1; 1186 break; 1187 1188 case ERA: 1189 case MONTH: 1190 case AM_PM: 1191 case HOUR: 1192 case HOUR_OF_DAY: 1193 case MINUTE: 1194 case SECOND: 1195 case MILLISECOND: 1196 set(field, mod(fields[field] + value, maximums[field] + 1)); 1197 if (field == MONTH && fields[DATE] > daysInMonth()) { 1198 set(DATE, daysInMonth()); 1199 } else if (field == AM_PM) { 1200 lastTimeFieldSet = HOUR; 1201 } 1202 break; 1203 } 1204 if (max != -1) { 1205 set(field, mod(fields[field] - 1 + value, max) + 1); 1206 } 1207 complete(); 1208 } 1209 1210 /** 1211 * Increments or decrements the specified field and wraps the value of the 1212 * field when it goes beyond the maximum or minimum value for the current 1213 * date. Other fields will be adjusted as required to maintain a consistent 1214 * date. For example, March 31 will roll to April 30 when rolling the month 1215 * field. 1216 * 1217 * @param field 1218 * the field to roll. 1219 * @param increment 1220 * {@code true} to increment the field, {@code false} to 1221 * decrement. 1222 * @throws IllegalArgumentException 1223 * if an invalid field is specified. 1224 */ 1225 @Override 1226 public void roll(int field, boolean increment) { 1227 roll(field, increment ? 1 : -1); 1228 } 1229 1230 /** 1231 * Sets the gregorian change date of this calendar. 1232 */ 1233 public void setGregorianChange(Date date) { 1234 gregorianCutover = date.getTime(); 1235 GregorianCalendar cal = new GregorianCalendar(TimeZone.getTimeZone("GMT")); 1236 cal.setTime(date); 1237 changeYear = cal.get(YEAR); 1238 if (cal.get(ERA) == BC) { 1239 changeYear = 1 - changeYear; 1240 } 1241 julianSkew = ((changeYear - 2000) / 400) + julianError() 1242 - ((changeYear - 2000) / 100); 1243 int dayOfYear = cal.get(DAY_OF_YEAR); 1244 if (dayOfYear < julianSkew) { 1245 currentYearSkew = dayOfYear-1; 1246 lastYearSkew = julianSkew - dayOfYear + 1; 1247 } else { 1248 lastYearSkew = 0; 1249 currentYearSkew = julianSkew; 1250 } 1251 } 1252 1253 private void writeObject(ObjectOutputStream stream) throws IOException { 1254 stream.defaultWriteObject(); 1255 } 1256 1257 private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { 1258 stream.defaultReadObject(); 1259 setGregorianChange(new Date(gregorianCutover)); 1260 } 1261 } 1262