Commit 933e5b28 by Mark Wielaard

[multiple changes]

2005-02-21  Mark Wielaard  <mark@klomp.org>

	* gnu/java/locale/LocaleInformation_en.java: Extend
	localPatternChars to "GyMdkHmsSEDFwWahKzYeugAZ".

2005-02-21  Mark Wielaard  <mark@klomp.org>

	* java/text/SimpleDateFormat.java
	(SimpleDateFormat(String, DateFormatSymbols)): Throw
	NullPointerException when formatData is null.

2005-02-21  Mark Wielaard  <mark@klomp.org>

	* java/util/SimpleTimeZone.java (getOffset): Calculate beforeEnd by
	taking dstSavings into account.

2005-02-21  Sven de Marothy <sven@physto.se>

	* java/text/SimpleDateFormat.java,
	(parse): Set correct DST_OFFSET to the correct value.

2005-02-21  Mark Wielaard  <mark@klomp.org>

	* java/util/SimpleTimeZone.java (checkRule): Throw
	IllegalArgumentException when month out of range.

2005-02-21  Sven de Marothy  <sven@physto.se>

	* java/util/GregorianCalendar.java,
	(add): Don't set fields directly anymore. Use set()

2005-02-21  Mark Wielaard  <mark@klomp.org>

	* java/text/SimpleDateFormat.java (CompiledField.toString):
	Use StringBuffer, not StringBuilder.
	(toString): Likewise.

2005-02-21  Sven de Marothy  <sven@physto.se>

	* java/util/Calendar.java
	(clear): Dates should clear to local time.
	* java/util/GregorianCalendar.java
	(computeTime): Fix priority problem with DAY_OF_WEEK,
	Handle non-sunday-startig weeks and minimumDaysInFirstWeek.

2005-02-21  Sven de Marothy  <sven@physto.se>

	* java/util/Calendar.java
	(Calendar): Constructor should clear fields.

2005-02-21  Sven de Marothy  <sven@physto.se>

	* java/text/SimpleDateFormat.java
	(parse): Tweak handling of 2-year dates
	* java/util/Calendar.java
	(clear): Clear fields to correct value.
	* java/util/GregorianCalendar.java
	(computeTime): Correct handling of time zones.
	Correct field minimum values.

2005-02-21  Sven de Marothy  <sven@physto.se>

	* java/util/Calendar.java
	(set) Invalidate all fields on first call to set().

2005-02-21  Sven de Marothy  <sven@physto.se>

	* java/util/GregorianCalendar.java
	(computeTime): Fixed handling of time zones.

2005-02-21  Sven de Marothy  <sven@physto.se>

	* java/util/Calendar.java
	(clear): Set values to Epoch instead of zero.
	(set): Set isSet to the relevant field pattern instead of just
	the field.
	* java/util/GregorianCalendar.java
	(getBundle): Removed.
	(getDayOfYear): Removed.
	(getFirstDayOfMonth): New private method.
	(nonLeniencyCheck): New private method.
	(computeTime): Correct handling of insufficient data.

2005-02-21  Sven de Marothy <sven@physto.se>

	* java/util/Calendar.java: Invalidate ERA field on setting
	the YEAR.
	* java/util/SimpleTimeZone.java:
	(getDaysInMonth): Reimplemented.
	* java/util/GregorianCalendar.java:
	(getLinearTime): Removed.
	(isLeapYear(int,boolean)): Removed.
	(before(), after()): Removed.
	(computeTime): Reimplemented.

2005-02-21  Sven de Marothy <sven@physto.se>

	* java/util/Calendar.java: Reformatted.
	* java/util/GregorianCalendar.java: Reformatted.
	* java/util/SimpleTimeZone.java: Reformatted.

2005-02-21  Sven de Marothy <sven@physto.se>

        * java/util/GregorianCalendar.java
        (GregorianCalendar): Update fields in the constructor

2005-02-21  Noa Resare  <noa@resare.com>

        * java/util/Calendar.java (explicitDSTOffset): New instance field.
        (set(int,int)): Set and use new field.
        (set(int,int,int)): Check new field.

2005-02-21  Noa Resare  <address@hidden>

	* java/util/Calendar.java(set):
	Fix for DST related regression.

2005-02-21  Jeroen Frijters  <jeroen@frijters.net>

	* java/util/Calendar.java
	(setTimeInMillis): Added call to clear, removed computeFields call.
	* java/util/Date.java
	(Date(int,int,int,int,int,int)): Removed workaround for
	GregorianCalendar bug.
	* java/util/GregorianCalendar.java
	(GregorianCalendar): Chained all constructors to a (new)
	common constructor.
	(computeTime): Fixed support for lenient month treatment.
	(getLinearDay): Return long instead of int.
	(calculateDay): Added fields argument and changed day argument
	to long.

2005-02-21  Andrew John Hughes  <gnu_andrew@member.fsf.org>

	* java/text/SimpleDateFormat.java
	Lots of documentation updates.
	(readObject(java.io.ObjectInputStream)): Wraps
	IllegalArgumentException as specified.
	(compileFormat(String)): Uses standardChars
	rather than the local pattern characters.
	Throws IllegalArgumentException rather than
	storing a -1 field.
	(toString()): Extended to include all variables
	in a better format.
	(translateLocalizedPattern(String, String, String)):
	Renamed to better define the use of this method.

2005-02-21  Andrew John Hughes  <gnu_andrew@member.fsf.org>

	* java/text/DateFormat.java:
	Documented pattern character offset constants and
	added new ones.
	(Field): Added new static fields for new pattern chars.
	* java/text/SimpleDateFormat.java:
	(CompiledField): Changed name of FieldSizePair class
	to CompiledField after adding the character as an
	attribute.  Changed fields to private and added
	accessors to give encapsulation.
	(CompiledField.CompiledField(int,int,char)): Extended
	with character field.
	(CompiledField.getField()): New accessor method.
	(CompiledField.getSize()): New acceessor method.
	(CompiledField.getCharacter()): New accessor method.
	(CompiledField.toString()): Added primarily for debugging.
	(standardChars): Now uses extended 24 character sequence.
	(compileFormat(String)): Changed to use CompiledField.
	(formatWithAttribute(java.util.Date, gnu.java.text.FormatBuffer,
	java.text.FieldPosition)): Changed to use CompiledField.
	New handler for RFC 822 timezones added.

2005-02-21  Andrew John Hughes  <gnu_andrew@member.fsf.org>

	* java/text/SimpleDateFormat.java:
	(parse(String, java.text.ParsePosition)):
	Changed 'E' and 'M' cases to use both
	short and long names.  Extended 'z'
	case to also handle 'Z', and deal
	with simple GMT offsets such as +0100.
	(computeOffset(String)): New private method,
	which converts a GMT offset specification,
	such as GMT-0500 to a numeric offset in
	milliseconds.
	* java/util/TimeZone.java:
	(timezones()): Added "CEST", the daylight
	savings time version of "CET", or Central
	European Time.

2005-02-21  Ito Kazumitsu  <kaz@maczuka.gcd.org>

	* java/text/SimpleDateFormat.java:
	(parse): Set the DST offset to 0 when parsing
	GMT offset timezones.

2005-02-21  Ito Kazumitsu  <kaz@maczuka.gcd.org>

	* java/text/SimpleDateFormat.java:
	(parse): Use offset to set ZONE_OFFSET
	rather than the DST_OFFSET, so that
	GMT offset timezones change the right
	one.

2005-02-21  Andrew John Hughes  <gnu_andrew@member.fsf.org>

	* java/text/SimpleDateFormat.java:
	(getDateFormatSymbols()): return a copy
	(setDateFormatSymbols(java.text.DateFormatSymbols)):
	throw exception on null input
	(clone()): implemented to clone
	internal fields

2005-02-21  Sven de Marothy <sven@physto.se>

	* java/text/SimpleDateFormat.java
	(parse): comparison should be case-insensitive, ignore null
	strings.

