FSF GCC merge 02/23/03
[official-gcc.git] / libjava / java / util / GregorianCalendar.java
blobb01d971edca889b13191e5f7518232f46dc115eb
1 /* java.util.GregorianCalendar
2 Copyright (C) 1998, 1999, 2001, 2002 Free Software Foundation, Inc.
4 This file is part of GNU Classpath.
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.
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.
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.
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.
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. */
39 package java.util;
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>
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
53 * There are to eras available for the Gregorian calendar, namely BC and AD.
55 * @see Calendar
56 * @see TimeZone
58 public class GregorianCalendar extends Calendar
60 /**
61 * Constant representing the era BC (before Christ).
63 public static final int BC = 0;
65 /**
66 * Constant representing the era AD (Anno Domini).
68 public static final int AD = 1;
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).
76 private long gregorianCutover;
78 static final long serialVersionUID = -8125100834729963327L;
80 /**
81 * The name of the resource bundle.
83 private static final String bundleName = "gnu.java.locale.Calendar";
85 /**
86 * Constructs a new GregorianCalender representing the current
87 * time, using the default time zone and the default locale.
89 public GregorianCalendar()
91 this(TimeZone.getDefault(), Locale.getDefault());
94 /**
95 * Constructs a new GregorianCalender representing the current
96 * time, using the specified time zone and the default locale.
97 * @param zone a time zone.
99 public GregorianCalendar(TimeZone zone)
101 this(zone, Locale.getDefault());
105 * Constructs a new GregorianCalender representing the current
106 * time, using the default time zone and the specified locale.
107 * @param locale a locale.
109 public GregorianCalendar(Locale locale)
111 this(TimeZone.getDefault(), locale);
115 * Constructs a new GregorianCalender representing the current
116 * time with the given time zone and the given locale.
117 * @param zone a time zone.
118 * @param locale a locale.
120 public GregorianCalendar(TimeZone zone, Locale locale)
122 super(zone, locale);
123 ResourceBundle rb = ResourceBundle.getBundle(bundleName, locale);
124 gregorianCutover = ((Date) rb.getObject("gregorianCutOver")).getTime();
125 setTimeInMillis(System.currentTimeMillis());
129 * Constructs a new GregorianCalendar representing midnight on the
130 * given date with the default time zone and locale.
131 * @param year corresponds to the YEAR time field.
132 * @param month corresponds to the MONTH time field.
133 * @param day corresponds to the DAY time field.
135 public GregorianCalendar(int year, int month, int day)
137 super();
138 set(year, month, day);
142 * Constructs a new GregorianCalendar representing midnight on the
143 * given date with the default time zone and locale.
144 * @param year corresponds to the YEAR time field.
145 * @param month corresponds to the MONTH time field.
146 * @param day corresponds to the DAY time field.
147 * @param hour corresponds to the HOUR_OF_DAY time field.
148 * @param minute corresponds to the MINUTE time field.
150 public GregorianCalendar(int year, int month, int day, int hour, int minute)
152 super();
153 set(year, month, day, hour, minute);
157 * Constructs a new GregorianCalendar representing midnight on the
158 * given date with the default time zone and locale.
159 * @param year corresponds to the YEAR time field.
160 * @param month corresponds to the MONTH time field.
161 * @param day corresponds to the DAY time field.
162 * @param hour corresponds to the HOUR_OF_DAY time field.
163 * @param minute corresponds to the MINUTE time field.
164 * @param second corresponds to the SECOND time field.
166 public GregorianCalendar(int year, int month, int day,
167 int hour, int minute, int second)
169 super();
170 set(year, month, day, hour, minute, second);
174 * Sets the date of the switch from Julian dates to Gregorian dates.
175 * You can use <code>new Date(Long.MAX_VALUE)</code> to use a pure
176 * Julian calendar, or <code>Long.MIN_VALUE</code> for a pure Gregorian
177 * calendar.
178 * @param date the date of the change.
180 public void setGregorianChange(Date date)
182 gregorianCutover = date.getTime();
186 * Gets the date of the switch from Julian dates to Gregorian dates.
187 * @return the date of the change.
189 public final Date getGregorianChange()
191 return new Date(gregorianCutover);
195 * Determines if the given year is a leap year. The result is
196 * undefined if the gregorian change took place in 1800, so that
197 * the end of february is skiped and you give that year
198 * (well...).<br>
200 * The year should be positive and you can't give an ERA. But
201 * remember that before 4 BC there wasn't a consistent leap year
202 * rule, so who cares.
204 * @param year a year use nonnegative value for BC.
205 * @return true, if the given year is a leap year, false otherwise. */
206 public boolean isLeapYear(int year)
208 if ((year & 3) != 0)
209 // Only years divisible by 4 can be leap years
210 return false;
212 // compute the linear day of the 29. February of that year.
213 // The 13 is the number of days, that were omitted in the Gregorian
214 // Calender until the epoch.
215 int julianDay = (((year-1) * (365*4+1)) >> 2) + (31+29 -
216 (((1970-1) * (365*4+1)) / 4 + 1 - 13));
218 // If that day is smaller than the gregorianChange the julian
219 // rule applies: This is a leap year since it is divisible by 4.
220 if (julianDay * (24 * 60 * 60 * 1000L) < gregorianCutover)
221 return true;
223 return ((year % 100) != 0 || (year % 400) == 0);
227 * Get the linear time in milliseconds since the epoch. If you
228 * specify a nonpositive year it is interpreted as BC as
229 * following: 0 is 1 BC, -1 is 2 BC and so on. The date is
230 * interpreted as gregorian if the change occurred before that date.
232 * @param year the year of the date.
233 * @param dayOfYear the day of year of the date; 1 based.
234 * @param millis the millisecond in that day.
235 * @return the days since the epoch, may be negative. */
236 private long getLinearTime(int year, int dayOfYear, int millis)
238 // The 13 is the number of days, that were omitted in the Gregorian
239 // Calender until the epoch.
240 // We shift right by 2 instead of dividing by 4, to get correct
241 // results for negative years (and this is even more efficient).
242 int julianDay = ((year * (365 * 4 + 1)) >> 2) + dayOfYear -
243 ((1970 * (365 * 4 + 1)) / 4 + 1 - 13);
244 long time = julianDay * (24 * 60 * 60 * 1000L) + millis;
246 if (time >= gregorianCutover)
248 // subtract the days that are missing in gregorian calendar
249 // with respect to julian calendar.
251 // Okay, here we rely on the fact that the gregorian
252 // calendar was introduced in the AD era. This doesn't work
253 // with negative years.
255 // The additional leap year factor accounts for the fact that
256 // a leap day is not seen on Jan 1 of the leap year.
257 int gregOffset = (year / 400) - (year / 100) + 2;
258 if (isLeapYear (year, true) && dayOfYear < 31 + 29)
259 --gregOffset;
260 time += gregOffset * (24 * 60 * 60 * 1000L);
262 return time;
265 private int getWeekDay(int year, int dayOfYear)
267 int day =
268 (int) (getLinearTime(year, dayOfYear, 0) / (24 * 60 * 60 * 1000L));
270 // The epoch was a thursday.
271 int weekday = (day + THURSDAY) % 7;
272 if (weekday <= 0)
273 weekday += 7;
274 return weekday;
278 * Calculate the dayOfYear from the fields array.
279 * The relativeDays is used, to account for weeks that begin before
280 * the gregorian change and end after it.<br>
282 * We return two values, the first is used to determine, if we
283 * should use Gregorian calendar or Julian calendar, in case of
284 * the change year, the second is a relative day after the given
285 * day. This is necessary for week calculation in the year in
286 * which gregorian change occurs. <br>
288 * @param year the year, negative for BC.
289 * @return an array of two int values, the first containing a reference
290 * day of current year, the second a relative count since this reference
291 * day. */
292 private int[] getDayOfYear(int year)
294 if (isSet[MONTH])
296 int dayOfYear;
297 if (fields[MONTH] > FEBRUARY)
300 // The months after February are regular:
301 // 9 is an offset found by try and error.
302 dayOfYear = (fields[MONTH] * (31 + 30 + 31 + 30 + 31) - 9) / 5;
303 if (isLeapYear(year))
304 dayOfYear++;
306 else
307 dayOfYear = 31 * fields[MONTH];
309 if (isSet[DAY_OF_MONTH])
311 return new int[]
313 dayOfYear + fields[DAY_OF_MONTH], 0};
315 if (isSet[WEEK_OF_MONTH] && isSet[DAY_OF_WEEK])
317 // the weekday of the first day in that month is:
318 int weekday = getWeekDay(year, ++dayOfYear);
320 return new int[]
322 dayOfYear,
323 // the day of week in the first week
324 // (weeks starting on sunday) is:
325 fields[DAY_OF_WEEK] - weekday +
326 // Now jump to the right week and correct the possible
327 // error made by assuming sunday is the first week day.
328 7 * (fields[WEEK_OF_MONTH]
329 + (fields[DAY_OF_WEEK] < getFirstDayOfWeek()? 0 : -1)
330 + (weekday < getFirstDayOfWeek()? -1 : 0))};
332 if (isSet[DAY_OF_WEEK] && isSet[DAY_OF_WEEK_IN_MONTH])
334 // the weekday of the first day in that month is:
335 int weekday = getWeekDay(year, ++dayOfYear);
336 return new int[] {
337 dayOfYear,
338 fields[DAY_OF_WEEK] - weekday +
339 7 * (fields[DAY_OF_WEEK_IN_MONTH]
340 + (fields[DAY_OF_WEEK] < weekday ? 0 : -1))};
344 // MONTH + something did not succeed.
345 if (isSet[DAY_OF_YEAR])
347 return new int[] {0, fields[DAY_OF_YEAR]};
350 if (isSet[DAY_OF_WEEK] && isSet[WEEK_OF_YEAR])
352 int dayOfYear = getMinimalDaysInFirstWeek();
353 // the weekday of the day, that begins the first week
354 // in that year is:
355 int weekday = getWeekDay(year, dayOfYear);
357 return new int[] {
358 dayOfYear,
359 // the day of week in the first week
360 // (weeks starting on sunday) is:
361 fields[DAY_OF_WEEK] - weekday
362 // Now jump to the right week and correct the possible
363 // error made by assuming sunday is the first week day.
364 + 7 * (fields[WEEK_OF_YEAR]
365 + (fields[DAY_OF_WEEK] < getFirstDayOfWeek()? 0 : -1)
366 + (weekday < getFirstDayOfWeek()? -1 : 0))};
369 // As last resort return Jan, 1st.
370 return new int[] {1, 0};
374 * Converts the time field values (<code>fields</code>) to
375 * milliseconds since the epoch UTC (<code>time</code>).
377 protected synchronized void computeTime()
379 int era = isSet[ERA] ? fields[ERA] : AD;
380 int year = isSet[YEAR] ? fields[YEAR] : 1970;
381 if (era == BC)
382 year = 1 - year;
384 int[] daysOfYear = getDayOfYear(year);
386 int hour = 0;
387 if (isSet[HOUR_OF_DAY])
388 hour = fields[HOUR_OF_DAY];
389 else if (isSet[HOUR])
391 hour = fields[HOUR];
392 if (isSet[AM_PM] && fields[AM_PM] == PM)
393 hour += 12;
396 int minute = isSet[MINUTE] ? fields[MINUTE] : 0;
397 int second = isSet[SECOND] ? fields[SECOND] : 0;
398 int millis = isSet[MILLISECOND] ? fields[MILLISECOND] : 0;
399 int millisInDay;
401 if (isLenient())
403 // prevent overflow
404 long allMillis = (((hour * 60L) + minute) * 60L + second) * 1000L
405 + millis;
406 daysOfYear[1] += allMillis / (24 * 60 * 60 * 1000L);
407 millisInDay = (int) (allMillis % (24 * 60 * 60 * 1000L));
409 else
411 if (hour < 0 || hour >= 24 || minute < 0 || minute > 59
412 || second < 0 || second > 59 || millis < 0 || millis >= 1000)
413 throw new IllegalArgumentException();
414 millisInDay = (((hour * 60) + minute) * 60 + second) * 1000 + millis;
416 time = getLinearTime(year, daysOfYear[0], millisInDay);
418 // Add the relative days after calculating the linear time, to
419 // get right behaviour when jumping over the gregorianCutover.
420 time += daysOfYear[1] * (24 * 60 * 60 * 1000L);
423 TimeZone zone = getTimeZone();
424 int rawOffset = isSet[ZONE_OFFSET]
425 ? fields[ZONE_OFFSET] : zone.getRawOffset();
427 int dayOfYear = daysOfYear[0] + daysOfYear[1];
428 int month = (dayOfYear * 5 + 3) / (31 + 30 + 31 + 30 + 31);
429 int day = (6 + (dayOfYear * 5 + 3) % (31 + 30 + 31 + 30 + 31)) / 5;
430 int weekday = ((int) (time / (24 * 60 * 60 * 1000L)) + THURSDAY) % 7;
431 if (weekday <= 0)
432 weekday += 7;
433 int dstOffset = isSet[DST_OFFSET]
434 ? fields[DST_OFFSET] : (zone.getOffset((year < 0) ? BC : AD,
435 (year < 0) ? 1 - year : year,
436 month, day, weekday, millisInDay)
437 - zone.getRawOffset());
438 time -= rawOffset + dstOffset;
439 isTimeSet = true;
443 * Determines if the given year is a leap year.
445 * The year should be positive and you can't give an ERA. But
446 * remember that before 4 BC there wasn't a consistent leap year
447 * rule, so who cares.
449 * @param year a year use nonnegative value for BC.
450 * @param gregorian if true, use gregorian leap year rule.
451 * @return true, if the given year is a leap year, false otherwise. */
452 private boolean isLeapYear(int year, boolean gregorian)
454 if ((year & 3) != 0)
455 // Only years divisible by 4 can be leap years
456 return false;
458 if (!gregorian)
459 return true;
461 // We rely on AD area here.
462 return ((year % 100) != 0 || (year % 400) == 0);
466 * Get the linear day in days since the epoch, using the
467 * Julian or Gregorian calendar as specified. If you specify a
468 * nonpositive year it is interpreted as BC as following: 0 is 1
469 * BC, -1 is 2 BC and so on.
471 * @param year the year of the date.
472 * @param dayOfYear the day of year of the date; 1 based.
473 * @param gregorian True, if we should use Gregorian rules.
474 * @return the days since the epoch, may be negative. */
475 private int getLinearDay(int year, int dayOfYear, boolean gregorian)
477 // The 13 is the number of days, that were omitted in the Gregorian
478 // Calender until the epoch.
479 // We shift right by 2 instead of dividing by 4, to get correct
480 // results for negative years (and this is even more efficient).
481 int julianDay = ((year * (365 * 4 + 1)) >> 2) + dayOfYear -
482 ((1970 * (365 * 4 + 1)) / 4 + 1 - 13);
484 if (gregorian)
486 // subtract the days that are missing in gregorian calendar
487 // with respect to julian calendar.
489 // Okay, here we rely on the fact that the gregorian
490 // calendar was introduced in the AD era. This doesn't work
491 // with negative years.
493 // The additional leap year factor accounts for the fact that
494 // a leap day is not seen on Jan 1 of the leap year.
495 int gregOffset = (year / 400) - (year / 100) + 2;
496 if (isLeapYear (year, true) && dayOfYear < 31 + 29)
497 --gregOffset;
498 julianDay += gregOffset;
500 return julianDay;
504 * Converts the given linear day into era, year, month,
505 * day_of_year, day_of_month, day_of_week, and writes the result
506 * into the fields array.
507 * @param day the linear day.
509 private void calculateDay(int day, boolean gregorian)
511 // the epoch is a Thursday.
512 int weekday = (day + THURSDAY) % 7;
513 if (weekday <= 0)
514 weekday += 7;
515 fields[DAY_OF_WEEK] = weekday;
517 // get a first approximation of the year. This may be one
518 // year to big.
519 int year = 1970 + (gregorian
520 ? ((day - 100) * 400) / (365 * 400 + 100 - 4 + 1)
521 : ((day - 100) * 4) / (365 * 4 + 1));
522 if (day >= 0)
523 year++;
525 int firstDayOfYear = getLinearDay(year, 1, gregorian);
527 // Now look in which year day really lies.
528 if (day < firstDayOfYear)
530 year--;
531 firstDayOfYear = getLinearDay(year, 1, gregorian);
534 day -= firstDayOfYear - 1; // day of year, one based.
536 fields[DAY_OF_YEAR] = day;
537 if (year <= 0)
539 fields[ERA] = BC;
540 fields[YEAR] = 1 - year;
542 else
544 fields[ERA] = AD;
545 fields[YEAR] = year;
548 int leapday = isLeapYear(year, gregorian) ? 1 : 0;
549 if (day <= 31 + 28 + leapday)
551 fields[MONTH] = day / 32; // 31->JANUARY, 32->FEBRUARY
552 fields[DAY_OF_MONTH] = day - 31 * fields[MONTH];
554 else
556 // A few more magic formulas
557 int scaledDay = (day - leapday) * 5 + 8;
558 fields[MONTH] = scaledDay / (31 + 30 + 31 + 30 + 31);
559 fields[DAY_OF_MONTH] = (scaledDay % (31 + 30 + 31 + 30 + 31)) / 5 + 1;
564 * Converts the milliseconds since the epoch UTC
565 * (<code>time</code>) to time fields
566 * (<code>fields</code>).
568 protected synchronized void computeFields()
570 boolean gregorian = (time >= gregorianCutover);
572 TimeZone zone = getTimeZone();
573 fields[ZONE_OFFSET] = zone.getRawOffset();
574 long localTime = time + fields[ZONE_OFFSET];
576 int day = (int) (localTime / (24 * 60 * 60 * 1000L));
577 int millisInDay = (int) (localTime % (24 * 60 * 60 * 1000L));
578 if (millisInDay < 0)
580 millisInDay += (24 * 60 * 60 * 1000);
581 day--;
584 calculateDay(day, gregorian);
585 fields[DST_OFFSET] =
586 zone.getOffset(fields[ERA], fields[YEAR], fields[MONTH],
587 fields[DAY_OF_MONTH], fields[DAY_OF_WEEK],
588 millisInDay) - fields[ZONE_OFFSET];
590 millisInDay += fields[DST_OFFSET];
591 if (millisInDay >= 24 * 60 * 60 * 1000)
593 millisInDay -= 24 * 60 * 60 * 1000;
594 calculateDay(++day, gregorian);
597 fields[DAY_OF_WEEK_IN_MONTH] = (fields[DAY_OF_MONTH] + 6) / 7;
599 // which day of the week are we (0..6), relative to getFirstDayOfWeek
600 int relativeWeekday = (7 + fields[DAY_OF_WEEK] - getFirstDayOfWeek()) % 7;
602 fields[WEEK_OF_MONTH] = (fields[DAY_OF_MONTH] - relativeWeekday + 6) / 7;
604 int weekOfYear = (fields[DAY_OF_YEAR] - relativeWeekday + 6) / 7;
606 // Do the Correction: getMinimalDaysInFirstWeek() is always in the
607 // first week.
608 int minDays = getMinimalDaysInFirstWeek();
609 int firstWeekday =
610 (7 + getWeekDay(fields[YEAR], minDays) - getFirstDayOfWeek()) % 7;
611 if (minDays - firstWeekday < 1)
612 weekOfYear++;
613 fields[WEEK_OF_YEAR] = weekOfYear;
616 int hourOfDay = millisInDay / (60 * 60 * 1000);
617 fields[AM_PM] = (hourOfDay < 12) ? AM : PM;
618 int hour = hourOfDay % 12;
619 fields[HOUR] = (hour == 0) ? 12 : hour;
620 fields[HOUR_OF_DAY] = hourOfDay;
621 millisInDay %= (60 * 60 * 1000);
622 fields[MINUTE] = millisInDay / (60 * 1000);
623 millisInDay %= (60 * 1000);
624 fields[SECOND] = millisInDay / (1000);
625 fields[MILLISECOND] = millisInDay % 1000;
628 areFieldsSet = isSet[ERA] = isSet[YEAR] = isSet[MONTH] =
629 isSet[WEEK_OF_YEAR] = isSet[WEEK_OF_MONTH] =
630 isSet[DAY_OF_MONTH] = isSet[DAY_OF_YEAR] = isSet[DAY_OF_WEEK] =
631 isSet[DAY_OF_WEEK_IN_MONTH] = isSet[AM_PM] = isSet[HOUR] =
632 isSet[HOUR_OF_DAY] = isSet[MINUTE] = isSet[SECOND] =
633 isSet[MILLISECOND] = isSet[ZONE_OFFSET] = isSet[DST_OFFSET] = true;
638 * Compares the given calender with this.
639 * @param o the object to that we should compare.
640 * @return true, if the given object is a calendar, that represents
641 * the same time (but doesn't necessary have the same fields).
642 * @XXX Should we check if time zones, locale, cutover etc. are equal?
644 public boolean equals(Object o)
646 if (!(o instanceof GregorianCalendar))
647 return false;
649 GregorianCalendar cal = (GregorianCalendar) o;
650 return (cal.getTimeInMillis() == getTimeInMillis());
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, and this calendar
657 // * represents a smaller time than the calender o.
658 // */
659 // public boolean before(Object o) {
660 // if (!(o instanceof GregorianCalendar))
661 // return false;
663 // GregorianCalendar cal = (GregorianCalendar) o;
664 // return (cal.getTimeInMillis() < getTimeInMillis());
665 // }
667 // /**
668 // * Compares the given calender with this.
669 // * @param o the object to that we should compare.
670 // * @return true, if the given object is a calendar, and this calendar
671 // * represents a bigger time than the calender o.
672 // */
673 // public boolean after(Object o) {
674 // if (!(o instanceof GregorianCalendar))
675 // return false;
677 // GregorianCalendar cal = (GregorianCalendar) o;
678 // return (cal.getTimeInMillis() > getTimeInMillis());
679 // }
682 * Adds the specified amount of time to the given time field. The
683 * amount may be negative to subtract the time. If the field overflows
684 * it does what you expect: Jan, 25 + 10 Days is Feb, 4.
685 * @param field the time field. One of the time field constants.
686 * @param amount the amount of time.
688 public void add(int field, int amount)
690 switch (field)
692 case YEAR:
693 complete();
694 fields[YEAR] += amount;
695 isTimeSet = false;
696 break;
697 case MONTH:
698 complete();
699 int months = fields[MONTH] + amount;
700 fields[YEAR] += months / 12;
701 fields[MONTH] = months % 12;
702 if (fields[MONTH] < 0)
704 fields[MONTH] += 12;
705 fields[YEAR]--;
707 isTimeSet = false;
708 int maxDay = getActualMaximum(DAY_OF_MONTH);
709 if (fields[DAY_OF_MONTH] > maxDay)
711 fields[DAY_OF_MONTH] = maxDay;
712 isTimeSet = false;
714 break;
715 case DAY_OF_MONTH:
716 case DAY_OF_YEAR:
717 case DAY_OF_WEEK:
718 if (!isTimeSet)
719 computeTime();
720 time += amount * (24 * 60 * 60 * 1000L);
721 areFieldsSet = false;
722 break;
723 case WEEK_OF_YEAR:
724 case WEEK_OF_MONTH:
725 case DAY_OF_WEEK_IN_MONTH:
726 if (!isTimeSet)
727 computeTime();
728 time += amount * (7 * 24 * 60 * 60 * 1000L);
729 areFieldsSet = false;
730 break;
731 case AM_PM:
732 if (!isTimeSet)
733 computeTime();
734 time += amount * (12 * 60 * 60 * 1000L);
735 areFieldsSet = false;
736 break;
737 case HOUR:
738 case HOUR_OF_DAY:
739 if (!isTimeSet)
740 computeTime();
741 time += amount * (60 * 60 * 1000L);
742 areFieldsSet = false;
743 break;
744 case MINUTE:
745 if (!isTimeSet)
746 computeTime();
747 time += amount * (60 * 1000L);
748 areFieldsSet = false;
749 break;
750 case SECOND:
751 if (!isTimeSet)
752 computeTime();
753 time += amount * (1000L);
754 areFieldsSet = false;
755 break;
756 case MILLISECOND:
757 if (!isTimeSet)
758 computeTime();
759 time += amount;
760 areFieldsSet = false;
761 break;
762 case ZONE_OFFSET:
763 complete();
764 fields[ZONE_OFFSET] += amount;
765 time -= amount;
766 break;
767 case DST_OFFSET:
768 complete();
769 fields[DST_OFFSET] += amount;
770 isTimeSet = false;
771 break;
772 default:
773 throw new IllegalArgumentException
774 ("Unknown Calendar field: " + field);
780 * Rolls the specified time field up or down. This means add one
781 * to the specified field, but don't change the other fields. If
782 * the maximum for this field is reached, start over with the
783 * minimum value.
785 * <strong>Note:</strong> There may be situation, where the other
786 * fields must be changed, e.g rolling the month on May, 31.
787 * The date June, 31 is automatically converted to July, 1.
788 * This requires lenient settings.
790 * @param field the time field. One of the time field constants.
791 * @param up the direction, true for up, false for down.
793 public void roll(int field, boolean up)
795 roll(field, up ? 1 : -1);
798 private void cleanUpAfterRoll(int field, int delta)
800 switch (field)
802 case ERA:
803 case YEAR:
804 case MONTH:
805 // check that day of month is still in correct range
806 if (fields[DAY_OF_MONTH] > getActualMaximum(DAY_OF_MONTH))
807 fields[DAY_OF_MONTH] = getActualMaximum(DAY_OF_MONTH);
808 isTimeSet = false;
809 isSet[WEEK_OF_MONTH] = false;
810 isSet[DAY_OF_WEEK] = false;
811 isSet[DAY_OF_WEEK_IN_MONTH] = false;
812 isSet[DAY_OF_YEAR] = false;
813 isSet[WEEK_OF_YEAR] = false;
814 break;
816 case DAY_OF_MONTH:
817 isSet[WEEK_OF_MONTH] = false;
818 isSet[DAY_OF_WEEK] = false;
819 isSet[DAY_OF_WEEK_IN_MONTH] = false;
820 isSet[DAY_OF_YEAR] = false;
821 isSet[WEEK_OF_YEAR] = false;
822 time += delta * (24 * 60 * 60 * 1000L);
823 break;
825 case WEEK_OF_MONTH:
826 isSet[DAY_OF_MONTH] = false;
827 isSet[DAY_OF_WEEK_IN_MONTH] = false;
828 isSet[DAY_OF_YEAR] = false;
829 isSet[WEEK_OF_YEAR] = false;
830 time += delta * (7 * 24 * 60 * 60 * 1000L);
831 break;
832 case DAY_OF_WEEK_IN_MONTH:
833 isSet[DAY_OF_MONTH] = false;
834 isSet[WEEK_OF_MONTH] = false;
835 isSet[DAY_OF_YEAR] = false;
836 isSet[WEEK_OF_YEAR] = false;
837 time += delta * (7 * 24 * 60 * 60 * 1000L);
838 break;
839 case DAY_OF_YEAR:
840 isSet[MONTH] = false;
841 isSet[DAY_OF_MONTH] = false;
842 isSet[WEEK_OF_MONTH] = false;
843 isSet[DAY_OF_WEEK_IN_MONTH] = false;
844 isSet[DAY_OF_WEEK] = false;
845 isSet[WEEK_OF_YEAR] = false;
846 time += delta * (24 * 60 * 60 * 1000L);
847 break;
848 case WEEK_OF_YEAR:
849 isSet[MONTH] = false;
850 isSet[DAY_OF_MONTH] = false;
851 isSet[WEEK_OF_MONTH] = false;
852 isSet[DAY_OF_WEEK_IN_MONTH] = false;
853 isSet[DAY_OF_YEAR] = false;
854 time += delta * (7 * 24 * 60 * 60 * 1000L);
855 break;
857 case AM_PM:
858 isSet[HOUR_OF_DAY] = false;
859 time += delta * (12 * 60 * 60 * 1000L);
860 break;
861 case HOUR:
862 isSet[HOUR_OF_DAY] = false;
863 time += delta * (60 * 60 * 1000L);
864 break;
865 case HOUR_OF_DAY:
866 isSet[HOUR] = false;
867 isSet[AM_PM] = false;
868 time += delta * (60 * 60 * 1000L);
869 break;
871 case MINUTE:
872 time += delta * (60 * 1000L);
873 break;
874 case SECOND:
875 time += delta * (1000L);
876 break;
877 case MILLISECOND:
878 time += delta;
879 break;
884 * Rolls the specified time field by the given amount. This means
885 * add amount to the specified field, but don't change the other
886 * fields. If the maximum for this field is reached, start over
887 * with the minimum value and vice versa for negative amounts.
889 * <strong>Note:</strong> There may be situation, where the other
890 * fields must be changed, e.g rolling the month on May, 31.
891 * The date June, 31 is automatically corrected to June, 30.
893 * @param field the time field. One of the time field constants.
894 * @param amount the amount by which we should roll.
896 public void roll(int field, int amount)
898 switch (field)
900 case DAY_OF_WEEK:
901 // day of week is special: it rolls automatically
902 add(field, amount);
903 return;
904 case ZONE_OFFSET:
905 case DST_OFFSET:
906 throw new IllegalArgumentException("Can't roll time zone");
908 complete();
909 int min = getActualMinimum(field);
910 int range = getActualMaximum(field) - min + 1;
911 int oldval = fields[field];
912 int newval = (oldval - min + range + amount) % range + min;
913 if (newval < min)
914 newval += range;
915 fields[field] = newval;
916 cleanUpAfterRoll(field, newval - oldval);
919 private static final int[] minimums =
920 { BC, 1, 0, 0, 1, 1, 1, SUNDAY, 1,
921 AM, 1, 0, 1, 1, 1, -(12*60*60*1000), 0 };
923 private static final int[] maximums =
924 { AD, 5000000, 11, 53, 5, 31, 366, SATURDAY, 5,
925 PM, 12, 23, 59, 59, 999, +(12*60*60*1000), (12*60*60*1000) };
928 * Gets the smallest value that is allowed for the specified field.
929 * @param field the time field. One of the time field constants.
930 * @return the smallest value.
932 public int getMinimum(int field)
934 return minimums[field];
938 * Gets the biggest value that is allowed for the specified field.
939 * @param field the time field. One of the time field constants.
940 * @return the biggest value.
942 public int getMaximum(int field)
944 return maximums[field];
949 * Gets the greatest minimum value that is allowed for the specified field.
950 * @param field the time field. One of the time field constants.
951 * @return the greatest minimum value.
953 public int getGreatestMinimum(int field)
955 if (field == WEEK_OF_YEAR)
956 return 1;
957 return minimums[field];
961 * Gets the smallest maximum value that is allowed for the
962 * specified field. For example this is 28 for DAY_OF_MONTH.
963 * @param field the time field. One of the time field constants.
964 * @return the least maximum value.
965 * @since jdk1.2
967 public int getLeastMaximum(int field)
969 switch (field)
971 case WEEK_OF_YEAR:
972 return 52;
973 case DAY_OF_MONTH:
974 return 28;
975 case DAY_OF_YEAR:
976 return 365;
977 case DAY_OF_WEEK_IN_MONTH:
978 case WEEK_OF_MONTH:
979 return 4;
980 default:
981 return maximums[field];
986 * Gets the actual minimum value that is allowed for the specified field.
987 * This value is dependent on the values of the other fields. Note that
988 * this calls <code>complete()</code> if not enough fields are set. This
989 * can have ugly side effects.
990 * @param field the time field. One of the time field constants.
991 * @return the actual minimum value.
992 * @since jdk1.2
994 public int getActualMinimum(int field)
996 if (field == WEEK_OF_YEAR)
998 int min = getMinimalDaysInFirstWeek();
999 if (min == 0)
1000 return 1;
1001 if (!areFieldsSet || !isSet[ERA] || !isSet[YEAR])
1002 complete();
1004 int year = fields[ERA] == AD ? fields[YEAR] : 1 - fields[YEAR];
1005 int weekday = getWeekDay(year, min);
1006 if ((7 + weekday - getFirstDayOfWeek()) % 7 >= min - 1)
1007 return 1;
1008 return 0;
1010 return minimums[field];
1014 * Gets the actual maximum value that is allowed for the specified field.
1015 * This value is dependent on the values of the other fields. Note that
1016 * this calls <code>complete()</code> if not enough fields are set. This
1017 * can have ugly side effects.
1018 * @param field the time field. One of the time field constants.
1019 * @return the actual maximum value.
1021 public int getActualMaximum(int field)
1023 switch (field)
1025 case WEEK_OF_YEAR:
1027 if (!areFieldsSet || !isSet[ERA] || !isSet[YEAR])
1028 complete();
1029 // This is wrong for the year that contains the gregorian change.
1030 // I.e it gives the weeks in the julian year or in the gregorian
1031 // year in that case.
1032 int year = fields[ERA] == AD ? fields[YEAR] : 1 - fields[YEAR];
1033 int lastDay = isLeapYear(year) ? 366 : 365;
1034 int weekday = getWeekDay(year, lastDay);
1035 int week = (lastDay + 6
1036 - (7 + weekday - getFirstDayOfWeek()) % 7) / 7;
1038 int minimalDays = getMinimalDaysInFirstWeek();
1039 int firstWeekday = getWeekDay(year, minimalDays);
1040 if (minimalDays - (7 + firstWeekday - getFirstDayOfWeek()) % 7 < 1)
1041 return week + 1;
1043 case DAY_OF_MONTH:
1045 if (!areFieldsSet || !isSet[MONTH])
1046 complete();
1047 int month = fields[MONTH];
1048 // If you change this, you should also change
1049 // SimpleTimeZone.getDaysInMonth();
1050 if (month == FEBRUARY)
1052 if (!isSet[YEAR] || !isSet[ERA])
1053 complete();
1054 int year = fields[ERA] == AD ? fields[YEAR] : 1 - fields[YEAR];
1055 return isLeapYear(year) ? 29 : 28;
1057 else if (month < AUGUST)
1058 return 31 - (month & 1);
1059 else
1060 return 30 + (month & 1);
1062 case DAY_OF_YEAR:
1064 if (!areFieldsSet || !isSet[ERA] || !isSet[YEAR])
1065 complete();
1066 int year = fields[ERA] == AD ? fields[YEAR] : 1 - fields[YEAR];
1067 return isLeapYear(year) ? 366 : 365;
1069 case DAY_OF_WEEK_IN_MONTH:
1071 // This is wrong for the month that contains the gregorian change.
1072 int daysInMonth = getActualMaximum(DAY_OF_MONTH);
1073 // That's black magic, I know
1074 return (daysInMonth - (fields[DAY_OF_MONTH] - 1) % 7 + 6) / 7;
1076 case WEEK_OF_MONTH:
1078 int daysInMonth = getActualMaximum(DAY_OF_MONTH);
1079 int weekday = (daysInMonth - fields[DAY_OF_MONTH]
1080 + fields[DAY_OF_WEEK] - SUNDAY) % 7 + SUNDAY;
1081 return (daysInMonth + 6
1082 - (7 + weekday - getFirstDayOfWeek()) % 7) / 7;
1084 default:
1085 return maximums[field];