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)
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
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
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. */
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.
58 public class GregorianCalendar
extends Calendar
61 * Constant representing the era BC (before Christ).
63 public static final int BC
= 0;
66 * Constant representing the era AD (Anno Domini).
68 public static final int AD
= 1;
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;
81 * The name of the resource bundle.
83 private static final String bundleName
= "gnu.java.locale.Calendar";
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());
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
)
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
)
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
)
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
)
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
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
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
)
209 // Only years divisible by 4 can be leap years
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
)
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)
260 time
+= gregOffset
* (24 * 60 * 60 * 1000L);
265 private int getWeekDay(int year
, int dayOfYear
)
268 (int) (getLinearTime(year
, dayOfYear
, 0) / (24 * 60 * 60 * 1000L));
270 // The epoch was a thursday.
271 int weekday
= (day
+ THURSDAY
) % 7;
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
292 private int[] getDayOfYear(int year
)
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
))
307 dayOfYear
= 31 * fields
[MONTH
];
309 if (isSet
[DAY_OF_MONTH
])
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
);
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
);
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
355 int weekday
= getWeekDay(year
, 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;
384 int[] daysOfYear
= getDayOfYear(year
);
387 if (isSet
[HOUR_OF_DAY
])
388 hour
= fields
[HOUR_OF_DAY
];
389 else if (isSet
[HOUR
])
392 if (isSet
[AM_PM
] && fields
[AM_PM
] == PM
)
396 int minute
= isSet
[MINUTE
] ? fields
[MINUTE
] : 0;
397 int second
= isSet
[SECOND
] ? fields
[SECOND
] : 0;
398 int millis
= isSet
[MILLISECOND
] ? fields
[MILLISECOND
] : 0;
404 long allMillis
= (((hour
* 60L) + minute
) * 60L + second
) * 1000L
406 daysOfYear
[1] += allMillis
/ (24 * 60 * 60 * 1000L);
407 millisInDay
= (int) (allMillis
% (24 * 60 * 60 * 1000L));
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;
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
;
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
)
455 // Only years divisible by 4 can be leap years
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);
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)
498 julianDay
+= gregOffset
;
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;
515 fields
[DAY_OF_WEEK
] = weekday
;
517 // get a first approximation of the year. This may be one
519 int year
= 1970 + (gregorian
520 ?
((day
- 100) * 400) / (365 * 400 + 100 - 4 + 1)
521 : ((day
- 100) * 4) / (365 * 4 + 1));
525 int firstDayOfYear
= getLinearDay(year
, 1, gregorian
);
527 // Now look in which year day really lies.
528 if (day
< firstDayOfYear
)
531 firstDayOfYear
= getLinearDay(year
, 1, gregorian
);
534 day
-= firstDayOfYear
- 1; // day of year, one based.
536 fields
[DAY_OF_YEAR
] = day
;
540 fields
[YEAR
] = 1 - 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
];
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));
580 millisInDay
+= (24 * 60 * 60 * 1000);
584 calculateDay(day
, gregorian
);
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
608 int minDays
= getMinimalDaysInFirstWeek();
610 (7 + getWeekDay(fields
[YEAR
], minDays
) - getFirstDayOfWeek()) % 7;
611 if (minDays
- firstWeekday
< 1)
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
))
649 GregorianCalendar cal
= (GregorianCalendar
) o
;
650 return (cal
.getTimeInMillis() == getTimeInMillis());
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.
659 // public boolean before(Object o) {
660 // if (!(o instanceof GregorianCalendar))
663 // GregorianCalendar cal = (GregorianCalendar) o;
664 // return (cal.getTimeInMillis() < getTimeInMillis());
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.
673 // public boolean after(Object o) {
674 // if (!(o instanceof GregorianCalendar))
677 // GregorianCalendar cal = (GregorianCalendar) o;
678 // return (cal.getTimeInMillis() > getTimeInMillis());
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
)
694 fields
[YEAR
] += amount
;
699 int months
= fields
[MONTH
] + amount
;
700 fields
[YEAR
] += months
/ 12;
701 fields
[MONTH
] = months
% 12;
702 if (fields
[MONTH
] < 0)
708 int maxDay
= getActualMaximum(DAY_OF_MONTH
);
709 if (fields
[DAY_OF_MONTH
] > maxDay
)
711 fields
[DAY_OF_MONTH
] = maxDay
;
720 time
+= amount
* (24 * 60 * 60 * 1000L);
721 areFieldsSet
= false;
725 case DAY_OF_WEEK_IN_MONTH
:
728 time
+= amount
* (7 * 24 * 60 * 60 * 1000L);
729 areFieldsSet
= false;
734 time
+= amount
* (12 * 60 * 60 * 1000L);
735 areFieldsSet
= false;
741 time
+= amount
* (60 * 60 * 1000L);
742 areFieldsSet
= false;
747 time
+= amount
* (60 * 1000L);
748 areFieldsSet
= false;
753 time
+= amount
* (1000L);
754 areFieldsSet
= false;
760 areFieldsSet
= false;
764 fields
[ZONE_OFFSET
] += amount
;
769 fields
[DST_OFFSET
] += amount
;
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
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
)
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
);
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;
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);
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);
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);
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);
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);
858 isSet
[HOUR_OF_DAY
] = false;
859 time
+= delta
* (12 * 60 * 60 * 1000L);
862 isSet
[HOUR_OF_DAY
] = false;
863 time
+= delta
* (60 * 60 * 1000L);
867 isSet
[AM_PM
] = false;
868 time
+= delta
* (60 * 60 * 1000L);
872 time
+= delta
* (60 * 1000L);
875 time
+= delta
* (1000L);
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
)
901 // day of week is special: it rolls automatically
906 throw new IllegalArgumentException("Can't roll time zone");
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
;
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
)
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.
967 public int getLeastMaximum(int field
)
977 case DAY_OF_WEEK_IN_MONTH
:
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.
994 public int getActualMinimum(int field
)
996 if (field
== WEEK_OF_YEAR
)
998 int min
= getMinimalDaysInFirstWeek();
1001 if (!areFieldsSet
|| !isSet
[ERA
] || !isSet
[YEAR
])
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)
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
)
1027 if (!areFieldsSet
|| !isSet
[ERA
] || !isSet
[YEAR
])
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)
1045 if (!areFieldsSet
|| !isSet
[MONTH
])
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
])
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);
1060 return 30 + (month
& 1);
1064 if (!areFieldsSet
|| !isSet
[ERA
] || !isSet
[YEAR
])
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;
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;
1085 return maximums
[field
];