From-SVN: r95368
parent 665794a6
2005-02-21 Mark Wielaard <mark@klomp.org>
* gnu/java/locale/LocaleInformation_en.java: Extend
localPatternChars to "GyMdkHmsSEDFwWahKzYeugAZ".
2005-02-21 Mark Wielaard <mark@klomp.org>
* java/text/SimpleDateFormat.java
(SimpleDateFormat(String, DateFormatSymbols)): Throw
NullPointerException when formatData is null.
2005-02-21 Mark Wielaard <mark@klomp.org>
* java/util/SimpleTimeZone.java (getOffset): Calculate beforeEnd by
taking dstSavings into account.
2005-02-21 Sven de Marothy <sven@physto.se>
* java/text/SimpleDateFormat.java,
(parse): Set correct DST_OFFSET to the correct value.
2005-02-21 Mark Wielaard <mark@klomp.org>
* java/util/SimpleTimeZone.java (checkRule): Throw
IllegalArgumentException when month out of range.
2005-02-21 Sven de Marothy <sven@physto.se>
* java/util/GregorianCalendar.java,
(add): Don't set fields directly anymore. Use set()
2005-02-21 Mark Wielaard <mark@klomp.org>
* java/text/SimpleDateFormat.java (CompiledField.toString):
Use StringBuffer, not StringBuilder.
(toString): Likewise.
2005-02-21 Sven de Marothy <sven@physto.se>
* java/util/Calendar.java
(clear): Dates should clear to local time.
* java/util/GregorianCalendar.java
(computeTime): Fix priority problem with DAY_OF_WEEK,
Handle non-sunday-startig weeks and minimumDaysInFirstWeek.
2005-02-21 Sven de Marothy <sven@physto.se>
* java/util/Calendar.java
(Calendar): Constructor should clear fields.
2005-02-21 Sven de Marothy <sven@physto.se>
* java/text/SimpleDateFormat.java
(parse): Tweak handling of 2-year dates
* java/util/Calendar.java
(clear): Clear fields to correct value.
* java/util/GregorianCalendar.java
(computeTime): Correct handling of time zones.
Correct field minimum values.
2005-02-21 Sven de Marothy <sven@physto.se>
* java/util/Calendar.java
(set) Invalidate all fields on first call to set().
2005-02-21 Sven de Marothy <sven@physto.se>
* java/util/GregorianCalendar.java
(computeTime): Fixed handling of time zones.
2005-02-21 Sven de Marothy <sven@physto.se>
* java/util/Calendar.java
(clear): Set values to Epoch instead of zero.
(set): Set isSet to the relevant field pattern instead of just
the field.
* java/util/GregorianCalendar.java
(getBundle): Removed.
(getDayOfYear): Removed.
(getFirstDayOfMonth): New private method.
(nonLeniencyCheck): New private method.
(computeTime): Correct handling of insufficient data.
2005-02-21 Sven de Marothy <sven@physto.se>
* java/util/Calendar.java: Invalidate ERA field on setting
the YEAR.
* java/util/SimpleTimeZone.java:
(getDaysInMonth): Reimplemented.
* java/util/GregorianCalendar.java:
(getLinearTime): Removed.
(isLeapYear(int,boolean)): Removed.
(before(), after()): Removed.
(computeTime): Reimplemented.
2005-02-21 Sven de Marothy <sven@physto.se>
* java/util/Calendar.java: Reformatted.
* java/util/GregorianCalendar.java: Reformatted.
* java/util/SimpleTimeZone.java: Reformatted.
2005-02-21 Sven de Marothy <sven@physto.se>
* java/util/GregorianCalendar.java
(GregorianCalendar): Update fields in the constructor
2005-02-21 Noa Resare <noa@resare.com>
* java/util/Calendar.java (explicitDSTOffset): New instance field.
(set(int,int)): Set and use new field.
(set(int,int,int)): Check new field.
2005-02-21 Noa Resare <address@hidden>
* java/util/Calendar.java(set):
Fix for DST related regression.
2005-02-21 Jeroen Frijters <jeroen@frijters.net>
* java/util/Calendar.java
(setTimeInMillis): Added call to clear, removed computeFields call.
* java/util/Date.java
(Date(int,int,int,int,int,int)): Removed workaround for
GregorianCalendar bug.
* java/util/GregorianCalendar.java
(GregorianCalendar): Chained all constructors to a (new)
common constructor.
(computeTime): Fixed support for lenient month treatment.
(getLinearDay): Return long instead of int.
(calculateDay): Added fields argument and changed day argument
to long.
2005-02-21 Andrew John Hughes <gnu_andrew@member.fsf.org>
* java/text/SimpleDateFormat.java
Lots of documentation updates.
(readObject(java.io.ObjectInputStream)): Wraps
IllegalArgumentException as specified.
(compileFormat(String)): Uses standardChars
rather than the local pattern characters.
Throws IllegalArgumentException rather than
storing a -1 field.
(toString()): Extended to include all variables
in a better format.
(translateLocalizedPattern(String, String, String)):
Renamed to better define the use of this method.
2005-02-21 Andrew John Hughes <gnu_andrew@member.fsf.org>
* java/text/DateFormat.java:
Documented pattern character offset constants and
added new ones.
(Field): Added new static fields for new pattern chars.
* java/text/SimpleDateFormat.java:
(CompiledField): Changed name of FieldSizePair class
to CompiledField after adding the character as an
attribute. Changed fields to private and added
accessors to give encapsulation.
(CompiledField.CompiledField(int,int,char)): Extended
with character field.
(CompiledField.getField()): New accessor method.
(CompiledField.getSize()): New acceessor method.
(CompiledField.getCharacter()): New accessor method.
(CompiledField.toString()): Added primarily for debugging.
(standardChars): Now uses extended 24 character sequence.
(compileFormat(String)): Changed to use CompiledField.
(formatWithAttribute(java.util.Date, gnu.java.text.FormatBuffer,
java.text.FieldPosition)): Changed to use CompiledField.
New handler for RFC 822 timezones added.
2005-02-21 Andrew John Hughes <gnu_andrew@member.fsf.org>
* java/text/SimpleDateFormat.java:
(parse(String, java.text.ParsePosition)):
Changed 'E' and 'M' cases to use both
short and long names. Extended 'z'
case to also handle 'Z', and deal
with simple GMT offsets such as +0100.
(computeOffset(String)): New private method,
which converts a GMT offset specification,
such as GMT-0500 to a numeric offset in
milliseconds.
* java/util/TimeZone.java:
(timezones()): Added "CEST", the daylight
savings time version of "CET", or Central
European Time.
2005-02-21 Ito Kazumitsu <kaz@maczuka.gcd.org>
* java/text/SimpleDateFormat.java:
(parse): Set the DST offset to 0 when parsing
GMT offset timezones.
2005-02-21 Ito Kazumitsu <kaz@maczuka.gcd.org>
* java/text/SimpleDateFormat.java:
(parse): Use offset to set ZONE_OFFSET
rather than the DST_OFFSET, so that
GMT offset timezones change the right
one.
2005-02-21 Andrew John Hughes <gnu_andrew@member.fsf.org>
* java/text/SimpleDateFormat.java:
(getDateFormatSymbols()): return a copy
(setDateFormatSymbols(java.text.DateFormatSymbols)):
throw exception on null input
(clone()): implemented to clone
internal fields
2005-02-21 Sven de Marothy <sven@physto.se>
* java/text/SimpleDateFormat.java
(parse): comparison should be case-insensitive, ignore null
strings.
2005-02-21 Robert Schuster <theBohemian@gmx.net> 2005-02-21 Robert Schuster <theBohemian@gmx.net>
* gnu/java/beans/IntrospectionIncubator.java * gnu/java/beans/IntrospectionIncubator.java
......
...@@ -159,7 +159,7 @@ public class LocaleInformation_en extends ListResourceBundle ...@@ -159,7 +159,7 @@ public class LocaleInformation_en extends ListResourceBundle
{ "shortWeekdays", shortWeekdays }, { "shortWeekdays", shortWeekdays },
{ "ampms", ampms }, { "ampms", ampms },
{ "eras", eras }, { "eras", eras },
{ "localPatternChars", "GyMdkHmsSEDFwWahKz" }, { "localPatternChars", "GyMdkHmsSEDFwWahKzYeugAZ" },
{ "zoneStrings", zoneStrings }, { "zoneStrings", zoneStrings },
{ "shortDateFormat", "M/d/yy" }, // Java's Y2K bug. { "shortDateFormat", "M/d/yy" }, // Java's Y2K bug.
......
...@@ -70,29 +70,221 @@ public abstract class DateFormat extends Format implements Cloneable ...@@ -70,29 +70,221 @@ public abstract class DateFormat extends Format implements Cloneable
/* These constants need to have these exact values. They /* These constants need to have these exact values. They
* correspond to index positions within the localPatternChars * correspond to index positions within the localPatternChars
* string for a given locale. For example, the US locale uses * string for a given locale. Each locale may specify its
* the string "GyMdkHmsSEDFwWahKz", where 'G' is the character * own character for a particular field, but the position
* for era, 'y' for year, and so on down to 'z' for time zone. * of these characters must correspond to an appropriate field
* number (as listed below), in order for their meaning to
* be determined. For example, the US locale uses
* the string "GyMdkHmsSEDFwWahKzYeugAZ", where 'G' is the character
* for era, 'y' for year, and so on down to 'Z' for time zone.
*/ */
/**
* Represents the position of the era
* pattern character in the array of
* localized pattern characters.
* For example, 'AD' is an era used
* in the Gregorian calendar system.
* In the U.S. locale, this is 'G'.
*/
public static final int ERA_FIELD = 0; public static final int ERA_FIELD = 0;
/**
* Represents the position of the year
* pattern character in the array of
* localized pattern characters.
* In the U.S. locale, this is 'y'.
*/
public static final int YEAR_FIELD = 1; public static final int YEAR_FIELD = 1;
/**
* Represents the position of the month
* pattern character in the array of
* localized pattern characters.
* In the U.S. locale, this is 'M'.
*/
public static final int MONTH_FIELD = 2; public static final int MONTH_FIELD = 2;
/**
* Represents the position of the date
* or day of the month pattern character
* in the array of localized pattern
* characters. In the U.S. locale,
* this is 'd'.
*/
public static final int DATE_FIELD = 3; public static final int DATE_FIELD = 3;
/**
* Represents the position of the 24
* hour pattern character in the array of
* localized pattern characters.
* In the U.S. locale, this is 'k'.
* This field numbers hours from 1 to 24.
*/
public static final int HOUR_OF_DAY1_FIELD = 4; public static final int HOUR_OF_DAY1_FIELD = 4;
/**
* Represents the position of the 24
* hour pattern character in the array of
* localized pattern characters.
* In the U.S. locale, this is 'H'.
* This field numbers hours from 0 to 23.
*/
public static final int HOUR_OF_DAY0_FIELD = 5; public static final int HOUR_OF_DAY0_FIELD = 5;
/**
* Represents the position of the minute
* pattern character in the array of
* localized pattern characters.
* In the U.S. locale, this is 'm'.
*/
public static final int MINUTE_FIELD = 6; public static final int MINUTE_FIELD = 6;
/**
* Represents the position of the second
* pattern character in the array of
* localized pattern characters.
* In the U.S. locale, this is 's'.
*/
public static final int SECOND_FIELD = 7; public static final int SECOND_FIELD = 7;
/**
* Represents the position of the millisecond
* pattern character in the array of
* localized pattern characters.
* In the U.S. locale, this is 'S'.
*/
public static final int MILLISECOND_FIELD = 8; public static final int MILLISECOND_FIELD = 8;
/**
* Represents the position of the day of the
* week pattern character in the array of
* localized pattern characters.
* In the U.S. locale, this is 'E'.
*/
public static final int DAY_OF_WEEK_FIELD = 9; public static final int DAY_OF_WEEK_FIELD = 9;
/**
* Represents the position of the day of the
* year pattern character in the array of
* localized pattern characters.
* In the U.S. locale, this is 'D'.
*/
public static final int DAY_OF_YEAR_FIELD = 10; public static final int DAY_OF_YEAR_FIELD = 10;
/**
* Represents the position of the day of the
* week in the month pattern character in the
* array of localized pattern characters.
* In the U.S. locale, this is 'F'.
*/
public static final int DAY_OF_WEEK_IN_MONTH_FIELD = 11; public static final int DAY_OF_WEEK_IN_MONTH_FIELD = 11;
/**
* Represents the position of the week of the
* year pattern character in the array of
* localized pattern characters.
* In the U.S. locale, this is 'w'.
*/
public static final int WEEK_OF_YEAR_FIELD = 12; public static final int WEEK_OF_YEAR_FIELD = 12;
/**
* Represents the position of the week of the
* month pattern character in the array of
* localized pattern characters.
* In the U.S. locale, this is 'W'.
*/
public static final int WEEK_OF_MONTH_FIELD = 13; public static final int WEEK_OF_MONTH_FIELD = 13;
/**
* Represents the position of the am/pm
* pattern character in the array of
* localized pattern characters.
* In the U.S. locale, this is 'a'.
*/
public static final int AM_PM_FIELD = 14; public static final int AM_PM_FIELD = 14;
/**
* Represents the position of the 12
* hour pattern character in the array of
* localized pattern characters.
* In the U.S. locale, this is 'h'.
* This field numbers hours from 1 to 12.
*/
public static final int HOUR1_FIELD = 15; public static final int HOUR1_FIELD = 15;
/**
* Represents the position of the 12
* hour pattern character in the array of
* localized pattern characters.
* In the U.S. locale, this is 'K'.
* This field numbers hours from 0 to 11.
*/
public static final int HOUR0_FIELD = 16; public static final int HOUR0_FIELD = 16;
/**
* Represents the position of the generic
* timezone pattern character in the array of
* localized pattern characters.
* In the U.S. locale, this is 'z'.
*/
public static final int TIMEZONE_FIELD = 17; public static final int TIMEZONE_FIELD = 17;
/**
* Represents the position of the ISO year
* pattern character in the array of
* localized pattern characters.
* In the U.S. locale, this is 'Y'.
* This is a GNU extension in accordance with
* the CLDR data used. This value may
* differ from the normal year value.
*/
public static final int ISO_YEAR_FIELD = 18;
/**
* Represents the position of the localized
* day of the week pattern character in the
* array of localized pattern characters.
* In the U.S. locale, this is 'e'.
* This is a GNU extension in accordance with
* the CLDR data used. This value only
* differs from the day of the week with
* numeric formatting, in which case the
* locale's first day of the week is used.
*/
public static final int LOCALIZED_DAY_OF_WEEK_FIELD = 19;
/**
* Represents the position of the extended year
* pattern character in the array of
* localized pattern characters.
* In the U.S. locale, this is 'u'.
* This is a GNU extension in accordance with
* the CLDR data used. This value modifies
* the year value, so as to incorporate the era.
* For example, in the Gregorian calendar system,
* the extended year is negative instead of being
* marked as BC.
*/
public static final int EXTENDED_YEAR_FIELD = 20;
/**
* Represents the position of the modified Julian
* day pattern character in the array of
* localized pattern characters.
* In the U.S. locale, this is 'g'.
* This is a GNU extension in accordance with
* the CLDR data used. This value differs
* from the standard Julian day in that days
* are marked from midnight onwards rather than
* noon, and the local time zone affects the value.
* In simple terms, it can be thought of as all
* the date fields represented as a single number.
*/
public static final int MODIFIED_JULIAN_DAY_FIELD = 21;
/**
* Represents the position of the millisecond
* in the day pattern character in the array of
* localized pattern characters.
* In the U.S. locale, this is 'A'.
* This is a GNU extension in accordance with
* the CLDR data used. This value represents
* all the time fields (excluding the time zone)
* numerically, giving the number of milliseconds
* into the day (e.g. 10 in the morning would
* be 10 * 60 * 60 * 1000). Any daylight savings
* offset also affects this value.
*/
public static final int MILLISECOND_IN_DAY_FIELD = 22;
/**
* Represents the position of the RFC822
* timezone pattern character in the array of
* localized pattern characters.
* In the U.S. locale, this is 'Z'.
* This is a GNU extension in accordance with
* the CLDR data used. The value is the offset
* of the current time from GMT e.g. -0500 would
* be five hours prior to GMT.
*/
public static final int RFC822_TIMEZONE_FIELD = 23;
public static class Field extends Format.Field public static class Field extends Format.Field
{ {
...@@ -136,14 +328,28 @@ public abstract class DateFormat extends Format implements Cloneable ...@@ -136,14 +328,28 @@ public abstract class DateFormat extends Format implements Cloneable
= new Field("hour0", Calendar.HOUR); = new Field("hour0", Calendar.HOUR);
public static final DateFormat.Field TIME_ZONE public static final DateFormat.Field TIME_ZONE
= new Field("timezone", Calendar.ZONE_OFFSET); = new Field("timezone", Calendar.ZONE_OFFSET);
public static final DateFormat.Field ISO_YEAR
= new Field("iso year", Calendar.YEAR);
public static final DateFormat.Field LOCALIZED_DAY_OF_WEEK
= new Field("localized day of week", Calendar.DAY_OF_WEEK);
public static final DateFormat.Field EXTENDED_YEAR
= new Field("extended year", Calendar.YEAR);
public static final DateFormat.Field MODIFIED_JULIAN_DAY
= new Field("julian day", -1);
public static final DateFormat.Field MILLISECOND_IN_DAY
= new Field("millisecond in day", -1);
public static final DateFormat.Field RFC822_TIME_ZONE
= new Field("rfc822 timezone", Calendar.ZONE_OFFSET);
static final DateFormat.Field[] allFields = static final DateFormat.Field[] allFields =
{ {
ERA, YEAR, MONTH, DAY_OF_MONTH, HOUR_OF_DAY1, ERA, YEAR, MONTH, DAY_OF_MONTH, HOUR_OF_DAY1,
HOUR_OF_DAY0, MINUTE, SECOND, MILLISECOND, HOUR_OF_DAY0, MINUTE, SECOND, MILLISECOND,
DAY_OF_WEEK, DAY_OF_YEAR, DAY_OF_WEEK_IN_MONTH, DAY_OF_WEEK, DAY_OF_YEAR, DAY_OF_WEEK_IN_MONTH,
WEEK_OF_YEAR, WEEK_OF_MONTH, AM_PM, HOUR1, HOUR0, WEEK_OF_YEAR, WEEK_OF_MONTH, AM_PM, HOUR1, HOUR0,
TIME_ZONE TIME_ZONE, ISO_YEAR, LOCALIZED_DAY_OF_WEEK,
EXTENDED_YEAR, MODIFIED_JULIAN_DAY, MILLISECOND_IN_DAY,
RFC822_TIME_ZONE
}; };
// For deserialization // For deserialization
......
/* SimpleDateFormat.java -- A class for parsing/formating simple /* SimpleDateFormat.java -- A class for parsing/formating simple
date constructs date constructs
Copyright (C) 1998, 1999, 2000, 2001, 2003, 2004 Copyright (C) 1998, 1999, 2000, 2001, 2003, 2004, 2005
Free Software Foundation, Inc. Free Software Foundation, Inc.
This file is part of GNU Classpath. This file is part of GNU Classpath.
...@@ -45,6 +45,7 @@ import gnu.java.text.FormatBuffer; ...@@ -45,6 +45,7 @@ import gnu.java.text.FormatBuffer;
import gnu.java.text.FormatCharacterIterator; import gnu.java.text.FormatCharacterIterator;
import gnu.java.text.StringFormatBuffer; import gnu.java.text.StringFormatBuffer;
import java.io.InvalidObjectException;
import java.io.IOException; import java.io.IOException;
import java.io.ObjectInputStream; import java.io.ObjectInputStream;
import java.util.ArrayList; import java.util.ArrayList;
...@@ -55,6 +56,8 @@ import java.util.Iterator; ...@@ -55,6 +56,8 @@ import java.util.Iterator;
import java.util.Locale; import java.util.Locale;
import java.util.SimpleTimeZone; import java.util.SimpleTimeZone;
import java.util.TimeZone; import java.util.TimeZone;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/** /**
* SimpleDateFormat provides convenient methods for parsing and formatting * SimpleDateFormat provides convenient methods for parsing and formatting
...@@ -62,34 +65,189 @@ import java.util.TimeZone; ...@@ -62,34 +65,189 @@ import java.util.TimeZone;
*/ */
public class SimpleDateFormat extends DateFormat public class SimpleDateFormat extends DateFormat
{ {
/** A pair class used by SimpleDateFormat as a compiled representation /**
* of a format string. * This class is used by <code>SimpleDateFormat</code> as a
* compiled representation of a format string. The field
* ID, size, and character used are stored for each sequence
* of pattern characters.
*/ */
private class FieldSizePair private class CompiledField
{ {
public int field; /**
public int size; * The ID of the field within the local pattern characters,
*/
private int field;
/**
* The size of the character sequence.
*/
private int size;
/**
* The character used.
*/
private char character;
/** Constructs a pair with the given field and size values */ /**
public FieldSizePair(int f, int s) { * Constructs a compiled field using the
* the given field ID, size and character
* values.
*
* @param f the field ID.
* @param s the size of the field.
* @param c the character used.
*/
public CompiledField(int f, int s, char c) {
field = f; field = f;
size = s; size = s;
character = c;
}
/**
* Retrieves the ID of the field relative to
* the local pattern characters.
*/
public int getField()
{
return field;
}
/**
* Retrieves the size of the character sequence.
*/
public int getSize()
{
return size;
}
/**
* Retrieves the character used in the sequence.
*/
public char getCharacter()
{
return character;
}
/**
* Returns a <code>String</code> representation
* of the compiled field, primarily for debugging
* purposes.
*
* @return a <code>String</code> representation.
*/
public String toString()
{
StringBuffer builder;
builder = new StringBuffer(getClass().getName());
builder.append("[field=");
builder.append(field);
builder.append(", size=");
builder.append(size);
builder.append(", character=");
builder.append(character);
builder.append("]");
return builder.toString();
} }
} }
/**
* A list of <code>CompiledField</code>s,
* representing the compiled version of the pattern.
*
* @see CompiledField
* @serial Ignored.
*/
private transient ArrayList tokens; private transient ArrayList tokens;
/**
* The localised data used in formatting,
* such as the day and month names in the local
* language, and the localized pattern characters.
*
* @see DateFormatSymbols
* @serial The localisation data. May not be null.
*/
private DateFormatSymbols formatData; // formatData private DateFormatSymbols formatData; // formatData
/**
* The date representing the start of the century
* used for interpreting two digit years. For
* example, 24/10/2004 would cause two digit
* years to be interpreted as representing
* the years between 2004 and 2104.
*
* @see get2DigitYearStart()
* @see set2DigitYearStart(java.util.Date)
* @see Date
* @serial The start date of the century for parsing two digit years.
* May not be null.
*/
private Date defaultCenturyStart; private Date defaultCenturyStart;
/**
* The year at which interpretation of two
* digit years starts.
*
* @see get2DigitYearStart()
* @see set2DigitYearStart(java.util.Date)
* @serial Ignored.
*/
private transient int defaultCentury; private transient int defaultCentury;
/**
* The non-localized pattern string. This
* only ever contains the pattern characters
* stored in standardChars. Localized patterns
* are translated to this form.
*
* @see applyPattern(String)
* @see applyLocalizedPattern(String)
* @see toPattern()
* @see toLocalizedPattern()
* @serial The non-localized pattern string. May not be null.
*/
private String pattern; private String pattern;
/**
* The version of serialized data used by this class.
* Version 0 only includes the pattern and formatting
* data. Version 1 adds the start date for interpreting
* two digit years.
*
* @serial This specifies the version of the data being serialized.
* Version 0 (or no version) specifies just <code>pattern</code>
* and <code>formatData</code>. Version 1 adds
* the <code>defaultCenturyStart</code>. This implementation
* always writes out version 1 data.
*/
private int serialVersionOnStream = 1; // 0 indicates JDK1.1.3 or earlier private int serialVersionOnStream = 1; // 0 indicates JDK1.1.3 or earlier
/**
* For compatability.
*/
private static final long serialVersionUID = 4774881970558875024L; private static final long serialVersionUID = 4774881970558875024L;
// This string is specified in the JCL. We set it here rather than // This string is specified in the root of the CLDR. We set it here
// do a DateFormatSymbols(Locale.US).getLocalPatternChars() since // rather than doing a DateFormatSymbols(Locale.US).getLocalPatternChars()
// someone could theoretically change those values (though unlikely). // since someone could theoretically change those values (though unlikely).
private static final String standardChars = "GyMdkHmsSEDFwWahKzZ"; private static final String standardChars = "GyMdkHmsSEDFwWahKzYeugAZ";
/**
* Reads the serialized version of this object.
* If the serialized data is only version 0,
* then the date for the start of the century
* for interpreting two digit years is computed.
* The pattern is parsed and compiled following the process
* of reading in the serialized data.
*
* @param stream the object stream to read the data from.
* @throws IOException if an I/O error occurs.
* @throws ClassNotFoundException if the class of the serialized data
* could not be found.
* @throws InvalidObjectException if the pattern is invalid.
*/
private void readObject(ObjectInputStream stream) private void readObject(ObjectInputStream stream)
throws IOException, ClassNotFoundException throws IOException, ClassNotFoundException
{ {
...@@ -105,9 +263,25 @@ public class SimpleDateFormat extends DateFormat ...@@ -105,9 +263,25 @@ public class SimpleDateFormat extends DateFormat
// Set up items normally taken care of by the constructor. // Set up items normally taken care of by the constructor.
tokens = new ArrayList(); tokens = new ArrayList();
compileFormat(pattern); try
{
compileFormat(pattern);
}
catch (IllegalArgumentException e)
{
throw new InvalidObjectException("The stream pattern was invalid.");
}
} }
/**
* Compiles the supplied non-localized pattern into a form
* from which formatting and parsing can be performed.
* This also detects errors in the pattern, which will
* be raised on later use of the compiled data.
*
* @param pattern the non-localized pattern to compile.
* @throws IllegalArgumentException if the pattern is invalid.
*/
private void compileFormat(String pattern) private void compileFormat(String pattern)
{ {
// Any alphabetical characters are treated as pattern characters // Any alphabetical characters are treated as pattern characters
...@@ -116,24 +290,25 @@ public class SimpleDateFormat extends DateFormat ...@@ -116,24 +290,25 @@ public class SimpleDateFormat extends DateFormat
char thisChar; char thisChar;
int pos; int pos;
int field; int field;
FieldSizePair current = null; CompiledField current = null;
for (int i=0; i<pattern.length(); i++) { for (int i=0; i<pattern.length(); i++) {
thisChar = pattern.charAt(i); thisChar = pattern.charAt(i);
field = formatData.getLocalPatternChars().indexOf(thisChar); field = standardChars.indexOf(thisChar);
if (field == -1) { if (field == -1) {
current = null; current = null;
if ((thisChar >= 'A' && thisChar <= 'Z') if ((thisChar >= 'A' && thisChar <= 'Z')
|| (thisChar >= 'a' && thisChar <= 'z')) { || (thisChar >= 'a' && thisChar <= 'z')) {
// Not a valid letter // Not a valid letter
tokens.add(new FieldSizePair(-1,0)); throw new IllegalArgumentException("Invalid letter " + thisChar +
"encountered at character " + i
+ ".");
} else if (thisChar == '\'') { } else if (thisChar == '\'') {
// Quoted text section; skip to next single quote // Quoted text section; skip to next single quote
pos = pattern.indexOf('\'',i+1); pos = pattern.indexOf('\'',i+1);
if (pos == -1) { if (pos == -1) {
// This ought to be an exception, but spec does not throw new IllegalArgumentException("Quotes starting at character "
// let us throw one. + i + " not closed.");
tokens.add(new FieldSizePair(-1,0));
} }
if ((pos+1 < pattern.length()) && (pattern.charAt(pos+1) == '\'')) { if ((pos+1 < pattern.length()) && (pattern.charAt(pos+1) == '\'')) {
tokens.add(pattern.substring(i+1,pos+1)); tokens.add(pattern.substring(i+1,pos+1));
...@@ -150,20 +325,38 @@ public class SimpleDateFormat extends DateFormat ...@@ -150,20 +325,38 @@ public class SimpleDateFormat extends DateFormat
if ((current != null) && (field == current.field)) { if ((current != null) && (field == current.field)) {
current.size++; current.size++;
} else { } else {
current = new FieldSizePair(field,1); current = new CompiledField(field,1,thisChar);
tokens.add(current); tokens.add(current);
} }
} }
} }
} }
/**
* Returns a string representation of this
* class.
*
* @return a string representation of the <code>SimpleDateFormat</code>
* instance.
*/
public String toString() public String toString()
{ {
StringBuffer output = new StringBuffer(); StringBuffer output = new StringBuffer(getClass().getName());
Iterator i = tokens.iterator(); output.append("[tokens=");
while (i.hasNext()) { output.append(tokens);
output.append(i.next().toString()); output.append(", formatData=");
} output.append(formatData);
output.append(", defaultCenturyStart=");
output.append(defaultCenturyStart);
output.append(", defaultCentury=");
output.append(defaultCentury);
output.append(", pattern=");
output.append(pattern);
output.append(", serialVersionOnStream=");
output.append(serialVersionOnStream);
output.append(", standardChars=");
output.append(standardChars);
output.append("]");
return output.toString(); return output.toString();
} }
...@@ -194,8 +387,12 @@ public class SimpleDateFormat extends DateFormat ...@@ -194,8 +387,12 @@ public class SimpleDateFormat extends DateFormat
} }
/** /**
* Creates a date formatter using the specified pattern, with the default * Creates a date formatter using the specified non-localized pattern,
* DateFormatSymbols for the default locale. * with the default DateFormatSymbols for the default locale.
*
* @param pattern the pattern to use.
* @throws NullPointerException if the pattern is null.
* @throws IllegalArgumentException if the pattern is invalid.
*/ */
public SimpleDateFormat(String pattern) public SimpleDateFormat(String pattern)
{ {
...@@ -203,8 +400,13 @@ public class SimpleDateFormat extends DateFormat ...@@ -203,8 +400,13 @@ public class SimpleDateFormat extends DateFormat
} }
/** /**
* Creates a date formatter using the specified pattern, with the default * Creates a date formatter using the specified non-localized pattern,
* DateFormatSymbols for the given locale. * with the default DateFormatSymbols for the given locale.
*
* @param pattern the non-localized pattern to use.
* @param locale the locale to use for the formatting symbols.
* @throws NullPointerException if the pattern is null.
* @throws IllegalArgumentException if the pattern is invalid.
*/ */
public SimpleDateFormat(String pattern, Locale locale) public SimpleDateFormat(String pattern, Locale locale)
{ {
...@@ -222,8 +424,14 @@ public class SimpleDateFormat extends DateFormat ...@@ -222,8 +424,14 @@ public class SimpleDateFormat extends DateFormat
} }
/** /**
* Creates a date formatter using the specified pattern. The * Creates a date formatter using the specified non-localized
* specified DateFormatSymbols will be used when formatting. * pattern. The specified DateFormatSymbols will be used when
* formatting.
*
* @param pattern the non-localized pattern to use.
* @param formatData the formatting symbols to use.
* @throws NullPointerException if the pattern or formatData is null.
* @throws IllegalArgumentException if the pattern is invalid.
*/ */
public SimpleDateFormat(String pattern, DateFormatSymbols formatData) public SimpleDateFormat(String pattern, DateFormatSymbols formatData)
{ {
...@@ -231,6 +439,8 @@ public class SimpleDateFormat extends DateFormat ...@@ -231,6 +439,8 @@ public class SimpleDateFormat extends DateFormat
calendar = new GregorianCalendar(); calendar = new GregorianCalendar();
computeCenturyStart (); computeCenturyStart ();
tokens = new ArrayList(); tokens = new ArrayList();
if (formatData == null)
throw new NullPointerException("formatData");
this.formatData = formatData; this.formatData = formatData;
compileFormat(pattern); compileFormat(pattern);
this.pattern = pattern; this.pattern = pattern;
...@@ -240,9 +450,6 @@ public class SimpleDateFormat extends DateFormat ...@@ -240,9 +450,6 @@ public class SimpleDateFormat extends DateFormat
numberFormat.setMaximumFractionDigits (0); numberFormat.setMaximumFractionDigits (0);
} }
// What is the difference between localized and unlocalized? The
// docs don't say.
/** /**
* This method returns a string with the formatting pattern being used * This method returns a string with the formatting pattern being used
* by this object. This string is unlocalized. * by this object. This string is unlocalized.
...@@ -263,7 +470,7 @@ public class SimpleDateFormat extends DateFormat ...@@ -263,7 +470,7 @@ public class SimpleDateFormat extends DateFormat
public String toLocalizedPattern() public String toLocalizedPattern()
{ {
String localChars = formatData.getLocalPatternChars(); String localChars = formatData.getLocalPatternChars();
return applyLocalizedPattern (pattern, standardChars, localChars); return translateLocalizedPattern(pattern, standardChars, localChars);
} }
/** /**
...@@ -271,6 +478,8 @@ public class SimpleDateFormat extends DateFormat ...@@ -271,6 +478,8 @@ public class SimpleDateFormat extends DateFormat
* object. This string is not localized. * object. This string is not localized.
* *
* @param pattern The new format pattern. * @param pattern The new format pattern.
* @throws NullPointerException if the pattern is null.
* @throws IllegalArgumentException if the pattern is invalid.
*/ */
public void applyPattern(String pattern) public void applyPattern(String pattern)
{ {
...@@ -284,16 +493,34 @@ public class SimpleDateFormat extends DateFormat ...@@ -284,16 +493,34 @@ public class SimpleDateFormat extends DateFormat
* object. This string is localized. * object. This string is localized.
* *
* @param pattern The new format pattern. * @param pattern The new format pattern.
* @throws NullPointerException if the pattern is null.
* @throws IllegalArgumentException if the pattern is invalid.
*/ */
public void applyLocalizedPattern(String pattern) public void applyLocalizedPattern(String pattern)
{ {
String localChars = formatData.getLocalPatternChars(); String localChars = formatData.getLocalPatternChars();
pattern = applyLocalizedPattern (pattern, localChars, standardChars); pattern = translateLocalizedPattern(pattern, localChars, standardChars);
applyPattern(pattern); applyPattern(pattern);
} }
private String applyLocalizedPattern(String pattern, /**
String oldChars, String newChars) * Translates either from or to a localized variant of the pattern
* string. For example, in the German locale, 't' (for 'tag') is
* used instead of 'd' (for 'date'). This method translates
* a localized pattern (such as 'ttt') to a non-localized pattern
* (such as 'ddd'), or vice versa. Non-localized patterns use
* a standard set of characters, which match those of the U.S. English
* locale.
*
* @param pattern the pattern to translate.
* @param oldChars the old set of characters (used in the pattern).
* @param newChars the new set of characters (which will be used in the
* pattern).
* @return a version of the pattern using the characters in
* <code>newChars</code>.
*/
private String translateLocalizedPattern(String pattern,
String oldChars, String newChars)
{ {
int len = pattern.length(); int len = pattern.length();
StringBuffer buf = new StringBuffer(len); StringBuffer buf = new StringBuffer(len);
...@@ -341,14 +568,14 @@ public class SimpleDateFormat extends DateFormat ...@@ -341,14 +568,14 @@ public class SimpleDateFormat extends DateFormat
} }
/** /**
* This method returns the format symbol information used for parsing * This method returns a copy of the format symbol information used
* and formatting dates. * for parsing and formatting dates.
* *
* @return The date format symbols. * @return a copy of the date format symbols.
*/ */
public DateFormatSymbols getDateFormatSymbols() public DateFormatSymbols getDateFormatSymbols()
{ {
return formatData; return (DateFormatSymbols) formatData.clone();
} }
/** /**
...@@ -356,9 +583,15 @@ public class SimpleDateFormat extends DateFormat ...@@ -356,9 +583,15 @@ public class SimpleDateFormat extends DateFormat
* and formatting dates. * and formatting dates.
* *
* @param formatData The date format symbols. * @param formatData The date format symbols.
* @throws NullPointerException if <code>formatData</code> is null.
*/ */
public void setDateFormatSymbols(DateFormatSymbols formatData) public void setDateFormatSymbols(DateFormatSymbols formatData)
{ {
if (formatData == null)
{
throw new
NullPointerException("The supplied format data was null.");
}
this.formatData = formatData; this.formatData = formatData;
} }
...@@ -431,12 +664,12 @@ public class SimpleDateFormat extends DateFormat ...@@ -431,12 +664,12 @@ public class SimpleDateFormat extends DateFormat
while (iter.hasNext()) while (iter.hasNext())
{ {
Object o = iter.next(); Object o = iter.next();
if (o instanceof FieldSizePair) if (o instanceof CompiledField)
{ {
FieldSizePair p = (FieldSizePair) o; CompiledField cf = (CompiledField) o;
int beginIndex = buffer.length(); int beginIndex = buffer.length();
switch (p.field) switch (cf.getField())
{ {
case ERA_FIELD: case ERA_FIELD:
buffer.append (formatData.eras[calendar.get (Calendar.ERA)], DateFormat.Field.ERA); buffer.append (formatData.eras[calendar.get (Calendar.ERA)], DateFormat.Field.ERA);
...@@ -445,75 +678,75 @@ public class SimpleDateFormat extends DateFormat ...@@ -445,75 +678,75 @@ public class SimpleDateFormat extends DateFormat
// If we have two digits, then we truncate. Otherwise, we // If we have two digits, then we truncate. Otherwise, we
// use the size of the pattern, and zero pad. // use the size of the pattern, and zero pad.
buffer.setDefaultAttribute (DateFormat.Field.YEAR); buffer.setDefaultAttribute (DateFormat.Field.YEAR);
if (p.size == 2) if (cf.getSize() == 2)
{ {
temp = String.valueOf (calendar.get (Calendar.YEAR)); temp = String.valueOf (calendar.get (Calendar.YEAR));
buffer.append (temp.substring (temp.length() - 2)); buffer.append (temp.substring (temp.length() - 2));
} }
else else
withLeadingZeros (calendar.get (Calendar.YEAR), p.size, buffer); withLeadingZeros (calendar.get (Calendar.YEAR), cf.getSize(), buffer);
break; break;
case MONTH_FIELD: case MONTH_FIELD:
buffer.setDefaultAttribute (DateFormat.Field.MONTH); buffer.setDefaultAttribute (DateFormat.Field.MONTH);
if (p.size < 3) if (cf.getSize() < 3)
withLeadingZeros (calendar.get (Calendar.MONTH) + 1, p.size, buffer); withLeadingZeros (calendar.get (Calendar.MONTH) + 1, cf.getSize(), buffer);
else if (p.size < 4) else if (cf.getSize() < 4)
buffer.append (formatData.shortMonths[calendar.get (Calendar.MONTH)]); buffer.append (formatData.shortMonths[calendar.get (Calendar.MONTH)]);
else else
buffer.append (formatData.months[calendar.get (Calendar.MONTH)]); buffer.append (formatData.months[calendar.get (Calendar.MONTH)]);
break; break;
case DATE_FIELD: case DATE_FIELD:
buffer.setDefaultAttribute (DateFormat.Field.DAY_OF_MONTH); buffer.setDefaultAttribute (DateFormat.Field.DAY_OF_MONTH);
withLeadingZeros (calendar.get (Calendar.DATE), p.size, buffer); withLeadingZeros (calendar.get (Calendar.DATE), cf.getSize(), buffer);
break; break;
case HOUR_OF_DAY1_FIELD: // 1-24 case HOUR_OF_DAY1_FIELD: // 1-24
buffer.setDefaultAttribute(DateFormat.Field.HOUR_OF_DAY1); buffer.setDefaultAttribute(DateFormat.Field.HOUR_OF_DAY1);
withLeadingZeros ( ((calendar.get (Calendar.HOUR_OF_DAY) + 23) % 24) + 1, withLeadingZeros ( ((calendar.get (Calendar.HOUR_OF_DAY) + 23) % 24) + 1,
p.size, buffer); cf.getSize(), buffer);
break; break;
case HOUR_OF_DAY0_FIELD: // 0-23 case HOUR_OF_DAY0_FIELD: // 0-23
buffer.setDefaultAttribute (DateFormat.Field.HOUR_OF_DAY0); buffer.setDefaultAttribute (DateFormat.Field.HOUR_OF_DAY0);
withLeadingZeros (calendar.get (Calendar.HOUR_OF_DAY), p.size, buffer); withLeadingZeros (calendar.get (Calendar.HOUR_OF_DAY), cf.getSize(), buffer);
break; break;
case MINUTE_FIELD: case MINUTE_FIELD:
buffer.setDefaultAttribute (DateFormat.Field.MINUTE); buffer.setDefaultAttribute (DateFormat.Field.MINUTE);
withLeadingZeros (calendar.get (Calendar.MINUTE), withLeadingZeros (calendar.get (Calendar.MINUTE),
p.size, buffer); cf.getSize(), buffer);
break; break;
case SECOND_FIELD: case SECOND_FIELD:
buffer.setDefaultAttribute (DateFormat.Field.SECOND); buffer.setDefaultAttribute (DateFormat.Field.SECOND);
withLeadingZeros(calendar.get (Calendar.SECOND), withLeadingZeros(calendar.get (Calendar.SECOND),
p.size, buffer); cf.getSize(), buffer);
break; break;
case MILLISECOND_FIELD: case MILLISECOND_FIELD:
buffer.setDefaultAttribute (DateFormat.Field.MILLISECOND); buffer.setDefaultAttribute (DateFormat.Field.MILLISECOND);
withLeadingZeros (calendar.get (Calendar.MILLISECOND), p.size, buffer); withLeadingZeros (calendar.get (Calendar.MILLISECOND), cf.getSize(), buffer);
break; break;
case DAY_OF_WEEK_FIELD: case DAY_OF_WEEK_FIELD:
buffer.setDefaultAttribute (DateFormat.Field.DAY_OF_WEEK); buffer.setDefaultAttribute (DateFormat.Field.DAY_OF_WEEK);
if (p.size < 4) if (cf.getSize() < 4)
buffer.append (formatData.shortWeekdays[calendar.get (Calendar.DAY_OF_WEEK)]); buffer.append (formatData.shortWeekdays[calendar.get (Calendar.DAY_OF_WEEK)]);
else else
buffer.append (formatData.weekdays[calendar.get (Calendar.DAY_OF_WEEK)]); buffer.append (formatData.weekdays[calendar.get (Calendar.DAY_OF_WEEK)]);
break; break;
case DAY_OF_YEAR_FIELD: case DAY_OF_YEAR_FIELD:
buffer.setDefaultAttribute (DateFormat.Field.DAY_OF_YEAR); buffer.setDefaultAttribute (DateFormat.Field.DAY_OF_YEAR);
withLeadingZeros (calendar.get (Calendar.DAY_OF_YEAR), p.size, buffer); withLeadingZeros (calendar.get (Calendar.DAY_OF_YEAR), cf.getSize(), buffer);
break; break;
case DAY_OF_WEEK_IN_MONTH_FIELD: case DAY_OF_WEEK_IN_MONTH_FIELD:
buffer.setDefaultAttribute (DateFormat.Field.DAY_OF_WEEK_IN_MONTH); buffer.setDefaultAttribute (DateFormat.Field.DAY_OF_WEEK_IN_MONTH);
withLeadingZeros (calendar.get (Calendar.DAY_OF_WEEK_IN_MONTH), withLeadingZeros (calendar.get (Calendar.DAY_OF_WEEK_IN_MONTH),
p.size, buffer); cf.getSize(), buffer);
break; break;
case WEEK_OF_YEAR_FIELD: case WEEK_OF_YEAR_FIELD:
buffer.setDefaultAttribute (DateFormat.Field.WEEK_OF_YEAR); buffer.setDefaultAttribute (DateFormat.Field.WEEK_OF_YEAR);
withLeadingZeros (calendar.get (Calendar.WEEK_OF_YEAR), withLeadingZeros (calendar.get (Calendar.WEEK_OF_YEAR),
p.size, buffer); cf.getSize(), buffer);
break; break;
case WEEK_OF_MONTH_FIELD: case WEEK_OF_MONTH_FIELD:
buffer.setDefaultAttribute (DateFormat.Field.WEEK_OF_MONTH); buffer.setDefaultAttribute (DateFormat.Field.WEEK_OF_MONTH);
withLeadingZeros (calendar.get (Calendar.WEEK_OF_MONTH), withLeadingZeros (calendar.get (Calendar.WEEK_OF_MONTH),
p.size, buffer); cf.getSize(), buffer);
break; break;
case AM_PM_FIELD: case AM_PM_FIELD:
buffer.setDefaultAttribute (DateFormat.Field.AM_PM); buffer.setDefaultAttribute (DateFormat.Field.AM_PM);
...@@ -521,25 +754,39 @@ public class SimpleDateFormat extends DateFormat ...@@ -521,25 +754,39 @@ public class SimpleDateFormat extends DateFormat
break; break;
case HOUR1_FIELD: // 1-12 case HOUR1_FIELD: // 1-12
buffer.setDefaultAttribute (DateFormat.Field.HOUR1); buffer.setDefaultAttribute (DateFormat.Field.HOUR1);
withLeadingZeros (((calendar.get (Calendar.HOUR) + 11) % 12) + 1, p.size, buffer); withLeadingZeros (((calendar.get (Calendar.HOUR) + 11) % 12) + 1,
cf.getSize(), buffer);
break; break;
case HOUR0_FIELD: // 0-11 case HOUR0_FIELD: // 0-11
buffer.setDefaultAttribute (DateFormat.Field.HOUR0); buffer.setDefaultAttribute (DateFormat.Field.HOUR0);
withLeadingZeros (calendar.get (Calendar.HOUR), p.size, buffer); withLeadingZeros (calendar.get (Calendar.HOUR), cf.getSize(), buffer);
break; break;
case TIMEZONE_FIELD: case TIMEZONE_FIELD:
buffer.setDefaultAttribute (DateFormat.Field.TIME_ZONE); buffer.setDefaultAttribute (DateFormat.Field.TIME_ZONE);
TimeZone zone = calendar.getTimeZone(); TimeZone zone = calendar.getTimeZone();
boolean isDST = calendar.get (Calendar.DST_OFFSET) != 0; boolean isDST = calendar.get (Calendar.DST_OFFSET) != 0;
// FIXME: XXX: This should be a localized time zone. // FIXME: XXX: This should be a localized time zone.
String zoneID = zone.getDisplayName (isDST, p.size > 3 ? TimeZone.LONG : TimeZone.SHORT); String zoneID = zone.getDisplayName
(isDST, cf.getSize() > 3 ? TimeZone.LONG : TimeZone.SHORT);
buffer.append (zoneID); buffer.append (zoneID);
break; break;
case RFC822_TIMEZONE_FIELD:
buffer.setDefaultAttribute(DateFormat.Field.RFC822_TIME_ZONE);
int pureMinutes = (calendar.get(Calendar.ZONE_OFFSET) +
calendar.get(Calendar.DST_OFFSET)) / (1000 * 60);
String sign = (pureMinutes < 0) ? "-" : "+";
int hours = pureMinutes / 60;
int minutes = pureMinutes % 60;
buffer.append(sign);
withLeadingZeros(hours, 2, buffer);
withLeadingZeros(minutes, 2, buffer);
break;
default: default:
throw new IllegalArgumentException ("Illegal pattern character " + p.field); throw new IllegalArgumentException ("Illegal pattern character " +
cf.getCharacter());
} }
if (pos != null && (buffer.getDefaultAttribute() == pos.getFieldAttribute() if (pos != null && (buffer.getDefaultAttribute() == pos.getFieldAttribute()
|| p.field == pos.getField())) || cf.getField() == pos.getField()))
{ {
pos.setBeginIndex(beginIndex); pos.setBeginIndex(beginIndex);
pos.setEndIndex(buffer.length()); pos.setEndIndex(buffer.length());
...@@ -614,232 +861,265 @@ public class SimpleDateFormat extends DateFormat ...@@ -614,232 +861,265 @@ public class SimpleDateFormat extends DateFormat
boolean saw_timezone = false; boolean saw_timezone = false;
int quote_start = -1; int quote_start = -1;
boolean is2DigitYear = false; boolean is2DigitYear = false;
for (; fmt_index < fmt_max; ++fmt_index) try
{ {
char ch = pattern.charAt(fmt_index); for (; fmt_index < fmt_max; ++fmt_index)
if (ch == '\'')
{ {
int index = pos.getIndex(); char ch = pattern.charAt(fmt_index);
if (fmt_index < fmt_max - 1 if (ch == '\'')
&& pattern.charAt(fmt_index + 1) == '\'') {
int index = pos.getIndex();
if (fmt_index < fmt_max - 1
&& pattern.charAt(fmt_index + 1) == '\'')
{
if (! expect (dateStr, pos, ch))
return null;
++fmt_index;
}
else
quote_start = quote_start < 0 ? fmt_index : -1;
continue;
}
if (quote_start != -1
|| ((ch < 'a' || ch > 'z')
&& (ch < 'A' || ch > 'Z')))
{ {
if (! expect (dateStr, pos, ch)) if (! expect (dateStr, pos, ch))
return null; return null;
++fmt_index; continue;
} }
else
quote_start = quote_start < 0 ? fmt_index : -1; // We've arrived at a potential pattern character in the
continue; // pattern.
} int fmt_count = 1;
while (++fmt_index < fmt_max && pattern.charAt(fmt_index) == ch)
if (quote_start != -1
|| ((ch < 'a' || ch > 'z')
&& (ch < 'A' || ch > 'Z')))
{
if (! expect (dateStr, pos, ch))
return null;
continue;
}
// We've arrived at a potential pattern character in the
// pattern.
int first = fmt_index;
while (++fmt_index < fmt_max && pattern.charAt(fmt_index) == ch)
;
int fmt_count = fmt_index - first;
// We might need to limit the number of digits to parse in
// some cases. We look to the next pattern character to
// decide.
boolean limit_digits = false;
if (fmt_index < fmt_max
&& standardChars.indexOf(pattern.charAt(fmt_index)) >= 0)
limit_digits = true;
--fmt_index;
// We can handle most fields automatically: most either are
// numeric or are looked up in a string vector. In some cases
// we need an offset. When numeric, `offset' is added to the
// resulting value. When doing a string lookup, offset is the
// initial index into the string array.
int calendar_field;
boolean is_numeric = true;
String[] match = null;
int offset = 0;
boolean maybe2DigitYear = false;
switch (ch)
{
case 'd':
calendar_field = Calendar.DATE;
break;
case 'D':
calendar_field = Calendar.DAY_OF_YEAR;
break;
case 'F':
calendar_field = Calendar.DAY_OF_WEEK_IN_MONTH;
break;
case 'E':
is_numeric = false;
offset = 1;
calendar_field = Calendar.DAY_OF_WEEK;
match = (fmt_count <= 3
? formatData.getShortWeekdays()
: formatData.getWeekdays());
break;
case 'w':
calendar_field = Calendar.WEEK_OF_YEAR;
break;
case 'W':
calendar_field = Calendar.WEEK_OF_MONTH;
break;
case 'M':
calendar_field = Calendar.MONTH;
if (fmt_count <= 2)
offset = -1;
else
{ {
is_numeric = false; ++fmt_count;
match = (fmt_count <= 3
? formatData.getShortMonths()
: formatData.getMonths());
} }
break;
case 'y': // We might need to limit the number of digits to parse in
calendar_field = Calendar.YEAR; // some cases. We look to the next pattern character to
if (fmt_count <= 2) // decide.
maybe2DigitYear = true; boolean limit_digits = false;
break; if (fmt_index < fmt_max
case 'K': && standardChars.indexOf(pattern.charAt(fmt_index)) >= 0)
calendar_field = Calendar.HOUR; limit_digits = true;
break; --fmt_index;
case 'h':
calendar_field = Calendar.HOUR; // We can handle most fields automatically: most either are
break; // numeric or are looked up in a string vector. In some cases
case 'H': // we need an offset. When numeric, `offset' is added to the
calendar_field = Calendar.HOUR_OF_DAY; // resulting value. When doing a string lookup, offset is the
break; // initial index into the string array.
case 'k': int calendar_field;
calendar_field = Calendar.HOUR_OF_DAY; boolean is_numeric = true;
break; int offset = 0;
case 'm': boolean maybe2DigitYear = false;
calendar_field = Calendar.MINUTE; Integer simpleOffset;
break; String[] set1 = null;
case 's': String[] set2 = null;
calendar_field = Calendar.SECOND; switch (ch)
break;
case 'S':
calendar_field = Calendar.MILLISECOND;
break;
case 'a':
is_numeric = false;
calendar_field = Calendar.AM_PM;
match = formatData.getAmPmStrings();
break;
case 'z':
// We need a special case for the timezone, because it
// uses a different data structure than the other cases.
is_numeric = false;
calendar_field = Calendar.DST_OFFSET;
String[][] zoneStrings = formatData.getZoneStrings();
int zoneCount = zoneStrings.length;
int index = pos.getIndex();
boolean found_zone = false;
for (int j = 0; j < zoneCount; j++)
{ {
String[] strings = zoneStrings[j]; case 'd':
int k; calendar_field = Calendar.DATE;
for (k = 1; k < strings.length; ++k) break;
case 'D':
calendar_field = Calendar.DAY_OF_YEAR;
break;
case 'F':
calendar_field = Calendar.DAY_OF_WEEK_IN_MONTH;
break;
case 'E':
is_numeric = false;
offset = 1;
calendar_field = Calendar.DAY_OF_WEEK;
set1 = formatData.getWeekdays();
set2 = formatData.getShortWeekdays();
break;
case 'w':
calendar_field = Calendar.WEEK_OF_YEAR;
break;
case 'W':
calendar_field = Calendar.WEEK_OF_MONTH;
break;
case 'M':
calendar_field = Calendar.MONTH;
if (fmt_count <= 2)
offset = -1;
else
{ {
if (dateStr.startsWith(strings[k], index)) is_numeric = false;
break; set1 = formatData.getMonths();
set2 = formatData.getShortMonths();
} }
if (k != strings.length) break;
case 'y':
calendar_field = Calendar.YEAR;
if (fmt_count <= 2)
maybe2DigitYear = true;
break;
case 'K':
calendar_field = Calendar.HOUR;
break;
case 'h':
calendar_field = Calendar.HOUR;
break;
case 'H':
calendar_field = Calendar.HOUR_OF_DAY;
break;
case 'k':
calendar_field = Calendar.HOUR_OF_DAY;
break;
case 'm':
calendar_field = Calendar.MINUTE;
break;
case 's':
calendar_field = Calendar.SECOND;
break;
case 'S':
calendar_field = Calendar.MILLISECOND;
break;
case 'a':
is_numeric = false;
calendar_field = Calendar.AM_PM;
set1 = formatData.getAmPmStrings();
break;
case 'z':
case 'Z':
// We need a special case for the timezone, because it
// uses a different data structure than the other cases.
is_numeric = false;
calendar_field = Calendar.ZONE_OFFSET;
String[][] zoneStrings = formatData.getZoneStrings();
int zoneCount = zoneStrings.length;
int index = pos.getIndex();
boolean found_zone = false;
simpleOffset = computeOffset(dateStr.substring(index));
if (simpleOffset != null)
{ {
found_zone = true; found_zone = true;
saw_timezone = true; saw_timezone = true;
TimeZone tz = TimeZone.getTimeZone (strings[0]); calendar.set(Calendar.DST_OFFSET, 0);
calendar.set (Calendar.ZONE_OFFSET, tz.getRawOffset ()); offset = simpleOffset.intValue();
offset = 0; }
if (k > 2 && tz instanceof SimpleTimeZone) else
{
for (int j = 0; j < zoneCount; j++)
{ {
SimpleTimeZone stz = (SimpleTimeZone) tz; String[] strings = zoneStrings[j];
offset = stz.getDSTSavings (); int k;
for (k = 0; k < strings.length; ++k)
{
if (dateStr.startsWith(strings[k], index))
break;
}
if (k != strings.length)
{
found_zone = true;
saw_timezone = true;
TimeZone tz = TimeZone.getTimeZone (strings[0]);
// Check if it's a DST zone or ordinary
if(k == 3 || k == 4)
calendar.set (Calendar.DST_OFFSET, tz.getDSTSavings());
else
calendar.set (Calendar.DST_OFFSET, 0);
offset = tz.getRawOffset ();
pos.setIndex(index + strings[k].length());
break;
}
} }
pos.setIndex(index + strings[k].length());
break;
} }
} if (! found_zone)
if (! found_zone) {
{ pos.setErrorIndex(pos.getIndex());
return null;
}
break;
default:
pos.setErrorIndex(pos.getIndex()); pos.setErrorIndex(pos.getIndex());
return null; return null;
} }
break;
default: // Compute the value we should assign to the field.
pos.setErrorIndex(pos.getIndex()); int value;
return null; int index = -1;
} if (is_numeric)
// Compute the value we should assign to the field.
int value;
int index = -1;
if (is_numeric)
{
numberFormat.setMinimumIntegerDigits(fmt_count);
if (limit_digits)
numberFormat.setMaximumIntegerDigits(fmt_count);
if (maybe2DigitYear)
index = pos.getIndex();
Number n = numberFormat.parse(dateStr, pos);
if (pos == null || ! (n instanceof Long))
return null;
value = n.intValue() + offset;
}
else if (match != null)
{
index = pos.getIndex();
int i;
for (i = offset; i < match.length; ++i)
{ {
if (dateStr.startsWith(match[i], index)) numberFormat.setMinimumIntegerDigits(fmt_count);
break; if (limit_digits)
numberFormat.setMaximumIntegerDigits(fmt_count);
if (maybe2DigitYear)
index = pos.getIndex();
Number n = numberFormat.parse(dateStr, pos);
if (pos == null || ! (n instanceof Long))
return null;
value = n.intValue() + offset;
} }
if (i == match.length) else if (set1 != null)
{ {
pos.setErrorIndex(index); index = pos.getIndex();
return null; int i;
boolean found = false;
for (i = offset; i < set1.length; ++i)
{
if (set1[i] != null)
if (dateStr.toUpperCase().startsWith(set1[i].toUpperCase(),
index))
{
found = true;
pos.setIndex(index + set1[i].length());
break;
}
}
if (!found && set2 != null)
{
for (i = offset; i < set2.length; ++i)
{
if (set2[i] != null)
if (dateStr.toUpperCase().startsWith(set2[i].toUpperCase(),
index))
{
found = true;
pos.setIndex(index + set2[i].length());
break;
}
}
}
if (!found)
{
pos.setErrorIndex(index);
return null;
}
value = i;
} }
pos.setIndex(index + match[i].length()); else
value = i; value = offset;
}
else
value = offset;
if (maybe2DigitYear) if (maybe2DigitYear)
{ {
// Parse into default century if the numeric year string has // Parse into default century if the numeric year string has
// exactly 2 digits. // exactly 2 digits.
int digit_count = pos.getIndex() - index; int digit_count = pos.getIndex() - index;
if (digit_count == 2) if (digit_count == 2)
is2DigitYear = true; {
is2DigitYear = true;
value += defaultCentury;
}
}
// Assign the value and move on.
calendar.set(calendar_field, value);
} }
// Assign the value and move on.
calendar.set(calendar_field, value);
}
if (is2DigitYear) if (is2DigitYear)
{ {
// Apply the 80-20 heuristic to dermine the full year based on // Apply the 80-20 heuristic to dermine the full year based on
// defaultCenturyStart. // defaultCenturyStart.
int year = defaultCentury + calendar.get(Calendar.YEAR); int year = calendar.get(Calendar.YEAR);
calendar.set(Calendar.YEAR, year); if (calendar.getTime().compareTo(defaultCenturyStart) < 0)
if (calendar.getTime().compareTo(defaultCenturyStart) < 0) calendar.set(Calendar.YEAR, year + 100);
calendar.set(Calendar.YEAR, year + 100); }
}
try
{
if (! saw_timezone) if (! saw_timezone)
{ {
// Use the real rules to determine whether or not this // Use the real rules to determine whether or not this
...@@ -854,6 +1134,69 @@ public class SimpleDateFormat extends DateFormat ...@@ -854,6 +1134,69 @@ public class SimpleDateFormat extends DateFormat
pos.setErrorIndex(pos.getIndex()); pos.setErrorIndex(pos.getIndex());
return null; return null;
} }
}
/**
* <p>
* Computes the time zone offset in milliseconds
* relative to GMT, based on the supplied
* <code>String</code> representation.
* </p>
* <p>
* The supplied <code>String</code> must be a three
* or four digit signed number, with an optional 'GMT'
* prefix. The first one or two digits represents the hours,
* while the last two represent the minutes. The
* two sets of digits can optionally be separated by
* ':'. The mandatory sign prefix (either '+' or '-')
* indicates the direction of the offset from GMT.
* </p>
* <p>
* For example, 'GMT+0200' specifies 2 hours after
* GMT, while '-05:00' specifies 5 hours prior to
* GMT. The special case of 'GMT' alone can be used
* to represent the offset, 0.
* </p>
* <p>
* If the <code>String</code> can not be parsed,
* the result will be null. The resulting offset
* is wrapped in an <code>Integer</code> object, in
* order to allow such failure to be represented.
* </p>
*
* @param zoneString a string in the form
* (GMT)? sign hours : minutes
* where sign = '+' or '-', hours
* is a one or two digits representing
* a number between 0 and 23, and
* minutes is two digits representing
* a number between 0 and 59.
* @return the parsed offset, or null if parsing
* failed.
*/
private Integer computeOffset(String zoneString)
{
Pattern pattern =
Pattern.compile("(GMT)?([+-])([012])?([0-9]):?([0-9]{2})");
Matcher matcher = pattern.matcher(zoneString);
if (matcher.matches())
{
int sign = matcher.group(2).equals("+") ? 1 : -1;
int hour = (Integer.parseInt(matcher.group(3)) * 10)
+ Integer.parseInt(matcher.group(4));
int minutes = Integer.parseInt(matcher.group(5));
if (hour > 23)
return null;
int offset = sign * ((hour * 60) + minutes) * 60000;
return new Integer(offset);
}
else if (zoneString.startsWith("GMT"))
{
return new Integer(0);
}
return null;
} }
// Compute the start of the current century as defined by // Compute the start of the current century as defined by
...@@ -864,4 +1207,19 @@ public class SimpleDateFormat extends DateFormat ...@@ -864,4 +1207,19 @@ public class SimpleDateFormat extends DateFormat
calendar.set(Calendar.YEAR, year - 80); calendar.set(Calendar.YEAR, year - 80);
set2DigitYearStart(calendar.getTime()); set2DigitYearStart(calendar.getTime());
} }
/**
* Returns a copy of this instance of
* <code>SimpleDateFormat</code>. The copy contains
* clones of the formatting symbols and the 2-digit
* year century start date.
*/
public Object clone()
{
SimpleDateFormat clone = (SimpleDateFormat) super.clone();
clone.setDateFormatSymbols((DateFormatSymbols) formatData.clone());
clone.set2DigitYearStart((Date) defaultCenturyStart.clone());
return clone;
}
} }
...@@ -51,7 +51,7 @@ import java.lang.reflect.InvocationTargetException; ...@@ -51,7 +51,7 @@ import java.lang.reflect.InvocationTargetException;
* integer fields which represent <code>YEAR</code>, * integer fields which represent <code>YEAR</code>,
* <code>MONTH</code>, <code>DAY</code>, etc. The <code>Date</code> * <code>MONTH</code>, <code>DAY</code>, etc. The <code>Date</code>
* object represents a time in milliseconds since the Epoch. <br> * object represents a time in milliseconds since the Epoch. <br>
* *
* This class is locale sensitive. To get the Object matching the * This class is locale sensitive. To get the Object matching the
* current locale you can use <code>getInstance</code>. You can even provide * current locale you can use <code>getInstance</code>. You can even provide
* a locale or a timezone. <code>getInstance</code> returns currently * a locale or a timezone. <code>getInstance</code> returns currently
...@@ -78,13 +78,13 @@ import java.lang.reflect.InvocationTargetException; ...@@ -78,13 +78,13 @@ import java.lang.reflect.InvocationTargetException;
* and for the first line all fields are set, that line is used to * and for the first line all fields are set, that line is used to
* compute the day. <br> * compute the day. <br>
* *
* *
<pre>month + day_of_month <pre>month + day_of_month
month + week_of_month + day_of_week month + week_of_month + day_of_week
month + day_of_week_of_month + day_of_week month + day_of_week_of_month + day_of_week
day_of_year day_of_year
day_of_week + week_of_year</pre> day_of_week + week_of_year</pre>
* *
* The hour_of_day-field takes precedence over the ampm and * The hour_of_day-field takes precedence over the ampm and
* hour_of_ampm fields. <br> * hour_of_ampm fields. <br>
* *
...@@ -92,7 +92,7 @@ day_of_week + week_of_year</pre> ...@@ -92,7 +92,7 @@ day_of_week + week_of_year</pre>
* *
* To convert a calendar to a human readable form and vice versa, use * To convert a calendar to a human readable form and vice versa, use
* the <code>java.text.DateFormat</code> class. <br> * the <code>java.text.DateFormat</code> class. <br>
* *
* Other useful things you can do with an calendar, is * Other useful things you can do with an calendar, is
* <code>roll</code>ing fields (that means increase/decrease a * <code>roll</code>ing fields (that means increase/decrease a
* specific field by one, propagating overflows), or * specific field by one, propagating overflows), or
...@@ -101,7 +101,7 @@ day_of_week + week_of_year</pre> ...@@ -101,7 +101,7 @@ day_of_week + week_of_year</pre>
* @see Date * @see Date
* @see GregorianCalendar * @see GregorianCalendar
* @see TimeZone * @see TimeZone
* @see java.text.DateFormat * @see java.text.DateFormat
*/ */
public abstract class Calendar implements Serializable, Cloneable public abstract class Calendar implements Serializable, Cloneable
{ {
...@@ -109,43 +109,52 @@ public abstract class Calendar implements Serializable, Cloneable ...@@ -109,43 +109,52 @@ public abstract class Calendar implements Serializable, Cloneable
* Constant representing the era time field. * Constant representing the era time field.
*/ */
public static final int ERA = 0; public static final int ERA = 0;
/** /**
* Constant representing the year time field. * Constant representing the year time field.
*/ */
public static final int YEAR = 1; public static final int YEAR = 1;
/** /**
* Constant representing the month time field. This field * Constant representing the month time field. This field
* should contain one of the JANUARY,...,DECEMBER constants below. * should contain one of the JANUARY,...,DECEMBER constants below.
*/ */
public static final int MONTH = 2; public static final int MONTH = 2;
/** /**
* Constant representing the week of the year field. * Constant representing the week of the year field.
* @see #setFirstDayOfWeek(int) * @see #setFirstDayOfWeek(int)
*/ */
public static final int WEEK_OF_YEAR = 3; public static final int WEEK_OF_YEAR = 3;
/** /**
* Constant representing the week of the month time field. * Constant representing the week of the month time field.
* @see #setFirstDayOfWeek(int) * @see #setFirstDayOfWeek(int)
*/ */
public static final int WEEK_OF_MONTH = 4; public static final int WEEK_OF_MONTH = 4;
/** /**
* Constant representing the day time field, synonym for DAY_OF_MONTH. * Constant representing the day time field, synonym for DAY_OF_MONTH.
*/ */
public static final int DATE = 5; public static final int DATE = 5;
/** /**
* Constant representing the day time field. * Constant representing the day time field.
*/ */
public static final int DAY_OF_MONTH = 5; public static final int DAY_OF_MONTH = 5;
/** /**
* Constant representing the day of year time field. This is * Constant representing the day of year time field. This is
* 1 for the first day in month. * 1 for the first day in month.
*/ */
public static final int DAY_OF_YEAR = 6; public static final int DAY_OF_YEAR = 6;
/** /**
* Constant representing the day of week time field. This field * Constant representing the day of week time field. This field
* should contain one of the SUNDAY,...,SATURDAY constants below. * should contain one of the SUNDAY,...,SATURDAY constants below.
*/ */
public static final int DAY_OF_WEEK = 7; public static final int DAY_OF_WEEK = 7;
/** /**
* Constant representing the day-of-week-in-month field. For * Constant representing the day-of-week-in-month field. For
* instance this field contains 2 for the second thursday in a * instance this field contains 2 for the second thursday in a
...@@ -153,42 +162,51 @@ public abstract class Calendar implements Serializable, Cloneable ...@@ -153,42 +162,51 @@ public abstract class Calendar implements Serializable, Cloneable
* from the end of the month. * from the end of the month.
*/ */
public static final int DAY_OF_WEEK_IN_MONTH = 8; public static final int DAY_OF_WEEK_IN_MONTH = 8;
/** /**
* Constant representing the part of the day for 12-hour clock. This * Constant representing the part of the day for 12-hour clock. This
* should be one of AM or PM. * should be one of AM or PM.
*/ */
public static final int AM_PM = 9; public static final int AM_PM = 9;
/** /**
* Constant representing the hour time field for 12-hour clock. * Constant representing the hour time field for 12-hour clock.
*/ */
public static final int HOUR = 10; public static final int HOUR = 10;
/** /**
* Constant representing the hour of day time field for 24-hour clock. * Constant representing the hour of day time field for 24-hour clock.
*/ */
public static final int HOUR_OF_DAY = 11; public static final int HOUR_OF_DAY = 11;
/** /**
* Constant representing the minute of hour time field. * Constant representing the minute of hour time field.
*/ */
public static final int MINUTE = 12; public static final int MINUTE = 12;
/** /**
* Constant representing the second time field. * Constant representing the second time field.
*/ */
public static final int SECOND = 13; public static final int SECOND = 13;
/** /**
* Constant representing the millisecond time field. * Constant representing the millisecond time field.
*/ */
public static final int MILLISECOND = 14; public static final int MILLISECOND = 14;
/** /**
* Constant representing the time zone offset time field for the * Constant representing the time zone offset time field for the
* time given in the other fields. It is measured in * time given in the other fields. It is measured in
* milliseconds. The default is the offset of the time zone. * milliseconds. The default is the offset of the time zone.
*/ */
public static final int ZONE_OFFSET = 15; public static final int ZONE_OFFSET = 15;
/** /**
* Constant representing the daylight saving time offset in * Constant representing the daylight saving time offset in
* milliseconds. The default is the value given by the time zone. * milliseconds. The default is the value given by the time zone.
*/ */
public static final int DST_OFFSET = 16; public static final int DST_OFFSET = 16;
/** /**
* Number of time fields. * Number of time fields.
*/ */
...@@ -198,26 +216,32 @@ public abstract class Calendar implements Serializable, Cloneable ...@@ -198,26 +216,32 @@ public abstract class Calendar implements Serializable, Cloneable
* Constant representing Sunday. * Constant representing Sunday.
*/ */
public static final int SUNDAY = 1; public static final int SUNDAY = 1;
/** /**
* Constant representing Monday. * Constant representing Monday.
*/ */
public static final int MONDAY = 2; public static final int MONDAY = 2;
/** /**
* Constant representing Tuesday. * Constant representing Tuesday.
*/ */
public static final int TUESDAY = 3; public static final int TUESDAY = 3;
/** /**
* Constant representing Wednesday. * Constant representing Wednesday.
*/ */
public static final int WEDNESDAY = 4; public static final int WEDNESDAY = 4;
/** /**
* Constant representing Thursday. * Constant representing Thursday.
*/ */
public static final int THURSDAY = 5; public static final int THURSDAY = 5;
/** /**
* Constant representing Friday. * Constant representing Friday.
*/ */
public static final int FRIDAY = 6; public static final int FRIDAY = 6;
/** /**
* Constant representing Saturday. * Constant representing Saturday.
*/ */
...@@ -227,50 +251,62 @@ public abstract class Calendar implements Serializable, Cloneable ...@@ -227,50 +251,62 @@ public abstract class Calendar implements Serializable, Cloneable
* Constant representing January. * Constant representing January.
*/ */
public static final int JANUARY = 0; public static final int JANUARY = 0;
/** /**
* Constant representing February. * Constant representing February.
*/ */
public static final int FEBRUARY = 1; public static final int FEBRUARY = 1;
/** /**
* Constant representing March. * Constant representing March.
*/ */
public static final int MARCH = 2; public static final int MARCH = 2;
/** /**
* Constant representing April. * Constant representing April.
*/ */
public static final int APRIL = 3; public static final int APRIL = 3;
/** /**
* Constant representing May. * Constant representing May.
*/ */
public static final int MAY = 4; public static final int MAY = 4;
/** /**
* Constant representing June. * Constant representing June.
*/ */
public static final int JUNE = 5; public static final int JUNE = 5;
/** /**
* Constant representing July. * Constant representing July.
*/ */
public static final int JULY = 6; public static final int JULY = 6;
/** /**
* Constant representing August. * Constant representing August.
*/ */
public static final int AUGUST = 7; public static final int AUGUST = 7;
/** /**
* Constant representing September. * Constant representing September.
*/ */
public static final int SEPTEMBER = 8; public static final int SEPTEMBER = 8;
/** /**
* Constant representing October. * Constant representing October.
*/ */
public static final int OCTOBER = 9; public static final int OCTOBER = 9;
/** /**
* Constant representing November. * Constant representing November.
*/ */
public static final int NOVEMBER = 10; public static final int NOVEMBER = 10;
/** /**
* Constant representing December. * Constant representing December.
*/ */
public static final int DECEMBER = 11; public static final int DECEMBER = 11;
/** /**
* Constant representing Undecimber. This is an artificial name useful * Constant representing Undecimber. This is an artificial name useful
* for lunar calendars. * for lunar calendars.
...@@ -281,6 +317,7 @@ public abstract class Calendar implements Serializable, Cloneable ...@@ -281,6 +317,7 @@ public abstract class Calendar implements Serializable, Cloneable
* Useful constant for 12-hour clock. * Useful constant for 12-hour clock.
*/ */
public static final int AM = 0; public static final int AM = 0;
/** /**
* Useful constant for 12-hour clock. * Useful constant for 12-hour clock.
*/ */
...@@ -292,21 +329,25 @@ public abstract class Calendar implements Serializable, Cloneable ...@@ -292,21 +329,25 @@ public abstract class Calendar implements Serializable, Cloneable
* @serial * @serial
*/ */
protected int[] fields = new int[FIELD_COUNT]; protected int[] fields = new int[FIELD_COUNT];
/** /**
* The flags which tell if the fields above have a value. * The flags which tell if the fields above have a value.
* @serial * @serial
*/ */
protected boolean[] isSet = new boolean[FIELD_COUNT]; protected boolean[] isSet = new boolean[FIELD_COUNT];
/** /**
* The time in milliseconds since the epoch. * The time in milliseconds since the epoch.
* @serial * @serial
*/ */
protected long time; protected long time;
/** /**
* Tells if the above field has a valid value. * Tells if the above field has a valid value.
* @serial * @serial
*/ */
protected boolean isTimeSet; protected boolean isTimeSet;
/** /**
* Tells if the fields have a valid value. This superseeds the isSet * Tells if the fields have a valid value. This superseeds the isSet
* array. * array.
...@@ -332,7 +373,7 @@ public abstract class Calendar implements Serializable, Cloneable ...@@ -332,7 +373,7 @@ public abstract class Calendar implements Serializable, Cloneable
/** /**
* Sets what the first day of week is. This is used for * Sets what the first day of week is. This is used for
* WEEK_OF_MONTH and WEEK_OF_YEAR fields. * WEEK_OF_MONTH and WEEK_OF_YEAR fields.
* @serial * @serial
*/ */
private int firstDayOfWeek; private int firstDayOfWeek;
...@@ -347,7 +388,14 @@ public abstract class Calendar implements Serializable, Cloneable ...@@ -347,7 +388,14 @@ public abstract class Calendar implements Serializable, Cloneable
private int minimalDaysInFirstWeek; private int minimalDaysInFirstWeek;
/** /**
* The version of the serialized data on the stream. * Is set to true if DST_OFFSET is explicitly set. In that case
* it's value overrides the value computed from the current
* time and the timezone.
*/
private boolean explicitDSTOffset = false;
/**
* The version of the serialized data on the stream.
* <dl><dt>0 or not present</dt> * <dl><dt>0 or not present</dt>
* <dd> JDK 1.1.5 or later.</dd> * <dd> JDK 1.1.5 or later.</dd>
* <dl><dt>1</dt> * <dl><dt>1</dt>
...@@ -371,14 +419,14 @@ public abstract class Calendar implements Serializable, Cloneable ...@@ -371,14 +419,14 @@ public abstract class Calendar implements Serializable, Cloneable
private static final String bundleName = "gnu.java.locale.Calendar"; private static final String bundleName = "gnu.java.locale.Calendar";
/** /**
* get resource bundle: * get resource bundle:
* The resources should be loaded via this method only. Iff an application * The resources should be loaded via this method only. Iff an application
* uses this method, the resourcebundle is required. * uses this method, the resourcebundle is required.
*/ */
private static ResourceBundle getBundle(Locale locale) private static ResourceBundle getBundle(Locale locale)
{ {
return ResourceBundle.getBundle(bundleName, locale, return ResourceBundle.getBundle(bundleName, locale,
ClassLoader.getSystemClassLoader()); ClassLoader.getSystemClassLoader());
} }
/** /**
...@@ -404,8 +452,9 @@ public abstract class Calendar implements Serializable, Cloneable ...@@ -404,8 +452,9 @@ public abstract class Calendar implements Serializable, Cloneable
ResourceBundle rb = getBundle(locale); ResourceBundle rb = getBundle(locale);
firstDayOfWeek = ((Integer) rb.getObject("firstDayOfWeek")).intValue(); firstDayOfWeek = ((Integer) rb.getObject("firstDayOfWeek")).intValue();
minimalDaysInFirstWeek = minimalDaysInFirstWeek = ((Integer) rb.getObject("minimalDaysInFirstWeek"))
((Integer) rb.getObject("minimalDaysInFirstWeek")).intValue(); .intValue();
clear();
} }
/** /**
...@@ -437,15 +486,17 @@ public abstract class Calendar implements Serializable, Cloneable ...@@ -437,15 +486,17 @@ public abstract class Calendar implements Serializable, Cloneable
return getInstance(TimeZone.getDefault(), locale); return getInstance(TimeZone.getDefault(), locale);
} }
/** /**
* Cache of locale->calendar-class mappings. This avoids having to do a ResourceBundle * Cache of locale->calendar-class mappings. This avoids having to do a ResourceBundle
* lookup for every getInstance call. * lookup for every getInstance call.
*/ */
private static HashMap cache = new HashMap(); private static HashMap cache = new HashMap();
/** Preset argument types for calendar-class constructor lookup. */ /** Preset argument types for calendar-class constructor lookup. */
private static Class[] ctorArgTypes private static Class[] ctorArgTypes = new Class[]
= new Class[] {TimeZone.class, Locale.class}; {
TimeZone.class, Locale.class
};
/** /**
* Creates a calendar representing the actual time, using the given * Creates a calendar representing the actual time, using the given
...@@ -473,7 +524,7 @@ public abstract class Calendar implements Serializable, Cloneable ...@@ -473,7 +524,7 @@ public abstract class Calendar implements Serializable, Cloneable
} }
} }
// GregorianCalendar is by far the most common case. Optimize by // GregorianCalendar is by far the most common case. Optimize by
// avoiding reflection. // avoiding reflection.
if (calendarClass == GregorianCalendar.class) if (calendarClass == GregorianCalendar.class)
return new GregorianCalendar(zone, locale); return new GregorianCalendar(zone, locale);
...@@ -481,7 +532,7 @@ public abstract class Calendar implements Serializable, Cloneable ...@@ -481,7 +532,7 @@ public abstract class Calendar implements Serializable, Cloneable
if (Calendar.class.isAssignableFrom(calendarClass)) if (Calendar.class.isAssignableFrom(calendarClass))
{ {
Constructor ctor = calendarClass.getConstructor(ctorArgTypes); Constructor ctor = calendarClass.getConstructor(ctorArgTypes);
return (Calendar) ctor.newInstance(new Object[] {zone, locale}); return (Calendar) ctor.newInstance(new Object[] { zone, locale });
} }
} }
catch (ClassNotFoundException ex) catch (ClassNotFoundException ex)
...@@ -504,9 +555,9 @@ public abstract class Calendar implements Serializable, Cloneable ...@@ -504,9 +555,9 @@ public abstract class Calendar implements Serializable, Cloneable
{ {
exception = ex; exception = ex;
} }
throw new RuntimeException("Error instantiating calendar for locale " + throw new RuntimeException("Error instantiating calendar for locale "
locale, exception); + locale, exception);
} }
/** /**
...@@ -530,7 +581,7 @@ public abstract class Calendar implements Serializable, Cloneable ...@@ -530,7 +581,7 @@ public abstract class Calendar implements Serializable, Cloneable
* Converts the milliseconds since the epoch UTC * Converts the milliseconds since the epoch UTC
* (<code>time</code>) to time fields * (<code>time</code>) to time fields
* (<code>fields</code>). Override this method if you write your * (<code>fields</code>). Override this method if you write your
* own Calendar. * own Calendar.
*/ */
protected abstract void computeFields(); protected abstract void computeFields();
...@@ -541,7 +592,7 @@ public abstract class Calendar implements Serializable, Cloneable ...@@ -541,7 +592,7 @@ public abstract class Calendar implements Serializable, Cloneable
*/ */
public final Date getTime() public final Date getTime()
{ {
if (!isTimeSet) if (! isTimeSet)
computeTime(); computeTime();
return new Date(time); return new Date(time);
} }
...@@ -562,7 +613,7 @@ public abstract class Calendar implements Serializable, Cloneable ...@@ -562,7 +613,7 @@ public abstract class Calendar implements Serializable, Cloneable
*/ */
public long getTimeInMillis() public long getTimeInMillis()
{ {
if (!isTimeSet) if (! isTimeSet)
computeTime(); computeTime();
return time; return time;
} }
...@@ -575,9 +626,9 @@ public abstract class Calendar implements Serializable, Cloneable ...@@ -575,9 +626,9 @@ public abstract class Calendar implements Serializable, Cloneable
*/ */
public void setTimeInMillis(long time) public void setTimeInMillis(long time)
{ {
clear();
this.time = time; this.time = time;
isTimeSet = true; isTimeSet = true;
computeFields();
} }
/** /**
...@@ -593,14 +644,14 @@ public abstract class Calendar implements Serializable, Cloneable ...@@ -593,14 +644,14 @@ public abstract class Calendar implements Serializable, Cloneable
public int get(int field) public int get(int field)
{ {
// If the requested field is invalid, force all fields to be recomputed. // If the requested field is invalid, force all fields to be recomputed.
if (!isSet[field]) if (! isSet[field])
areFieldsSet = false; areFieldsSet = false;
complete(); complete();
return fields[field]; return fields[field];
} }
/** /**
* Gets the value of the specified field. This method doesn't * Gets the value of the specified field. This method doesn't
* recompute the fields, if they are invalid. * recompute the fields, if they are invalid.
* @param field the time field. One of the time field constants. * @param field the time field. One of the time field constants.
* @return the value of the specified field, undefined if * @return the value of the specified field, undefined if
...@@ -626,21 +677,72 @@ public abstract class Calendar implements Serializable, Cloneable ...@@ -626,21 +677,72 @@ public abstract class Calendar implements Serializable, Cloneable
*/ */
public void set(int field, int value) public void set(int field, int value)
{ {
if (isTimeSet)
for (int i = 0; i < FIELD_COUNT; i++)
isSet[i] = false;
isTimeSet = false; isTimeSet = false;
fields[field] = value; fields[field] = value;
isSet[field] = true; isSet[field] = true;
// The five valid date patterns, in order of priority
// 1 YEAR + MONTH + DAY_OF_MONTH
// 2 YEAR + MONTH + WEEK_OF_MONTH + DAY_OF_WEEK
// 3 YEAR + MONTH + DAY_OF_WEEK_IN_MONTH + DAY_OF_WEEK
// 4 YEAR + DAY_OF_YEAR
// 5 YEAR + DAY_OF_WEEK + WEEK_OF_YEAR
switch (field) switch (field)
{ {
case YEAR: case MONTH: // pattern 1,2 or 3
case MONTH: isSet[DAY_OF_YEAR] = false;
case DATE: isSet[WEEK_OF_YEAR] = false;
break;
case DAY_OF_MONTH: // pattern 1
isSet[YEAR] = true;
isSet[MONTH] = true;
isSet[WEEK_OF_MONTH] = true;
isSet[DAY_OF_WEEK] = false;
isSet[DAY_OF_WEEK_IN_MONTH] = false;
isSet[DAY_OF_YEAR] = false;
isSet[WEEK_OF_YEAR] = false;
break;
case WEEK_OF_MONTH: // pattern 2
isSet[YEAR] = true;
isSet[MONTH] = true;
isSet[DAY_OF_WEEK] = true;
isSet[DAY_OF_MONTH] = false;
isSet[DAY_OF_WEEK_IN_MONTH] = false;
isSet[DAY_OF_YEAR] = false;
isSet[WEEK_OF_YEAR] = false; isSet[WEEK_OF_YEAR] = false;
break;
case DAY_OF_WEEK_IN_MONTH: // pattern 3
isSet[YEAR] = true;
isSet[MONTH] = true;
isSet[DAY_OF_WEEK] = true;
isSet[DAY_OF_YEAR] = false; isSet[DAY_OF_YEAR] = false;
isSet[DAY_OF_MONTH] = false;
isSet[WEEK_OF_MONTH] = false;
isSet[WEEK_OF_YEAR] = false;
break;
case DAY_OF_YEAR: // pattern 4
isSet[YEAR] = true;
isSet[MONTH] = false;
isSet[WEEK_OF_MONTH] = false; isSet[WEEK_OF_MONTH] = false;
isSet[DAY_OF_MONTH] = false;
isSet[DAY_OF_WEEK] = false; isSet[DAY_OF_WEEK] = false;
isSet[WEEK_OF_YEAR] = false;
isSet[DAY_OF_WEEK_IN_MONTH] = false;
break;
case WEEK_OF_YEAR: // pattern 5
isSet[YEAR] = true;
isSet[DAY_OF_WEEK] = true;
isSet[MONTH] = false;
isSet[DAY_OF_MONTH] = false;
isSet[WEEK_OF_MONTH] = false;
isSet[DAY_OF_YEAR] = false;
isSet[DAY_OF_WEEK_IN_MONTH] = false; isSet[DAY_OF_WEEK_IN_MONTH] = false;
break; break;
case AM_PM: case AM_PM:
isSet[HOUR] = true;
isSet[HOUR_OF_DAY] = false; isSet[HOUR_OF_DAY] = false;
break; break;
case HOUR_OF_DAY: case HOUR_OF_DAY:
...@@ -648,12 +750,15 @@ public abstract class Calendar implements Serializable, Cloneable ...@@ -648,12 +750,15 @@ public abstract class Calendar implements Serializable, Cloneable
isSet[HOUR] = false; isSet[HOUR] = false;
break; break;
case HOUR: case HOUR:
isSet[AM_PM] = true;
isSet[HOUR_OF_DAY] = false; isSet[HOUR_OF_DAY] = false;
break; break;
case DST_OFFSET:
explicitDSTOffset = true;
} }
// May have crossed over a DST boundary. // May have crossed over a DST boundary.
if (field != DST_OFFSET && field != ZONE_OFFSET) if (! explicitDSTOffset && (field != DST_OFFSET && field != ZONE_OFFSET))
isSet[DST_OFFSET] = false; isSet[DST_OFFSET] = false;
} }
...@@ -675,8 +780,10 @@ public abstract class Calendar implements Serializable, Cloneable ...@@ -675,8 +780,10 @@ public abstract class Calendar implements Serializable, Cloneable
isSet[WEEK_OF_MONTH] = false; isSet[WEEK_OF_MONTH] = false;
isSet[DAY_OF_WEEK] = false; isSet[DAY_OF_WEEK] = false;
isSet[DAY_OF_WEEK_IN_MONTH] = false; isSet[DAY_OF_WEEK_IN_MONTH] = false;
isSet[ERA] = false;
isSet[DST_OFFSET] = false; // May have crossed a DST boundary. if (! explicitDSTOffset)
isSet[DST_OFFSET] = false; // May have crossed a DST boundary.
} }
/** /**
...@@ -706,8 +813,8 @@ public abstract class Calendar implements Serializable, Cloneable ...@@ -706,8 +813,8 @@ public abstract class Calendar implements Serializable, Cloneable
* @param minute the minute. * @param minute the minute.
* @param second the second. * @param second the second.
*/ */
public final void set(int year, int month, int date, public final void set(int year, int month, int date, int hour, int minute,
int hour, int minute, int second) int second)
{ {
set(year, month, date, hour, minute); set(year, month, date, hour, minute);
fields[SECOND] = second; fields[SECOND] = second;
...@@ -721,11 +828,15 @@ public abstract class Calendar implements Serializable, Cloneable ...@@ -721,11 +828,15 @@ public abstract class Calendar implements Serializable, Cloneable
{ {
isTimeSet = false; isTimeSet = false;
areFieldsSet = false; areFieldsSet = false;
int zoneOffs = zone.getRawOffset();
int[] tempFields =
{
1, 1970, JANUARY, 1, 1, 1, 1, THURSDAY, 1, AM, 0, 0, 0,
0, 0, zoneOffs, 0
};
fields = tempFields;
for (int i = 0; i < FIELD_COUNT; i++) for (int i = 0; i < FIELD_COUNT; i++)
{ isSet[i] = false;
isSet[i] = false;
fields[i] = 0;
}
} }
/** /**
...@@ -737,10 +848,15 @@ public abstract class Calendar implements Serializable, Cloneable ...@@ -737,10 +848,15 @@ public abstract class Calendar implements Serializable, Cloneable
*/ */
public final void clear(int field) public final void clear(int field)
{ {
int[] tempFields =
{
1, 1970, JANUARY, 1, 1, 1, 1, THURSDAY, 1, AM, 0, 0, 0,
0, 0, zone.getRawOffset(), 0
};
isTimeSet = false; isTimeSet = false;
areFieldsSet = false; areFieldsSet = false;
isSet[field] = false; isSet[field] = false;
fields[field] = 0; fields[field] = tempFields[field];
} }
/** /**
...@@ -757,18 +873,18 @@ public abstract class Calendar implements Serializable, Cloneable ...@@ -757,18 +873,18 @@ public abstract class Calendar implements Serializable, Cloneable
/** /**
* Fills any unset fields in the time field list * Fills any unset fields in the time field list
* @return true if the specified field has a value. * @return true if the specified field has a value.
*/ */
protected void complete() protected void complete()
{ {
if (!isTimeSet) if (! isTimeSet)
computeTime(); computeTime();
if (!areFieldsSet) if (! areFieldsSet)
computeFields(); computeFields();
} }
/** /**
* Compares the given calendar with this. * Compares the given calendar with this.
* @param o the object to that we should compare. * @param o the object to that we should compare.
* @return true, if the given object is a calendar, that represents * @return true, if the given object is a calendar, that represents
* the same time (but doesn't necessary have the same fields). * the same time (but doesn't necessary have the same fields).
...@@ -776,12 +892,12 @@ public abstract class Calendar implements Serializable, Cloneable ...@@ -776,12 +892,12 @@ public abstract class Calendar implements Serializable, Cloneable
public boolean equals(Object o) public boolean equals(Object o)
{ {
return (o instanceof Calendar) return (o instanceof Calendar)
&& getTimeInMillis() == ((Calendar) o).getTimeInMillis(); && getTimeInMillis() == ((Calendar) o).getTimeInMillis();
} }
/** /**
* Returns a hash code for this calendar. * Returns a hash code for this calendar.
* @return a hash code, which fullfits the general contract of * @return a hash code, which fullfits the general contract of
* <code>hashCode()</code> * <code>hashCode()</code>
*/ */
public int hashCode() public int hashCode()
...@@ -791,7 +907,7 @@ public abstract class Calendar implements Serializable, Cloneable ...@@ -791,7 +907,7 @@ public abstract class Calendar implements Serializable, Cloneable
} }
/** /**
* Compares the given calendar with this. * Compares the given calendar with this.
* @param o the object to that we should compare. * @param o the object to that we should compare.
* @return true, if the given object is a calendar, and this calendar * @return true, if the given object is a calendar, and this calendar
* represents a smaller time than the calendar o. * represents a smaller time than the calendar o.
...@@ -804,7 +920,7 @@ public abstract class Calendar implements Serializable, Cloneable ...@@ -804,7 +920,7 @@ public abstract class Calendar implements Serializable, Cloneable
} }
/** /**
* Compares the given calendar with this. * Compares the given calendar with this.
* @param o the object to that we should compare. * @param o the object to that we should compare.
* @return true, if the given object is a calendar, and this calendar * @return true, if the given object is a calendar, and this calendar
* represents a bigger time than the calendar o. * represents a bigger time than the calendar o.
...@@ -831,11 +947,11 @@ public abstract class Calendar implements Serializable, Cloneable ...@@ -831,11 +947,11 @@ public abstract class Calendar implements Serializable, Cloneable
/** /**
* Rolls the specified time field up or down. This means add one * Rolls the specified time field up or down. This means add one
* to the specified field, but don't change the other fields. If * to the specified field, but don't change the other fields. If
* the maximum for this field is reached, start over with the * the maximum for this field is reached, start over with the
* minimum value. <br> * minimum value. <br>
* *
* <strong>Note:</strong> There may be situation, where the other * <strong>Note:</strong> There may be situation, where the other
* fields must be changed, e.g rolling the month on May, 31. * fields must be changed, e.g rolling the month on May, 31.
* The date June, 31 is automatically converted to July, 1. * The date June, 31 is automatically converted to July, 1.
* @param field the time field. One of the time field constants. * @param field the time field. One of the time field constants.
* @param up the direction, true for up, false for down. * @param up the direction, true for up, false for down.
...@@ -854,7 +970,7 @@ public abstract class Calendar implements Serializable, Cloneable ...@@ -854,7 +970,7 @@ public abstract class Calendar implements Serializable, Cloneable
* *
* @param field the time field. One of the time field constants. * @param field the time field. One of the time field constants.
* @param amount the amount to roll by, positive for rolling up, * @param amount the amount to roll by, positive for rolling up,
* negative for rolling down. * negative for rolling down.
* @throws ArrayIndexOutOfBoundsException if the field is outside * @throws ArrayIndexOutOfBoundsException if the field is outside
* the valid range. The value of field must be >= 0 and * the valid range. The value of field must be >= 0 and
* <= <code>FIELD_COUNT</code>. * <= <code>FIELD_COUNT</code>.
...@@ -874,7 +990,6 @@ public abstract class Calendar implements Serializable, Cloneable ...@@ -874,7 +990,6 @@ public abstract class Calendar implements Serializable, Cloneable
} }
} }
/** /**
* Sets the time zone to the specified value. * Sets the time zone to the specified value.
* @param zone the new time zone * @param zone the new time zone
...@@ -918,7 +1033,7 @@ public abstract class Calendar implements Serializable, Cloneable ...@@ -918,7 +1033,7 @@ public abstract class Calendar implements Serializable, Cloneable
/** /**
* Sets what the first day of week is. This is used for * Sets what the first day of week is. This is used for
* WEEK_OF_MONTH and WEEK_OF_YEAR fields. * WEEK_OF_MONTH and WEEK_OF_YEAR fields.
* @param value the first day of week. One of SUNDAY to SATURDAY. * @param value the first day of week. One of SUNDAY to SATURDAY.
*/ */
public void setFirstDayOfWeek(int value) public void setFirstDayOfWeek(int value)
...@@ -928,7 +1043,7 @@ public abstract class Calendar implements Serializable, Cloneable ...@@ -928,7 +1043,7 @@ public abstract class Calendar implements Serializable, Cloneable
/** /**
* Gets what the first day of week is. This is used for * Gets what the first day of week is. This is used for
* WEEK_OF_MONTH and WEEK_OF_YEAR fields. * WEEK_OF_MONTH and WEEK_OF_YEAR fields.
* @return the first day of week. One of SUNDAY to SATURDAY. * @return the first day of week. One of SUNDAY to SATURDAY.
*/ */
public int getFirstDayOfWeek() public int getFirstDayOfWeek()
...@@ -972,7 +1087,6 @@ public abstract class Calendar implements Serializable, Cloneable ...@@ -972,7 +1087,6 @@ public abstract class Calendar implements Serializable, Cloneable
*/ */
public abstract int getMaximum(int field); public abstract int getMaximum(int field);
/** /**
* Gets the greatest minimum value that is allowed for the specified field. * Gets the greatest minimum value that is allowed for the specified field.
* @param field the time field. One of the time field constants. * @param field the time field. One of the time field constants.
...@@ -984,7 +1098,7 @@ public abstract class Calendar implements Serializable, Cloneable ...@@ -984,7 +1098,7 @@ public abstract class Calendar implements Serializable, Cloneable
* Gets the smallest maximum value that is allowed for the * Gets the smallest maximum value that is allowed for the
* specified field. For example this is 28 for DAY_OF_MONTH. * specified field. For example this is 28 for DAY_OF_MONTH.
* @param field the time field. One of the time field constants. * @param field the time field. One of the time field constants.
* @return the least maximum value. * @return the least maximum value.
*/ */
public abstract int getLeastMaximum(int field); public abstract int getLeastMaximum(int field);
...@@ -1000,16 +1114,15 @@ public abstract class Calendar implements Serializable, Cloneable ...@@ -1000,16 +1114,15 @@ public abstract class Calendar implements Serializable, Cloneable
*/ */
public int getActualMinimum(int field) public int getActualMinimum(int field)
{ {
Calendar tmp = (Calendar)clone(); // To avoid restoring state Calendar tmp = (Calendar) clone(); // To avoid restoring state
int min = tmp.getGreatestMinimum(field); int min = tmp.getGreatestMinimum(field);
int end = tmp.getMinimum(field); int end = tmp.getMinimum(field);
tmp.set(field, min); tmp.set(field, min);
for (; min > end; min--) for (; min > end; min--)
{ {
tmp.add(field, -1); // Try to get smaller tmp.add(field, -1); // Try to get smaller
if (tmp.get(field) != min - 1) if (tmp.get(field) != min - 1)
break; // Done if not successful break; // Done if not successful
} }
return min; return min;
} }
...@@ -1018,7 +1131,7 @@ public abstract class Calendar implements Serializable, Cloneable ...@@ -1018,7 +1131,7 @@ public abstract class Calendar implements Serializable, Cloneable
* Gets the actual maximum value that is allowed for the specified field. * Gets the actual maximum value that is allowed for the specified field.
* This value is dependent on the values of the other fields. * This value is dependent on the values of the other fields.
* @param field the time field. One of the time field constants. * @param field the time field. One of the time field constants.
* @return the actual maximum value. * @return the actual maximum value.
* @throws ArrayIndexOutOfBoundsException if the field is outside * @throws ArrayIndexOutOfBoundsException if the field is outside
* the valid range. The value of field must be >= 0 and * the valid range. The value of field must be >= 0 and
* <= <code>FIELD_COUNT</code>. * <= <code>FIELD_COUNT</code>.
...@@ -1026,7 +1139,7 @@ public abstract class Calendar implements Serializable, Cloneable ...@@ -1026,7 +1139,7 @@ public abstract class Calendar implements Serializable, Cloneable
*/ */
public int getActualMaximum(int field) public int getActualMaximum(int field)
{ {
Calendar tmp = (Calendar)clone(); // To avoid restoring state Calendar tmp = (Calendar) clone(); // To avoid restoring state
int max = tmp.getLeastMaximum(field); int max = tmp.getLeastMaximum(field);
int end = tmp.getMaximum(field); int end = tmp.getMaximum(field);
tmp.set(field, max); tmp.set(field, max);
...@@ -1048,7 +1161,7 @@ public abstract class Calendar implements Serializable, Cloneable ...@@ -1048,7 +1161,7 @@ public abstract class Calendar implements Serializable, Cloneable
{ {
Calendar cal = (Calendar) super.clone(); Calendar cal = (Calendar) super.clone();
cal.fields = (int[]) fields.clone(); cal.fields = (int[]) fields.clone();
cal.isSet = (boolean[])isSet.clone(); cal.isSet = (boolean[]) isSet.clone();
return cal; return cal;
} }
catch (CloneNotSupportedException ex) catch (CloneNotSupportedException ex)
...@@ -1057,16 +1170,19 @@ public abstract class Calendar implements Serializable, Cloneable ...@@ -1057,16 +1170,19 @@ public abstract class Calendar implements Serializable, Cloneable
} }
} }
private static final String[] fieldNames = { private static final String[] fieldNames =
",ERA=", ",YEAR=", ",MONTH=", {
",WEEK_OF_YEAR=", ",WEEK_OF_MONTH=", ",ERA=", ",YEAR=", ",MONTH=",
",DAY_OF_MONTH=", ",DAY_OF_YEAR=", ",DAY_OF_WEEK=", ",WEEK_OF_YEAR=",
",DAY_OF_WEEK_IN_MONTH=", ",WEEK_OF_MONTH=",
",AM_PM=", ",HOUR=", ",HOUR_OF_DAY=", ",DAY_OF_MONTH=",
",MINUTE=", ",SECOND=", ",MILLISECOND=", ",DAY_OF_YEAR=", ",DAY_OF_WEEK=",
",ZONE_OFFSET=", ",DST_OFFSET=" ",DAY_OF_WEEK_IN_MONTH=",
}; ",AM_PM=", ",HOUR=",
",HOUR_OF_DAY=", ",MINUTE=",
",SECOND=", ",MILLISECOND=",
",ZONE_OFFSET=", ",DST_OFFSET="
};
/** /**
* Returns a string representation of this object. It is mainly * Returns a string representation of this object. It is mainly
...@@ -1109,7 +1225,7 @@ public abstract class Calendar implements Serializable, Cloneable ...@@ -1109,7 +1225,7 @@ public abstract class Calendar implements Serializable, Cloneable
* says, that it could be omitted. */ * says, that it could be omitted. */
private void writeObject(ObjectOutputStream stream) throws IOException private void writeObject(ObjectOutputStream stream) throws IOException
{ {
if (!isTimeSet) if (! isTimeSet)
computeTime(); computeTime();
stream.defaultWriteObject(); stream.defaultWriteObject();
} }
...@@ -1121,7 +1237,7 @@ public abstract class Calendar implements Serializable, Cloneable ...@@ -1121,7 +1237,7 @@ public abstract class Calendar implements Serializable, Cloneable
throws IOException, ClassNotFoundException throws IOException, ClassNotFoundException
{ {
stream.defaultReadObject(); stream.defaultReadObject();
if (!isTimeSet) if (! isTimeSet)
computeTime(); computeTime();
if (serialVersionOnStream > 1) if (serialVersionOnStream > 1)
...@@ -1130,7 +1246,6 @@ public abstract class Calendar implements Serializable, Cloneable ...@@ -1130,7 +1246,6 @@ public abstract class Calendar implements Serializable, Cloneable
// Sun wants to remove all fields from the stream someday // Sun wants to remove all fields from the stream someday
// and will then increase the serialVersion number again. // and will then increase the serialVersion number again.
// We prepare to be compatible. // We prepare to be compatible.
fields = new int[FIELD_COUNT]; fields = new int[FIELD_COUNT];
isSet = new boolean[FIELD_COUNT]; isSet = new boolean[FIELD_COUNT];
areFieldsSet = false; areFieldsSet = false;
......
...@@ -39,6 +39,7 @@ exception statement from your version. */ ...@@ -39,6 +39,7 @@ exception statement from your version. */
package java.util; package java.util;
/** /**
* <p> * <p>
* This class represents the Gregorian calendar, that is used in most * This class represents the Gregorian calendar, that is used in most
...@@ -46,7 +47,7 @@ package java.util; ...@@ -46,7 +47,7 @@ package java.util;
* for dates smaller than the date of the change to the Gregorian calendar. * for dates smaller than the date of the change to the Gregorian calendar.
* The Gregorian calendar differs from the Julian calendar by a different * 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 * leap year rule (no leap year every 100 years, except if year is divisible
* by 400). * by 400).
* </p> * </p>
* <p> * <p>
* This change date is different from country to country, and can be changed with * This change date is different from country to country, and can be changed with
...@@ -136,7 +137,7 @@ public class GregorianCalendar extends Calendar ...@@ -136,7 +137,7 @@ public class GregorianCalendar extends Calendar
* Constant representing the era BC (Before Christ). * Constant representing the era BC (Before Christ).
*/ */
public static final int BC = 0; public static final int BC = 0;
/** /**
* Constant representing the era AD (Anno Domini). * Constant representing the era AD (Anno Domini).
*/ */
...@@ -164,43 +165,46 @@ public class GregorianCalendar extends Calendar ...@@ -164,43 +165,46 @@ public class GregorianCalendar extends Calendar
private static final String bundleName = "gnu.java.locale.Calendar"; private static final String bundleName = "gnu.java.locale.Calendar";
/** /**
* Retrieves the resource bundle. The resources should be loaded * Days in the epoch. Relative Jan 1, year '0' which is not a leap year.
* via this method only. Iff an application uses this method, the * (although there is no year zero, this does not matter.)
* resourcebundle is required. * This is consistent with the formula:
* = (year-1)*365L + ((year-1) >> 2)
*
* Plus the gregorian correction:
* Math.floor((year-1) / 400.) - Math.floor((year-1) / 100.);
* For a correct julian date, the correction is -2 instead.
* *
* @param locale the locale in use for this calendar. * The gregorian cutover in 1582 was 10 days, so by calculating the
* @return A resource bundle for the calendar for the specified locale. * correction from year zero, we have 15 non-leap days (even centuries)
* minus 3 leap days (year 400,800,1200) = 12. Subtracting two corrects
* this to the correct number 10.
*/ */
private static ResourceBundle getBundle(Locale locale) private static final int EPOCH_DAYS = 719162;
{
return ResourceBundle.getBundle(bundleName, locale,
ClassLoader.getSystemClassLoader());
}
/** /**
* Constructs a new GregorianCalender representing the current * Constructs a new GregorianCalender representing the current
* time, using the default time zone and the default locale. * time, using the default time zone and the default locale.
*/ */
public GregorianCalendar() public GregorianCalendar()
{ {
this(TimeZone.getDefault(), Locale.getDefault()); this(TimeZone.getDefault(), Locale.getDefault());
} }
/** /**
* Constructs a new GregorianCalender representing the current * Constructs a new GregorianCalender representing the current
* time, using the specified time zone and the default locale. * time, using the specified time zone and the default locale.
* *
* @param zone a time zone. * @param zone a time zone.
*/ */
public GregorianCalendar(TimeZone zone) public GregorianCalendar(TimeZone zone)
{ {
this(zone, Locale.getDefault()); this(zone, Locale.getDefault());
} }
/** /**
* Constructs a new GregorianCalender representing the current * Constructs a new GregorianCalender representing the current
* time, using the default time zone and the specified locale. * time, using the default time zone and the specified locale.
* *
* @param locale a locale. * @param locale a locale.
*/ */
public GregorianCalendar(Locale locale) public GregorianCalendar(Locale locale)
...@@ -212,15 +216,30 @@ public class GregorianCalendar extends Calendar ...@@ -212,15 +216,30 @@ public class GregorianCalendar extends Calendar
* Constructs a new GregorianCalender representing the current * Constructs a new GregorianCalender representing the current
* time with the given time zone and the given locale. * time with the given time zone and the given locale.
* *
* @param zone a time zone. * @param zone a time zone.
* @param locale a locale. * @param locale a locale.
*/ */
public GregorianCalendar(TimeZone zone, Locale locale) public GregorianCalendar(TimeZone zone, Locale locale)
{ {
this(zone, locale, false);
setTimeInMillis(System.currentTimeMillis());
complete();
}
/**
* Common constructor that all constructors should call.
* @param zone a time zone.
* @param locale a locale.
* @param unused unused parameter to make the signature differ from
* the public constructor (TimeZone, Locale).
*/
private GregorianCalendar(TimeZone zone, Locale locale, boolean unused)
{
super(zone, locale); super(zone, locale);
ResourceBundle rb = getBundle(locale); ResourceBundle rb = ResourceBundle.getBundle(bundleName, locale,
ClassLoader
.getSystemClassLoader());
gregorianCutover = ((Date) rb.getObject("gregorianCutOver")).getTime(); gregorianCutover = ((Date) rb.getObject("gregorianCutOver")).getTime();
setTimeInMillis(System.currentTimeMillis());
} }
/** /**
...@@ -232,7 +251,7 @@ public class GregorianCalendar extends Calendar ...@@ -232,7 +251,7 @@ public class GregorianCalendar extends Calendar
*/ */
public GregorianCalendar(int year, int month, int day) public GregorianCalendar(int year, int month, int day)
{ {
super(); this(TimeZone.getDefault(), Locale.getDefault(), false);
set(year, month, day); set(year, month, day);
} }
...@@ -248,7 +267,7 @@ public class GregorianCalendar extends Calendar ...@@ -248,7 +267,7 @@ public class GregorianCalendar extends Calendar
*/ */
public GregorianCalendar(int year, int month, int day, int hour, int minute) public GregorianCalendar(int year, int month, int day, int hour, int minute)
{ {
super(); this(TimeZone.getDefault(), Locale.getDefault(), false);
set(year, month, day, hour, minute); set(year, month, day, hour, minute);
} }
...@@ -264,10 +283,10 @@ public class GregorianCalendar extends Calendar ...@@ -264,10 +283,10 @@ public class GregorianCalendar extends Calendar
* @param minute corresponds to the MINUTE time field. * @param minute corresponds to the MINUTE time field.
* @param second corresponds to the SECOND time field. * @param second corresponds to the SECOND time field.
*/ */
public GregorianCalendar(int year, int month, int day, public GregorianCalendar(int year, int month, int day, int hour, int minute,
int hour, int minute, int second) int second)
{ {
super(); this(TimeZone.getDefault(), Locale.getDefault(), false);
set(year, month, day, hour, minute, second); set(year, month, day, hour, minute, second);
} }
...@@ -308,71 +327,23 @@ public class GregorianCalendar extends Calendar ...@@ -308,71 +327,23 @@ public class GregorianCalendar extends Calendar
* </p> * </p>
* *
* @param year a year (use a negative value for BC). * @param year a year (use a negative value for BC).
* @return true, if the given year is a leap year, false otherwise. * @return true, if the given year is a leap year, false otherwise.
*/ */
public boolean isLeapYear(int year) public boolean isLeapYear(int year)
{ {
// Only years divisible by 4 can be leap years
if ((year & 3) != 0) if ((year & 3) != 0)
// Only years divisible by 4 can be leap years
return false; return false;
// compute the linear day of the 29. February of that year. // Is the leap-day a Julian date? Then it's a leap year
// The 13 is the number of days, that were omitted in the Gregorian if (! isGregorian(year, 31 + 29 - 1))
// Calender until the epoch.
int julianDay = (((year-1) * (365*4+1)) >> 2) + (31+29 -
(((1970-1) * (365*4+1)) / 4 + 1 - 13));
// If that day is smaller than the gregorianChange the julian
// rule applies: This is a leap year since it is divisible by 4.
if (julianDay * (24 * 60 * 60 * 1000L) < gregorianCutover)
return true; return true;
// Apply gregorian rules otherwise
return ((year % 100) != 0 || (year % 400) == 0); return ((year % 100) != 0 || (year % 400) == 0);
} }
/** /**
* Get the linear time in milliseconds since the epoch. If you
* specify a nonpositive year it is interpreted as BC as
* following: 0 is 1 BC, -1 is 2 BC and so on. The date is
* interpreted as gregorian if the change occurred before that date.
*
* @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.
*/
private long getLinearTime(int year, int dayOfYear, int millis)
{
// The 13 is the number of days, that were omitted in the Gregorian
// 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;
if (time >= gregorianCutover)
{
// subtract the days that are missing in gregorian calendar
// with respect to julian calendar.
//
// Okay, here we rely on the fact that the gregorian
// calendar was introduced in the AD era. This doesn't work
// with negative years.
//
// The additional leap year factor accounts for the fact that
// a leap day is not seen on Jan 1 of the leap year.
// And on and after the leap day, the leap day has already been
// included in dayOfYear.
int gregOffset = (year / 400) - (year / 100) + 2;
if (isLeapYear (year, true))
--gregOffset;
time += gregOffset * (24 * 60 * 60 * 1000L);
}
return time;
}
/**
* Retrieves the day of the week corresponding to the specified * Retrieves the day of the week corresponding to the specified
* day of the specified year. * day of the specified year.
* *
...@@ -382,8 +353,8 @@ public class GregorianCalendar extends Calendar ...@@ -382,8 +353,8 @@ public class GregorianCalendar extends Calendar
*/ */
private int getWeekDay(int year, int dayOfYear) private int getWeekDay(int year, int dayOfYear)
{ {
int day = boolean greg = isGregorian(year, dayOfYear);
(int) (getLinearTime(year, dayOfYear, 0) / (24 * 60 * 60 * 1000L)); int day = (int) getLinearDay(year, dayOfYear, greg);
// The epoch was a thursday. // The epoch was a thursday.
int weekday = (day + THURSDAY) % 7; int weekday = (day + THURSDAY) % 7;
...@@ -393,235 +364,360 @@ public class GregorianCalendar extends Calendar ...@@ -393,235 +364,360 @@ public class GregorianCalendar extends Calendar
} }
/** /**
* <p> * Returns the day of the week for the first day of a given month (0..11)
* 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.
* </p>
* <p>
* 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 the Gregorian change occurs.
* </p>
*
* @param year the year, negative for BC.
* @return an array of two integer values, the first containing a reference
* day in the current year, the second a relative count since this reference
* day.
*/ */
private int[] getDayOfYear(int year) private int getFirstDayOfMonth(int year, int month)
{ {
if (isSet[MONTH]) int[] dayCount = { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 };
{
int dayOfYear;
if (fields[MONTH] > FEBRUARY)
{
// The months after February are regular: if (month > 11)
// 9 is an offset found by try and error. {
dayOfYear = (fields[MONTH] * (31 + 30 + 31 + 30 + 31) - 9) / 5; year += (month / 12);
if (isLeapYear(year)) month = month % 12;
dayOfYear++; }
}
else
dayOfYear = 31 * fields[MONTH];
if (isSet[DAY_OF_MONTH]) if (month < 0)
{
year += (int) month / 12;
month = month % 12;
if (month < 0)
{ {
return new int[] month += 12;
{ year--;
dayOfYear + fields[DAY_OF_MONTH], 0};
} }
if (isSet[WEEK_OF_MONTH] && isSet[DAY_OF_WEEK]) }
{
// the weekday of the first day in that month is:
int weekday = getWeekDay(year, ++dayOfYear);
return new int[] int dayOfYear = dayCount[month] + 1;
{ if (month > 1)
dayOfYear, if (isLeapYear(year))
// the day of week in the first week dayOfYear++;
// (weeks starting on sunday) is:
fields[DAY_OF_WEEK] - weekday + boolean greg = isGregorian(year, dayOfYear);
// Now jump to the right week and correct the possible int day = (int) getLinearDay(year, dayOfYear, greg);
// error made by assuming sunday is the first week day.
7 * (fields[WEEK_OF_MONTH] // The epoch was a thursday.
+ (fields[DAY_OF_WEEK] < getFirstDayOfWeek()? 0 : -1) int weekday = (day + THURSDAY) % 7;
+ (weekday < getFirstDayOfWeek()? -1 : 0))}; if (weekday <= 0)
} weekday += 7;
if (isSet[DAY_OF_WEEK] && isSet[DAY_OF_WEEK_IN_MONTH]) return weekday;
{ }
// the weekday of the first day in that month is:
int weekday = getWeekDay(year, ++dayOfYear); /**
return new int[] { * Takes a year, and a (zero based) day of year and determines
dayOfYear, * if it is gregorian or not.
fields[DAY_OF_WEEK] - weekday + */
7 * (fields[DAY_OF_WEEK_IN_MONTH] private boolean isGregorian(int year, int dayOfYear)
+ (fields[DAY_OF_WEEK] < weekday ? 0 : -1))}; {
} int relativeDay = (year - 1) * 365 + ((year - 1) >> 2) + dayOfYear
- EPOCH_DAYS; // gregorian days from 1 to epoch.
int gregFactor = (int) Math.floor((double) (year - 1) / 400.)
- (int) Math.floor((double) (year - 1) / 100.);
return ((relativeDay + gregFactor) * 60L * 60L * 24L * 1000L >= gregorianCutover);
}
/**
* Check set fields for validity, without leniency.
*
* @throws IllegalArgumentException if a field is invalid
*/
private void nonLeniencyCheck() throws IllegalArgumentException
{
int[] month_days = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
int year = fields[YEAR];
int month = fields[MONTH];
int leap = isLeapYear(year) ? 1 : 0;
if (isSet[ERA] && fields[ERA] != AD && fields[ERA] != BC)
throw new IllegalArgumentException("Illegal ERA.");
if (isSet[YEAR] && fields[YEAR] < 1)
throw new IllegalArgumentException("Illegal YEAR.");
if (isSet[MONTH] && (month < 0 || month > 11))
throw new IllegalArgumentException("Illegal MONTH.");
if (isSet[WEEK_OF_YEAR])
{
int daysInYear = 365 + leap;
daysInYear += (getFirstDayOfMonth(year, 0) - 1); // pad first week
int last = getFirstDayOfMonth(year, 11) + 4;
if (last > 7)
last -= 7;
daysInYear += 7 - last;
int weeks = daysInYear / 7;
if (fields[WEEK_OF_YEAR] < 1 || fields[WEEK_OF_YEAR] > weeks)
throw new IllegalArgumentException("Illegal WEEK_OF_YEAR.");
} }
// MONTH + something did not succeed. if (isSet[WEEK_OF_MONTH])
if (isSet[DAY_OF_YEAR])
{ {
return new int[] {0, fields[DAY_OF_YEAR]}; int weeks = (month == 1 && leap == 0) ? 4 : 5;
if (fields[WEEK_OF_MONTH] < 1 || fields[WEEK_OF_MONTH] > weeks)
throw new IllegalArgumentException("Illegal WEEK_OF_MONTH.");
} }
if (isSet[DAY_OF_WEEK] && isSet[WEEK_OF_YEAR]) if (isSet[DAY_OF_MONTH])
if (fields[DAY_OF_MONTH] < 1
|| fields[DAY_OF_MONTH] > month_days[month]
+ ((month == 1) ? leap : 0))
throw new IllegalArgumentException("Illegal DAY_OF_MONTH.");
if (isSet[DAY_OF_YEAR]
&& (fields[DAY_OF_YEAR] < 1 || fields[DAY_OF_YEAR] > 365 + leap))
throw new IllegalArgumentException("Illegal DAY_OF_YEAR.");
if (isSet[DAY_OF_WEEK]
&& (fields[DAY_OF_WEEK] < 1 || fields[DAY_OF_WEEK] > 7))
throw new IllegalArgumentException("Illegal DAY_OF_WEEK.");
if (isSet[DAY_OF_WEEK_IN_MONTH])
{ {
int dayOfYear = getMinimalDaysInFirstWeek(); int weeks = (month == 1 && leap == 0) ? 4 : 5;
// the weekday of the day, that begins the first week if (fields[DAY_OF_WEEK_IN_MONTH] < -weeks
// in that year is: || fields[DAY_OF_WEEK_IN_MONTH] > weeks)
int weekday = getWeekDay(year, dayOfYear); throw new IllegalArgumentException("Illegal DAY_OF_WEEK_IN_MONTH.");
return new int[] {
dayOfYear,
// the day of week in the first week
// (weeks starting on sunday) is:
fields[DAY_OF_WEEK] - weekday
// Now jump to the right week and correct the possible
// error made by assuming sunday is the first week day.
+ 7 * (fields[WEEK_OF_YEAR]
+ (fields[DAY_OF_WEEK] < getFirstDayOfWeek()? 0 : -1)
+ (weekday < getFirstDayOfWeek()? -1 : 0))};
} }
// As last resort return Jan, 1st. if (isSet[AM_PM] && fields[AM_PM] != AM && fields[AM_PM] != PM)
return new int[] {1, 0}; throw new IllegalArgumentException("Illegal AM_PM.");
if (isSet[HOUR] && (fields[HOUR] < 0 || fields[HOUR] > 12))
throw new IllegalArgumentException("Illegal HOUR.");
if (isSet[HOUR_OF_DAY]
&& (fields[HOUR_OF_DAY] < 0 || fields[HOUR_OF_DAY] > 23))
throw new IllegalArgumentException("Illegal HOUR_OF_DAY.");
if (isSet[MINUTE] && (fields[MINUTE] < 0 || fields[MINUTE] > 59))
throw new IllegalArgumentException("Illegal MINUTE.");
if (isSet[SECOND] && (fields[SECOND] < 0 || fields[SECOND] > 59))
throw new IllegalArgumentException("Illegal SECOND.");
if (isSet[MILLISECOND]
&& (fields[MILLISECOND] < 0 || fields[MILLISECOND] > 999))
throw new IllegalArgumentException("Illegal MILLISECOND.");
if (isSet[ZONE_OFFSET]
&& (fields[ZONE_OFFSET] < -12 * 60 * 60 * 1000L
|| fields[ZONE_OFFSET] > 12 * 60 * 60 * 1000L))
throw new IllegalArgumentException("Illegal ZONE_OFFSET.");
if (isSet[DST_OFFSET]
&& (fields[DST_OFFSET] < -12 * 60 * 60 * 1000L
|| fields[DST_OFFSET] > 12 * 60 * 60 * 1000L))
throw new IllegalArgumentException("Illegal DST_OFFSET.");
} }
/** /**
* Converts the time field values (<code>fields</code>) to * Converts the time field values (<code>fields</code>) to
* milliseconds since the epoch UTC (<code>time</code>). * milliseconds since the epoch UTC (<code>time</code>).
* *
* @throws IllegalArgumentException if any calendar fields * @throws IllegalArgumentException if any calendar fields
* are invalid. * are invalid.
*/ */
protected synchronized void computeTime() protected synchronized void computeTime()
{ {
int era = isSet[ERA] ? fields[ERA] : AD; int millisInDay = 0;
int year = isSet[YEAR] ? fields[YEAR] : 1970; int era = fields[ERA];
if (era == BC) int year = fields[YEAR];
year = 1 - year; int month = fields[MONTH];
int day = fields[DAY_OF_MONTH];
int minute = fields[MINUTE];
int second = fields[SECOND];
int millis = fields[MILLISECOND];
int[] month_days = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
int[] dayCount = { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 };
int hour = 0;
int[] daysOfYear = getDayOfYear(year); if (! isLenient())
nonLeniencyCheck();
int hour = 0; if (! isSet[MONTH] && (! isSet[DAY_OF_WEEK] || isSet[WEEK_OF_YEAR]))
if (isSet[HOUR_OF_DAY]) {
hour = fields[HOUR_OF_DAY]; // 5: YEAR + DAY_OF_WEEK + WEEK_OF_YEAR
else if (isSet[HOUR]) if (isSet[WEEK_OF_YEAR])
{
int first = getFirstDayOfMonth(year, 0);
int offs = 1;
int daysInFirstWeek = getFirstDayOfWeek() - first;
if (daysInFirstWeek <= 0)
daysInFirstWeek += 7;
if (daysInFirstWeek < getMinimalDaysInFirstWeek())
offs += daysInFirstWeek;
else
offs -= 7 - daysInFirstWeek;
month = 0;
day = offs + 7 * (fields[WEEK_OF_YEAR] - 1);
offs = fields[DAY_OF_WEEK] - getFirstDayOfWeek();
if (offs < 0)
offs += 7;
day += offs;
}
else
{
// 4: YEAR + DAY_OF_YEAR
month = 0;
day = fields[DAY_OF_YEAR];
}
}
else
{
if (isSet[DAY_OF_WEEK])
{
int first = getFirstDayOfMonth(year, month);
// 3: YEAR + MONTH + DAY_OF_WEEK_IN_MONTH + DAY_OF_WEEK
if (isSet[DAY_OF_WEEK_IN_MONTH])
{
int offs = fields[DAY_OF_WEEK] - first;
if (offs < 0)
offs += 7;
day = 1 + 7 * (fields[DAY_OF_WEEK_IN_MONTH] - 1);
day += offs;
}
else
{ // 2: YEAR + MONTH + WEEK_OF_MONTH + DAY_OF_WEEK
int offs = 1;
int daysInFirstWeek = getFirstDayOfWeek() - first;
if (daysInFirstWeek <= 0)
daysInFirstWeek += 7;
if (daysInFirstWeek < getMinimalDaysInFirstWeek())
offs += daysInFirstWeek;
else
offs -= 7 - daysInFirstWeek;
day = offs + 7 * (fields[WEEK_OF_MONTH] - 1);
offs = fields[DAY_OF_WEEK] - getFirstDayOfWeek();
if (offs < 0)
offs += 7;
day += offs;
}
}
// 1: YEAR + MONTH + DAY_OF_MONTH
}
if (era == BC && year > 0)
year = 1 - year;
// rest of code assumes day/month/year set
// should negative BC years be AD?
// get the hour (but no check for validity)
if (isSet[HOUR])
{ {
hour = fields[HOUR]; hour = fields[HOUR];
if (isSet[AM_PM] && fields[AM_PM] == PM) if (fields[AM_PM] == PM)
if (hour != 12) /* not Noon */ if (hour != 12) /* not Noon */
hour += 12; hour += 12;
/* Fix the problem of the status of 12:00 AM (midnight). */ /* Fix the problem of the status of 12:00 AM (midnight). */
if (isSet[AM_PM] && fields[AM_PM] == AM && hour == 12) if (fields[AM_PM] == AM && hour == 12)
hour = 0; hour = 0;
} }
else
hour = fields[HOUR_OF_DAY];
int minute = isSet[MINUTE] ? fields[MINUTE] : 0; // Read the era,year,month,day fields and convert as appropriate.
int second = isSet[SECOND] ? fields[SECOND] : 0; // Calculate number of milliseconds into the day
int millis = isSet[MILLISECOND] ? fields[MILLISECOND] : 0; // This takes care of both h, m, s, ms over/underflows.
int millisInDay; long allMillis = (((hour * 60L) + minute) * 60L + second) * 1000L + millis;
day += allMillis / (24 * 60 * 60 * 1000L);
millisInDay = (int) (allMillis % (24 * 60 * 60 * 1000L));
if (isLenient()) if (month < 0)
{ {
// prevent overflow year += (int) month / 12;
long allMillis = (((hour * 60L) + minute) * 60L + second) * 1000L month = month % 12;
+ millis; if (month < 0)
daysOfYear[1] += allMillis / (24 * 60 * 60 * 1000L); {
millisInDay = (int) (allMillis % (24 * 60 * 60 * 1000L)); month += 12;
year--;
}
} }
else if (month > 11)
{ {
if (hour < 0 || hour >= 24 || minute < 0 || minute > 59 year += (month / 12);
|| second < 0 || second > 59 || millis < 0 || millis >= 1000) month = month % 12;
throw new IllegalArgumentException();
millisInDay = (((hour * 60) + minute) * 60 + second) * 1000 + millis;
} }
time = getLinearTime(year, daysOfYear[0], millisInDay);
// Add the relative days after calculating the linear time, to month_days[1] = isLeapYear(year) ? 29 : 28;
// get right behaviour when jumping over the gregorianCutover.
time += daysOfYear[1] * (24 * 60 * 60 * 1000L);
while (day <= 0)
{
if (month == 0)
{
year--;
month_days[1] = isLeapYear(year) ? 29 : 28;
}
month = (month + 11) % 12;
day += month_days[month];
}
while (day > month_days[month])
{
day -= (month_days[month]);
month = (month + 1) % 12;
if (month == 0)
{
year++;
month_days[1] = isLeapYear(year) ? 29 : 28;
}
}
TimeZone zone = getTimeZone(); // ok, by here we have valid day,month,year,era and millisinday
int rawOffset = isSet[ZONE_OFFSET] int dayOfYear = dayCount[month] + day - 1; // (day starts on 1)
? fields[ZONE_OFFSET] : zone.getRawOffset(); if (isLeapYear(year) && month > 1)
dayOfYear++;
int dayOfYear = daysOfYear[0] + daysOfYear[1];
// This formula isn't right, so check for month as a quick fix. int relativeDay = (year - 1) * 365 + ((year - 1) >> 2) + dayOfYear
// It doesn't compensate for leap years and puts day 30 in month 1 - EPOCH_DAYS; // gregorian days from 1 to epoch.
// instead of month 0. int gregFactor = (int) Math.floor((double) (year - 1) / 400.)
int month = isSet[MONTH] - (int) Math.floor((double) (year - 1) / 100.);
? fields[MONTH] : (dayOfYear * 5 + 3) / (31 + 30 + 31 + 30 + 31);
// This formula isn't right, so check for day as a quick fix. It if ((relativeDay + gregFactor) * 60L * 60L * 24L * 1000L >= gregorianCutover)
// doesn't compensate for leap years, either. relativeDay += gregFactor;
int day = isSet[DAY_OF_MONTH] ? fields[DAY_OF_MONTH] else
: (6 + (dayOfYear * 5 + 3) % (31 + 30 + 31 + 30 + 31)) / 5; relativeDay -= 2;
int weekday = ((int) (time / (24 * 60 * 60 * 1000L)) + THURSDAY) % 7;
time = relativeDay * (24 * 60 * 60 * 1000L) + millisInDay;
// the epoch was a Thursday.
int weekday = (int) (relativeDay + THURSDAY) % 7;
if (weekday <= 0) if (weekday <= 0)
weekday += 7; weekday += 7;
int dstOffset = isSet[DST_OFFSET] fields[DAY_OF_WEEK] = weekday;
? fields[DST_OFFSET] : (zone.getOffset((year < 0) ? BC : AD,
(year < 0) ? 1 - year : year,
month, day, weekday, millisInDay)
- zone.getRawOffset());
time -= rawOffset + dstOffset;
isTimeSet = true;
}
/** // Time zone corrections.
* <p> TimeZone zone = getTimeZone();
* Determines if the given year is a leap year. int rawOffset = isSet[ZONE_OFFSET] ? fields[ZONE_OFFSET]
* </p> : zone.getRawOffset();
* <p>
* 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.
* </p>
*
* @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)
// Only years divisible by 4 can be leap years
return false;
if (!gregorian) int dstOffset = isSet[DST_OFFSET] ? fields[DST_OFFSET]
return true; : (zone.getOffset((year < 0) ? BC : AD,
(year < 0) ? 1 - year
: year,
month, day, weekday,
millisInDay)
- zone.getRawOffset());
// We rely on AD area here. time -= rawOffset + dstOffset;
return ((year % 100) != 0 || (year % 400) == 0);
isTimeSet = true;
} }
/** /**
* Get the linear day in days since the epoch, using the * Get the linear day in days since the epoch, using the
* Julian or Gregorian calendar as specified. If you specify a * Julian or Gregorian calendar as specified. If you specify a
* nonpositive year it is interpreted as BC as following: 0 is 1 * nonpositive year it is interpreted as BC as following: 0 is 1
* BC, -1 is 2 BC and so on. * BC, -1 is 2 BC and so on.
* *
* @param year the year of the date. * @param year the year of the date.
* @param dayOfYear the day of year of the date; 1 based. * @param dayOfYear the day of year of the date; 1 based.
* @param gregorian <code>true</code>, if we should use the Gregorian rules. * @param gregorian <code>true</code>, if we should use the Gregorian rules.
* @return the days since the epoch, may be negative. * @return the days since the epoch, may be negative.
*/ */
private int getLinearDay(int year, int dayOfYear, boolean gregorian) public long getLinearDay(int year, int dayOfYear, boolean gregorian)
{ {
// The 13 is the number of days, that were omitted in the Gregorian // The 13 is the number of days, that were omitted in the Gregorian
// Calender until the epoch. // Calender until the epoch.
// We shift right by 2 instead of dividing by 4, to get correct // We shift right by 2 instead of dividing by 4, to get correct
// results for negative years (and this is even more efficient). // results for negative years (and this is even more efficient).
int julianDay = ((year * (365 * 4 + 1)) >> 2) + dayOfYear - long julianDay = (year - 1) * 365L + ((year - 1) >> 2) + (dayOfYear - 1)
((1970 * (365 * 4 + 1)) / 4 + 1 - 13); - EPOCH_DAYS; // gregorian days from 1 to epoch.
if (gregorian) if (gregorian)
{ {
// subtract the days that are missing in gregorian calendar // subtract the days that are missing in gregorian calendar
...@@ -633,11 +729,13 @@ public class GregorianCalendar extends Calendar ...@@ -633,11 +729,13 @@ public class GregorianCalendar extends Calendar
// //
// The additional leap year factor accounts for the fact that // The additional leap year factor accounts for the fact that
// a leap day is not seen on Jan 1 of the leap year. // a leap day is not seen on Jan 1 of the leap year.
int gregOffset = (year / 400) - (year / 100) + 2; int gregOffset = (int) Math.floor((double) (year - 1) / 400.)
if (isLeapYear (year, true) && dayOfYear < 31 + 29) - (int) Math.floor((double) (year - 1) / 100.);
--gregOffset;
julianDay += gregOffset; return julianDay + gregOffset;
} }
else
julianDay -= 2;
return julianDay; return julianDay;
} }
...@@ -646,26 +744,27 @@ public class GregorianCalendar extends Calendar ...@@ -646,26 +744,27 @@ public class GregorianCalendar extends Calendar
* day_of_year, day_of_month, day_of_week, and writes the result * day_of_year, day_of_month, day_of_week, and writes the result
* into the fields array. * into the fields array.
* *
* @param day the linear day. * @param day the linear day.
* @param gregorian true, if we should use Gregorian rules. * @param gregorian true, if we should use Gregorian rules.
*/ */
private void calculateDay(int day, boolean gregorian) private void calculateDay(int[] fields, long day, boolean gregorian)
{ {
// the epoch is a Thursday. // the epoch was a Thursday.
int weekday = (day + THURSDAY) % 7; int weekday = (int) (day + THURSDAY) % 7;
if (weekday <= 0) if (weekday <= 0)
weekday += 7; weekday += 7;
fields[DAY_OF_WEEK] = weekday; fields[DAY_OF_WEEK] = weekday;
// get a first approximation of the year. This may be one // get a first approximation of the year. This may be one
// year too big. // year too big.
int year = 1970 + (gregorian int year = 1970
? ((day - 100) * 400) / (365 * 400 + 100 - 4 + 1) + (int) (gregorian
: ((day - 100) * 4) / (365 * 4 + 1)); ? ((day - 100L) * 400L) / (365L * 400L + 100L - 4L
+ 1L) : ((day - 100L) * 4L) / (365L * 4L + 1L));
if (day >= 0) if (day >= 0)
year++; year++;
int firstDayOfYear = getLinearDay(year, 1, gregorian); long firstDayOfYear = getLinearDay(year, 1, gregorian);
// Now look in which year day really lies. // Now look in which year day really lies.
if (day < firstDayOfYear) if (day < firstDayOfYear)
...@@ -674,9 +773,9 @@ public class GregorianCalendar extends Calendar ...@@ -674,9 +773,9 @@ public class GregorianCalendar extends Calendar
firstDayOfYear = getLinearDay(year, 1, gregorian); firstDayOfYear = getLinearDay(year, 1, gregorian);
} }
day -= firstDayOfYear - 1; // day of year, one based. day -= firstDayOfYear - 1; // day of year, one based.
fields[DAY_OF_YEAR] = day; fields[DAY_OF_YEAR] = (int) day;
if (year <= 0) if (year <= 0)
{ {
fields[ERA] = BC; fields[ERA] = BC;
...@@ -688,16 +787,16 @@ public class GregorianCalendar extends Calendar ...@@ -688,16 +787,16 @@ public class GregorianCalendar extends Calendar
fields[YEAR] = year; fields[YEAR] = year;
} }
int leapday = isLeapYear(year, gregorian) ? 1 : 0; int leapday = isLeapYear(year) ? 1 : 0;
if (day <= 31 + 28 + leapday) if (day <= 31 + 28 + leapday)
{ {
fields[MONTH] = day / 32; // 31->JANUARY, 32->FEBRUARY fields[MONTH] = (int) day / 32; // 31->JANUARY, 32->FEBRUARY
fields[DAY_OF_MONTH] = day - 31 * fields[MONTH]; fields[DAY_OF_MONTH] = (int) day - 31 * fields[MONTH];
} }
else else
{ {
// A few more magic formulas // A few more magic formulas
int scaledDay = (day - leapday) * 5 + 8; int scaledDay = ((int) day - leapday) * 5 + 8;
fields[MONTH] = scaledDay / (31 + 30 + 31 + 30 + 31); fields[MONTH] = scaledDay / (31 + 30 + 31 + 30 + 31);
fields[DAY_OF_MONTH] = (scaledDay % (31 + 30 + 31 + 30 + 31)) / 5 + 1; fields[DAY_OF_MONTH] = (scaledDay % (31 + 30 + 31 + 30 + 31)) / 5 + 1;
} }
...@@ -716,25 +815,26 @@ public class GregorianCalendar extends Calendar ...@@ -716,25 +815,26 @@ public class GregorianCalendar extends Calendar
fields[ZONE_OFFSET] = zone.getRawOffset(); fields[ZONE_OFFSET] = zone.getRawOffset();
long localTime = time + fields[ZONE_OFFSET]; long localTime = time + fields[ZONE_OFFSET];
int day = (int) (localTime / (24 * 60 * 60 * 1000L)); long day = localTime / (24 * 60 * 60 * 1000L);
int millisInDay = (int) (localTime % (24 * 60 * 60 * 1000L)); int millisInDay = (int) (localTime % (24 * 60 * 60 * 1000L));
if (millisInDay < 0) if (millisInDay < 0)
{ {
millisInDay += (24 * 60 * 60 * 1000); millisInDay += (24 * 60 * 60 * 1000);
day--; day--;
} }
calculateDay(day, gregorian); calculateDay(fields, day, gregorian);
fields[DST_OFFSET] = fields[DST_OFFSET] = zone.getOffset(fields[ERA], fields[YEAR],
zone.getOffset(fields[ERA], fields[YEAR], fields[MONTH], fields[MONTH], fields[DAY_OF_MONTH],
fields[DAY_OF_MONTH], fields[DAY_OF_WEEK], fields[DAY_OF_WEEK], millisInDay)
millisInDay) - fields[ZONE_OFFSET]; - fields[ZONE_OFFSET];
millisInDay += fields[DST_OFFSET]; millisInDay += fields[DST_OFFSET];
if (millisInDay >= 24 * 60 * 60 * 1000) if (millisInDay >= 24 * 60 * 60 * 1000)
{ {
millisInDay -= 24 * 60 * 60 * 1000; millisInDay -= 24 * 60 * 60 * 1000;
calculateDay(++day, gregorian); calculateDay(fields, ++day, gregorian);
} }
fields[DAY_OF_WEEK_IN_MONTH] = (fields[DAY_OF_MONTH] + 6) / 7; fields[DAY_OF_WEEK_IN_MONTH] = (fields[DAY_OF_MONTH] + 6) / 7;
...@@ -749,13 +849,12 @@ public class GregorianCalendar extends Calendar ...@@ -749,13 +849,12 @@ public class GregorianCalendar extends Calendar
// Do the Correction: getMinimalDaysInFirstWeek() is always in the // Do the Correction: getMinimalDaysInFirstWeek() is always in the
// first week. // first week.
int minDays = getMinimalDaysInFirstWeek(); int minDays = getMinimalDaysInFirstWeek();
int firstWeekday = int firstWeekday = (7 + getWeekDay(fields[YEAR], minDays)
(7 + getWeekDay(fields[YEAR], minDays) - getFirstDayOfWeek()) % 7; - getFirstDayOfWeek()) % 7;
if (minDays - firstWeekday < 1) if (minDays - firstWeekday < 1)
weekOfYear++; weekOfYear++;
fields[WEEK_OF_YEAR] = weekOfYear; fields[WEEK_OF_YEAR] = weekOfYear;
int hourOfDay = millisInDay / (60 * 60 * 1000); int hourOfDay = millisInDay / (60 * 60 * 1000);
fields[AM_PM] = (hourOfDay < 12) ? AM : PM; fields[AM_PM] = (hourOfDay < 12) ? AM : PM;
int hour = hourOfDay % 12; int hour = hourOfDay % 12;
...@@ -767,14 +866,7 @@ public class GregorianCalendar extends Calendar ...@@ -767,14 +866,7 @@ public class GregorianCalendar extends Calendar
fields[SECOND] = millisInDay / (1000); fields[SECOND] = millisInDay / (1000);
fields[MILLISECOND] = millisInDay % 1000; fields[MILLISECOND] = millisInDay % 1000;
areFieldsSet = isSet[ERA] = isSet[YEAR] = isSet[MONTH] = isSet[WEEK_OF_YEAR] = isSet[WEEK_OF_MONTH] = isSet[DAY_OF_MONTH] = isSet[DAY_OF_YEAR] = isSet[DAY_OF_WEEK] = isSet[DAY_OF_WEEK_IN_MONTH] = isSet[AM_PM] = isSet[HOUR] = isSet[HOUR_OF_DAY] = isSet[MINUTE] = isSet[SECOND] = isSet[MILLISECOND] = isSet[ZONE_OFFSET] = isSet[DST_OFFSET] = true;
areFieldsSet = isSet[ERA] = isSet[YEAR] = isSet[MONTH] =
isSet[WEEK_OF_YEAR] = isSet[WEEK_OF_MONTH] =
isSet[DAY_OF_MONTH] = isSet[DAY_OF_YEAR] = isSet[DAY_OF_WEEK] =
isSet[DAY_OF_WEEK_IN_MONTH] = isSet[AM_PM] = isSet[HOUR] =
isSet[HOUR_OF_DAY] = isSet[MINUTE] = isSet[SECOND] =
isSet[MILLISECOND] = isSet[ZONE_OFFSET] = isSet[DST_OFFSET] = true;
} }
/** /**
...@@ -782,7 +874,7 @@ public class GregorianCalendar extends Calendar ...@@ -782,7 +874,7 @@ public class GregorianCalendar extends Calendar
* equivalent to this if it is also a <code>GregorianCalendar</code> * equivalent to this if it is also a <code>GregorianCalendar</code>
* with the same time since the epoch under the same conditions * with the same time since the epoch under the same conditions
* (same change date and same time zone). * (same change date and same time zone).
* *
* @param o the object to that we should compare. * @param o the object to that we should compare.
* @return true, if the given object is a calendar, that represents * @return true, if the given object is a calendar, that represents
* the same time (but doesn't necessarily have the same fields). * the same time (but doesn't necessarily have the same fields).
...@@ -794,48 +886,20 @@ public class GregorianCalendar extends Calendar ...@@ -794,48 +886,20 @@ public class GregorianCalendar extends Calendar
*/ */
public boolean equals(Object o) public boolean equals(Object o)
{ {
if (!(o instanceof GregorianCalendar)) if (! (o instanceof GregorianCalendar))
return false; return false;
GregorianCalendar cal = (GregorianCalendar) o; GregorianCalendar cal = (GregorianCalendar) o;
return (cal.getTimeInMillis() == getTimeInMillis()); 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 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 * Adds the specified amount of time to the given time field. The
* amount may be negative to subtract the time. If the field overflows * amount may be negative to subtract the time. If the field overflows
* it does what you expect: Jan, 25 + 10 Days is Feb, 4. * it does what you expect: Jan, 25 + 10 Days is Feb, 4.
* @param field one of the time field constants. * @param field one of the time field constants.
* @param amount the amount of time to add. * @param amount the amount of time to add.
* @exception IllegalArgumentException if <code>field</code> is * @exception IllegalArgumentException if <code>field</code> is
* <code>ZONE_OFFSET</code>, <code>DST_OFFSET</code>, or invalid; or * <code>ZONE_OFFSET</code>, <code>DST_OFFSET</code>, or invalid; or
* if <code>amount</code> contains an out-of-range value and the calendar * if <code>amount</code> contains an out-of-range value and the calendar
* is not in lenient mode. * is not in lenient mode.
...@@ -859,18 +923,18 @@ public class GregorianCalendar extends Calendar ...@@ -859,18 +923,18 @@ public class GregorianCalendar extends Calendar
fields[MONTH] += 12; fields[MONTH] += 12;
fields[YEAR]--; fields[YEAR]--;
} }
isTimeSet = false;
int maxDay = getActualMaximum(DAY_OF_MONTH); int maxDay = getActualMaximum(DAY_OF_MONTH);
if (fields[DAY_OF_MONTH] > maxDay) if (fields[DAY_OF_MONTH] > maxDay)
{ {
fields[DAY_OF_MONTH] = maxDay; fields[DAY_OF_MONTH] = maxDay;
isTimeSet = false;
} }
set(YEAR, fields[YEAR]);
set(MONTH, fields[MONTH]);
break; break;
case DAY_OF_MONTH: case DAY_OF_MONTH:
case DAY_OF_YEAR: case DAY_OF_YEAR:
case DAY_OF_WEEK: case DAY_OF_WEEK:
if (!isTimeSet) if (! isTimeSet)
computeTime(); computeTime();
time += amount * (24 * 60 * 60 * 1000L); time += amount * (24 * 60 * 60 * 1000L);
areFieldsSet = false; areFieldsSet = false;
...@@ -878,59 +942,57 @@ public class GregorianCalendar extends Calendar ...@@ -878,59 +942,57 @@ public class GregorianCalendar extends Calendar
case WEEK_OF_YEAR: case WEEK_OF_YEAR:
case WEEK_OF_MONTH: case WEEK_OF_MONTH:
case DAY_OF_WEEK_IN_MONTH: case DAY_OF_WEEK_IN_MONTH:
if (!isTimeSet) if (! isTimeSet)
computeTime(); computeTime();
time += amount * (7 * 24 * 60 * 60 * 1000L); time += amount * (7 * 24 * 60 * 60 * 1000L);
areFieldsSet = false; areFieldsSet = false;
break; break;
case AM_PM: case AM_PM:
if (!isTimeSet) if (! isTimeSet)
computeTime(); computeTime();
time += amount * (12 * 60 * 60 * 1000L); time += amount * (12 * 60 * 60 * 1000L);
areFieldsSet = false; areFieldsSet = false;
break; break;
case HOUR: case HOUR:
case HOUR_OF_DAY: case HOUR_OF_DAY:
if (!isTimeSet) if (! isTimeSet)
computeTime(); computeTime();
time += amount * (60 * 60 * 1000L); time += amount * (60 * 60 * 1000L);
areFieldsSet = false; areFieldsSet = false;
break; break;
case MINUTE: case MINUTE:
if (!isTimeSet) if (! isTimeSet)
computeTime(); computeTime();
time += amount * (60 * 1000L); time += amount * (60 * 1000L);
areFieldsSet = false; areFieldsSet = false;
break; break;
case SECOND: case SECOND:
if (!isTimeSet) if (! isTimeSet)
computeTime(); computeTime();
time += amount * (1000L); time += amount * (1000L);
areFieldsSet = false; areFieldsSet = false;
break; break;
case MILLISECOND: case MILLISECOND:
if (!isTimeSet) if (! isTimeSet)
computeTime(); computeTime();
time += amount; time += amount;
areFieldsSet = false; areFieldsSet = false;
break; break;
case ZONE_OFFSET: case ZONE_OFFSET:
case DST_OFFSET: case DST_OFFSET:default:
default:
throw new IllegalArgumentException("Invalid or unknown field"); throw new IllegalArgumentException("Invalid or unknown field");
} }
} }
/** /**
* Rolls the specified time field up or down. This means add one * Rolls the specified time field up or down. This means add one
* to the specified field, but don't change the other fields. If * to the specified field, but don't change the other fields. If
* the maximum for this field is reached, start over with the * the maximum for this field is reached, start over with the
* minimum value. * minimum value.
* *
* <strong>Note:</strong> There may be situation, where the other * <strong>Note:</strong> There may be situation, where the other
* fields must be changed, e.g rolling the month on May, 31. * fields must be changed, e.g rolling the month on May, 31.
* The date June, 31 is automatically converted to July, 1. * The date June, 31 is automatically converted to July, 1.
* This requires lenient settings. * This requires lenient settings.
* *
* @param field the time field. One of the time field constants. * @param field the time field. One of the time field constants.
...@@ -972,7 +1034,6 @@ public class GregorianCalendar extends Calendar ...@@ -972,7 +1034,6 @@ public class GregorianCalendar extends Calendar
isSet[DAY_OF_YEAR] = false; isSet[DAY_OF_YEAR] = false;
isSet[WEEK_OF_YEAR] = false; isSet[WEEK_OF_YEAR] = false;
break; break;
case DAY_OF_MONTH: case DAY_OF_MONTH:
isSet[WEEK_OF_MONTH] = false; isSet[WEEK_OF_MONTH] = false;
isSet[DAY_OF_WEEK] = false; isSet[DAY_OF_WEEK] = false;
...@@ -981,7 +1042,6 @@ public class GregorianCalendar extends Calendar ...@@ -981,7 +1042,6 @@ public class GregorianCalendar extends Calendar
isSet[WEEK_OF_YEAR] = false; isSet[WEEK_OF_YEAR] = false;
time += delta * (24 * 60 * 60 * 1000L); time += delta * (24 * 60 * 60 * 1000L);
break; break;
case WEEK_OF_MONTH: case WEEK_OF_MONTH:
isSet[DAY_OF_MONTH] = false; isSet[DAY_OF_MONTH] = false;
isSet[DAY_OF_WEEK_IN_MONTH] = false; isSet[DAY_OF_WEEK_IN_MONTH] = false;
...@@ -1013,7 +1073,6 @@ public class GregorianCalendar extends Calendar ...@@ -1013,7 +1073,6 @@ public class GregorianCalendar extends Calendar
isSet[DAY_OF_YEAR] = false; isSet[DAY_OF_YEAR] = false;
time += delta * (7 * 24 * 60 * 60 * 1000L); time += delta * (7 * 24 * 60 * 60 * 1000L);
break; break;
case AM_PM: case AM_PM:
isSet[HOUR_OF_DAY] = false; isSet[HOUR_OF_DAY] = false;
time += delta * (12 * 60 * 60 * 1000L); time += delta * (12 * 60 * 60 * 1000L);
...@@ -1027,7 +1086,6 @@ public class GregorianCalendar extends Calendar ...@@ -1027,7 +1086,6 @@ public class GregorianCalendar extends Calendar
isSet[AM_PM] = false; isSet[AM_PM] = false;
time += delta * (60 * 60 * 1000L); time += delta * (60 * 60 * 1000L);
break; break;
case MINUTE: case MINUTE:
time += delta * (60 * 1000L); time += delta * (60 * 1000L);
break; break;
...@@ -1047,7 +1105,7 @@ public class GregorianCalendar extends Calendar ...@@ -1047,7 +1105,7 @@ public class GregorianCalendar extends Calendar
* with the minimum value and vice versa for negative amounts. * with the minimum value and vice versa for negative amounts.
* *
* <strong>Note:</strong> There may be situation, where the other * <strong>Note:</strong> There may be situation, where the other
* fields must be changed, e.g rolling the month on May, 31. * fields must be changed, e.g rolling the month on May, 31.
* The date June, 31 is automatically corrected to June, 30. * The date June, 31 is automatically corrected to June, 30.
* *
* @param field the time field. One of the time field constants. * @param field the time field. One of the time field constants.
...@@ -1084,16 +1142,23 @@ public class GregorianCalendar extends Calendar ...@@ -1084,16 +1142,23 @@ public class GregorianCalendar extends Calendar
/** /**
* The minimum values for the calendar fields. * The minimum values for the calendar fields.
*/ */
private static final int[] minimums = 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 }; BC, 1, 0, 0, 1, 1, 1, SUNDAY, 1, AM,
1, 0, 0, 0, 0, -(12 * 60 * 60 * 1000),
0
};
/** /**
* The maximum values for the calendar fields. * The maximum values for the calendar fields.
*/ */
private static final int[] maximums = 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) }; 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. * Gets the smallest value that is allowed for the specified field.
...@@ -1117,7 +1182,6 @@ public class GregorianCalendar extends Calendar ...@@ -1117,7 +1182,6 @@ public class GregorianCalendar extends Calendar
return maximums[field]; return maximums[field];
} }
/** /**
* Gets the greatest minimum value that is allowed for the specified field. * Gets the greatest minimum value that is allowed for the specified field.
* This is the largest value returned by the <code>getActualMinimum(int)</code> * This is the largest value returned by the <code>getActualMinimum(int)</code>
...@@ -1142,7 +1206,7 @@ public class GregorianCalendar extends Calendar ...@@ -1142,7 +1206,7 @@ public class GregorianCalendar extends Calendar
* 28 days). * 28 days).
* *
* @param field the time field. One of the time field constants. * @param field the time field. One of the time field constants.
* @return the least maximum value. * @return the least maximum value.
* @see #getActualMaximum(int) * @see #getActualMaximum(int)
* @since 1.2 * @since 1.2
*/ */
...@@ -1182,7 +1246,7 @@ public class GregorianCalendar extends Calendar ...@@ -1182,7 +1246,7 @@ public class GregorianCalendar extends Calendar
int min = getMinimalDaysInFirstWeek(); int min = getMinimalDaysInFirstWeek();
if (min == 0) if (min == 0)
return 1; return 1;
if (!areFieldsSet || !isSet[ERA] || !isSet[YEAR]) if (! areFieldsSet || ! isSet[ERA] || ! isSet[YEAR])
complete(); complete();
int year = fields[ERA] == AD ? fields[YEAR] : 1 - fields[YEAR]; int year = fields[ERA] == AD ? fields[YEAR] : 1 - fields[YEAR];
...@@ -1203,45 +1267,46 @@ public class GregorianCalendar extends Calendar ...@@ -1203,45 +1267,46 @@ public class GregorianCalendar extends Calendar
* 29, rather than 28. * 29, rather than 28.
* *
* @param field the time field. One of the time field constants. * @param field the time field. One of the time field constants.
* @return the actual maximum value. * @return the actual maximum value.
*/ */
public int getActualMaximum(int field) public int getActualMaximum(int field)
{ {
switch (field) switch (field)
{ {
case WEEK_OF_YEAR: case WEEK_OF_YEAR:
{ {
if (!areFieldsSet || !isSet[ERA] || !isSet[YEAR]) if (! areFieldsSet || ! isSet[ERA] || ! isSet[YEAR])
complete(); complete();
// This is wrong for the year that contains the gregorian change. // This is wrong for the year that contains the gregorian change.
// I.e it gives the weeks in the julian year or in the gregorian // I.e it gives the weeks in the julian year or in the gregorian
// year in that case. // year in that case.
int year = fields[ERA] == AD ? fields[YEAR] : 1 - fields[YEAR]; int year = fields[ERA] == AD ? fields[YEAR] : 1 - fields[YEAR];
int lastDay = isLeapYear(year) ? 366 : 365; int lastDay = isLeapYear(year) ? 366 : 365;
int weekday = getWeekDay(year, lastDay); int weekday = getWeekDay(year, lastDay);
int week = (lastDay + 6 int week = (lastDay + 6 - (7 + weekday - getFirstDayOfWeek()) % 7) / 7;
- (7 + weekday - getFirstDayOfWeek()) % 7) / 7;
int minimalDays = getMinimalDaysInFirstWeek(); int minimalDays = getMinimalDaysInFirstWeek();
int firstWeekday = getWeekDay(year, minimalDays); int firstWeekday = getWeekDay(year, minimalDays);
/* /*
* Is there a set of days at the beginning of the year, before the * 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 * first day of the week, equal to or greater than the minimum number
* of days required in the first week? * of days required in the first week?
*/ */
if (minimalDays - (7 + firstWeekday - getFirstDayOfWeek()) % 7 < 1) if (minimalDays - (7 + firstWeekday - getFirstDayOfWeek()) % 7 < 1)
return week + 1; /* Add week 1: firstWeekday through to firstDayOfWeek */ return week + 1; /* Add week 1: firstWeekday through to firstDayOfWeek */
} }
case DAY_OF_MONTH: case DAY_OF_MONTH:
{ {
if (!areFieldsSet || !isSet[MONTH]) if (! areFieldsSet || ! isSet[MONTH])
complete(); complete();
int month = fields[MONTH]; int month = fields[MONTH];
// If you change this, you should also change // If you change this, you should also change
// SimpleTimeZone.getDaysInMonth(); // SimpleTimeZone.getDaysInMonth();
if (month == FEBRUARY) if (month == FEBRUARY)
{ {
if (!isSet[YEAR] || !isSet[ERA]) if (! isSet[YEAR] || ! isSet[ERA])
complete(); complete();
int year = fields[ERA] == AD ? fields[YEAR] : 1 - fields[YEAR]; int year = fields[ERA] == AD ? fields[YEAR] : 1 - fields[YEAR];
return isLeapYear(year) ? 29 : 28; return isLeapYear(year) ? 29 : 28;
...@@ -1250,33 +1315,31 @@ public class GregorianCalendar extends Calendar ...@@ -1250,33 +1315,31 @@ public class GregorianCalendar extends Calendar
return 31 - (month & 1); return 31 - (month & 1);
else else
return 30 + (month & 1); return 30 + (month & 1);
} }
case DAY_OF_YEAR: case DAY_OF_YEAR:
{ {
if (!areFieldsSet || !isSet[ERA] || !isSet[YEAR]) if (! areFieldsSet || ! isSet[ERA] || ! isSet[YEAR])
complete(); complete();
int year = fields[ERA] == AD ? fields[YEAR] : 1 - fields[YEAR]; int year = fields[ERA] == AD ? fields[YEAR] : 1 - fields[YEAR];
return isLeapYear(year) ? 366 : 365; return isLeapYear(year) ? 366 : 365;
} }
case DAY_OF_WEEK_IN_MONTH: case DAY_OF_WEEK_IN_MONTH:
{ {
// This is wrong for the month that contains the gregorian change. // This is wrong for the month that contains the gregorian change.
int daysInMonth = getActualMaximum(DAY_OF_MONTH); int daysInMonth = getActualMaximum(DAY_OF_MONTH);
// That's black magic, I know // That's black magic, I know
return (daysInMonth - (fields[DAY_OF_MONTH] - 1) % 7 + 6) / 7; return (daysInMonth - (fields[DAY_OF_MONTH] - 1) % 7 + 6) / 7;
} }
case WEEK_OF_MONTH: case WEEK_OF_MONTH:
{ {
int daysInMonth = getActualMaximum(DAY_OF_MONTH); int daysInMonth = getActualMaximum(DAY_OF_MONTH);
int weekday = (daysInMonth - fields[DAY_OF_MONTH] int weekday = (daysInMonth - fields[DAY_OF_MONTH]
+ fields[DAY_OF_WEEK] - SUNDAY) % 7 + SUNDAY; + fields[DAY_OF_WEEK] - SUNDAY) % 7 + SUNDAY;
return (daysInMonth + 6 return (daysInMonth + 6 - (7 + weekday - getFirstDayOfWeek()) % 7) / 7;
- (7 + weekday - getFirstDayOfWeek()) % 7) / 7; }
}
default: default:
return maximums[field]; return maximums[field];
} }
} }
} }
...@@ -38,6 +38,7 @@ exception statement from your version. */ ...@@ -38,6 +38,7 @@ exception statement from your version. */
package java.util; package java.util;
/** /**
* This class represents a simple time zone offset and handles * This class represents a simple time zone offset and handles
* daylight savings. It can only handle one daylight savings rule, so * daylight savings. It can only handle one daylight savings rule, so
...@@ -49,14 +50,14 @@ package java.util; ...@@ -49,14 +50,14 @@ package java.util;
* lying in the AD era. * lying in the AD era.
* *
* @see Calendar * @see Calendar
* @see GregorianCalender * @see GregorianCalender
* @author Jochen Hoenicke * @author Jochen Hoenicke
*/ */
public class SimpleTimeZone extends TimeZone public class SimpleTimeZone extends TimeZone
{ {
/** /**
* The raw time zone offset in milliseconds to GMT, ignoring * The raw time zone offset in milliseconds to GMT, ignoring
* daylight savings. * daylight savings.
* @serial * @serial
*/ */
private int rawOffset; private int rawOffset;
...@@ -70,23 +71,22 @@ public class SimpleTimeZone extends TimeZone ...@@ -70,23 +71,22 @@ public class SimpleTimeZone extends TimeZone
/** /**
* The daylight savings offset. This is a positive offset in * The daylight savings offset. This is a positive offset in
* milliseconds with respect to standard time. Typically this * milliseconds with respect to standard time. Typically this
* is one hour, but for some time zones this may be half an hour. * is one hour, but for some time zones this may be half an our.
* @serial * @serial
* @since JDK1.1.4 * @since JDK1.1.4
*/ */
private int dstSavings = 60 * 60 * 1000; private int dstSavings = 60 * 60 * 1000;
/** /**
* The first year, in which daylight savings rules applies. * The first year, in which daylight savings rules applies.
* @serial * @serial
*/ */
private int startYear; private int startYear;
private static final int DOM_MODE = 1; private static final int DOM_MODE = 1;
private static final int DOW_IN_MONTH_MODE = 2; private static final int DOW_IN_MONTH_MODE = 2;
private static final int DOW_GE_DOM_MODE = 3; private static final int DOW_GE_DOM_MODE = 3;
private static final int DOW_LE_DOM_MODE = 4; private static final int DOW_LE_DOM_MODE = 4;
/** /**
* The mode of the start rule. This takes one of the following values: * The mode of the start rule. This takes one of the following values:
* <dl> * <dl>
...@@ -119,7 +119,7 @@ public class SimpleTimeZone extends TimeZone ...@@ -119,7 +119,7 @@ public class SimpleTimeZone extends TimeZone
/** /**
* The month in which daylight savings start. This is one of the * The month in which daylight savings start. This is one of the
* constants Calendar.JANUARY, ..., Calendar.DECEMBER. * constants Calendar.JANUARY, ..., Calendar.DECEMBER.
* @serial * @serial
*/ */
private int startMonth; private int startMonth;
...@@ -128,21 +128,21 @@ public class SimpleTimeZone extends TimeZone ...@@ -128,21 +128,21 @@ public class SimpleTimeZone extends TimeZone
* This variable can have different meanings. See startMode for details * This variable can have different meanings. See startMode for details
* @see #startMode; * @see #startMode;
* @serial * @serial
*/ */
private int startDay; private int startDay;
/** /**
* This variable specifies the day of week the change takes place. If * This variable specifies the day of week the change takes place. If
* startMode == DOM_MODE, this is undefined. * startMode == DOM_MODE, this is undefined.
* @serial * @serial
* @see #startMode; * @see #startMode;
*/ */
private int startDayOfWeek; private int startDayOfWeek;
/** /**
* This variable specifies the time of change to daylight savings. * This variable specifies the time of change to daylight savings.
* This time is given in milliseconds after midnight local * This time is given in milliseconds after midnight local
* standard time. * standard time.
* @serial * @serial
*/ */
private int startTime; private int startTime;
...@@ -157,9 +157,9 @@ public class SimpleTimeZone extends TimeZone ...@@ -157,9 +157,9 @@ public class SimpleTimeZone extends TimeZone
/** /**
* The month in which daylight savings ends. This is one of the * The month in which daylight savings ends. This is one of the
* constants Calendar.JANUARY, ..., Calendar.DECEMBER. * constants Calendar.JANUARY, ..., Calendar.DECEMBER.
* @serial * @serial
*/ */
private int endMonth; private int endMonth;
/** /**
...@@ -167,7 +167,7 @@ public class SimpleTimeZone extends TimeZone ...@@ -167,7 +167,7 @@ public class SimpleTimeZone extends TimeZone
* It can take the same values as startMode. * It can take the same values as startMode.
* @serial * @serial
* @see #startMode * @see #startMode
*/ */
private int endMode; private int endMode;
/** /**
...@@ -176,19 +176,19 @@ public class SimpleTimeZone extends TimeZone ...@@ -176,19 +176,19 @@ public class SimpleTimeZone extends TimeZone
* @see #startMode; * @see #startMode;
*/ */
private int endDay; private int endDay;
/** /**
* This variable specifies the day of week the change takes place. If * This variable specifies the day of week the change takes place. If
* endMode == DOM_MODE, this is undefined. * endMode == DOM_MODE, this is undefined.
* @serial * @serial
* @see #startMode; * @see #startMode;
*/ */
private int endDayOfWeek; private int endDayOfWeek;
/** /**
* This variable specifies the time of change back to standard time. * This variable specifies the time of change back to standard time.
* This time is given in milliseconds after midnight local * This time is given in milliseconds after midnight local
* standard time. * standard time.
* @serial * @serial
*/ */
private int endTime; private int endTime;
...@@ -210,8 +210,11 @@ public class SimpleTimeZone extends TimeZone ...@@ -210,8 +210,11 @@ public class SimpleTimeZone extends TimeZone
* @serial * @serial
*/ */
private byte[] monthLength = monthArr; private byte[] monthLength = monthArr;
private static final byte[] monthArr = private static final byte[] monthArr =
{31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; {
31, 28, 31, 30, 31, 30, 31, 31, 30,
31, 30, 31
};
/** /**
* The version of the serialized data on the stream. * The version of the serialized data on the stream.
...@@ -232,10 +235,9 @@ public class SimpleTimeZone extends TimeZone ...@@ -232,10 +235,9 @@ public class SimpleTimeZone extends TimeZone
* When streaming out this class it is always written in the latest * When streaming out this class it is always written in the latest
* version. * version.
* @serial * @serial
* @since JDK1.1.4 * @since JDK1.1.4
*/ */
private int serialVersionOnStream = 2; private int serialVersionOnStream = 2;
private static final long serialVersionUID = -403250971215465050L; private static final long serialVersionUID = -403250971215465050L;
/** /**
...@@ -257,9 +259,9 @@ public class SimpleTimeZone extends TimeZone ...@@ -257,9 +259,9 @@ public class SimpleTimeZone extends TimeZone
/** /**
* Create a <code>SimpleTimeZone</code> with the given time offset * Create a <code>SimpleTimeZone</code> with the given time offset
* from GMT and without daylight savings. * from GMT and without daylight savings.
* @param rawOffset the time offset from GMT in milliseconds. * @param rawOffset the time offset from GMT in milliseconds.
* @param id The identifier of this time zone. * @param id The identifier of this time zone.
*/ */
public SimpleTimeZone(int rawOffset, String id) public SimpleTimeZone(int rawOffset, String id)
{ {
...@@ -273,7 +275,7 @@ public class SimpleTimeZone extends TimeZone ...@@ -273,7 +275,7 @@ public class SimpleTimeZone extends TimeZone
* Create a <code>SimpleTimeZone</code> with the given time offset * Create a <code>SimpleTimeZone</code> with the given time offset
* from GMT and with daylight savings. The start/end parameters * from GMT and with daylight savings. The start/end parameters
* can have different meaning (replace WEEKDAY with a real day of * can have different meaning (replace WEEKDAY with a real day of
* week). Only the first two meanings were supported by earlier * week). Only the first two meanings were supported by earlier
* versions of jdk. * versions of jdk.
* *
* <dl> * <dl>
...@@ -296,12 +298,12 @@ public class SimpleTimeZone extends TimeZone ...@@ -296,12 +298,12 @@ public class SimpleTimeZone extends TimeZone
* must make sure that this day lies in the same month. </dd> * must make sure that this day lies in the same month. </dd>
* </dl> * </dl>
* *
* If you give a non existing month, a day that is zero, or too big, * If you give a non existing month, a day that is zero, or too big,
* or a dayOfWeek that is too big, the result is undefined. * or a dayOfWeek that is too big, the result is undefined.
* *
* The start rule must have a different month than the end rule. * The start rule must have a different month than the end rule.
* This restriction shouldn't hurt for all possible time zones. * This restriction shouldn't hurt for all possible time zones.
* *
* @param rawOffset The time offset from GMT in milliseconds. * @param rawOffset The time offset from GMT in milliseconds.
* @param id The identifier of this time zone. * @param id The identifier of this time zone.
* @param startMonth The start month of daylight savings; use the * @param startMonth The start month of daylight savings; use the
...@@ -312,29 +314,26 @@ public class SimpleTimeZone extends TimeZone ...@@ -312,29 +314,26 @@ public class SimpleTimeZone extends TimeZone
* @param startTime A time in millis in standard time. * @param startTime A time in millis in standard time.
* @param endMonth The end month of daylight savings; use the * @param endMonth The end month of daylight savings; use the
* constants in Calendar. * constants in Calendar.
* @param endday A day in month or a day of week number, as * @param endday A day in month or a day of week number, as
* described above. * described above.
* @param endDayOfWeek The end rule day of week; see above. * @param endDayOfWeek The end rule day of week; see above.
* @param endTime A time in millis in standard time. * @param endTime A time in millis in standard time.
* @throws IllegalArgumentException if parameters are invalid or out of * @throws IllegalArgumentException if parameters are invalid or out of
* range. * range.
*/ */
public SimpleTimeZone(int rawOffset, String id, public SimpleTimeZone(int rawOffset, String id, int startMonth,
int startMonth, int startDayOfWeekInMonth, int startDayOfWeekInMonth, int startDayOfWeek,
int startDayOfWeek, int startTime, int startTime, int endMonth, int endDayOfWeekInMonth,
int endMonth, int endDayOfWeekInMonth, int endDayOfWeek, int endTime)
int endDayOfWeek, int endTime)
{ {
this.rawOffset = rawOffset; this.rawOffset = rawOffset;
setID(id); setID(id);
useDaylight = true; useDaylight = true;
setStartRule(startMonth, startDayOfWeekInMonth, setStartRule(startMonth, startDayOfWeekInMonth, startDayOfWeek, startTime);
startDayOfWeek, startTime);
setEndRule(endMonth, endDayOfWeekInMonth, endDayOfWeek, endTime); setEndRule(endMonth, endDayOfWeekInMonth, endDayOfWeek, endTime);
if (startMonth == endMonth) if (startMonth == endMonth)
throw new IllegalArgumentException throw new IllegalArgumentException("startMonth and endMonth must be different");
("startMonth and endMonth must be different");
this.startYear = 0; this.startYear = 0;
} }
...@@ -347,15 +346,13 @@ public class SimpleTimeZone extends TimeZone ...@@ -347,15 +346,13 @@ public class SimpleTimeZone extends TimeZone
* time in milliseconds. This must be positive. * time in milliseconds. This must be positive.
* @since 1.2 * @since 1.2
*/ */
public SimpleTimeZone(int rawOffset, String id, public SimpleTimeZone(int rawOffset, String id, int startMonth,
int startMonth, int startDayOfWeekInMonth, int startDayOfWeekInMonth, int startDayOfWeek,
int startDayOfWeek, int startTime, int startTime, int endMonth, int endDayOfWeekInMonth,
int endMonth, int endDayOfWeekInMonth, int endDayOfWeek, int endTime, int dstSavings)
int endDayOfWeek, int endTime, int dstSavings)
{ {
this(rawOffset, id, this(rawOffset, id, startMonth, startDayOfWeekInMonth, startDayOfWeek,
startMonth, startDayOfWeekInMonth, startDayOfWeek, startTime, startTime, endMonth, endDayOfWeekInMonth, endDayOfWeek, endTime);
endMonth, endDayOfWeekInMonth, endDayOfWeek, endTime);
this.dstSavings = dstSavings; this.dstSavings = dstSavings;
} }
...@@ -376,12 +373,11 @@ public class SimpleTimeZone extends TimeZone ...@@ -376,12 +373,11 @@ public class SimpleTimeZone extends TimeZone
* range. * range.
* @since 1.4 * @since 1.4
*/ */
public SimpleTimeZone(int rawOffset, String id, public SimpleTimeZone(int rawOffset, String id, int startMonth,
int startMonth, int startDayOfWeekInMonth, int startDayOfWeekInMonth, int startDayOfWeek,
int startDayOfWeek, int startTime, int startTimeMode, int startTime, int startTimeMode, int endMonth,
int endMonth, int endDayOfWeekInMonth, int endDayOfWeekInMonth, int endDayOfWeek,
int endDayOfWeek, int endTime, int endTimeMode, int endTime, int endTimeMode, int dstSavings)
int dstSavings)
{ {
this.rawOffset = rawOffset; this.rawOffset = rawOffset;
setID(id); setID(id);
...@@ -394,12 +390,10 @@ public class SimpleTimeZone extends TimeZone ...@@ -394,12 +390,10 @@ public class SimpleTimeZone extends TimeZone
this.startTimeMode = startTimeMode; this.startTimeMode = startTimeMode;
this.endTimeMode = endTimeMode; this.endTimeMode = endTimeMode;
setStartRule(startMonth, startDayOfWeekInMonth, setStartRule(startMonth, startDayOfWeekInMonth, startDayOfWeek, startTime);
startDayOfWeek, startTime);
setEndRule(endMonth, endDayOfWeekInMonth, endDayOfWeek, endTime); setEndRule(endMonth, endDayOfWeekInMonth, endDayOfWeek, endTime);
if (startMonth == endMonth) if (startMonth == endMonth)
throw new IllegalArgumentException throw new IllegalArgumentException("startMonth and endMonth must be different");
("startMonth and endMonth must be different");
this.startYear = 0; this.startYear = 0;
this.dstSavings = dstSavings; this.dstSavings = dstSavings;
...@@ -432,6 +426,7 @@ public class SimpleTimeZone extends TimeZone ...@@ -432,6 +426,7 @@ public class SimpleTimeZone extends TimeZone
{ {
if (month < 0 || month > 11) if (month < 0 || month > 11)
throw new IllegalArgumentException("month out of range"); throw new IllegalArgumentException("month out of range");
int daysInMonth = getDaysInMonth(month, 1); int daysInMonth = getDaysInMonth(month, 1);
if (dayOfWeek == 0) if (dayOfWeek == 0)
{ {
...@@ -460,7 +455,6 @@ public class SimpleTimeZone extends TimeZone ...@@ -460,7 +455,6 @@ public class SimpleTimeZone extends TimeZone
} }
} }
/** /**
* Sets the daylight savings start rule. You must also set the * Sets the daylight savings start rule. You must also set the
* end rule with <code>setEndRule</code> or the result of * end rule with <code>setEndRule</code> or the result of
...@@ -514,14 +508,16 @@ public class SimpleTimeZone extends TimeZone ...@@ -514,14 +508,16 @@ public class SimpleTimeZone extends TimeZone
* @since 1.2 * @since 1.2
* @see SimpleTimeZone * @see SimpleTimeZone
*/ */
public void setStartRule(int month, int day, int dayOfWeek, int time, boolean after) public void setStartRule(int month, int day, int dayOfWeek, int time,
boolean after)
{ {
// FIXME: XXX: Validate that checkRule and offset processing work with on // FIXME: XXX: Validate that checkRule and offset processing work with on
// or before mode. // or before mode.
this.startDay = after ? Math.abs(day) : -Math.abs(day); this.startDay = after ? Math.abs(day) : -Math.abs(day);
this.startDayOfWeek = after ? Math.abs(dayOfWeek) : -Math.abs(dayOfWeek); this.startDayOfWeek = after ? Math.abs(dayOfWeek) : -Math.abs(dayOfWeek);
this.startMode = (dayOfWeek != 0) ? (after ? DOW_GE_DOM_MODE : DOW_LE_DOM_MODE) this.startMode = (dayOfWeek != 0)
: checkRule(month, day, dayOfWeek); ? (after ? DOW_GE_DOM_MODE : DOW_LE_DOM_MODE)
: checkRule(month, day, dayOfWeek);
this.startDay = Math.abs(this.startDay); this.startDay = Math.abs(this.startDay);
this.startDayOfWeek = Math.abs(this.startDayOfWeek); this.startDayOfWeek = Math.abs(this.startDayOfWeek);
...@@ -591,7 +587,7 @@ public class SimpleTimeZone extends TimeZone ...@@ -591,7 +587,7 @@ public class SimpleTimeZone extends TimeZone
* *
* Note that this API isn't incredibly well specified. It appears that the * Note that this API isn't incredibly well specified. It appears that the
* after flag must override the parameters, since normally, the day and * after flag must override the parameters, since normally, the day and
* dayofweek can select this. I.e., if day &lt; 0 and dayOfWeek &lt; 0, on or * dayofweek can select this. I.e., if day < 0 and dayOfWeek < 0, on or
* before mode is chosen. But if after == true, this implementation * before mode is chosen. But if after == true, this implementation
* overrides the signs of the other arguments. And if dayOfWeek == 0, it * overrides the signs of the other arguments. And if dayOfWeek == 0, it
* falls back to the behavior in the other APIs. I guess this should be * falls back to the behavior in the other APIs. I guess this should be
...@@ -606,14 +602,16 @@ public class SimpleTimeZone extends TimeZone ...@@ -606,14 +602,16 @@ public class SimpleTimeZone extends TimeZone
* @since 1.2 * @since 1.2
* @see #setStartRule * @see #setStartRule
*/ */
public void setEndRule(int month, int day, int dayOfWeek, int time, boolean after) public void setEndRule(int month, int day, int dayOfWeek, int time,
boolean after)
{ {
// FIXME: XXX: Validate that checkRule and offset processing work with on // FIXME: XXX: Validate that checkRule and offset processing work with on
// or before mode. // or before mode.
this.endDay = after ? Math.abs(day) : -Math.abs(day); this.endDay = after ? Math.abs(day) : -Math.abs(day);
this.endDayOfWeek = after ? Math.abs(dayOfWeek) : -Math.abs(dayOfWeek); this.endDayOfWeek = after ? Math.abs(dayOfWeek) : -Math.abs(dayOfWeek);
this.endMode = (dayOfWeek != 0) ? (after ? DOW_GE_DOM_MODE : DOW_LE_DOM_MODE) this.endMode = (dayOfWeek != 0)
: checkRule(month, day, dayOfWeek); ? (after ? DOW_GE_DOM_MODE : DOW_LE_DOM_MODE)
: checkRule(month, day, dayOfWeek);
this.endDay = Math.abs(this.endDay); this.endDay = Math.abs(this.endDay);
this.endDayOfWeek = Math.abs(endDayOfWeek); this.endDayOfWeek = Math.abs(endDayOfWeek);
...@@ -648,7 +646,7 @@ public class SimpleTimeZone extends TimeZone ...@@ -648,7 +646,7 @@ public class SimpleTimeZone extends TimeZone
} }
/** /**
* Gets the time zone offset, for current date, modified in case of * Gets the time zone offset, for current date, modified in case of
* daylight savings. This is the offset to add to UTC to get the local * daylight savings. This is the offset to add to UTC to get the local
* time. * time.
* *
...@@ -674,8 +672,8 @@ public class SimpleTimeZone extends TimeZone ...@@ -674,8 +672,8 @@ public class SimpleTimeZone extends TimeZone
* @return the time zone offset in milliseconds. * @return the time zone offset in milliseconds.
* @throws IllegalArgumentException if arguments are incorrect. * @throws IllegalArgumentException if arguments are incorrect.
*/ */
public int getOffset(int era, int year, int month, public int getOffset(int era, int year, int month, int day, int dayOfWeek,
int day, int dayOfWeek, int millis) int millis)
{ {
int daysInMonth = getDaysInMonth(month, year); int daysInMonth = getDaysInMonth(month, year);
if (day < 1 || day > daysInMonth) if (day < 1 || day > daysInMonth)
...@@ -683,7 +681,7 @@ public class SimpleTimeZone extends TimeZone ...@@ -683,7 +681,7 @@ public class SimpleTimeZone extends TimeZone
if (dayOfWeek < Calendar.SUNDAY || dayOfWeek > Calendar.SATURDAY) if (dayOfWeek < Calendar.SUNDAY || dayOfWeek > Calendar.SATURDAY)
throw new IllegalArgumentException("dayOfWeek out of range"); throw new IllegalArgumentException("dayOfWeek out of range");
if (month < Calendar.JANUARY || month > Calendar.DECEMBER) if (month < Calendar.JANUARY || month > Calendar.DECEMBER)
throw new IllegalArgumentException("month out of range"); throw new IllegalArgumentException("month out of range:" + month);
// This method is called by Calendar, so we mustn't use that class. // This method is called by Calendar, so we mustn't use that class.
int daylightSavings = 0; int daylightSavings = 0;
...@@ -691,27 +689,22 @@ public class SimpleTimeZone extends TimeZone ...@@ -691,27 +689,22 @@ public class SimpleTimeZone extends TimeZone
{ {
// This does only work for Gregorian calendars :-( // This does only work for Gregorian calendars :-(
// This is mainly because setStartYear doesn't take an era. // This is mainly because setStartYear doesn't take an era.
boolean afterStart = ! isBefore(year, month, day, dayOfWeek, millis,
boolean afterStart = !isBefore(year, month, day, dayOfWeek, millis, startMode, startMonth, startDay,
startMode, startMonth, startDayOfWeek, startTime);
startDay, startDayOfWeek, startTime);
boolean beforeEnd = isBefore(year, month, day, dayOfWeek, boolean beforeEnd = isBefore(year, month, day, dayOfWeek,
millis + dstSavings, millis + dstSavings,
endMode, endMonth, endMode, endMonth, endDay, endDayOfWeek,
endDay, endDayOfWeek, endTime); endTime);
if (startMonth < endMonth) if (startMonth < endMonth)
{ // use daylight savings, if the date is after the start of
// use daylight savings, if the date is after the start of // savings, and before the end of savings.
// savings, and before the end of savings. daylightSavings = afterStart && beforeEnd ? dstSavings : 0;
daylightSavings = afterStart && beforeEnd ? dstSavings : 0;
}
else else
{ // use daylight savings, if the date is before the end of
// use daylight savings, if the date is before the end of // savings, or after the start of savings.
// savings, or after the start of savings. daylightSavings = beforeEnd || afterStart ? dstSavings : 0;
daylightSavings = beforeEnd || afterStart ? dstSavings : 0;
}
} }
return rawOffset + daylightSavings; return rawOffset + daylightSavings;
} }
...@@ -740,7 +733,7 @@ public class SimpleTimeZone extends TimeZone ...@@ -740,7 +733,7 @@ public class SimpleTimeZone extends TimeZone
* milliseconds with respect to standard time. Typically this * milliseconds with respect to standard time. Typically this
* is one hour, but for some time zones this may be half an our. * is one hour, but for some time zones this may be half an our.
* @return the daylight savings offset in milliseconds. * @return the daylight savings offset in milliseconds.
* *
* @since 1.2 * @since 1.2
*/ */
public int getDSTSavings() public int getDSTSavings()
...@@ -760,7 +753,7 @@ public class SimpleTimeZone extends TimeZone ...@@ -760,7 +753,7 @@ public class SimpleTimeZone extends TimeZone
{ {
if (dstSavings <= 0) if (dstSavings <= 0)
throw new IllegalArgumentException("illegal value for dstSavings"); throw new IllegalArgumentException("illegal value for dstSavings");
this.dstSavings = dstSavings; this.dstSavings = dstSavings;
} }
...@@ -774,23 +767,28 @@ public class SimpleTimeZone extends TimeZone ...@@ -774,23 +767,28 @@ public class SimpleTimeZone extends TimeZone
} }
/** /**
* Returns the number of days in the given month. It does always * Returns the number of days in the given month.
* use the Gregorian leap year rule. * Uses gregorian rules prior to 1582 (The default and earliest cutover)
* @param month The month, zero based; use one of the Calendar constants. * @param month The month, zero based; use one of the Calendar constants.
* @param year The year. * @param year The year.
*/ */
private int getDaysInMonth(int month, int year) private int getDaysInMonth(int month, int year)
{ {
// Most of this is copied from GregorianCalendar.getActualMaximum()
if (month == Calendar.FEBRUARY) if (month == Calendar.FEBRUARY)
{ {
return ((year & 3) == 0 && (year % 100 != 0 || year % 400 == 0)) if ((year & 3) != 0)
? 29 : 28; return 28;
// Assume default Gregorian cutover,
// all years prior to this must be Julian
if (year < 1582)
return 29;
// Gregorian rules
return ((year % 100) != 0 || (year % 400) == 0) ? 29 : 28;
} }
else if (month < Calendar.AUGUST)
return 31 - (month & 1);
else else
return 30 + (month & 1); return monthArr[month];
} }
/** /**
...@@ -804,23 +802,19 @@ public class SimpleTimeZone extends TimeZone ...@@ -804,23 +802,19 @@ public class SimpleTimeZone extends TimeZone
* @param mode the change mode; same semantic as startMode. * @param mode the change mode; same semantic as startMode.
* @param month the change month; same semantic as startMonth. * @param month the change month; same semantic as startMonth.
* @param day the change day; same semantic as startDay. * @param day the change day; same semantic as startDay.
* @param dayOfWeek the change day of week; * @param dayOfWeek the change day of week;
* @param millis the change time in millis since midnight standard time. * @param millis the change time in millis since midnight standard time.
* same semantic as startDayOfWeek. * same semantic as startDayOfWeek.
* @return true, if cal is before the change, false if cal is on * @return true, if cal is before the change, false if cal is on
* or after the change. * or after the change.
*/ */
private boolean isBefore(int calYear, private boolean isBefore(int calYear, int calMonth, int calDayOfMonth,
int calMonth, int calDayOfMonth, int calDayOfWeek, int calDayOfWeek, int calMillis, int mode,
int calMillis, int mode, int month, int month, int day, int dayOfWeek, int millis)
int day, int dayOfWeek, int millis)
{ {
// This method is called by Calendar, so we mustn't use that class. // This method is called by Calendar, so we mustn't use that class.
// We have to do all calculations by hand. // We have to do all calculations by hand.
// check the months: // check the months:
// XXX - this is not correct: // XXX - this is not correct:
// for the DOW_GE_DOM and DOW_LE_DOM modes the change date may // for the DOW_GE_DOM and DOW_LE_DOM modes the change date may
// be in a different month. // be in a different month.
...@@ -835,7 +829,7 @@ public class SimpleTimeZone extends TimeZone ...@@ -835,7 +829,7 @@ public class SimpleTimeZone extends TimeZone
return calDayOfMonth < day; return calDayOfMonth < day;
break; break;
case DOW_IN_MONTH_MODE: case DOW_IN_MONTH_MODE:
{ {
// This computes the day of month of the day of type // This computes the day of month of the day of type
// "dayOfWeek" that lies in the same (sunday based) week as cal. // "dayOfWeek" that lies in the same (sunday based) week as cal.
calDayOfMonth += (dayOfWeek - calDayOfWeek); calDayOfMonth += (dayOfWeek - calDayOfWeek);
...@@ -844,7 +838,6 @@ public class SimpleTimeZone extends TimeZone ...@@ -844,7 +838,6 @@ public class SimpleTimeZone extends TimeZone
// after dividing by 7). If we count from the end of the // after dividing by 7). If we count from the end of the
// month, we get want a -7 based number counting the days from // month, we get want a -7 based number counting the days from
// the end: // the end:
if (day < 0) if (day < 0)
calDayOfMonth -= getDaysInMonth(calMonth, calYear) + 7; calDayOfMonth -= getDaysInMonth(calMonth, calYear) + 7;
else else
...@@ -857,9 +850,9 @@ public class SimpleTimeZone extends TimeZone ...@@ -857,9 +850,9 @@ public class SimpleTimeZone extends TimeZone
// 20 21 22 23 24 25 26 -23-22-21-20-19-18-17 // 20 21 22 23 24 25 26 -23-22-21-20-19-18-17
// 27 28 29 30 31 32 33 -16-15-14-13-12-11-10 // 27 28 29 30 31 32 33 -16-15-14-13-12-11-10
// 34 35 36 -9 -8 -7 // 34 35 36 -9 -8 -7
// Now we calculate the day of week in month: // Now we calculate the day of week in month:
int week = calDayOfMonth / 7; int week = calDayOfMonth / 7;
// day > 0 day < 0 // day > 0 day < 0
// S M T W T F S S M T W T F S // S M T W T F S S M T W T F S
// 1 1 1 1 1 1 -5 -5 -4 -4 -4 -4 // 1 1 1 1 1 1 -5 -5 -4 -4 -4 -4
...@@ -867,7 +860,6 @@ public class SimpleTimeZone extends TimeZone ...@@ -867,7 +860,6 @@ public class SimpleTimeZone extends TimeZone
// 2 3 3 3 3 3 3 -3 -3 -3 -2 -2 -2 -2 // 2 3 3 3 3 3 3 -3 -3 -3 -2 -2 -2 -2
// 3 4 4 4 4 4 4 -2 -2 -2 -1 -1 -1 -1 // 3 4 4 4 4 4 4 -2 -2 -2 -1 -1 -1 -1
// 4 5 5 -1 -1 -1 // 4 5 5 -1 -1 -1
if (week != day) if (week != day)
return week < day; return week < day;
...@@ -876,26 +868,25 @@ public class SimpleTimeZone extends TimeZone ...@@ -876,26 +868,25 @@ public class SimpleTimeZone extends TimeZone
// daylight savings starts/ends on the given day. // daylight savings starts/ends on the given day.
break; break;
} }
case DOW_LE_DOM_MODE: case DOW_LE_DOM_MODE:
// The greatest sunday before or equal December, 12 // The greatest sunday before or equal December, 12
// is the same as smallest sunday after or equal December, 6. // is the same as smallest sunday after or equal December, 6.
day = Math.abs(day) - 6; day = Math.abs(day) - 6;
case DOW_GE_DOM_MODE: case DOW_GE_DOM_MODE:
// Calculate the day of month of the day of type // Calculate the day of month of the day of type
// "dayOfWeek" that lies before (or on) the given date. // "dayOfWeek" that lies before (or on) the given date.
calDayOfMonth -= (calDayOfWeek < dayOfWeek ? 7 : 0) calDayOfMonth -= (calDayOfWeek < dayOfWeek ? 7 : 0) + calDayOfWeek
+ calDayOfWeek - dayOfWeek; - dayOfWeek;
if (calDayOfMonth < day) if (calDayOfMonth < day)
return true; return true;
if (calDayOfWeek != dayOfWeek || calDayOfMonth >= day + 7) if (calDayOfWeek != dayOfWeek || calDayOfMonth >= day + 7)
return false; return false;
// now we have the same day // now we have the same day
break; break;
} }
// the millis decides: // the millis decides:
return (calMillis < millis); return (calMillis < millis);
} }
...@@ -914,40 +905,35 @@ public class SimpleTimeZone extends TimeZone ...@@ -914,40 +905,35 @@ public class SimpleTimeZone extends TimeZone
/** /**
* Generates the hashCode for the SimpleDateFormat object. It is * Generates the hashCode for the SimpleDateFormat object. It is
* the rawOffset, possibly, if useDaylightSavings is true, xored * the rawOffset, possibly, if useDaylightSavings is true, xored
* with startYear, startMonth, startDayOfWeekInMonth, ..., endTime. * with startYear, startMonth, startDayOfWeekInMonth, ..., endTime.
*/ */
public synchronized int hashCode() public synchronized int hashCode()
{ {
return rawOffset ^ return rawOffset
(useDaylight ? ^ (useDaylight
startMonth ^ startDay ^ startDayOfWeek ^ startTime ? startMonth ^ startDay ^ startDayOfWeek ^ startTime ^ endMonth
^ endMonth ^ endDay ^ endDayOfWeek ^ endTime : 0); ^ endDay ^ endDayOfWeek ^ endTime : 0);
} }
public synchronized boolean equals(Object o) public synchronized boolean equals(Object o)
{ {
if (this == o) if (this == o)
return true; return true;
if (!(o instanceof SimpleTimeZone)) if (! (o instanceof SimpleTimeZone))
return false; return false;
SimpleTimeZone zone = (SimpleTimeZone) o; SimpleTimeZone zone = (SimpleTimeZone) o;
if (zone.hashCode() != hashCode() if (zone.hashCode() != hashCode() || ! getID().equals(zone.getID())
|| !getID().equals(zone.getID()) || rawOffset != zone.rawOffset || useDaylight != zone.useDaylight)
|| rawOffset != zone.rawOffset || useDaylight != zone.useDaylight)
return false; return false;
if (!useDaylight) if (! useDaylight)
return true; return true;
return (startYear == zone.startYear return (startYear == zone.startYear && startMonth == zone.startMonth
&& startMonth == zone.startMonth && startDay == zone.startDay
&& startDay == zone.startDay && startDayOfWeek == zone.startDayOfWeek
&& startDayOfWeek == zone.startDayOfWeek && startTime == zone.startTime
&& startTime == zone.startTime && startTimeMode == zone.startTimeMode && endMonth == zone.endMonth
&& startTimeMode == zone.startTimeMode && endDay == zone.endDay && endDayOfWeek == zone.endDayOfWeek
&& endMonth == zone.endMonth && endTime == zone.endTime && endTimeMode == zone.endTimeMode);
&& endDay == zone.endDay
&& endDayOfWeek == zone.endDayOfWeek
&& endTime == zone.endTime
&& endTimeMode == zone.endTimeMode);
} }
/** /**
...@@ -962,25 +948,21 @@ public class SimpleTimeZone extends TimeZone ...@@ -962,25 +948,21 @@ public class SimpleTimeZone extends TimeZone
{ {
if (this == other) if (this == other)
return true; return true;
if (!(other instanceof SimpleTimeZone)) if (! (other instanceof SimpleTimeZone))
return false; return false;
SimpleTimeZone zone = (SimpleTimeZone) other; SimpleTimeZone zone = (SimpleTimeZone) other;
if (zone.hashCode() != hashCode() if (zone.hashCode() != hashCode() || rawOffset != zone.rawOffset
|| rawOffset != zone.rawOffset || useDaylight != zone.useDaylight) || useDaylight != zone.useDaylight)
return false; return false;
if (!useDaylight) if (! useDaylight)
return true; return true;
return (startYear == zone.startYear return (startYear == zone.startYear && startMonth == zone.startMonth
&& startMonth == zone.startMonth && startDay == zone.startDay
&& startDay == zone.startDay && startDayOfWeek == zone.startDayOfWeek
&& startDayOfWeek == zone.startDayOfWeek && startTime == zone.startTime
&& startTime == zone.startTime && startTimeMode == zone.startTimeMode && endMonth == zone.endMonth
&& startTimeMode == zone.startTimeMode && endDay == zone.endDay && endDayOfWeek == zone.endDayOfWeek
&& endMonth == zone.endMonth && endTime == zone.endTime && endTimeMode == zone.endTimeMode);
&& endDay == zone.endDay
&& endDayOfWeek == zone.endDayOfWeek
&& endTime == zone.endTime
&& endTimeMode == zone.endTimeMode);
} }
/** /**
...@@ -991,26 +973,17 @@ public class SimpleTimeZone extends TimeZone ...@@ -991,26 +973,17 @@ public class SimpleTimeZone extends TimeZone
{ {
// the test for useDaylight is an incompatibility to jdk1.2, but // the test for useDaylight is an incompatibility to jdk1.2, but
// I think this shouldn't hurt. // I think this shouldn't hurt.
return getClass().getName() + "[" return getClass().getName() + "[" + "id=" + getID() + ",offset="
+ "id=" + getID() + rawOffset + ",dstSavings=" + dstSavings + ",useDaylight="
+ ",offset=" + rawOffset + useDaylight
+ ",dstSavings=" + dstSavings + (useDaylight
+ ",useDaylight=" + useDaylight ? ",startYear=" + startYear + ",startMode=" + startMode
+ (useDaylight ? + ",startMonth=" + startMonth + ",startDay=" + startDay
",startYear=" + startYear + ",startDayOfWeek=" + startDayOfWeek + ",startTime="
+ ",startMode=" + startMode + startTime + ",startTimeMode=" + startTimeMode + ",endMode="
+ ",startMonth=" + startMonth + endMode + ",endMonth=" + endMonth + ",endDay=" + endDay
+ ",startDay=" + startDay + ",endDayOfWeek=" + endDayOfWeek + ",endTime=" + endTime
+ ",startDayOfWeek=" + startDayOfWeek + ",endTimeMode=" + endTimeMode : "") + "]";
+ ",startTime=" + startTime
+ ",startTimeMode=" + startTimeMode
+ ",endMode=" + endMode
+ ",endMonth=" + endMonth
+ ",endDay=" + endDay
+ ",endDayOfWeek=" + endDayOfWeek
+ ",endTime=" + endTime
+ ",endTimeMode=" + endTimeMode
: "") + "]";
} }
/** /**
...@@ -1029,7 +1002,8 @@ public class SimpleTimeZone extends TimeZone ...@@ -1029,7 +1002,8 @@ public class SimpleTimeZone extends TimeZone
startMode = DOW_IN_MONTH_MODE; startMode = DOW_IN_MONTH_MODE;
startTimeMode = WALL_TIME; startTimeMode = WALL_TIME;
endTimeMode = WALL_TIME; endTimeMode = WALL_TIME;
serialVersionOnStream = 2; } serialVersionOnStream = 2;
}
else else
{ {
int length = input.readInt(); int length = input.readInt();
...@@ -1054,29 +1028,31 @@ public class SimpleTimeZone extends TimeZone ...@@ -1054,29 +1028,31 @@ public class SimpleTimeZone extends TimeZone
* <code>start/endDay(OfWeek)</code>-Fields are written in the * <code>start/endDay(OfWeek)</code>-Fields are written in the
* DOW_IN_MONTH_MODE rule, since this was the only supported rule * DOW_IN_MONTH_MODE rule, since this was the only supported rule
* in 1.1. * in 1.1.
* *
* In the optional section, we write first the length of an byte * In the optional section, we write first the length of an byte
* array as int and afterwards the byte array itself. The byte * array as int and afterwards the byte array itself. The byte
* array contains in this release four elements, namely the real * array contains in this release four elements, namely the real
* startDay, startDayOfWeek endDay, endDayOfWeek in that Order. * startDay, startDayOfWeek endDay, endDayOfWeek in that Order.
* These fields are needed, because for compatibility reasons only * These fields are needed, because for compatibility reasons only
* approximative values are written to the required section, as * approximative values are written to the required section, as
* described above. * described above.
*/ */
private void writeObject(java.io.ObjectOutputStream output) private void writeObject(java.io.ObjectOutputStream output)
throws java.io.IOException throws java.io.IOException
{ {
byte[] byteArray = new byte[] byte[] byteArray = new byte[]
{ {
(byte) startDay, (byte) startDayOfWeek, (byte) startDay, (byte) startDayOfWeek, (byte) endDay,
(byte) endDay, (byte) endDayOfWeek}; (byte) endDayOfWeek
};
/* calculate the approximation for JDK 1.1 */ /* calculate the approximation for JDK 1.1 */
switch (startMode) switch (startMode)
{ {
case DOM_MODE: case DOM_MODE:
startDayOfWeek = Calendar.SUNDAY; // random day of week startDayOfWeek = Calendar.SUNDAY; // random day of week
// fall through
// fall through
case DOW_GE_DOM_MODE: case DOW_GE_DOM_MODE:
case DOW_LE_DOM_MODE: case DOW_LE_DOM_MODE:
startDay = (startDay + 6) / 7; startDay = (startDay + 6) / 7;
...@@ -1085,7 +1061,8 @@ public class SimpleTimeZone extends TimeZone ...@@ -1085,7 +1061,8 @@ public class SimpleTimeZone extends TimeZone
{ {
case DOM_MODE: case DOM_MODE:
endDayOfWeek = Calendar.SUNDAY; endDayOfWeek = Calendar.SUNDAY;
// fall through
// fall through
case DOW_GE_DOM_MODE: case DOW_GE_DOM_MODE:
case DOW_LE_DOM_MODE: case DOW_LE_DOM_MODE:
endDay = (endDay + 6) / 7; endDay = (endDay + 6) / 7;
......
...@@ -447,6 +447,7 @@ public abstract class TimeZone implements java.io.Serializable, Cloneable ...@@ -447,6 +447,7 @@ public abstract class TimeZone implements java.io.Serializable, Cloneable
Calendar.MARCH, -1, Calendar.SUNDAY, 2000 * 3600, Calendar.MARCH, -1, Calendar.SUNDAY, 2000 * 3600,
Calendar.OCTOBER, -1, Calendar.SUNDAY, 2000 * 3600); Calendar.OCTOBER, -1, Calendar.SUNDAY, 2000 * 3600);
timezones0.put("CET", tz); timezones0.put("CET", tz);
timezones0.put("CEST", tz);
timezones0.put("ECT", tz); timezones0.put("ECT", tz);
timezones0.put("MET", tz); timezones0.put("MET", tz);
timezones0.put("Africa/Ceuta", tz); timezones0.put("Africa/Ceuta", tz);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment