Index: ChangeLog =================================================================== RCS file: /cvsroot/classpath/classpath/ChangeLog,v retrieving revision 1.2474 diff -u -3 -p -u -r1.2474 ChangeLog --- ChangeLog 9 Sep 2004 21:33:40 -0000 1.2474 +++ ChangeLog 10 Sep 2004 02:44:10 -0000 @@ -1,3 +1,13 @@ +2004-09-10 Andrew John Hughes
+ + * java/util/GregorianCalendar.java + Added/amended documentation as well as: + (readObject(ObjectInputStream)): implemented to complete all + fields on deserialization. + (getActualMaximum(int)): fixed fall-through to default case + on WEEK_OF_YEAR + (getLinearTime(int,int,int)): changed time to linearTime + 2004-09-09 Mark Wielaard Fixes bug #8991. Index: java/util/GregorianCalendar.java =================================================================== RCS file: /cvsroot/classpath/classpath/java/util/GregorianCalendar.java,v retrieving revision 1.26 diff -u -3 -p -u -r1.26 GregorianCalendar.java --- java/util/GregorianCalendar.java 15 Jun 2004 22:40:39 -0000 1.26 +++ java/util/GregorianCalendar.java 10 Sep 2004 02:44:11 -0000 @@ -39,27 +39,104 @@ exception statement from your version. * package java.util; +import java.io.IOException; +import java.io.ObjectInputStream; + /** + *
* This class represents the Gregorian calendar, that is used in most
* countries all over the world. It does also handle the Julian calendar
* for dates smaller than the date of the change to the Gregorian calendar.
- * This change date is different from country to country, you can set it with
- * setGregorianChange
- *
* The Gregorian calendar differs from the Julian calendar by a different
* leap year rule (no leap year every 100 years, except if year is divisible
- * by 400). The non existing days that were omited when the change took
- * place are interpreted as gregorian date
- *
- * There are to eras available for the Gregorian calendar, namely BC and AD.
+ * by 400).
+ *
+ * This change date is different from country to country, and can be changed with
+ * setGregorianChange
. The first countries to adopt the Gregorian
+ * calendar did so on the 15th of October, 1582. This date followed October
+ * the 4th, 1582 in the Julian calendar system. The non-existant days that were
+ * omitted when the change took place are interpreted as Gregorian dates.
+ *
+ * Prior to the changeover date, New Year's Day occurred on the 25th of March. + * However, this class always takes New Year's Day as being the 1st of January. + * Client code should manually adapt the year value, if required, for dates + * between January the 1st and March the 24th in years prior to the changeover. + *
+ *+ * Any date infinitely forwards or backwards in time can be represented by + * this class. A proleptic calendar system is used, which allows + * future dates to be created via the existing rules. This allows meaningful + * and consistent dates to be produced for all years. However, dates are only + * historically accurate following March the 1st, 4AD when the Julian calendar + * system was adopted. Prior to this, leap year rules were applied erraticly. + *
+ *+ * There are two eras available for the Gregorian calendar, namely BC and AD. + *
+ *
+ * Weeks are defined as a period of seven days, beginning on the first day
+ * of the week, as returned by getFirstDayOfWeek()
, and ending
+ * on the day prior to this.
+ *
+ * The weeks of the year are numbered from 1 to a possible 53. The first week
+ * of the year is defined as the first week that contains at least the minimum
+ * number of days of the first week in the new year (retrieved via
+ * getMinimalDaysInFirstWeek()
). All weeks after this are numbered
+ * from 2 onwards.
+ *
+ * For example, take the year 2004. It began on a Thursday. The first week + * of 2004 depends both on where a week begins and how long it must minimally + * last. Let's say that the week begins on a Monday and must have a minimum + * of 5 days. In this case, the first week begins on Monday, the 5th of January. + * The first 4 days (Thursday to Sunday) are not eligible, as they are too few + * to make up the minimum number of days of the first week which must be in + * the new year. If the minimum was lowered to 4 days, then the first week + * would instead begin on Monday, the 29th of December, 2003. This first week + * has 4 of its days in the new year, and is now eligible. + *
+ *+ * The weeks of the month are numbered from 0 to a possible 6. The first week + * of the month (numbered 1) is a set of days, prior to the first day of the week, + * which number at least the minimum number of days in a week. Unlike the first + * week of the year, the first week of the month only uses days from that particular + * month. As a consequence, it may have a variable number of days (from the minimum + * number required up to a full week of 7) and it need not start on the first day of + * the week. It must, however, be following by the first day of the week, as this + * marks the beginning of week 2. Any days of the month which occur prior to the + * first week (because the first day of the week occurs before the minimum number + * of days is met) are seen as week 0. + *
+ *+ * Again, we will take the example of the year 2004 to demonstrate this. September + * 2004 begins on a Wednesday. Taking our first day of the week as Monday, and the + * minimum length of the first week as 6, we find that week 1 runs from Monday, + * the 6th of September to Sunday the 12th. Prior to the 6th, there are only + * 5 days (Wednesday through to Sunday). This is too small a number to meet the + * minimum, so these are classed as being days in week 0. Week 2 begins on the + * 13th, and so on. This changes if we reduce the minimum to 5. In this case, + * week 1 is a truncated week from Wednesday the 1st to Sunday the 5th, and week + * 0 doesn't exist. The first seven day week is week 2, starting on the 6th. + *
+ *
+ * On using the clear()
method, the Gregorian calendar returns
+ * to its default value of the 1st of January, 1970 AD 00:00:00 (the epoch).
+ * The day of the week is set to the correct day for that particular time.
+ * The day is also the first of the month, and the date is in week 0.
+ *
new Date(Long.MAX_VALUE)
to use a pure
* Julian calendar, or Long.MIN_VALUE
for a pure Gregorian
* calendar.
+ *
* @param date the date of the change.
*/
public void setGregorianChange(Date date)
@@ -196,6 +289,7 @@ public class GregorianCalendar extends C
/**
* Gets the date of the switch from Julian dates to Gregorian dates.
+ *
* @return the date of the change.
*/
public final Date getGregorianChange()
@@ -204,17 +298,21 @@ public class GregorianCalendar extends C
}
/**
+ *
* Determines if the given year is a leap year. The result is
- * undefined if the gregorian change took place in 1800, so that
- * the end of february is skiped and you give that year
- * (well...).
- *
- * The year should be positive and you can't give an ERA. But
- * remember that before 4 BC there wasn't a consistent leap year
- * rule, so who cares.
+ * undefined if the Gregorian change took place in 1800, so that
+ * the end of February is skipped, and that year is specified.
+ * (well...).
+ *
+ * To specify a year in the BC era, use a negative value calculated + * as 1 - y, where y is the required year in BC. So, 1 BC is 0, + * 2 BC is -1, 3 BC is -2, etc. + *
* - * @param year a year use nonnegative value for BC. - * @return true, if the given year is a leap year, false otherwise. */ + * @param year a year (use a negative value for BC). + * @return true, if the given year is a leap year, false otherwise. + */ public boolean isLeapYear(int year) { if ((year & 3) != 0) @@ -244,18 +342,19 @@ public class GregorianCalendar extends C * @param year the year of the date. * @param dayOfYear the day of year of the date; 1 based. * @param millis the millisecond in that day. - * @return the days since the epoch, may be negative. */ + * @return the days since the epoch, may be negative. + */ private long getLinearTime(int year, int dayOfYear, int millis) { // The 13 is the number of days, that were omitted in the Gregorian - // Calender until the epoch. + // Calendar until the epoch. // We shift right by 2 instead of dividing by 4, to get correct // results for negative years (and this is even more efficient). int julianDay = ((year * (365 * 4 + 1)) >> 2) + dayOfYear - ((1970 * (365 * 4 + 1)) / 4 + 1 - 13); - long time = julianDay * (24 * 60 * 60 * 1000L) + millis; + long linearTime = julianDay * (24 * 60 * 60 * 1000L) + millis; - if (time >= gregorianCutover) + if (linearTime >= gregorianCutover) { // subtract the days that are missing in gregorian calendar // with respect to julian calendar. @@ -271,11 +370,19 @@ public class GregorianCalendar extends C int gregOffset = (year / 400) - (year / 100) + 2; if (isLeapYear (year, true)) --gregOffset; - time += gregOffset * (24 * 60 * 60 * 1000L); + linearTime += gregOffset * (24 * 60 * 60 * 1000L); } - return time; + return linearTime; } + /** + * Retrieves the day of the week corresponding to the specified + * day of the specified year. + * + * @param year the year in which the dayOfYear occurs. + * @param dayOfYear the day of the year (an integer between 0 and + * and 366) + */ private int getWeekDay(int year, int dayOfYear) { int day = @@ -289,20 +396,24 @@ public class GregorianCalendar extends C } /** + *
* Calculate the dayOfYear from the fields array.
* The relativeDays is used, to account for weeks that begin before
- * the gregorian change and end after it.
- *
- * We return two values, the first is used to determine, if we
- * should use Gregorian calendar or Julian calendar, in case of
- * the change year, the second is a relative day after the given
+ * the Gregorian change and end after it.
+ *
+ * We return two values. The first is used to determine, if we
+ * should use the Gregorian calendar or the Julian calendar, in order
+ * to handle the change year. The second is a relative day after the given
* day. This is necessary for week calculation in the year in
- * which gregorian change occurs.
- *
+ * which the Gregorian change occurs.
+ *
fields
) to
* milliseconds since the epoch UTC (time
).
+ *
+ * @throws IllegalArgumentException if any calendar fields
+ * are invalid.
*/
protected synchronized void computeTime()
{
@@ -465,15 +579,19 @@ public class GregorianCalendar extends C
}
/**
+ * * Determines if the given year is a leap year. + *
+ *+ * To specify a year in the BC era, use a negative value calculated + * as 1 - y, where y is the required year in BC. So, 1 BC is 0, + * 2 BC is -1, 3 BC is -2, etc. + *
* - * The year should be positive and you can't give an ERA. But - * remember that before 4 BC there wasn't a consistent leap year - * rule, so who cares. - * - * @param year a year use nonnegative value for BC. - * @param gregorian if true, use gregorian leap year rule. - * @return true, if the given year is a leap year, false otherwise. */ + * @param year a year (use a negative value for BC). + * @param gregorian if true, use the gregorian leap year rule. + * @return true, if the given year is a leap year, false otherwise. + */ private boolean isLeapYear(int year, boolean gregorian) { if ((year & 3) != 0) @@ -495,8 +613,9 @@ public class GregorianCalendar extends C * * @param year the year of the date. * @param dayOfYear the day of year of the date; 1 based. - * @param gregorian True, if we should use Gregorian rules. - * @return the days since the epoch, may be negative. */ + * @param gregorian true, if we should use Gregorian rules. + * @return the days since the epoch, may be negative. + */ private int getLinearDay(int year, int dayOfYear, boolean gregorian) { // The 13 is the number of days, that were omitted in the Gregorian @@ -529,7 +648,9 @@ public class GregorianCalendar extends C * Converts the given linear day into era, year, month, * day_of_year, day_of_month, day_of_week, and writes the result * into the fields array. + * * @param day the linear day. + * @param gregorian true, if we should use Gregorian rules. */ private void calculateDay(int day, boolean gregorian) { @@ -588,7 +709,7 @@ public class GregorianCalendar extends C /** * Converts the milliseconds since the epoch UTC * (time
) to time fields
- * (fields
).
+ * (fields
).
*/
protected synchronized void computeFields()
{
@@ -660,11 +781,19 @@ public class GregorianCalendar extends C
}
/**
- * Compares the given calender with this.
+ * Compares the given calendar with this. An object, o, is
+ * equivalent to this if it is also a GregorianCalendar
+ * with the same time since the epoch under the same conditions
+ * (same change date and same time zone).
+ *
* @param o the object to that we should compare.
* @return true, if the given object is a calendar, that represents
- * the same time (but doesn't necessary have the same fields).
- * @XXX Should we check if time zones, locale, cutover etc. are equal?
+ * the same time (but doesn't necessarily have the same fields).
+ * @throws IllegalArgumentException if one of the fields
+ * ZONE_OFFSET
or DST_OFFSET
is
+ * specified, if an unknown field is specified or if one
+ * of the calendar fields receives an illegal value when
+ * leniancy is not enabled.
*/
public boolean equals(Object o)
{
@@ -672,43 +801,18 @@ public class GregorianCalendar extends C
return false;
GregorianCalendar cal = (GregorianCalendar) o;
- return (cal.getTimeInMillis() == getTimeInMillis());
+ return (cal.getTimeInMillis() == getTimeInMillis()
+ && cal.getGregorianChange() == getGregorianChange()
+ && cal.getTimeZone().equals(getTimeZone()));
}
-// /**
-// * Compares the given calender with this.
-// * @param o the object to that we should compare.
-// * @return true, if the given object is a calendar, and this calendar
-// * represents a smaller time than the calender o.
-// */
-// public boolean before(Object o) {
-// if (!(o instanceof GregorianCalendar))
-// return false;
-
-// GregorianCalendar cal = (GregorianCalendar) o;
-// return (cal.getTimeInMillis() < getTimeInMillis());
-// }
-
-// /**
-// * Compares the given calender with this.
-// * @param o the object to that we should compare.
-// * @return true, if the given object is a calendar, and this calendar
-// * represents a bigger time than the calender o.
-// */
-// public boolean after(Object o) {
-// if (!(o instanceof GregorianCalendar))
-// return false;
-
-// GregorianCalendar cal = (GregorianCalendar) o;
-// return (cal.getTimeInMillis() > getTimeInMillis());
-// }
-
/**
* Adds the specified amount of time to the given time field. The
* amount may be negative to subtract the time. If the field overflows
* it does what you expect: Jan, 25 + 10 Days is Feb, 4.
- * @param field the time field. One of the time field constants.
- * @param amount the amount of time.
+ *
+ * @param field one of the time field constants.
+ * @param amount the amount of time to add.
*/
public void add(int field, int amount)
{
@@ -814,12 +918,26 @@ public class GregorianCalendar extends C
*
* @param field the time field. One of the time field constants.
* @param up the direction, true for up, false for down.
+ * @throws IllegalArgumentException if one of the fields
+ * ZONE_OFFSET
or DST_OFFSET
is
+ * specified, if an unknown field is specified or if one
+ * of the calendar fields receives an illegal value when
+ * leniancy is not enabled.
*/
public void roll(int field, boolean up)
{
roll(field, up ? 1 : -1);
}
+ /**
+ * Checks that the fields are still within their legal bounds,
+ * following use of the roll()
method.
+ *
+ * @param field the field to check.
+ * @param delta multipler for alterations to the time
.
+ * @see #roll(int, boolean)
+ * @see #roll(int, int)
+ */
private void cleanUpAfterRoll(int field, int delta)
{
switch (field)
@@ -917,6 +1035,11 @@ public class GregorianCalendar extends C
*
* @param field the time field. One of the time field constants.
* @param amount the amount by which we should roll.
+ * @throws IllegalArgumentException if one of the fields
+ * ZONE_OFFSET
or DST_OFFSET
is
+ * specified, if an unknown field is specified or if one
+ * of the calendar fields receives an illegal value when
+ * leniancy is not enabled.
*/
public void roll(int field, int amount)
{
@@ -941,18 +1064,25 @@ public class GregorianCalendar extends C
cleanUpAfterRoll(field, newval - oldval);
}
+ /**
+ * The minimum values for the calendar fields.
+ */
private static final int[] minimums =
{ BC, 1, 0, 0, 1, 1, 1, SUNDAY, 1,
AM, 1, 0, 1, 1, 1, -(12*60*60*1000), 0 };
+ /**
+ * The maximum values for the calendar fields.
+ */
private static final int[] maximums =
{ AD, 5000000, 11, 53, 5, 31, 366, SATURDAY, 5,
PM, 12, 23, 59, 59, 999, +(12*60*60*1000), (12*60*60*1000) };
/**
* Gets the smallest value that is allowed for the specified field.
- * @param field the time field. One of the time field constants.
- * @return the smallest value.
+ *
+ * @param field one of the time field constants.
+ * @return the smallest value for the specified field.
*/
public int getMinimum(int field)
{
@@ -961,7 +1091,8 @@ public class GregorianCalendar extends C
/**
* Gets the biggest value that is allowed for the specified field.
- * @param field the time field. One of the time field constants.
+ &
+ * @param field one of the time field constants.
* @return the biggest value.
*/
public int getMaximum(int field)
@@ -972,8 +1103,12 @@ public class GregorianCalendar extends C
/**
* Gets the greatest minimum value that is allowed for the specified field.
+ * This is the largest value returned by the getActualMinimum(int)
+ * method.
+ *
* @param field the time field. One of the time field constants.
* @return the greatest minimum value.
+ * @see #getActualMinimum(int)
*/
public int getGreatestMinimum(int field)
{
@@ -984,10 +1119,15 @@ public class GregorianCalendar extends C
/**
* Gets the smallest maximum value that is allowed for the
- * specified field. For example this is 28 for DAY_OF_MONTH.
+ * specified field. This is the smallest value returned
+ * by the getActualMaximum(int)
. For example,
+ * this is 28 for DAY_OF_MONTH (as all months have at least
+ * 28 days).
+ *
* @param field the time field. One of the time field constants.
* @return the least maximum value.
- * @since jdk1.2
+ * @see #getActualMaximum(int)
+ * @since 1.2
*/
public int getLeastMaximum(int field)
{
@@ -1011,10 +1151,12 @@ public class GregorianCalendar extends C
* Gets the actual minimum value that is allowed for the specified field.
* This value is dependent on the values of the other fields. Note that
* this calls complete()
if not enough fields are set. This
- * can have ugly side effects.
+ * can have ugly side effects. The value given depends on the current
+ * time used by this instance.
+ *
* @param field the time field. One of the time field constants.
* @return the actual minimum value.
- * @since jdk1.2
+ * @since 1.2
*/
public int getActualMinimum(int field)
{
@@ -1039,7 +1181,10 @@ public class GregorianCalendar extends C
* Gets the actual maximum value that is allowed for the specified field.
* This value is dependent on the values of the other fields. Note that
* this calls complete()
if not enough fields are set. This
- * can have ugly side effects.
+ * can have ugly side effects. The value given depends on the current time
+ * used by this instance; thus, leap years have a maximum day of month value of
+ * 29, rather than 28.
+ *
* @param field the time field. One of the time field constants.
* @return the actual maximum value.
*/
@@ -1062,8 +1207,15 @@ public class GregorianCalendar extends C
int minimalDays = getMinimalDaysInFirstWeek();
int firstWeekday = getWeekDay(year, minimalDays);
+ /*
+ * Is there a set of days at the beginning of the year, before the
+ * first day of the week, equal to or greater than the minimum number
+ * of days required in the first week?
+ */
if (minimalDays - (7 + firstWeekday - getFirstDayOfWeek()) % 7 < 1)
- return week + 1;
+ return week + 1; /* Add week 1: firstWeekday through to firstDayOfWeek */
+ else
+ return week; /* First week begins on the firstDayOfWeek in the year */
}
case DAY_OF_MONTH:
{
@@ -1110,4 +1262,26 @@ public class GregorianCalendar extends C
return maximums[field];
}
}
+
+ /**
+ * Deserializes an instance of GregorianCalendar
+ * from an ObjectInputStream
. The fields are
+ * retrieved using the default handler, and then complete()
+ * is called to ensure all fields are correct.
+ *
+ * @param stream the object stream to read from.
+ * @throws IOException if an I/O error occurs.
+ * @throws ClassNotFoundException if the class of the deserialized
+ * object can not be found.
+ * @serialData complete
is called to synchronize all fields.
+ */
+ private void readObject(ObjectInputStream stream)
+ throws IOException, ClassNotFoundException
+ {
+ /* Handle default reading */
+ stream.defaultReadObject();
+ /* Ensure all time fields are correct */
+ complete();
+ }
+
}