1 /* java.util.GregorianCalendar 2 Copyright (C) 1998, 1999, 2001, 2002, 2003 Free Software Foundation, Inc. 3 4 This file is part of GNU Classpath. 5 6 GNU Classpath is free software; you can redistribute it and/or modify 7 it under the terms of the GNU General Public License as published by 8 the Free Software Foundation; either version 2, or (at your option) 9 any later version. 10 11 GNU Classpath is distributed in the hope that it will be useful, but 12 WITHOUT ANY WARRANTY; without even the implied warranty of 13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 General Public License for more details. 15 16 You should have received a copy of the GNU General Public License 17 along with GNU Classpath; see the file COPYING. If not, write to the 18 Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 19 02111-1307 USA. 20 21 Linking this library statically or dynamically with other modules is 22 making a combined work based on this library. Thus, the terms and 23 conditions of the GNU General Public License cover the whole 24 combination. 25 26 As a special exception, the copyright holders of this library give you 27 permission to link this library with independent modules to produce an 28 executable, regardless of the license terms of these independent 29 modules, and to copy and distribute the resulting executable under 30 terms of your choice, provided that you also meet, for each linked 31 independent module, the terms and conditions of the license of that 32 module. An independent module is a module which is not derived from 33 or based on this library. If you modify this library, you may extend 34 this exception to your version of the library, but you are not 35 obligated to do so. If you do not wish to do so, delete this 36 exception statement from your version. */ 37 38 39 package java.util; 40 41 /** 42 * This class represents the Gregorian calendar, that is used in most 43 * countries all over the world. It does also handle the Julian calendar 44 * for dates smaller than the date of the change to the Gregorian calendar. 45 * This change date is different from country to country, you can set it with 46 * <code>setGregorianChange</code> 47 * 48 * The Gregorian calendar differs from the Julian calendar by a different 49 * leap year rule (no leap year every 100 years, except if year is divisible 50 * by 400). The non existing days that were omited when the change took 51 * place are interpreted as gregorian date 52 * 53 * There are to eras available for the Gregorian calendar, namely BC and AD. 54 * 55 * @see Calendar 56 * @see TimeZone 57 */ 58 public class GregorianCalendar extends Calendar 59 { 60 /** 61 * Constant representing the era BC (before Christ). 62 */ 63 public static final int BC = 0; 64 65 /** 66 * Constant representing the era AD (Anno Domini). 67 */ 68 public static final int AD = 1; 69 70 /** 71 * The point at which the Gregorian calendar rules were used. 72 * This is locale dependent; the default for most catholic 73 * countries is midnight (UTC) on October 5, 1582 (Julian), 74 * or October 15, 1582 (Gregorian). 75 */ 76 private long gregorianCutover; 77 78 static final long serialVersionUID = -8125100834729963327L; 79 80 /** 81 * The name of the resource bundle. Used only by getBundle() 82 */ 83 private static final String bundleName = "gnu.java.locale.Calendar"; 84 85 /** 86 * get resource bundle: 87 * The resources should be loaded via this method only. Iff an application 88 * uses this method, the resourcebundle is required. --Fridi. 89 */ getBundle(Locale locale)90 private static ResourceBundle getBundle(Locale locale) 91 { 92 return ResourceBundle.getBundle(bundleName, locale); 93 } 94 95 /** 96 * Constructs a new GregorianCalender representing the current 97 * time, using the default time zone and the default locale. 98 */ GregorianCalendar()99 public GregorianCalendar() 100 { 101 this(TimeZone.getDefault(), Locale.getDefault()); 102 } 103 104 /** 105 * Constructs a new GregorianCalender representing the current 106 * time, using the specified time zone and the default locale. 107 * @param zone a time zone. 108 */ GregorianCalendar(TimeZone zone)109 public GregorianCalendar(TimeZone zone) 110 { 111 this(zone, Locale.getDefault()); 112 } 113 114 /** 115 * Constructs a new GregorianCalender representing the current 116 * time, using the default time zone and the specified locale. 117 * @param locale a locale. 118 */ GregorianCalendar(Locale locale)119 public GregorianCalendar(Locale locale) 120 { 121 this(TimeZone.getDefault(), locale); 122 } 123 124 /** 125 * Constructs a new GregorianCalender representing the current 126 * time with the given time zone and the given locale. 127 * @param zone a time zone. 128 * @param locale a locale. 129 */ GregorianCalendar(TimeZone zone, Locale locale)130 public GregorianCalendar(TimeZone zone, Locale locale) 131 { 132 super(zone, locale); 133 ResourceBundle rb = getBundle(locale); 134 gregorianCutover = ((Date) rb.getObject("gregorianCutOver")).getTime(); 135 setTimeInMillis(System.currentTimeMillis()); 136 } 137 138 /** 139 * Constructs a new GregorianCalendar representing midnight on the 140 * given date with the default time zone and locale. 141 * @param year corresponds to the YEAR time field. 142 * @param month corresponds to the MONTH time field. 143 * @param day corresponds to the DAY time field. 144 */ GregorianCalendar(int year, int month, int day)145 public GregorianCalendar(int year, int month, int day) 146 { 147 super(); 148 set(year, month, day); 149 } 150 151 /** 152 * Constructs a new GregorianCalendar representing midnight on the 153 * given date with the default time zone and locale. 154 * @param year corresponds to the YEAR time field. 155 * @param month corresponds to the MONTH time field. 156 * @param day corresponds to the DAY time field. 157 * @param hour corresponds to the HOUR_OF_DAY time field. 158 * @param minute corresponds to the MINUTE time field. 159 */ GregorianCalendar(int year, int month, int day, int hour, int minute)160 public GregorianCalendar(int year, int month, int day, int hour, int minute) 161 { 162 super(); 163 set(year, month, day, hour, minute); 164 } 165 166 /** 167 * Constructs a new GregorianCalendar representing midnight on the 168 * given date with the default time zone and locale. 169 * @param year corresponds to the YEAR time field. 170 * @param month corresponds to the MONTH time field. 171 * @param day corresponds to the DAY time field. 172 * @param hour corresponds to the HOUR_OF_DAY time field. 173 * @param minute corresponds to the MINUTE time field. 174 * @param second corresponds to the SECOND time field. 175 */ GregorianCalendar(int year, int month, int day, int hour, int minute, int second)176 public GregorianCalendar(int year, int month, int day, 177 int hour, int minute, int second) 178 { 179 super(); 180 set(year, month, day, hour, minute, second); 181 } 182 183 /** 184 * Sets the date of the switch from Julian dates to Gregorian dates. 185 * You can use <code>new Date(Long.MAX_VALUE)</code> to use a pure 186 * Julian calendar, or <code>Long.MIN_VALUE</code> for a pure Gregorian 187 * calendar. 188 * @param date the date of the change. 189 */ setGregorianChange(Date date)190 public void setGregorianChange(Date date) 191 { 192 gregorianCutover = date.getTime(); 193 } 194 195 /** 196 * Gets the date of the switch from Julian dates to Gregorian dates. 197 * @return the date of the change. 198 */ getGregorianChange()199 public final Date getGregorianChange() 200 { 201 return new Date(gregorianCutover); 202 } 203 204 /** 205 * Determines if the given year is a leap year. The result is 206 * undefined if the gregorian change took place in 1800, so that 207 * the end of february is skiped and you give that year 208 * (well...).<br> 209 * 210 * The year should be positive and you can't give an ERA. But 211 * remember that before 4 BC there wasn't a consistent leap year 212 * rule, so who cares. 213 * 214 * @param year a year use nonnegative value for BC. 215 * @return true, if the given year is a leap year, false otherwise. */ isLeapYear(int year)216 public boolean isLeapYear(int year) 217 { 218 if ((year & 3) != 0) 219 // Only years divisible by 4 can be leap years 220 return false; 221 222 // compute the linear day of the 29. February of that year. 223 // The 13 is the number of days, that were omitted in the Gregorian 224 // Calender until the epoch. 225 int julianDay = (((year-1) * (365*4+1)) >> 2) + (31+29 - 226 (((1970-1) * (365*4+1)) / 4 + 1 - 13)); 227 228 // If that day is smaller than the gregorianChange the julian 229 // rule applies: This is a leap year since it is divisible by 4. 230 if (julianDay * (24 * 60 * 60 * 1000L) < gregorianCutover) 231 return true; 232 233 return ((year % 100) != 0 || (year % 400) == 0); 234 } 235 236 /** 237 * Get the linear time in milliseconds since the epoch. If you 238 * specify a nonpositive year it is interpreted as BC as 239 * following: 0 is 1 BC, -1 is 2 BC and so on. The date is 240 * interpreted as gregorian if the change occurred before that date. 241 * 242 * @param year the year of the date. 243 * @param dayOfYear the day of year of the date; 1 based. 244 * @param millis the millisecond in that day. 245 * @return the days since the epoch, may be negative. */ getLinearTime(int year, int dayOfYear, int millis)246 private long getLinearTime(int year, int dayOfYear, int millis) 247 { 248 // The 13 is the number of days, that were omitted in the Gregorian 249 // Calender until the epoch. 250 // We shift right by 2 instead of dividing by 4, to get correct 251 // results for negative years (and this is even more efficient). 252 int julianDay = ((year * (365 * 4 + 1)) >> 2) + dayOfYear - 253 ((1970 * (365 * 4 + 1)) / 4 + 1 - 13); 254 long time = julianDay * (24 * 60 * 60 * 1000L) + millis; 255 256 if (time >= gregorianCutover) 257 { 258 // subtract the days that are missing in gregorian calendar 259 // with respect to julian calendar. 260 // 261 // Okay, here we rely on the fact that the gregorian 262 // calendar was introduced in the AD era. This doesn't work 263 // with negative years. 264 // 265 // The additional leap year factor accounts for the fact that 266 // a leap day is not seen on Jan 1 of the leap year. 267 // And on and after the leap day, the leap day has already been 268 // included in dayOfYear. 269 int gregOffset = (year / 400) - (year / 100) + 2; 270 if (isLeapYear (year, true)) 271 --gregOffset; 272 time += gregOffset * (24 * 60 * 60 * 1000L); 273 } 274 return time; 275 } 276 getWeekDay(int year, int dayOfYear)277 private int getWeekDay(int year, int dayOfYear) 278 { 279 int day = 280 (int) (getLinearTime(year, dayOfYear, 0) / (24 * 60 * 60 * 1000L)); 281 282 // The epoch was a thursday. 283 int weekday = (day + THURSDAY) % 7; 284 if (weekday <= 0) 285 weekday += 7; 286 return weekday; 287 } 288 289 /** 290 * Calculate the dayOfYear from the fields array. 291 * The relativeDays is used, to account for weeks that begin before 292 * the gregorian change and end after it.<br> 293 * 294 * We return two values, the first is used to determine, if we 295 * should use Gregorian calendar or Julian calendar, in case of 296 * the change year, the second is a relative day after the given 297 * day. This is necessary for week calculation in the year in 298 * which gregorian change occurs. <br> 299 * 300 * @param year the year, negative for BC. 301 * @return an array of two int values, the first containing a reference 302 * day of current year, the second a relative count since this reference 303 * day. */ getDayOfYear(int year)304 private int[] getDayOfYear(int year) 305 { 306 if (isSet[MONTH]) 307 { 308 int dayOfYear; 309 if (fields[MONTH] > FEBRUARY) 310 { 311 312 // The months after February are regular: 313 // 9 is an offset found by try and error. 314 dayOfYear = (fields[MONTH] * (31 + 30 + 31 + 30 + 31) - 9) / 5; 315 if (isLeapYear(year)) 316 dayOfYear++; 317 } 318 else 319 dayOfYear = 31 * fields[MONTH]; 320 321 if (isSet[DAY_OF_MONTH]) 322 { 323 return new int[] 324 { 325 dayOfYear + fields[DAY_OF_MONTH], 0}; 326 } 327 if (isSet[WEEK_OF_MONTH] && isSet[DAY_OF_WEEK]) 328 { 329 // the weekday of the first day in that month is: 330 int weekday = getWeekDay(year, ++dayOfYear); 331 332 return new int[] 333 { 334 dayOfYear, 335 // the day of week in the first week 336 // (weeks starting on sunday) is: 337 fields[DAY_OF_WEEK] - weekday + 338 // Now jump to the right week and correct the possible 339 // error made by assuming sunday is the first week day. 340 7 * (fields[WEEK_OF_MONTH] 341 + (fields[DAY_OF_WEEK] < getFirstDayOfWeek()? 0 : -1) 342 + (weekday < getFirstDayOfWeek()? -1 : 0))}; 343 } 344 if (isSet[DAY_OF_WEEK] && isSet[DAY_OF_WEEK_IN_MONTH]) 345 { 346 // the weekday of the first day in that month is: 347 int weekday = getWeekDay(year, ++dayOfYear); 348 return new int[] { 349 dayOfYear, 350 fields[DAY_OF_WEEK] - weekday + 351 7 * (fields[DAY_OF_WEEK_IN_MONTH] 352 + (fields[DAY_OF_WEEK] < weekday ? 0 : -1))}; 353 } 354 } 355 356 // MONTH + something did not succeed. 357 if (isSet[DAY_OF_YEAR]) 358 { 359 return new int[] {0, fields[DAY_OF_YEAR]}; 360 } 361 362 if (isSet[DAY_OF_WEEK] && isSet[WEEK_OF_YEAR]) 363 { 364 int dayOfYear = getMinimalDaysInFirstWeek(); 365 // the weekday of the day, that begins the first week 366 // in that year is: 367 int weekday = getWeekDay(year, dayOfYear); 368 369 return new int[] { 370 dayOfYear, 371 // the day of week in the first week 372 // (weeks starting on sunday) is: 373 fields[DAY_OF_WEEK] - weekday 374 // Now jump to the right week and correct the possible 375 // error made by assuming sunday is the first week day. 376 + 7 * (fields[WEEK_OF_YEAR] 377 + (fields[DAY_OF_WEEK] < getFirstDayOfWeek()? 0 : -1) 378 + (weekday < getFirstDayOfWeek()? -1 : 0))}; 379 } 380 381 // As last resort return Jan, 1st. 382 return new int[] {1, 0}; 383 } 384 385 /** 386 * Converts the time field values (<code>fields</code>) to 387 * milliseconds since the epoch UTC (<code>time</code>). 388 */ computeTime()389 protected synchronized void computeTime() 390 { 391 int era = isSet[ERA] ? fields[ERA] : AD; 392 int year = isSet[YEAR] ? fields[YEAR] : 1970; 393 if (era == BC) 394 year = 1 - year; 395 396 int[] daysOfYear = getDayOfYear(year); 397 398 int hour = 0; 399 if (isSet[HOUR_OF_DAY]) 400 hour = fields[HOUR_OF_DAY]; 401 else if (isSet[HOUR]) 402 { 403 hour = fields[HOUR]; 404 if (isSet[AM_PM] && fields[AM_PM] == PM) 405 if (hour != 12) /* not Noon */ 406 hour += 12; 407 /* Fix the problem of the status of 12:00 AM (midnight). */ 408 if (isSet[AM_PM] && fields[AM_PM] == AM && hour == 12) 409 hour = 0; 410 } 411 412 int minute = isSet[MINUTE] ? fields[MINUTE] : 0; 413 int second = isSet[SECOND] ? fields[SECOND] : 0; 414 int millis = isSet[MILLISECOND] ? fields[MILLISECOND] : 0; 415 int millisInDay; 416 417 if (isLenient()) 418 { 419 // prevent overflow 420 long allMillis = (((hour * 60L) + minute) * 60L + second) * 1000L 421 + millis; 422 daysOfYear[1] += allMillis / (24 * 60 * 60 * 1000L); 423 millisInDay = (int) (allMillis % (24 * 60 * 60 * 1000L)); 424 } 425 else 426 { 427 if (hour < 0 || hour >= 24 || minute < 0 || minute > 59 428 || second < 0 || second > 59 || millis < 0 || millis >= 1000) 429 throw new IllegalArgumentException(); 430 millisInDay = (((hour * 60) + minute) * 60 + second) * 1000 + millis; 431 } 432 time = getLinearTime(year, daysOfYear[0], millisInDay); 433 434 // Add the relative days after calculating the linear time, to 435 // get right behaviour when jumping over the gregorianCutover. 436 time += daysOfYear[1] * (24 * 60 * 60 * 1000L); 437 438 439 TimeZone zone = getTimeZone(); 440 int rawOffset = isSet[ZONE_OFFSET] 441 ? fields[ZONE_OFFSET] : zone.getRawOffset(); 442 443 int dayOfYear = daysOfYear[0] + daysOfYear[1]; 444 int month = (dayOfYear * 5 + 3) / (31 + 30 + 31 + 30 + 31); 445 int day = (6 + (dayOfYear * 5 + 3) % (31 + 30 + 31 + 30 + 31)) / 5; 446 int weekday = ((int) (time / (24 * 60 * 60 * 1000L)) + THURSDAY) % 7; 447 if (weekday <= 0) 448 weekday += 7; 449 int dstOffset = isSet[DST_OFFSET] 450 ? fields[DST_OFFSET] : (zone.getOffset((year < 0) ? BC : AD, 451 (year < 0) ? 1 - year : year, 452 month, day, weekday, millisInDay) 453 - zone.getRawOffset()); 454 time -= rawOffset + dstOffset; 455 isTimeSet = true; 456 } 457 458 /** 459 * Determines if the given year is a leap year. 460 * 461 * The year should be positive and you can't give an ERA. But 462 * remember that before 4 BC there wasn't a consistent leap year 463 * rule, so who cares. 464 * 465 * @param year a year use nonnegative value for BC. 466 * @param gregorian if true, use gregorian leap year rule. 467 * @return true, if the given year is a leap year, false otherwise. */ isLeapYear(int year, boolean gregorian)468 private boolean isLeapYear(int year, boolean gregorian) 469 { 470 if ((year & 3) != 0) 471 // Only years divisible by 4 can be leap years 472 return false; 473 474 if (!gregorian) 475 return true; 476 477 // We rely on AD area here. 478 return ((year % 100) != 0 || (year % 400) == 0); 479 } 480 481 /** 482 * Get the linear day in days since the epoch, using the 483 * Julian or Gregorian calendar as specified. If you specify a 484 * nonpositive year it is interpreted as BC as following: 0 is 1 485 * BC, -1 is 2 BC and so on. 486 * 487 * @param year the year of the date. 488 * @param dayOfYear the day of year of the date; 1 based. 489 * @param gregorian True, if we should use Gregorian rules. 490 * @return the days since the epoch, may be negative. */ getLinearDay(int year, int dayOfYear, boolean gregorian)491 private int getLinearDay(int year, int dayOfYear, boolean gregorian) 492 { 493 // The 13 is the number of days, that were omitted in the Gregorian 494 // Calender until the epoch. 495 // We shift right by 2 instead of dividing by 4, to get correct 496 // results for negative years (and this is even more efficient). 497 int julianDay = ((year * (365 * 4 + 1)) >> 2) + dayOfYear - 498 ((1970 * (365 * 4 + 1)) / 4 + 1 - 13); 499 500 if (gregorian) 501 { 502 // subtract the days that are missing in gregorian calendar 503 // with respect to julian calendar. 504 // 505 // Okay, here we rely on the fact that the gregorian 506 // calendar was introduced in the AD era. This doesn't work 507 // with negative years. 508 // 509 // The additional leap year factor accounts for the fact that 510 // a leap day is not seen on Jan 1 of the leap year. 511 int gregOffset = (year / 400) - (year / 100) + 2; 512 if (isLeapYear (year, true) && dayOfYear < 31 + 29) 513 --gregOffset; 514 julianDay += gregOffset; 515 } 516 return julianDay; 517 } 518 519 /** 520 * Converts the given linear day into era, year, month, 521 * day_of_year, day_of_month, day_of_week, and writes the result 522 * into the fields array. 523 * @param day the linear day. 524 */ calculateDay(int day, boolean gregorian)525 private void calculateDay(int day, boolean gregorian) 526 { 527 // the epoch is a Thursday. 528 int weekday = (day + THURSDAY) % 7; 529 if (weekday <= 0) 530 weekday += 7; 531 fields[DAY_OF_WEEK] = weekday; 532 533 // get a first approximation of the year. This may be one 534 // year to big. 535 int year = 1970 + (gregorian 536 ? ((day - 100) * 400) / (365 * 400 + 100 - 4 + 1) 537 : ((day - 100) * 4) / (365 * 4 + 1)); 538 if (day >= 0) 539 year++; 540 541 int firstDayOfYear = getLinearDay(year, 1, gregorian); 542 543 // Now look in which year day really lies. 544 if (day < firstDayOfYear) 545 { 546 year--; 547 firstDayOfYear = getLinearDay(year, 1, gregorian); 548 } 549 550 day -= firstDayOfYear - 1; // day of year, one based. 551 552 fields[DAY_OF_YEAR] = day; 553 if (year <= 0) 554 { 555 fields[ERA] = BC; 556 fields[YEAR] = 1 - year; 557 } 558 else 559 { 560 fields[ERA] = AD; 561 fields[YEAR] = year; 562 } 563 564 int leapday = isLeapYear(year, gregorian) ? 1 : 0; 565 if (day <= 31 + 28 + leapday) 566 { 567 fields[MONTH] = day / 32; // 31->JANUARY, 32->FEBRUARY 568 fields[DAY_OF_MONTH] = day - 31 * fields[MONTH]; 569 } 570 else 571 { 572 // A few more magic formulas 573 int scaledDay = (day - leapday) * 5 + 8; 574 fields[MONTH] = scaledDay / (31 + 30 + 31 + 30 + 31); 575 fields[DAY_OF_MONTH] = (scaledDay % (31 + 30 + 31 + 30 + 31)) / 5 + 1; 576 } 577 } 578 579 /** 580 * Converts the milliseconds since the epoch UTC 581 * (<code>time</code>) to time fields 582 * (<code>fields</code>). 583 */ computeFields()584 protected synchronized void computeFields() 585 { 586 boolean gregorian = (time >= gregorianCutover); 587 588 TimeZone zone = getTimeZone(); 589 fields[ZONE_OFFSET] = zone.getRawOffset(); 590 long localTime = time + fields[ZONE_OFFSET]; 591 592 int day = (int) (localTime / (24 * 60 * 60 * 1000L)); 593 int millisInDay = (int) (localTime % (24 * 60 * 60 * 1000L)); 594 if (millisInDay < 0) 595 { 596 millisInDay += (24 * 60 * 60 * 1000); 597 day--; 598 } 599 600 calculateDay(day, gregorian); 601 fields[DST_OFFSET] = 602 zone.getOffset(fields[ERA], fields[YEAR], fields[MONTH], 603 fields[DAY_OF_MONTH], fields[DAY_OF_WEEK], 604 millisInDay) - fields[ZONE_OFFSET]; 605 606 millisInDay += fields[DST_OFFSET]; 607 if (millisInDay >= 24 * 60 * 60 * 1000) 608 { 609 millisInDay -= 24 * 60 * 60 * 1000; 610 calculateDay(++day, gregorian); 611 } 612 613 fields[DAY_OF_WEEK_IN_MONTH] = (fields[DAY_OF_MONTH] + 6) / 7; 614 615 // which day of the week are we (0..6), relative to getFirstDayOfWeek 616 int relativeWeekday = (7 + fields[DAY_OF_WEEK] - getFirstDayOfWeek()) % 7; 617 618 fields[WEEK_OF_MONTH] = (fields[DAY_OF_MONTH] - relativeWeekday + 12) / 7; 619 620 int weekOfYear = (fields[DAY_OF_YEAR] - relativeWeekday + 6) / 7; 621 622 // Do the Correction: getMinimalDaysInFirstWeek() is always in the 623 // first week. 624 int minDays = getMinimalDaysInFirstWeek(); 625 int firstWeekday = 626 (7 + getWeekDay(fields[YEAR], minDays) - getFirstDayOfWeek()) % 7; 627 if (minDays - firstWeekday < 1) 628 weekOfYear++; 629 fields[WEEK_OF_YEAR] = weekOfYear; 630 631 632 int hourOfDay = millisInDay / (60 * 60 * 1000); 633 fields[AM_PM] = (hourOfDay < 12) ? AM : PM; 634 int hour = hourOfDay % 12; 635 fields[HOUR] = (hour == 0) ? 12 : hour; 636 fields[HOUR_OF_DAY] = hourOfDay; 637 millisInDay %= (60 * 60 * 1000); 638 fields[MINUTE] = millisInDay / (60 * 1000); 639 millisInDay %= (60 * 1000); 640 fields[SECOND] = millisInDay / (1000); 641 fields[MILLISECOND] = millisInDay % 1000; 642 643 644 areFieldsSet = isSet[ERA] = isSet[YEAR] = isSet[MONTH] = 645 isSet[WEEK_OF_YEAR] = isSet[WEEK_OF_MONTH] = 646 isSet[DAY_OF_MONTH] = isSet[DAY_OF_YEAR] = isSet[DAY_OF_WEEK] = 647 isSet[DAY_OF_WEEK_IN_MONTH] = isSet[AM_PM] = isSet[HOUR] = 648 isSet[HOUR_OF_DAY] = isSet[MINUTE] = isSet[SECOND] = 649 isSet[MILLISECOND] = isSet[ZONE_OFFSET] = isSet[DST_OFFSET] = true; 650 651 } 652 653 /** 654 * Compares the given calender with this. 655 * @param o the object to that we should compare. 656 * @return true, if the given object is a calendar, that represents 657 * the same time (but doesn't necessary have the same fields). 658 * @XXX Should we check if time zones, locale, cutover etc. are equal? 659 */ equals(Object o)660 public boolean equals(Object o) 661 { 662 if (!(o instanceof GregorianCalendar)) 663 return false; 664 665 GregorianCalendar cal = (GregorianCalendar) o; 666 return (cal.getTimeInMillis() == getTimeInMillis()); 667 } 668 669 // /** 670 // * Compares the given calender with this. 671 // * @param o the object to that we should compare. 672 // * @return true, if the given object is a calendar, and this calendar 673 // * represents a smaller time than the calender o. 674 // */ 675 // public boolean before(Object o) { 676 // if (!(o instanceof GregorianCalendar)) 677 // return false; 678 679 // GregorianCalendar cal = (GregorianCalendar) o; 680 // return (cal.getTimeInMillis() < getTimeInMillis()); 681 // } 682 683 // /** 684 // * Compares the given calender with this. 685 // * @param o the object to that we should compare. 686 // * @return true, if the given object is a calendar, and this calendar 687 // * represents a bigger time than the calender o. 688 // */ 689 // public boolean after(Object o) { 690 // if (!(o instanceof GregorianCalendar)) 691 // return false; 692 693 // GregorianCalendar cal = (GregorianCalendar) o; 694 // return (cal.getTimeInMillis() > getTimeInMillis()); 695 // } 696 697 /** 698 * Adds the specified amount of time to the given time field. The 699 * amount may be negative to subtract the time. If the field overflows 700 * it does what you expect: Jan, 25 + 10 Days is Feb, 4. 701 * @param field the time field. One of the time field constants. 702 * @param amount the amount of time. 703 */ add(int field, int amount)704 public void add(int field, int amount) 705 { 706 switch (field) 707 { 708 case YEAR: 709 complete(); 710 fields[YEAR] += amount; 711 isTimeSet = false; 712 break; 713 case MONTH: 714 complete(); 715 int months = fields[MONTH] + amount; 716 fields[YEAR] += months / 12; 717 fields[MONTH] = months % 12; 718 if (fields[MONTH] < 0) 719 { 720 fields[MONTH] += 12; 721 fields[YEAR]--; 722 } 723 isTimeSet = false; 724 int maxDay = getActualMaximum(DAY_OF_MONTH); 725 if (fields[DAY_OF_MONTH] > maxDay) 726 { 727 fields[DAY_OF_MONTH] = maxDay; 728 isTimeSet = false; 729 } 730 break; 731 case DAY_OF_MONTH: 732 case DAY_OF_YEAR: 733 case DAY_OF_WEEK: 734 if (!isTimeSet) 735 computeTime(); 736 time += amount * (24 * 60 * 60 * 1000L); 737 areFieldsSet = false; 738 break; 739 case WEEK_OF_YEAR: 740 case WEEK_OF_MONTH: 741 case DAY_OF_WEEK_IN_MONTH: 742 if (!isTimeSet) 743 computeTime(); 744 time += amount * (7 * 24 * 60 * 60 * 1000L); 745 areFieldsSet = false; 746 break; 747 case AM_PM: 748 if (!isTimeSet) 749 computeTime(); 750 time += amount * (12 * 60 * 60 * 1000L); 751 areFieldsSet = false; 752 break; 753 case HOUR: 754 case HOUR_OF_DAY: 755 if (!isTimeSet) 756 computeTime(); 757 time += amount * (60 * 60 * 1000L); 758 areFieldsSet = false; 759 break; 760 case MINUTE: 761 if (!isTimeSet) 762 computeTime(); 763 time += amount * (60 * 1000L); 764 areFieldsSet = false; 765 break; 766 case SECOND: 767 if (!isTimeSet) 768 computeTime(); 769 time += amount * (1000L); 770 areFieldsSet = false; 771 break; 772 case MILLISECOND: 773 if (!isTimeSet) 774 computeTime(); 775 time += amount; 776 areFieldsSet = false; 777 break; 778 case ZONE_OFFSET: 779 complete(); 780 fields[ZONE_OFFSET] += amount; 781 time -= amount; 782 break; 783 case DST_OFFSET: 784 complete(); 785 fields[DST_OFFSET] += amount; 786 isTimeSet = false; 787 break; 788 default: 789 throw new IllegalArgumentException 790 ("Unknown Calendar field: " + field); 791 } 792 } 793 794 795 /** 796 * Rolls the specified time field up or down. This means add one 797 * to the specified field, but don't change the other fields. If 798 * the maximum for this field is reached, start over with the 799 * minimum value. 800 * 801 * <strong>Note:</strong> There may be situation, where the other 802 * fields must be changed, e.g rolling the month on May, 31. 803 * The date June, 31 is automatically converted to July, 1. 804 * This requires lenient settings. 805 * 806 * @param field the time field. One of the time field constants. 807 * @param up the direction, true for up, false for down. 808 */ roll(int field, boolean up)809 public void roll(int field, boolean up) 810 { 811 roll(field, up ? 1 : -1); 812 } 813 cleanUpAfterRoll(int field, int delta)814 private void cleanUpAfterRoll(int field, int delta) 815 { 816 switch (field) 817 { 818 case ERA: 819 case YEAR: 820 case MONTH: 821 // check that day of month is still in correct range 822 if (fields[DAY_OF_MONTH] > getActualMaximum(DAY_OF_MONTH)) 823 fields[DAY_OF_MONTH] = getActualMaximum(DAY_OF_MONTH); 824 isTimeSet = false; 825 isSet[WEEK_OF_MONTH] = false; 826 isSet[DAY_OF_WEEK] = false; 827 isSet[DAY_OF_WEEK_IN_MONTH] = false; 828 isSet[DAY_OF_YEAR] = false; 829 isSet[WEEK_OF_YEAR] = false; 830 break; 831 832 case DAY_OF_MONTH: 833 isSet[WEEK_OF_MONTH] = false; 834 isSet[DAY_OF_WEEK] = false; 835 isSet[DAY_OF_WEEK_IN_MONTH] = false; 836 isSet[DAY_OF_YEAR] = false; 837 isSet[WEEK_OF_YEAR] = false; 838 time += delta * (24 * 60 * 60 * 1000L); 839 break; 840 841 case WEEK_OF_MONTH: 842 isSet[DAY_OF_MONTH] = false; 843 isSet[DAY_OF_WEEK_IN_MONTH] = false; 844 isSet[DAY_OF_YEAR] = false; 845 isSet[WEEK_OF_YEAR] = false; 846 time += delta * (7 * 24 * 60 * 60 * 1000L); 847 break; 848 case DAY_OF_WEEK_IN_MONTH: 849 isSet[DAY_OF_MONTH] = false; 850 isSet[WEEK_OF_MONTH] = false; 851 isSet[DAY_OF_YEAR] = false; 852 isSet[WEEK_OF_YEAR] = false; 853 time += delta * (7 * 24 * 60 * 60 * 1000L); 854 break; 855 case DAY_OF_YEAR: 856 isSet[MONTH] = false; 857 isSet[DAY_OF_MONTH] = false; 858 isSet[WEEK_OF_MONTH] = false; 859 isSet[DAY_OF_WEEK_IN_MONTH] = false; 860 isSet[DAY_OF_WEEK] = false; 861 isSet[WEEK_OF_YEAR] = false; 862 time += delta * (24 * 60 * 60 * 1000L); 863 break; 864 case WEEK_OF_YEAR: 865 isSet[MONTH] = false; 866 isSet[DAY_OF_MONTH] = false; 867 isSet[WEEK_OF_MONTH] = false; 868 isSet[DAY_OF_WEEK_IN_MONTH] = false; 869 isSet[DAY_OF_YEAR] = false; 870 time += delta * (7 * 24 * 60 * 60 * 1000L); 871 break; 872 873 case AM_PM: 874 isSet[HOUR_OF_DAY] = false; 875 time += delta * (12 * 60 * 60 * 1000L); 876 break; 877 case HOUR: 878 isSet[HOUR_OF_DAY] = false; 879 time += delta * (60 * 60 * 1000L); 880 break; 881 case HOUR_OF_DAY: 882 isSet[HOUR] = false; 883 isSet[AM_PM] = false; 884 time += delta * (60 * 60 * 1000L); 885 break; 886 887 case MINUTE: 888 time += delta * (60 * 1000L); 889 break; 890 case SECOND: 891 time += delta * (1000L); 892 break; 893 case MILLISECOND: 894 time += delta; 895 break; 896 } 897 } 898 899 /** 900 * Rolls the specified time field by the given amount. This means 901 * add amount to the specified field, but don't change the other 902 * fields. If the maximum for this field is reached, start over 903 * with the minimum value and vice versa for negative amounts. 904 * 905 * <strong>Note:</strong> There may be situation, where the other 906 * fields must be changed, e.g rolling the month on May, 31. 907 * The date June, 31 is automatically corrected to June, 30. 908 * 909 * @param field the time field. One of the time field constants. 910 * @param amount the amount by which we should roll. 911 */ roll(int field, int amount)912 public void roll(int field, int amount) 913 { 914 switch (field) 915 { 916 case DAY_OF_WEEK: 917 // day of week is special: it rolls automatically 918 add(field, amount); 919 return; 920 case ZONE_OFFSET: 921 case DST_OFFSET: 922 throw new IllegalArgumentException("Can't roll time zone"); 923 } 924 complete(); 925 int min = getActualMinimum(field); 926 int range = getActualMaximum(field) - min + 1; 927 int oldval = fields[field]; 928 int newval = (oldval - min + range + amount) % range + min; 929 if (newval < min) 930 newval += range; 931 fields[field] = newval; 932 cleanUpAfterRoll(field, newval - oldval); 933 } 934 935 private static final int[] minimums = 936 { BC, 1, 0, 0, 1, 1, 1, SUNDAY, 1, 937 AM, 1, 0, 1, 1, 1, -(12*60*60*1000), 0 }; 938 939 private static final int[] maximums = 940 { AD, 5000000, 11, 53, 5, 31, 366, SATURDAY, 5, 941 PM, 12, 23, 59, 59, 999, +(12*60*60*1000), (12*60*60*1000) }; 942 943 /** 944 * Gets the smallest value that is allowed for the specified field. 945 * @param field the time field. One of the time field constants. 946 * @return the smallest value. 947 */ getMinimum(int field)948 public int getMinimum(int field) 949 { 950 return minimums[field]; 951 } 952 953 /** 954 * Gets the biggest value that is allowed for the specified field. 955 * @param field the time field. One of the time field constants. 956 * @return the biggest value. 957 */ getMaximum(int field)958 public int getMaximum(int field) 959 { 960 return maximums[field]; 961 } 962 963 964 /** 965 * Gets the greatest minimum value that is allowed for the specified field. 966 * @param field the time field. One of the time field constants. 967 * @return the greatest minimum value. 968 */ getGreatestMinimum(int field)969 public int getGreatestMinimum(int field) 970 { 971 if (field == WEEK_OF_YEAR) 972 return 1; 973 return minimums[field]; 974 } 975 976 /** 977 * Gets the smallest maximum value that is allowed for the 978 * specified field. For example this is 28 for DAY_OF_MONTH. 979 * @param field the time field. One of the time field constants. 980 * @return the least maximum value. 981 * @since jdk1.2 982 */ getLeastMaximum(int field)983 public int getLeastMaximum(int field) 984 { 985 switch (field) 986 { 987 case WEEK_OF_YEAR: 988 return 52; 989 case DAY_OF_MONTH: 990 return 28; 991 case DAY_OF_YEAR: 992 return 365; 993 case DAY_OF_WEEK_IN_MONTH: 994 case WEEK_OF_MONTH: 995 return 4; 996 default: 997 return maximums[field]; 998 } 999 } 1000 1001 /** 1002 * Gets the actual minimum value that is allowed for the specified field. 1003 * This value is dependent on the values of the other fields. Note that 1004 * this calls <code>complete()</code> if not enough fields are set. This 1005 * can have ugly side effects. 1006 * @param field the time field. One of the time field constants. 1007 * @return the actual minimum value. 1008 * @since jdk1.2 1009 */ getActualMinimum(int field)1010 public int getActualMinimum(int field) 1011 { 1012 if (field == WEEK_OF_YEAR) 1013 { 1014 int min = getMinimalDaysInFirstWeek(); 1015 if (min == 0) 1016 return 1; 1017 if (!areFieldsSet || !isSet[ERA] || !isSet[YEAR]) 1018 complete(); 1019 1020 int year = fields[ERA] == AD ? fields[YEAR] : 1 - fields[YEAR]; 1021 int weekday = getWeekDay(year, min); 1022 if ((7 + weekday - getFirstDayOfWeek()) % 7 >= min - 1) 1023 return 1; 1024 return 0; 1025 } 1026 return minimums[field]; 1027 } 1028 1029 /** 1030 * Gets the actual maximum value that is allowed for the specified field. 1031 * This value is dependent on the values of the other fields. Note that 1032 * this calls <code>complete()</code> if not enough fields are set. This 1033 * can have ugly side effects. 1034 * @param field the time field. One of the time field constants. 1035 * @return the actual maximum value. 1036 */ getActualMaximum(int field)1037 public int getActualMaximum(int field) 1038 { 1039 switch (field) 1040 { 1041 case WEEK_OF_YEAR: 1042 { 1043 if (!areFieldsSet || !isSet[ERA] || !isSet[YEAR]) 1044 complete(); 1045 // This is wrong for the year that contains the gregorian change. 1046 // I.e it gives the weeks in the julian year or in the gregorian 1047 // year in that case. 1048 int year = fields[ERA] == AD ? fields[YEAR] : 1 - fields[YEAR]; 1049 int lastDay = isLeapYear(year) ? 366 : 365; 1050 int weekday = getWeekDay(year, lastDay); 1051 int week = (lastDay + 6 1052 - (7 + weekday - getFirstDayOfWeek()) % 7) / 7; 1053 1054 int minimalDays = getMinimalDaysInFirstWeek(); 1055 int firstWeekday = getWeekDay(year, minimalDays); 1056 if (minimalDays - (7 + firstWeekday - getFirstDayOfWeek()) % 7 < 1) 1057 return week + 1; 1058 } 1059 case DAY_OF_MONTH: 1060 { 1061 if (!areFieldsSet || !isSet[MONTH]) 1062 complete(); 1063 int month = fields[MONTH]; 1064 // If you change this, you should also change 1065 // SimpleTimeZone.getDaysInMonth(); 1066 if (month == FEBRUARY) 1067 { 1068 if (!isSet[YEAR] || !isSet[ERA]) 1069 complete(); 1070 int year = fields[ERA] == AD ? fields[YEAR] : 1 - fields[YEAR]; 1071 return isLeapYear(year) ? 29 : 28; 1072 } 1073 else if (month < AUGUST) 1074 return 31 - (month & 1); 1075 else 1076 return 30 + (month & 1); 1077 } 1078 case DAY_OF_YEAR: 1079 { 1080 if (!areFieldsSet || !isSet[ERA] || !isSet[YEAR]) 1081 complete(); 1082 int year = fields[ERA] == AD ? fields[YEAR] : 1 - fields[YEAR]; 1083 return isLeapYear(year) ? 366 : 365; 1084 } 1085 case DAY_OF_WEEK_IN_MONTH: 1086 { 1087 // This is wrong for the month that contains the gregorian change. 1088 int daysInMonth = getActualMaximum(DAY_OF_MONTH); 1089 // That's black magic, I know 1090 return (daysInMonth - (fields[DAY_OF_MONTH] - 1) % 7 + 6) / 7; 1091 } 1092 case WEEK_OF_MONTH: 1093 { 1094 int daysInMonth = getActualMaximum(DAY_OF_MONTH); 1095 int weekday = (daysInMonth - fields[DAY_OF_MONTH] 1096 + fields[DAY_OF_WEEK] - SUNDAY) % 7 + SUNDAY; 1097 return (daysInMonth + 6 1098 - (7 + weekday - getFirstDayOfWeek()) % 7) / 7; 1099 } 1100 default: 1101 return maximums[field]; 1102 } 1103 } 1104 } 1105