1 /* java.util.GregorianCalendar
2 Copyright (C) 1998, 1999, 2001, 2002, 2003, 2004
3 Free Software Foundation, Inc.
5 This file is part of GNU Classpath.
7 GNU Classpath is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2, or (at your option)
12 GNU Classpath is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with GNU Classpath; see the file COPYING. If not, write to the
19 Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
22 Linking this library statically or dynamically with other modules is
23 making a combined work based on this library. Thus, the terms and
24 conditions of the GNU General Public License cover the whole
27 As a special exception, the copyright holders of this library give you
28 permission to link this library with independent modules to produce an
29 executable, regardless of the license terms of these independent
30 modules, and to copy and distribute the resulting executable under
31 terms of your choice, provided that you also meet, for each linked
32 independent module, the terms and conditions of the license of that
33 module. An independent module is a module which is not derived from
34 or based on this library. If you modify this library, you may extend
35 this exception to your version of the library, but you are not
36 obligated to do so. If you do not wish to do so, delete this
37 exception statement from your version. */
42 import java
.io
.IOException
;
43 import java
.io
.ObjectInputStream
;
47 * This class represents the Gregorian calendar, that is used in most
48 * countries all over the world. It does also handle the Julian calendar
49 * for dates smaller than the date of the change to the Gregorian calendar.
50 * The Gregorian calendar differs from the Julian calendar by a different
51 * leap year rule (no leap year every 100 years, except if year is divisible
55 * This change date is different from country to country, and can be changed with
56 * <code>setGregorianChange</code>. The first countries to adopt the Gregorian
57 * calendar did so on the 15th of October, 1582. This date followed October
58 * the 4th, 1582 in the Julian calendar system. The non-existant days that were
59 * omitted when the change took place are interpreted as Gregorian dates.
62 * Prior to the changeover date, New Year's Day occurred on the 25th of March.
63 * However, this class always takes New Year's Day as being the 1st of January.
64 * Client code should manually adapt the year value, if required, for dates
65 * between January the 1st and March the 24th in years prior to the changeover.
68 * Any date infinitely forwards or backwards in time can be represented by
69 * this class. A <em>proleptic</em> calendar system is used, which allows
70 * future dates to be created via the existing rules. This allows meaningful
71 * and consistent dates to be produced for all years. However, dates are only
72 * historically accurate following March the 1st, 4AD when the Julian calendar
73 * system was adopted. Prior to this, leap year rules were applied erraticly.
76 * There are two eras available for the Gregorian calendar, namely BC and AD.
79 * Weeks are defined as a period of seven days, beginning on the first day
80 * of the week, as returned by <code>getFirstDayOfWeek()</code>, and ending
81 * on the day prior to this.
84 * The weeks of the year are numbered from 1 to a possible 53. The first week
85 * of the year is defined as the first week that contains at least the minimum
86 * number of days of the first week in the new year (retrieved via
87 * <code>getMinimalDaysInFirstWeek()</code>). All weeks after this are numbered
91 * For example, take the year 2004. It began on a Thursday. The first week
92 * of 2004 depends both on where a week begins and how long it must minimally
93 * last. Let's say that the week begins on a Monday and must have a minimum
94 * of 5 days. In this case, the first week begins on Monday, the 5th of January.
95 * The first 4 days (Thursday to Sunday) are not eligible, as they are too few
96 * to make up the minimum number of days of the first week which must be in
97 * the new year. If the minimum was lowered to 4 days, then the first week
98 * would instead begin on Monday, the 29th of December, 2003. This first week
99 * has 4 of its days in the new year, and is now eligible.
102 * The weeks of the month are numbered from 0 to a possible 6. The first week
103 * of the month (numbered 1) is a set of days, prior to the first day of the week,
104 * which number at least the minimum number of days in a week. Unlike the first
105 * week of the year, the first week of the month only uses days from that particular
106 * month. As a consequence, it may have a variable number of days (from the minimum
107 * number required up to a full week of 7) and it need not start on the first day of
108 * the week. It must, however, be following by the first day of the week, as this
109 * marks the beginning of week 2. Any days of the month which occur prior to the
110 * first week (because the first day of the week occurs before the minimum number
111 * of days is met) are seen as week 0.
114 * Again, we will take the example of the year 2004 to demonstrate this. September
115 * 2004 begins on a Wednesday. Taking our first day of the week as Monday, and the
116 * minimum length of the first week as 6, we find that week 1 runs from Monday,
117 * the 6th of September to Sunday the 12th. Prior to the 6th, there are only
118 * 5 days (Wednesday through to Sunday). This is too small a number to meet the
119 * minimum, so these are classed as being days in week 0. Week 2 begins on the
120 * 13th, and so on. This changes if we reduce the minimum to 5. In this case,
121 * week 1 is a truncated week from Wednesday the 1st to Sunday the 5th, and week
122 * 0 doesn't exist. The first seven day week is week 2, starting on the 6th.
125 * On using the <code>clear()</code> method, the Gregorian calendar returns
126 * to its default value of the 1st of January, 1970 AD 00:00:00 (the epoch).
127 * The day of the week is set to the correct day for that particular time.
128 * The day is also the first of the month, and the date is in week 0.
133 * @see Calendar#getFirstDayOfWeek()
134 * @see Calendar#getMinimalDaysInFirstWeek()
136 public class GregorianCalendar
extends Calendar
139 * Constant representing the era BC (Before Christ).
141 public static final int BC
= 0;
144 * Constant representing the era AD (Anno Domini).
146 public static final int AD
= 1;
149 * The point at which the Gregorian calendar rules were used.
150 * This is locale dependent; the default for most catholic
151 * countries is midnight (UTC) on October 5, 1582 (Julian),
152 * or October 15, 1582 (Gregorian).
154 * @serial the changeover point from the Julian calendar
155 * system to the Gregorian.
157 private long gregorianCutover
;
160 * For compatability with Sun's JDK.
162 static final long serialVersionUID
= -8125100834729963327L;
165 * The name of the resource bundle. Used only by getBundle()
167 private static final String bundleName
= "gnu.java.locale.Calendar";
170 * Retrieves the resource bundle. The resources should be loaded
171 * via this method only. Iff an application uses this method, the
172 * resourcebundle is required.
174 * @param locale the locale in use for this calendar.
175 * @return A resource bundle for the calendar for the specified locale.
177 private static ResourceBundle
getBundle(Locale locale
)
179 return ResourceBundle
.getBundle(bundleName
, locale
,
180 ClassLoader
.getSystemClassLoader());
184 * Constructs a new GregorianCalender representing the current
185 * time, using the default time zone and the default locale.
187 public GregorianCalendar()
189 this(TimeZone
.getDefault(), Locale
.getDefault());
193 * Constructs a new GregorianCalender representing the current
194 * time, using the specified time zone and the default locale.
196 * @param zone a time zone.
198 public GregorianCalendar(TimeZone zone
)
200 this(zone
, Locale
.getDefault());
204 * Constructs a new GregorianCalender representing the current
205 * time, using the default time zone and the specified locale.
207 * @param locale a locale.
209 public GregorianCalendar(Locale locale
)
211 this(TimeZone
.getDefault(), locale
);
215 * Constructs a new GregorianCalender representing the current
216 * time with the given time zone and the given locale.
218 * @param zone a time zone.
219 * @param locale a locale.
221 public GregorianCalendar(TimeZone zone
, Locale locale
)
224 ResourceBundle rb
= getBundle(locale
);
225 gregorianCutover
= ((Date
) rb
.getObject("gregorianCutOver")).getTime();
226 setTimeInMillis(System
.currentTimeMillis());
230 * Constructs a new GregorianCalendar representing midnight on the
231 * given date with the default time zone and locale.
232 * @param year corresponds to the YEAR time field.
233 * @param month corresponds to the MONTH time field.
234 * @param day corresponds to the DAY time field.
236 public GregorianCalendar(int year
, int month
, int day
)
239 set(year
, month
, day
);
243 * Constructs a new GregorianCalendar representing midnight on the
244 * given date with the default time zone and locale.
246 * @param year corresponds to the YEAR time field.
247 * @param month corresponds to the MONTH time field.
248 * @param day corresponds to the DAY time field.
249 * @param hour corresponds to the HOUR_OF_DAY time field.
250 * @param minute corresponds to the MINUTE time field.
252 public GregorianCalendar(int year
, int month
, int day
, int hour
, int minute
)
255 set(year
, month
, day
, hour
, minute
);
259 * Constructs a new GregorianCalendar representing midnight on the
260 * given date with the default time zone and locale.
263 * @param year corresponds to the YEAR time field.
264 * @param month corresponds to the MONTH time field.
265 * @param day corresponds to the DAY time field.
266 * @param hour corresponds to the HOUR_OF_DAY time field.
267 * @param minute corresponds to the MINUTE time field.
268 * @param second corresponds to the SECOND time field.
270 public GregorianCalendar(int year
, int month
, int day
,
271 int hour
, int minute
, int second
)
274 set(year
, month
, day
, hour
, minute
, second
);
278 * Sets the date of the switch from Julian dates to Gregorian dates.
279 * You can use <code>new Date(Long.MAX_VALUE)</code> to use a pure
280 * Julian calendar, or <code>Long.MIN_VALUE</code> for a pure Gregorian
283 * @param date the date of the change.
285 public void setGregorianChange(Date date
)
287 gregorianCutover
= date
.getTime();
291 * Gets the date of the switch from Julian dates to Gregorian dates.
293 * @return the date of the change.
295 public final Date
getGregorianChange()
297 return new Date(gregorianCutover
);
302 * Determines if the given year is a leap year. The result is
303 * undefined if the Gregorian change took place in 1800, so that
304 * the end of February is skipped, and that year is specified.
308 * To specify a year in the BC era, use a negative value calculated
309 * as 1 - y, where y is the required year in BC. So, 1 BC is 0,
310 * 2 BC is -1, 3 BC is -2, etc.
313 * @param year a year (use a negative value for BC).
314 * @return true, if the given year is a leap year, false otherwise.
316 public boolean isLeapYear(int year
)
319 // Only years divisible by 4 can be leap years
322 // compute the linear day of the 29. February of that year.
323 // The 13 is the number of days, that were omitted in the Gregorian
324 // Calender until the epoch.
325 int julianDay
= (((year
-1) * (365*4+1)) >> 2) + (31+29 -
326 (((1970-1) * (365*4+1)) / 4 + 1 - 13));
328 // If that day is smaller than the gregorianChange the julian
329 // rule applies: This is a leap year since it is divisible by 4.
330 if (julianDay
* (24 * 60 * 60 * 1000L) < gregorianCutover
)
333 return ((year
% 100) != 0 || (year
% 400) == 0);
337 * Get the linear time in milliseconds since the epoch. If you
338 * specify a nonpositive year it is interpreted as BC as
339 * following: 0 is 1 BC, -1 is 2 BC and so on. The date is
340 * interpreted as gregorian if the change occurred before that date.
342 * @param year the year of the date.
343 * @param dayOfYear the day of year of the date; 1 based.
344 * @param millis the millisecond in that day.
345 * @return the days since the epoch, may be negative.
347 private long getLinearTime(int year
, int dayOfYear
, int millis
)
349 // The 13 is the number of days, that were omitted in the Gregorian
350 // Calendar until the epoch.
351 // We shift right by 2 instead of dividing by 4, to get correct
352 // results for negative years (and this is even more efficient).
353 int julianDay
= ((year
* (365 * 4 + 1)) >> 2) + dayOfYear
-
354 ((1970 * (365 * 4 + 1)) / 4 + 1 - 13);
355 long time
= julianDay
* (24 * 60 * 60 * 1000L) + millis
;
357 if (time
>= gregorianCutover
)
359 // subtract the days that are missing in gregorian calendar
360 // with respect to julian calendar.
362 // Okay, here we rely on the fact that the gregorian
363 // calendar was introduced in the AD era. This doesn't work
364 // with negative years.
366 // The additional leap year factor accounts for the fact that
367 // a leap day is not seen on Jan 1 of the leap year.
368 // And on and after the leap day, the leap day has already been
369 // included in dayOfYear.
370 int gregOffset
= (year
/ 400) - (year
/ 100) + 2;
371 if (isLeapYear (year
, true))
373 time
+= gregOffset
* (24 * 60 * 60 * 1000L);
379 * Retrieves the day of the week corresponding to the specified
380 * day of the specified year.
382 * @param year the year in which the dayOfYear occurs.
383 * @param dayOfYear the day of the year (an integer between 0 and
386 private int getWeekDay(int year
, int dayOfYear
)
389 (int) (getLinearTime(year
, dayOfYear
, 0) / (24 * 60 * 60 * 1000L));
391 // The epoch was a thursday.
392 int weekday
= (day
+ THURSDAY
) % 7;
400 * Calculate the dayOfYear from the fields array.
401 * The relativeDays is used, to account for weeks that begin before
402 * the Gregorian change and end after it.
405 * We return two values. The first is used to determine, if we
406 * should use the Gregorian calendar or the Julian calendar, in order
407 * to handle the change year. The second is a relative day after the given
408 * day. This is necessary for week calculation in the year in
409 * which the Gregorian change occurs.
412 * @param year the year, negative for BC.
413 * @return an array of two integer values, the first containing a reference
414 * day in the current year, the second a relative count since this reference
417 private int[] getDayOfYear(int year
)
422 if (fields
[MONTH
] > FEBRUARY
)
425 // The months after February are regular:
426 // 9 is an offset found by try and error.
427 dayOfYear
= (fields
[MONTH
] * (31 + 30 + 31 + 30 + 31) - 9) / 5;
428 if (isLeapYear(year
))
432 dayOfYear
= 31 * fields
[MONTH
];
434 if (isSet
[DAY_OF_MONTH
])
438 dayOfYear
+ fields
[DAY_OF_MONTH
], 0};
440 if (isSet
[WEEK_OF_MONTH
] && isSet
[DAY_OF_WEEK
])
442 // the weekday of the first day in that month is:
443 int weekday
= getWeekDay(year
, ++dayOfYear
);
448 // the day of week in the first week
449 // (weeks starting on sunday) is:
450 fields
[DAY_OF_WEEK
] - weekday
+
451 // Now jump to the right week and correct the possible
452 // error made by assuming sunday is the first week day.
453 7 * (fields
[WEEK_OF_MONTH
]
454 + (fields
[DAY_OF_WEEK
] < getFirstDayOfWeek()?
0 : -1)
455 + (weekday
< getFirstDayOfWeek()?
-1 : 0))};
457 if (isSet
[DAY_OF_WEEK
] && isSet
[DAY_OF_WEEK_IN_MONTH
])
459 // the weekday of the first day in that month is:
460 int weekday
= getWeekDay(year
, ++dayOfYear
);
463 fields
[DAY_OF_WEEK
] - weekday
+
464 7 * (fields
[DAY_OF_WEEK_IN_MONTH
]
465 + (fields
[DAY_OF_WEEK
] < weekday ?
0 : -1))};
469 // MONTH + something did not succeed.
470 if (isSet
[DAY_OF_YEAR
])
472 return new int[] {0, fields
[DAY_OF_YEAR
]};
475 if (isSet
[DAY_OF_WEEK
] && isSet
[WEEK_OF_YEAR
])
477 int dayOfYear
= getMinimalDaysInFirstWeek();
478 // the weekday of the day, that begins the first week
480 int weekday
= getWeekDay(year
, dayOfYear
);
484 // the day of week in the first week
485 // (weeks starting on sunday) is:
486 fields
[DAY_OF_WEEK
] - weekday
487 // Now jump to the right week and correct the possible
488 // error made by assuming sunday is the first week day.
489 + 7 * (fields
[WEEK_OF_YEAR
]
490 + (fields
[DAY_OF_WEEK
] < getFirstDayOfWeek()?
0 : -1)
491 + (weekday
< getFirstDayOfWeek()?
-1 : 0))};
494 // As last resort return Jan, 1st.
495 return new int[] {1, 0};
499 * Converts the time field values (<code>fields</code>) to
500 * milliseconds since the epoch UTC (<code>time</code>).
502 * @throws IllegalArgumentException if any calendar fields
505 protected synchronized void computeTime()
507 int era
= isSet
[ERA
] ? fields
[ERA
] : AD
;
508 int year
= isSet
[YEAR
] ? fields
[YEAR
] : 1970;
512 int[] daysOfYear
= getDayOfYear(year
);
515 if (isSet
[HOUR_OF_DAY
])
516 hour
= fields
[HOUR_OF_DAY
];
517 else if (isSet
[HOUR
])
520 if (isSet
[AM_PM
] && fields
[AM_PM
] == PM
)
521 if (hour
!= 12) /* not Noon */
523 /* Fix the problem of the status of 12:00 AM (midnight). */
524 if (isSet
[AM_PM
] && fields
[AM_PM
] == AM
&& hour
== 12)
528 int minute
= isSet
[MINUTE
] ? fields
[MINUTE
] : 0;
529 int second
= isSet
[SECOND
] ? fields
[SECOND
] : 0;
530 int millis
= isSet
[MILLISECOND
] ? fields
[MILLISECOND
] : 0;
536 long allMillis
= (((hour
* 60L) + minute
) * 60L + second
) * 1000L
538 daysOfYear
[1] += allMillis
/ (24 * 60 * 60 * 1000L);
539 millisInDay
= (int) (allMillis
% (24 * 60 * 60 * 1000L));
543 if (hour
< 0 || hour
>= 24 || minute
< 0 || minute
> 59
544 || second
< 0 || second
> 59 || millis
< 0 || millis
>= 1000)
545 throw new IllegalArgumentException();
546 millisInDay
= (((hour
* 60) + minute
) * 60 + second
) * 1000 + millis
;
548 time
= getLinearTime(year
, daysOfYear
[0], millisInDay
);
550 // Add the relative days after calculating the linear time, to
551 // get right behaviour when jumping over the gregorianCutover.
552 time
+= daysOfYear
[1] * (24 * 60 * 60 * 1000L);
555 TimeZone zone
= getTimeZone();
556 int rawOffset
= isSet
[ZONE_OFFSET
]
557 ? fields
[ZONE_OFFSET
] : zone
.getRawOffset();
559 int dayOfYear
= daysOfYear
[0] + daysOfYear
[1];
560 // This formula isn't right, so check for month as a quick fix.
561 // It doesn't compensate for leap years and puts day 30 in month 1
562 // instead of month 0.
563 int month
= isSet
[MONTH
]
564 ? fields
[MONTH
] : (dayOfYear
* 5 + 3) / (31 + 30 + 31 + 30 + 31);
565 // This formula isn't right, so check for day as a quick fix. It
566 // doesn't compensate for leap years, either.
567 int day
= isSet
[DAY_OF_MONTH
] ? fields
[DAY_OF_MONTH
]
568 : (6 + (dayOfYear
* 5 + 3) % (31 + 30 + 31 + 30 + 31)) / 5;
569 int weekday
= ((int) (time
/ (24 * 60 * 60 * 1000L)) + THURSDAY
) % 7;
572 int dstOffset
= isSet
[DST_OFFSET
]
573 ? fields
[DST_OFFSET
] : (zone
.getOffset((year
< 0) ? BC
: AD
,
574 (year
< 0) ?
1 - year
: year
,
575 month
, day
, weekday
, millisInDay
)
576 - zone
.getRawOffset());
577 time
-= rawOffset
+ dstOffset
;
583 * Determines if the given year is a leap year.
586 * To specify a year in the BC era, use a negative value calculated
587 * as 1 - y, where y is the required year in BC. So, 1 BC is 0,
588 * 2 BC is -1, 3 BC is -2, etc.
591 * @param year a year (use a negative value for BC).
592 * @param gregorian if true, use the gregorian leap year rule.
593 * @return true, if the given year is a leap year, false otherwise.
595 private boolean isLeapYear(int year
, boolean gregorian
)
598 // Only years divisible by 4 can be leap years
604 // We rely on AD area here.
605 return ((year
% 100) != 0 || (year
% 400) == 0);
609 * Get the linear day in days since the epoch, using the
610 * Julian or Gregorian calendar as specified. If you specify a
611 * nonpositive year it is interpreted as BC as following: 0 is 1
612 * BC, -1 is 2 BC and so on.
614 * @param year the year of the date.
615 * @param dayOfYear the day of year of the date; 1 based.
616 * @param gregorian <code>true</code>, if we should use the Gregorian rules.
617 * @return the days since the epoch, may be negative.
619 private int getLinearDay(int year
, int dayOfYear
, boolean gregorian
)
621 // The 13 is the number of days, that were omitted in the Gregorian
622 // Calender until the epoch.
623 // We shift right by 2 instead of dividing by 4, to get correct
624 // results for negative years (and this is even more efficient).
625 int julianDay
= ((year
* (365 * 4 + 1)) >> 2) + dayOfYear
-
626 ((1970 * (365 * 4 + 1)) / 4 + 1 - 13);
630 // subtract the days that are missing in gregorian calendar
631 // with respect to julian calendar.
633 // Okay, here we rely on the fact that the gregorian
634 // calendar was introduced in the AD era. This doesn't work
635 // with negative years.
637 // The additional leap year factor accounts for the fact that
638 // a leap day is not seen on Jan 1 of the leap year.
639 int gregOffset
= (year
/ 400) - (year
/ 100) + 2;
640 if (isLeapYear (year
, true) && dayOfYear
< 31 + 29)
642 julianDay
+= gregOffset
;
648 * Converts the given linear day into era, year, month,
649 * day_of_year, day_of_month, day_of_week, and writes the result
650 * into the fields array.
652 * @param day the linear day.
653 * @param gregorian true, if we should use Gregorian rules.
655 private void calculateDay(int day
, boolean gregorian
)
657 // the epoch is a Thursday.
658 int weekday
= (day
+ THURSDAY
) % 7;
661 fields
[DAY_OF_WEEK
] = weekday
;
663 // get a first approximation of the year. This may be one
665 int year
= 1970 + (gregorian
666 ?
((day
- 100) * 400) / (365 * 400 + 100 - 4 + 1)
667 : ((day
- 100) * 4) / (365 * 4 + 1));
671 int firstDayOfYear
= getLinearDay(year
, 1, gregorian
);
673 // Now look in which year day really lies.
674 if (day
< firstDayOfYear
)
677 firstDayOfYear
= getLinearDay(year
, 1, gregorian
);
680 day
-= firstDayOfYear
- 1; // day of year, one based.
682 fields
[DAY_OF_YEAR
] = day
;
686 fields
[YEAR
] = 1 - year
;
694 int leapday
= isLeapYear(year
, gregorian
) ?
1 : 0;
695 if (day
<= 31 + 28 + leapday
)
697 fields
[MONTH
] = day
/ 32; // 31->JANUARY, 32->FEBRUARY
698 fields
[DAY_OF_MONTH
] = day
- 31 * fields
[MONTH
];
702 // A few more magic formulas
703 int scaledDay
= (day
- leapday
) * 5 + 8;
704 fields
[MONTH
] = scaledDay
/ (31 + 30 + 31 + 30 + 31);
705 fields
[DAY_OF_MONTH
] = (scaledDay
% (31 + 30 + 31 + 30 + 31)) / 5 + 1;
710 * Converts the milliseconds since the epoch UTC
711 * (<code>time</code>) to time fields
712 * (<code>fields</code>).
714 protected synchronized void computeFields()
716 boolean gregorian
= (time
>= gregorianCutover
);
718 TimeZone zone
= getTimeZone();
719 fields
[ZONE_OFFSET
] = zone
.getRawOffset();
720 long localTime
= time
+ fields
[ZONE_OFFSET
];
722 int day
= (int) (localTime
/ (24 * 60 * 60 * 1000L));
723 int millisInDay
= (int) (localTime
% (24 * 60 * 60 * 1000L));
726 millisInDay
+= (24 * 60 * 60 * 1000);
730 calculateDay(day
, gregorian
);
732 zone
.getOffset(fields
[ERA
], fields
[YEAR
], fields
[MONTH
],
733 fields
[DAY_OF_MONTH
], fields
[DAY_OF_WEEK
],
734 millisInDay
) - fields
[ZONE_OFFSET
];
736 millisInDay
+= fields
[DST_OFFSET
];
737 if (millisInDay
>= 24 * 60 * 60 * 1000)
739 millisInDay
-= 24 * 60 * 60 * 1000;
740 calculateDay(++day
, gregorian
);
743 fields
[DAY_OF_WEEK_IN_MONTH
] = (fields
[DAY_OF_MONTH
] + 6) / 7;
745 // which day of the week are we (0..6), relative to getFirstDayOfWeek
746 int relativeWeekday
= (7 + fields
[DAY_OF_WEEK
] - getFirstDayOfWeek()) % 7;
748 fields
[WEEK_OF_MONTH
] = (fields
[DAY_OF_MONTH
] - relativeWeekday
+ 12) / 7;
750 int weekOfYear
= (fields
[DAY_OF_YEAR
] - relativeWeekday
+ 6) / 7;
752 // Do the Correction: getMinimalDaysInFirstWeek() is always in the
754 int minDays
= getMinimalDaysInFirstWeek();
756 (7 + getWeekDay(fields
[YEAR
], minDays
) - getFirstDayOfWeek()) % 7;
757 if (minDays
- firstWeekday
< 1)
759 fields
[WEEK_OF_YEAR
] = weekOfYear
;
762 int hourOfDay
= millisInDay
/ (60 * 60 * 1000);
763 fields
[AM_PM
] = (hourOfDay
< 12) ? AM
: PM
;
764 int hour
= hourOfDay
% 12;
765 fields
[HOUR
] = (hour
== 0) ?
12 : hour
;
766 fields
[HOUR_OF_DAY
] = hourOfDay
;
767 millisInDay
%= (60 * 60 * 1000);
768 fields
[MINUTE
] = millisInDay
/ (60 * 1000);
769 millisInDay
%= (60 * 1000);
770 fields
[SECOND
] = millisInDay
/ (1000);
771 fields
[MILLISECOND
] = millisInDay
% 1000;
774 areFieldsSet
= isSet
[ERA
] = isSet
[YEAR
] = isSet
[MONTH
] =
775 isSet
[WEEK_OF_YEAR
] = isSet
[WEEK_OF_MONTH
] =
776 isSet
[DAY_OF_MONTH
] = isSet
[DAY_OF_YEAR
] = isSet
[DAY_OF_WEEK
] =
777 isSet
[DAY_OF_WEEK_IN_MONTH
] = isSet
[AM_PM
] = isSet
[HOUR
] =
778 isSet
[HOUR_OF_DAY
] = isSet
[MINUTE
] = isSet
[SECOND
] =
779 isSet
[MILLISECOND
] = isSet
[ZONE_OFFSET
] = isSet
[DST_OFFSET
] = true;
784 * Compares the given calendar with this. An object, o, is
785 * equivalent to this if it is also a <code>GregorianCalendar</code>
786 * with the same time since the epoch under the same conditions
787 * (same change date and same time zone).
789 * @param o the object to that we should compare.
790 * @return true, if the given object is a calendar, that represents
791 * the same time (but doesn't necessarily have the same fields).
792 * @throws IllegalArgumentException if one of the fields
793 * <code>ZONE_OFFSET</code> or <code>DST_OFFSET</code> is
794 * specified, if an unknown field is specified or if one
795 * of the calendar fields receives an illegal value when
796 * leniancy is not enabled.
798 public boolean equals(Object o
)
800 if (!(o
instanceof GregorianCalendar
))
803 GregorianCalendar cal
= (GregorianCalendar
) o
;
804 return (cal
.getTimeInMillis() == getTimeInMillis());
808 // * Compares the given calender with this.
809 // * @param o the object to that we should compare.
810 // * @return true, if the given object is a calendar, and this calendar
811 // * represents a smaller time than the calender o.
813 // public boolean before(Object o) {
814 // if (!(o instanceof GregorianCalendar))
817 // GregorianCalendar cal = (GregorianCalendar) o;
818 // return (cal.getTimeInMillis() < getTimeInMillis());
822 // * Compares the given calender with this.
823 // * @param o the object to that we should compare.
824 // * @return true, if the given object is a calendar, and this calendar
825 // * represents a bigger time than the calender o.
827 // public boolean after(Object o) {
828 // if (!(o instanceof GregorianCalendar))
831 // GregorianCalendar cal = (GregorianCalendar) o;
832 // return (cal.getTimeInMillis() > getTimeInMillis());
836 * Adds the specified amount of time to the given time field. The
837 * amount may be negative to subtract the time. If the field overflows
838 * it does what you expect: Jan, 25 + 10 Days is Feb, 4.
839 * @param field one of the time field constants.
840 * @param amount the amount of time to add.
841 * @exception IllegalArgumentException if <code>field</code> is
842 * <code>ZONE_OFFSET</code>, <code>DST_OFFSET</code>, or invalid; or
843 * if <code>amount</code> contains an out-of-range value and the calendar
844 * is not in lenient mode.
846 public void add(int field
, int amount
)
852 fields
[YEAR
] += amount
;
857 int months
= fields
[MONTH
] + amount
;
858 fields
[YEAR
] += months
/ 12;
859 fields
[MONTH
] = months
% 12;
860 if (fields
[MONTH
] < 0)
866 int maxDay
= getActualMaximum(DAY_OF_MONTH
);
867 if (fields
[DAY_OF_MONTH
] > maxDay
)
869 fields
[DAY_OF_MONTH
] = maxDay
;
878 time
+= amount
* (24 * 60 * 60 * 1000L);
879 areFieldsSet
= false;
883 case DAY_OF_WEEK_IN_MONTH
:
886 time
+= amount
* (7 * 24 * 60 * 60 * 1000L);
887 areFieldsSet
= false;
892 time
+= amount
* (12 * 60 * 60 * 1000L);
893 areFieldsSet
= false;
899 time
+= amount
* (60 * 60 * 1000L);
900 areFieldsSet
= false;
905 time
+= amount
* (60 * 1000L);
906 areFieldsSet
= false;
911 time
+= amount
* (1000L);
912 areFieldsSet
= false;
918 areFieldsSet
= false;
923 throw new IllegalArgumentException("Invalid or unknown field");
929 * Rolls the specified time field up or down. This means add one
930 * to the specified field, but don't change the other fields. If
931 * the maximum for this field is reached, start over with the
934 * <strong>Note:</strong> There may be situation, where the other
935 * fields must be changed, e.g rolling the month on May, 31.
936 * The date June, 31 is automatically converted to July, 1.
937 * This requires lenient settings.
939 * @param field the time field. One of the time field constants.
940 * @param up the direction, true for up, false for down.
941 * @throws IllegalArgumentException if one of the fields
942 * <code>ZONE_OFFSET</code> or <code>DST_OFFSET</code> is
943 * specified, if an unknown field is specified or if one
944 * of the calendar fields receives an illegal value when
945 * leniancy is not enabled.
947 public void roll(int field
, boolean up
)
949 roll(field
, up ?
1 : -1);
953 * Checks that the fields are still within their legal bounds,
954 * following use of the <code>roll()</code> method.
956 * @param field the field to check.
957 * @param delta multipler for alterations to the <code>time</code>.
958 * @see #roll(int, boolean)
959 * @see #roll(int, int)
961 private void cleanUpAfterRoll(int field
, int delta
)
968 // check that day of month is still in correct range
969 if (fields
[DAY_OF_MONTH
] > getActualMaximum(DAY_OF_MONTH
))
970 fields
[DAY_OF_MONTH
] = getActualMaximum(DAY_OF_MONTH
);
972 isSet
[WEEK_OF_MONTH
] = false;
973 isSet
[DAY_OF_WEEK
] = false;
974 isSet
[DAY_OF_WEEK_IN_MONTH
] = false;
975 isSet
[DAY_OF_YEAR
] = false;
976 isSet
[WEEK_OF_YEAR
] = false;
980 isSet
[WEEK_OF_MONTH
] = false;
981 isSet
[DAY_OF_WEEK
] = false;
982 isSet
[DAY_OF_WEEK_IN_MONTH
] = false;
983 isSet
[DAY_OF_YEAR
] = false;
984 isSet
[WEEK_OF_YEAR
] = false;
985 time
+= delta
* (24 * 60 * 60 * 1000L);
989 isSet
[DAY_OF_MONTH
] = false;
990 isSet
[DAY_OF_WEEK_IN_MONTH
] = false;
991 isSet
[DAY_OF_YEAR
] = false;
992 isSet
[WEEK_OF_YEAR
] = false;
993 time
+= delta
* (7 * 24 * 60 * 60 * 1000L);
995 case DAY_OF_WEEK_IN_MONTH
:
996 isSet
[DAY_OF_MONTH
] = false;
997 isSet
[WEEK_OF_MONTH
] = false;
998 isSet
[DAY_OF_YEAR
] = false;
999 isSet
[WEEK_OF_YEAR
] = false;
1000 time
+= delta
* (7 * 24 * 60 * 60 * 1000L);
1003 isSet
[MONTH
] = false;
1004 isSet
[DAY_OF_MONTH
] = false;
1005 isSet
[WEEK_OF_MONTH
] = false;
1006 isSet
[DAY_OF_WEEK_IN_MONTH
] = false;
1007 isSet
[DAY_OF_WEEK
] = false;
1008 isSet
[WEEK_OF_YEAR
] = false;
1009 time
+= delta
* (24 * 60 * 60 * 1000L);
1012 isSet
[MONTH
] = false;
1013 isSet
[DAY_OF_MONTH
] = false;
1014 isSet
[WEEK_OF_MONTH
] = false;
1015 isSet
[DAY_OF_WEEK_IN_MONTH
] = false;
1016 isSet
[DAY_OF_YEAR
] = false;
1017 time
+= delta
* (7 * 24 * 60 * 60 * 1000L);
1021 isSet
[HOUR_OF_DAY
] = false;
1022 time
+= delta
* (12 * 60 * 60 * 1000L);
1025 isSet
[HOUR_OF_DAY
] = false;
1026 time
+= delta
* (60 * 60 * 1000L);
1029 isSet
[HOUR
] = false;
1030 isSet
[AM_PM
] = false;
1031 time
+= delta
* (60 * 60 * 1000L);
1035 time
+= delta
* (60 * 1000L);
1038 time
+= delta
* (1000L);
1047 * Rolls the specified time field by the given amount. This means
1048 * add amount to the specified field, but don't change the other
1049 * fields. If the maximum for this field is reached, start over
1050 * with the minimum value and vice versa for negative amounts.
1052 * <strong>Note:</strong> There may be situation, where the other
1053 * fields must be changed, e.g rolling the month on May, 31.
1054 * The date June, 31 is automatically corrected to June, 30.
1056 * @param field the time field. One of the time field constants.
1057 * @param amount the amount by which we should roll.
1058 * @throws IllegalArgumentException if one of the fields
1059 * <code>ZONE_OFFSET</code> or <code>DST_OFFSET</code> is
1060 * specified, if an unknown field is specified or if one
1061 * of the calendar fields receives an illegal value when
1062 * leniancy is not enabled.
1064 public void roll(int field
, int amount
)
1069 // day of week is special: it rolls automatically
1074 throw new IllegalArgumentException("Can't roll time zone");
1077 int min
= getActualMinimum(field
);
1078 int range
= getActualMaximum(field
) - min
+ 1;
1079 int oldval
= fields
[field
];
1080 int newval
= (oldval
- min
+ range
+ amount
) % range
+ min
;
1083 fields
[field
] = newval
;
1084 cleanUpAfterRoll(field
, newval
- oldval
);
1088 * The minimum values for the calendar fields.
1090 private static final int[] minimums
=
1091 { BC
, 1, 0, 0, 1, 1, 1, SUNDAY
, 1,
1092 AM
, 1, 0, 1, 1, 1, -(12*60*60*1000), 0 };
1095 * The maximum values for the calendar fields.
1097 private static final int[] maximums
=
1098 { AD
, 5000000, 11, 53, 5, 31, 366, SATURDAY
, 5,
1099 PM
, 12, 23, 59, 59, 999, +(12*60*60*1000), (12*60*60*1000) };
1102 * Gets the smallest value that is allowed for the specified field.
1104 * @param field one of the time field constants.
1105 * @return the smallest value for the specified field.
1107 public int getMinimum(int field
)
1109 return minimums
[field
];
1113 * Gets the biggest value that is allowed for the specified field.
1115 * @param field one of the time field constants.
1116 * @return the biggest value.
1118 public int getMaximum(int field
)
1120 return maximums
[field
];
1125 * Gets the greatest minimum value that is allowed for the specified field.
1126 * This is the largest value returned by the <code>getActualMinimum(int)</code>
1129 * @param field the time field. One of the time field constants.
1130 * @return the greatest minimum value.
1131 * @see #getActualMinimum(int)
1133 public int getGreatestMinimum(int field
)
1135 if (field
== WEEK_OF_YEAR
)
1137 return minimums
[field
];
1141 * Gets the smallest maximum value that is allowed for the
1142 * specified field. This is the smallest value returned
1143 * by the <code>getActualMaximum(int)</code>. For example,
1144 * this is 28 for DAY_OF_MONTH (as all months have at least
1147 * @param field the time field. One of the time field constants.
1148 * @return the least maximum value.
1149 * @see #getActualMaximum(int)
1152 public int getLeastMaximum(int field
)
1162 case DAY_OF_WEEK_IN_MONTH
:
1166 return maximums
[field
];
1171 * Gets the actual minimum value that is allowed for the specified field.
1172 * This value is dependent on the values of the other fields. Note that
1173 * this calls <code>complete()</code> if not enough fields are set. This
1174 * can have ugly side effects. The value given depends on the current
1175 * time used by this instance.
1177 * @param field the time field. One of the time field constants.
1178 * @return the actual minimum value.
1181 public int getActualMinimum(int field
)
1183 if (field
== WEEK_OF_YEAR
)
1185 int min
= getMinimalDaysInFirstWeek();
1188 if (!areFieldsSet
|| !isSet
[ERA
] || !isSet
[YEAR
])
1191 int year
= fields
[ERA
] == AD ? fields
[YEAR
] : 1 - fields
[YEAR
];
1192 int weekday
= getWeekDay(year
, min
);
1193 if ((7 + weekday
- getFirstDayOfWeek()) % 7 >= min
- 1)
1197 return minimums
[field
];
1201 * Gets the actual maximum value that is allowed for the specified field.
1202 * This value is dependent on the values of the other fields. Note that
1203 * this calls <code>complete()</code> if not enough fields are set. This
1204 * can have ugly side effects. The value given depends on the current time
1205 * used by this instance; thus, leap years have a maximum day of month value of
1206 * 29, rather than 28.
1208 * @param field the time field. One of the time field constants.
1209 * @return the actual maximum value.
1211 public int getActualMaximum(int field
)
1217 if (!areFieldsSet
|| !isSet
[ERA
] || !isSet
[YEAR
])
1219 // This is wrong for the year that contains the gregorian change.
1220 // I.e it gives the weeks in the julian year or in the gregorian
1221 // year in that case.
1222 int year
= fields
[ERA
] == AD ? fields
[YEAR
] : 1 - fields
[YEAR
];
1223 int lastDay
= isLeapYear(year
) ?
366 : 365;
1224 int weekday
= getWeekDay(year
, lastDay
);
1225 int week
= (lastDay
+ 6
1226 - (7 + weekday
- getFirstDayOfWeek()) % 7) / 7;
1228 int minimalDays
= getMinimalDaysInFirstWeek();
1229 int firstWeekday
= getWeekDay(year
, minimalDays
);
1231 * Is there a set of days at the beginning of the year, before the
1232 * first day of the week, equal to or greater than the minimum number
1233 * of days required in the first week?
1235 if (minimalDays
- (7 + firstWeekday
- getFirstDayOfWeek()) % 7 < 1)
1236 return week
+ 1; /* Add week 1: firstWeekday through to firstDayOfWeek */
1240 if (!areFieldsSet
|| !isSet
[MONTH
])
1242 int month
= fields
[MONTH
];
1243 // If you change this, you should also change
1244 // SimpleTimeZone.getDaysInMonth();
1245 if (month
== FEBRUARY
)
1247 if (!isSet
[YEAR
] || !isSet
[ERA
])
1249 int year
= fields
[ERA
] == AD ? fields
[YEAR
] : 1 - fields
[YEAR
];
1250 return isLeapYear(year
) ?
29 : 28;
1252 else if (month
< AUGUST
)
1253 return 31 - (month
& 1);
1255 return 30 + (month
& 1);
1259 if (!areFieldsSet
|| !isSet
[ERA
] || !isSet
[YEAR
])
1261 int year
= fields
[ERA
] == AD ? fields
[YEAR
] : 1 - fields
[YEAR
];
1262 return isLeapYear(year
) ?
366 : 365;
1264 case DAY_OF_WEEK_IN_MONTH
:
1266 // This is wrong for the month that contains the gregorian change.
1267 int daysInMonth
= getActualMaximum(DAY_OF_MONTH
);
1268 // That's black magic, I know
1269 return (daysInMonth
- (fields
[DAY_OF_MONTH
] - 1) % 7 + 6) / 7;
1273 int daysInMonth
= getActualMaximum(DAY_OF_MONTH
);
1274 int weekday
= (daysInMonth
- fields
[DAY_OF_MONTH
]
1275 + fields
[DAY_OF_WEEK
] - SUNDAY
) % 7 + SUNDAY
;
1276 return (daysInMonth
+ 6
1277 - (7 + weekday
- getFirstDayOfWeek()) % 7) / 7;
1280 return maximums
[field
];