diff options
author | mark <mark@138bc75d-0d04-0410-961f-82ee72b054a4> | 2005-02-22 00:58:36 +0000 |
---|---|---|
committer | mark <mark@138bc75d-0d04-0410-961f-82ee72b054a4> | 2005-02-22 00:58:36 +0000 |
commit | 6d3e2b562029c6aaa5a5c2c97f5a038449c8c200 (patch) | |
tree | 1ef9e4a70e8658f5a1afafb0f811039fb753f321 | |
parent | 7ba01ff216bc1b30c84aad56e06e0b58b24a8699 (diff) | |
download | gcc-6d3e2b562029c6aaa5a5c2c97f5a038449c8c200.tar.gz |
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.
git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@95368 138bc75d-0d04-0410-961f-82ee72b054a4
-rw-r--r-- | libjava/ChangeLog | 216 | ||||
-rw-r--r-- | libjava/gnu/java/locale/LocaleInformation_en.java | 2 | ||||
-rw-r--r-- | libjava/java/text/DateFormat.java | 218 | ||||
-rw-r--r-- | libjava/java/text/SimpleDateFormat.java | 890 | ||||
-rw-r--r-- | libjava/java/util/Calendar.java | 271 | ||||
-rw-r--r-- | libjava/java/util/GregorianCalendar.java | 811 | ||||
-rw-r--r-- | libjava/java/util/SimpleTimeZone.java | 333 | ||||
-rw-r--r-- | libjava/java/util/TimeZone.java | 1 |
8 files changed, 1839 insertions, 903 deletions
diff --git a/libjava/ChangeLog b/libjava/ChangeLog index f68958113f8..99183fff968 100644 --- a/libjava/ChangeLog +++ b/libjava/ChangeLog @@ -1,3 +1,219 @@ +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> * gnu/java/beans/IntrospectionIncubator.java diff --git a/libjava/gnu/java/locale/LocaleInformation_en.java b/libjava/gnu/java/locale/LocaleInformation_en.java index aa35091a526..df258095c1b 100644 --- a/libjava/gnu/java/locale/LocaleInformation_en.java +++ b/libjava/gnu/java/locale/LocaleInformation_en.java @@ -159,7 +159,7 @@ public class LocaleInformation_en extends ListResourceBundle { "shortWeekdays", shortWeekdays }, { "ampms", ampms }, { "eras", eras }, - { "localPatternChars", "GyMdkHmsSEDFwWahKz" }, + { "localPatternChars", "GyMdkHmsSEDFwWahKzYeugAZ" }, { "zoneStrings", zoneStrings }, { "shortDateFormat", "M/d/yy" }, // Java's Y2K bug. diff --git a/libjava/java/text/DateFormat.java b/libjava/java/text/DateFormat.java index da07fdde1ae..f19817ee2da 100644 --- a/libjava/java/text/DateFormat.java +++ b/libjava/java/text/DateFormat.java @@ -70,29 +70,221 @@ public abstract class DateFormat extends Format implements Cloneable /* These constants need to have these exact values. They * correspond to index positions within the localPatternChars - * string for a given locale. For example, the US locale uses - * the string "GyMdkHmsSEDFwWahKz", where 'G' is the character - * for era, 'y' for year, and so on down to 'z' for time zone. + * string for a given locale. Each locale may specify its + * own character for a particular field, but the position + * 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; + /** + * 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; + /** + * 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; + /** + * 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; + /** + * 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; + /** + * 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; + /** + * 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; + /** + * 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; + /** + * 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; + /** + * 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; + /** + * 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; + /** + * 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; + /** + * 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; + /** + * 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; + /** + * 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; + /** + * 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; + /** + * 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; + /** + * 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; - + /** + * 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 { @@ -136,14 +328,28 @@ public abstract class DateFormat extends Format implements Cloneable = new Field("hour0", Calendar.HOUR); public static final DateFormat.Field TIME_ZONE = 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 = { ERA, YEAR, MONTH, DAY_OF_MONTH, HOUR_OF_DAY1, HOUR_OF_DAY0, MINUTE, SECOND, MILLISECOND, DAY_OF_WEEK, DAY_OF_YEAR, DAY_OF_WEEK_IN_MONTH, 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 diff --git a/libjava/java/text/SimpleDateFormat.java b/libjava/java/text/SimpleDateFormat.java index 35083eb887e..d8e15f2aeca 100644 --- a/libjava/java/text/SimpleDateFormat.java +++ b/libjava/java/text/SimpleDateFormat.java @@ -1,6 +1,6 @@ /* SimpleDateFormat.java -- A class for parsing/formating simple date constructs - Copyright (C) 1998, 1999, 2000, 2001, 2003, 2004 + Copyright (C) 1998, 1999, 2000, 2001, 2003, 2004, 2005 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -45,6 +45,7 @@ import gnu.java.text.FormatBuffer; import gnu.java.text.FormatCharacterIterator; import gnu.java.text.StringFormatBuffer; +import java.io.InvalidObjectException; import java.io.IOException; import java.io.ObjectInputStream; import java.util.ArrayList; @@ -55,6 +56,8 @@ import java.util.Iterator; import java.util.Locale; import java.util.SimpleTimeZone; import java.util.TimeZone; +import java.util.regex.Matcher; +import java.util.regex.Pattern; /** * SimpleDateFormat provides convenient methods for parsing and formatting @@ -62,34 +65,189 @@ import java.util.TimeZone; */ 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; 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; + + /** + * 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 + + /** + * 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; + + /** + * The year at which interpretation of two + * digit years starts. + * + * @see get2DigitYearStart() + * @see set2DigitYearStart(java.util.Date) + * @serial Ignored. + */ 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; + + /** + * 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 + + /** + * For compatability. + */ private static final long serialVersionUID = 4774881970558875024L; - // This string is specified in the JCL. We set it here rather than - // do a DateFormatSymbols(Locale.US).getLocalPatternChars() since - // someone could theoretically change those values (though unlikely). - private static final String standardChars = "GyMdkHmsSEDFwWahKzZ"; + // This string is specified in the root of the CLDR. We set it here + // rather than doing a DateFormatSymbols(Locale.US).getLocalPatternChars() + // since someone could theoretically change those values (though unlikely). + 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) throws IOException, ClassNotFoundException { @@ -105,9 +263,25 @@ public class SimpleDateFormat extends DateFormat // Set up items normally taken care of by the constructor. 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) { // Any alphabetical characters are treated as pattern characters @@ -116,24 +290,25 @@ public class SimpleDateFormat extends DateFormat char thisChar; int pos; int field; - FieldSizePair current = null; + CompiledField current = null; for (int i=0; i<pattern.length(); i++) { thisChar = pattern.charAt(i); - field = formatData.getLocalPatternChars().indexOf(thisChar); + field = standardChars.indexOf(thisChar); if (field == -1) { current = null; if ((thisChar >= 'A' && thisChar <= 'Z') || (thisChar >= 'a' && thisChar <= 'z')) { - // Not a valid letter - tokens.add(new FieldSizePair(-1,0)); + // Not a valid letter + throw new IllegalArgumentException("Invalid letter " + thisChar + + "encountered at character " + i + + "."); } else if (thisChar == '\'') { // Quoted text section; skip to next single quote pos = pattern.indexOf('\'',i+1); if (pos == -1) { - // This ought to be an exception, but spec does not - // let us throw one. - tokens.add(new FieldSizePair(-1,0)); + throw new IllegalArgumentException("Quotes starting at character " + + i + " not closed."); } if ((pos+1 < pattern.length()) && (pattern.charAt(pos+1) == '\'')) { tokens.add(pattern.substring(i+1,pos+1)); @@ -150,20 +325,38 @@ public class SimpleDateFormat extends DateFormat if ((current != null) && (field == current.field)) { current.size++; } else { - current = new FieldSizePair(field,1); + current = new CompiledField(field,1,thisChar); tokens.add(current); } } } } + /** + * Returns a string representation of this + * class. + * + * @return a string representation of the <code>SimpleDateFormat</code> + * instance. + */ public String toString() { - StringBuffer output = new StringBuffer(); - Iterator i = tokens.iterator(); - while (i.hasNext()) { - output.append(i.next().toString()); - } + StringBuffer output = new StringBuffer(getClass().getName()); + output.append("[tokens="); + output.append(tokens); + 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(); } @@ -194,8 +387,12 @@ public class SimpleDateFormat extends DateFormat } /** - * Creates a date formatter using the specified pattern, with the default - * DateFormatSymbols for the default locale. + * Creates a date formatter using the specified non-localized pattern, + * 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) { @@ -203,8 +400,13 @@ public class SimpleDateFormat extends DateFormat } /** - * Creates a date formatter using the specified pattern, with the default - * DateFormatSymbols for the given locale. + * Creates a date formatter using the specified non-localized pattern, + * 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) { @@ -222,8 +424,14 @@ public class SimpleDateFormat extends DateFormat } /** - * Creates a date formatter using the specified pattern. The - * specified DateFormatSymbols will be used when formatting. + * Creates a date formatter using the specified non-localized + * 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) { @@ -231,6 +439,8 @@ public class SimpleDateFormat extends DateFormat calendar = new GregorianCalendar(); computeCenturyStart (); tokens = new ArrayList(); + if (formatData == null) + throw new NullPointerException("formatData"); this.formatData = formatData; compileFormat(pattern); this.pattern = pattern; @@ -240,9 +450,6 @@ public class SimpleDateFormat extends DateFormat 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 * by this object. This string is unlocalized. @@ -263,7 +470,7 @@ public class SimpleDateFormat extends DateFormat public String toLocalizedPattern() { String localChars = formatData.getLocalPatternChars(); - return applyLocalizedPattern (pattern, standardChars, localChars); + return translateLocalizedPattern(pattern, standardChars, localChars); } /** @@ -271,6 +478,8 @@ public class SimpleDateFormat extends DateFormat * object. This string is not localized. * * @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) { @@ -284,16 +493,34 @@ public class SimpleDateFormat extends DateFormat * object. This string is localized. * * @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) { String localChars = formatData.getLocalPatternChars(); - pattern = applyLocalizedPattern (pattern, localChars, standardChars); + pattern = translateLocalizedPattern(pattern, localChars, standardChars); 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(); StringBuffer buf = new StringBuffer(len); @@ -341,14 +568,14 @@ public class SimpleDateFormat extends DateFormat } /** - * This method returns the format symbol information used for parsing - * and formatting dates. + * This method returns a copy of the format symbol information used + * for parsing and formatting dates. * - * @return The date format symbols. + * @return a copy of the date format symbols. */ public DateFormatSymbols getDateFormatSymbols() { - return formatData; + return (DateFormatSymbols) formatData.clone(); } /** @@ -356,9 +583,15 @@ public class SimpleDateFormat extends DateFormat * and formatting dates. * * @param formatData The date format symbols. + * @throws NullPointerException if <code>formatData</code> is null. */ public void setDateFormatSymbols(DateFormatSymbols formatData) { + if (formatData == null) + { + throw new + NullPointerException("The supplied format data was null."); + } this.formatData = formatData; } @@ -431,12 +664,12 @@ public class SimpleDateFormat extends DateFormat while (iter.hasNext()) { Object o = iter.next(); - if (o instanceof FieldSizePair) + if (o instanceof CompiledField) { - FieldSizePair p = (FieldSizePair) o; + CompiledField cf = (CompiledField) o; int beginIndex = buffer.length(); - switch (p.field) + switch (cf.getField()) { case ERA_FIELD: buffer.append (formatData.eras[calendar.get (Calendar.ERA)], DateFormat.Field.ERA); @@ -445,75 +678,75 @@ public class SimpleDateFormat extends DateFormat // If we have two digits, then we truncate. Otherwise, we // use the size of the pattern, and zero pad. buffer.setDefaultAttribute (DateFormat.Field.YEAR); - if (p.size == 2) + if (cf.getSize() == 2) { temp = String.valueOf (calendar.get (Calendar.YEAR)); buffer.append (temp.substring (temp.length() - 2)); } else - withLeadingZeros (calendar.get (Calendar.YEAR), p.size, buffer); + withLeadingZeros (calendar.get (Calendar.YEAR), cf.getSize(), buffer); break; case MONTH_FIELD: buffer.setDefaultAttribute (DateFormat.Field.MONTH); - if (p.size < 3) - withLeadingZeros (calendar.get (Calendar.MONTH) + 1, p.size, buffer); - else if (p.size < 4) + if (cf.getSize() < 3) + withLeadingZeros (calendar.get (Calendar.MONTH) + 1, cf.getSize(), buffer); + else if (cf.getSize() < 4) buffer.append (formatData.shortMonths[calendar.get (Calendar.MONTH)]); else buffer.append (formatData.months[calendar.get (Calendar.MONTH)]); break; case DATE_FIELD: buffer.setDefaultAttribute (DateFormat.Field.DAY_OF_MONTH); - withLeadingZeros (calendar.get (Calendar.DATE), p.size, buffer); + withLeadingZeros (calendar.get (Calendar.DATE), cf.getSize(), buffer); break; case HOUR_OF_DAY1_FIELD: // 1-24 buffer.setDefaultAttribute(DateFormat.Field.HOUR_OF_DAY1); withLeadingZeros ( ((calendar.get (Calendar.HOUR_OF_DAY) + 23) % 24) + 1, - p.size, buffer); + cf.getSize(), buffer); break; case HOUR_OF_DAY0_FIELD: // 0-23 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; case MINUTE_FIELD: buffer.setDefaultAttribute (DateFormat.Field.MINUTE); withLeadingZeros (calendar.get (Calendar.MINUTE), - p.size, buffer); + cf.getSize(), buffer); break; case SECOND_FIELD: buffer.setDefaultAttribute (DateFormat.Field.SECOND); withLeadingZeros(calendar.get (Calendar.SECOND), - p.size, buffer); + cf.getSize(), buffer); break; case MILLISECOND_FIELD: buffer.setDefaultAttribute (DateFormat.Field.MILLISECOND); - withLeadingZeros (calendar.get (Calendar.MILLISECOND), p.size, buffer); + withLeadingZeros (calendar.get (Calendar.MILLISECOND), cf.getSize(), buffer); break; case DAY_OF_WEEK_FIELD: 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)]); else buffer.append (formatData.weekdays[calendar.get (Calendar.DAY_OF_WEEK)]); break; case DAY_OF_YEAR_FIELD: 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; case DAY_OF_WEEK_IN_MONTH_FIELD: buffer.setDefaultAttribute (DateFormat.Field.DAY_OF_WEEK_IN_MONTH); withLeadingZeros (calendar.get (Calendar.DAY_OF_WEEK_IN_MONTH), - p.size, buffer); + cf.getSize(), buffer); break; case WEEK_OF_YEAR_FIELD: buffer.setDefaultAttribute (DateFormat.Field.WEEK_OF_YEAR); withLeadingZeros (calendar.get (Calendar.WEEK_OF_YEAR), - p.size, buffer); + cf.getSize(), buffer); break; case WEEK_OF_MONTH_FIELD: buffer.setDefaultAttribute (DateFormat.Field.WEEK_OF_MONTH); withLeadingZeros (calendar.get (Calendar.WEEK_OF_MONTH), - p.size, buffer); + cf.getSize(), buffer); break; case AM_PM_FIELD: buffer.setDefaultAttribute (DateFormat.Field.AM_PM); @@ -521,25 +754,39 @@ public class SimpleDateFormat extends DateFormat break; case HOUR1_FIELD: // 1-12 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; case HOUR0_FIELD: // 0-11 buffer.setDefaultAttribute (DateFormat.Field.HOUR0); - withLeadingZeros (calendar.get (Calendar.HOUR), p.size, buffer); + withLeadingZeros (calendar.get (Calendar.HOUR), cf.getSize(), buffer); break; case TIMEZONE_FIELD: buffer.setDefaultAttribute (DateFormat.Field.TIME_ZONE); TimeZone zone = calendar.getTimeZone(); boolean isDST = calendar.get (Calendar.DST_OFFSET) != 0; // 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); 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: - throw new IllegalArgumentException ("Illegal pattern character " + p.field); + throw new IllegalArgumentException ("Illegal pattern character " + + cf.getCharacter()); } if (pos != null && (buffer.getDefaultAttribute() == pos.getFieldAttribute() - || p.field == pos.getField())) + || cf.getField() == pos.getField())) { pos.setBeginIndex(beginIndex); pos.setEndIndex(buffer.length()); @@ -614,232 +861,265 @@ public class SimpleDateFormat extends DateFormat boolean saw_timezone = false; int quote_start = -1; boolean is2DigitYear = false; - for (; fmt_index < fmt_max; ++fmt_index) + try { - char ch = pattern.charAt(fmt_index); - if (ch == '\'') + for (; fmt_index < fmt_max; ++fmt_index) { - int index = pos.getIndex(); - if (fmt_index < fmt_max - 1 - && pattern.charAt(fmt_index + 1) == '\'') + char ch = pattern.charAt(fmt_index); + if (ch == '\'') + { + 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)) return null; - ++fmt_index; + continue; } - 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)) - 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 + + // We've arrived at a potential pattern character in the + // pattern. + int fmt_count = 1; + while (++fmt_index < fmt_max && pattern.charAt(fmt_index) == ch) { - is_numeric = false; - match = (fmt_count <= 3 - ? formatData.getShortMonths() - : formatData.getMonths()); + ++fmt_count; } - 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; - 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++) + + // 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; + int offset = 0; + boolean maybe2DigitYear = false; + Integer simpleOffset; + String[] set1 = null; + String[] set2 = null; + switch (ch) { - String[] strings = zoneStrings[j]; - int k; - for (k = 1; k < strings.length; ++k) + 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; + 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)) - break; + is_numeric = false; + 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; saw_timezone = true; - TimeZone tz = TimeZone.getTimeZone (strings[0]); - calendar.set (Calendar.ZONE_OFFSET, tz.getRawOffset ()); - offset = 0; - if (k > 2 && tz instanceof SimpleTimeZone) + calendar.set(Calendar.DST_OFFSET, 0); + offset = simpleOffset.intValue(); + } + else + { + for (int j = 0; j < zoneCount; j++) { - SimpleTimeZone stz = (SimpleTimeZone) tz; - offset = stz.getDSTSavings (); + String[] strings = zoneStrings[j]; + 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()); return null; } - break; - default: - pos.setErrorIndex(pos.getIndex()); - return null; - } - - // 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) + + // Compute the value we should assign to the field. + int value; + int index = -1; + if (is_numeric) { - if (dateStr.startsWith(match[i], index)) - break; + 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; } - if (i == match.length) + else if (set1 != null) { - pos.setErrorIndex(index); - return null; + index = pos.getIndex(); + 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()); - value = i; - } - else - value = offset; + else + value = offset; - if (maybe2DigitYear) - { - // Parse into default century if the numeric year string has - // exactly 2 digits. - int digit_count = pos.getIndex() - index; - if (digit_count == 2) - is2DigitYear = true; + if (maybe2DigitYear) + { + // Parse into default century if the numeric year string has + // exactly 2 digits. + int digit_count = pos.getIndex() - index; + if (digit_count == 2) + { + 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) - { - // Apply the 80-20 heuristic to dermine the full year based on - // defaultCenturyStart. - int year = defaultCentury + calendar.get(Calendar.YEAR); - calendar.set(Calendar.YEAR, year); - if (calendar.getTime().compareTo(defaultCenturyStart) < 0) - calendar.set(Calendar.YEAR, year + 100); - } - - try - { + if (is2DigitYear) + { + // Apply the 80-20 heuristic to dermine the full year based on + // defaultCenturyStart. + int year = calendar.get(Calendar.YEAR); + if (calendar.getTime().compareTo(defaultCenturyStart) < 0) + calendar.set(Calendar.YEAR, year + 100); + } if (! saw_timezone) { // Use the real rules to determine whether or not this @@ -854,6 +1134,69 @@ public class SimpleDateFormat extends DateFormat pos.setErrorIndex(pos.getIndex()); 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 @@ -864,4 +1207,19 @@ public class SimpleDateFormat extends DateFormat calendar.set(Calendar.YEAR, year - 80); 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; + } + } diff --git a/libjava/java/util/Calendar.java b/libjava/java/util/Calendar.java index 1f0b27a56e8..8a37c5e695e 100644 --- a/libjava/java/util/Calendar.java +++ b/libjava/java/util/Calendar.java @@ -51,7 +51,7 @@ import java.lang.reflect.InvocationTargetException; * integer fields which represent <code>YEAR</code>, * <code>MONTH</code>, <code>DAY</code>, etc. The <code>Date</code> * object represents a time in milliseconds since the Epoch. <br> - * + * * This class is locale sensitive. To get the Object matching the * current locale you can use <code>getInstance</code>. You can even provide * a locale or a timezone. <code>getInstance</code> returns currently @@ -78,13 +78,13 @@ import java.lang.reflect.InvocationTargetException; * and for the first line all fields are set, that line is used to * compute the day. <br> * - * + * <pre>month + day_of_month month + week_of_month + day_of_week month + day_of_week_of_month + day_of_week day_of_year day_of_week + week_of_year</pre> - * + * * The hour_of_day-field takes precedence over the ampm and * hour_of_ampm fields. <br> * @@ -92,7 +92,7 @@ day_of_week + week_of_year</pre> * * To convert a calendar to a human readable form and vice versa, use * the <code>java.text.DateFormat</code> class. <br> - * + * * Other useful things you can do with an calendar, is * <code>roll</code>ing fields (that means increase/decrease a * specific field by one, propagating overflows), or @@ -101,7 +101,7 @@ day_of_week + week_of_year</pre> * @see Date * @see GregorianCalendar * @see TimeZone - * @see java.text.DateFormat + * @see java.text.DateFormat */ public abstract class Calendar implements Serializable, Cloneable { @@ -109,43 +109,52 @@ public abstract class Calendar implements Serializable, Cloneable * Constant representing the era time field. */ public static final int ERA = 0; + /** * Constant representing the year time field. */ public static final int YEAR = 1; + /** * Constant representing the month time field. This field * should contain one of the JANUARY,...,DECEMBER constants below. */ public static final int MONTH = 2; + /** * Constant representing the week of the year field. * @see #setFirstDayOfWeek(int) */ public static final int WEEK_OF_YEAR = 3; + /** * Constant representing the week of the month time field. * @see #setFirstDayOfWeek(int) */ public static final int WEEK_OF_MONTH = 4; + /** * Constant representing the day time field, synonym for DAY_OF_MONTH. */ public static final int DATE = 5; + /** * Constant representing the day time field. */ public static final int DAY_OF_MONTH = 5; + /** * Constant representing the day of year time field. This is * 1 for the first day in month. */ public static final int DAY_OF_YEAR = 6; + /** * Constant representing the day of week time field. This field * should contain one of the SUNDAY,...,SATURDAY constants below. */ public static final int DAY_OF_WEEK = 7; + /** * Constant representing the day-of-week-in-month field. For * instance this field contains 2 for the second thursday in a @@ -153,42 +162,51 @@ public abstract class Calendar implements Serializable, Cloneable * from the end of the month. */ public static final int DAY_OF_WEEK_IN_MONTH = 8; + /** * Constant representing the part of the day for 12-hour clock. This * should be one of AM or PM. */ public static final int AM_PM = 9; + /** * Constant representing the hour time field for 12-hour clock. */ public static final int HOUR = 10; + /** * Constant representing the hour of day time field for 24-hour clock. */ public static final int HOUR_OF_DAY = 11; + /** * Constant representing the minute of hour time field. */ public static final int MINUTE = 12; + /** * Constant representing the second time field. */ public static final int SECOND = 13; + /** * Constant representing the millisecond time field. */ public static final int MILLISECOND = 14; + /** * Constant representing the time zone offset time field for the * 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; + /** * 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; + /** * Number of time fields. */ @@ -198,26 +216,32 @@ public abstract class Calendar implements Serializable, Cloneable * Constant representing Sunday. */ public static final int SUNDAY = 1; + /** * Constant representing Monday. */ public static final int MONDAY = 2; + /** * Constant representing Tuesday. */ public static final int TUESDAY = 3; + /** * Constant representing Wednesday. */ public static final int WEDNESDAY = 4; + /** * Constant representing Thursday. */ public static final int THURSDAY = 5; + /** * Constant representing Friday. */ public static final int FRIDAY = 6; + /** * Constant representing Saturday. */ @@ -227,50 +251,62 @@ public abstract class Calendar implements Serializable, Cloneable * Constant representing January. */ public static final int JANUARY = 0; + /** * Constant representing February. */ public static final int FEBRUARY = 1; + /** * Constant representing March. */ public static final int MARCH = 2; + /** * Constant representing April. */ public static final int APRIL = 3; + /** * Constant representing May. */ public static final int MAY = 4; + /** * Constant representing June. */ public static final int JUNE = 5; + /** * Constant representing July. */ public static final int JULY = 6; + /** * Constant representing August. */ public static final int AUGUST = 7; + /** * Constant representing September. */ public static final int SEPTEMBER = 8; + /** * Constant representing October. */ public static final int OCTOBER = 9; + /** * Constant representing November. */ public static final int NOVEMBER = 10; + /** * Constant representing December. */ public static final int DECEMBER = 11; + /** * Constant representing Undecimber. This is an artificial name useful * for lunar calendars. @@ -281,6 +317,7 @@ public abstract class Calendar implements Serializable, Cloneable * Useful constant for 12-hour clock. */ public static final int AM = 0; + /** * Useful constant for 12-hour clock. */ @@ -292,21 +329,25 @@ public abstract class Calendar implements Serializable, Cloneable * @serial */ protected int[] fields = new int[FIELD_COUNT]; + /** * The flags which tell if the fields above have a value. * @serial */ protected boolean[] isSet = new boolean[FIELD_COUNT]; + /** * The time in milliseconds since the epoch. * @serial */ protected long time; + /** * Tells if the above field has a valid value. * @serial */ protected boolean isTimeSet; + /** * Tells if the fields have a valid value. This superseeds the isSet * array. @@ -332,7 +373,7 @@ public abstract class Calendar implements Serializable, Cloneable /** * 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 */ private int firstDayOfWeek; @@ -347,7 +388,14 @@ public abstract class Calendar implements Serializable, Cloneable 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> * <dd> JDK 1.1.5 or later.</dd> * <dl><dt>1</dt> @@ -371,14 +419,14 @@ public abstract class Calendar implements Serializable, Cloneable 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 - * 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, - ClassLoader.getSystemClassLoader()); + ClassLoader.getSystemClassLoader()); } /** @@ -404,8 +452,9 @@ public abstract class Calendar implements Serializable, Cloneable ResourceBundle rb = getBundle(locale); firstDayOfWeek = ((Integer) rb.getObject("firstDayOfWeek")).intValue(); - minimalDaysInFirstWeek = - ((Integer) rb.getObject("minimalDaysInFirstWeek")).intValue(); + minimalDaysInFirstWeek = ((Integer) rb.getObject("minimalDaysInFirstWeek")) + .intValue(); + clear(); } /** @@ -437,15 +486,17 @@ public abstract class Calendar implements Serializable, Cloneable return getInstance(TimeZone.getDefault(), locale); } - /** + /** * 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(); /** Preset argument types for calendar-class constructor lookup. */ - private static Class[] ctorArgTypes - = new Class[] {TimeZone.class, Locale.class}; + private static Class[] ctorArgTypes = new Class[] + { + TimeZone.class, Locale.class + }; /** * Creates a calendar representing the actual time, using the given @@ -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. if (calendarClass == GregorianCalendar.class) return new GregorianCalendar(zone, locale); @@ -481,7 +532,7 @@ public abstract class Calendar implements Serializable, Cloneable if (Calendar.class.isAssignableFrom(calendarClass)) { Constructor ctor = calendarClass.getConstructor(ctorArgTypes); - return (Calendar) ctor.newInstance(new Object[] {zone, locale}); + return (Calendar) ctor.newInstance(new Object[] { zone, locale }); } } catch (ClassNotFoundException ex) @@ -504,9 +555,9 @@ public abstract class Calendar implements Serializable, Cloneable { exception = ex; } - - throw new RuntimeException("Error instantiating calendar for locale " + - locale, exception); + + throw new RuntimeException("Error instantiating calendar for locale " + + locale, exception); } /** @@ -530,7 +581,7 @@ public abstract class Calendar implements Serializable, Cloneable * Converts the milliseconds since the epoch UTC * (<code>time</code>) to time fields * (<code>fields</code>). Override this method if you write your - * own Calendar. + * own Calendar. */ protected abstract void computeFields(); @@ -541,7 +592,7 @@ public abstract class Calendar implements Serializable, Cloneable */ public final Date getTime() { - if (!isTimeSet) + if (! isTimeSet) computeTime(); return new Date(time); } @@ -562,7 +613,7 @@ public abstract class Calendar implements Serializable, Cloneable */ public long getTimeInMillis() { - if (!isTimeSet) + if (! isTimeSet) computeTime(); return time; } @@ -575,9 +626,9 @@ public abstract class Calendar implements Serializable, Cloneable */ public void setTimeInMillis(long time) { + clear(); this.time = time; isTimeSet = true; - computeFields(); } /** @@ -593,14 +644,14 @@ public abstract class Calendar implements Serializable, Cloneable public int get(int field) { // If the requested field is invalid, force all fields to be recomputed. - if (!isSet[field]) + if (! isSet[field]) areFieldsSet = false; complete(); 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. * @param field the time field. One of the time field constants. * @return the value of the specified field, undefined if @@ -626,21 +677,72 @@ public abstract class Calendar implements Serializable, Cloneable */ public void set(int field, int value) { + if (isTimeSet) + for (int i = 0; i < FIELD_COUNT; i++) + isSet[i] = false; isTimeSet = false; fields[field] = value; 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) { - case YEAR: - case MONTH: - case DATE: + case MONTH: // pattern 1,2 or 3 + isSet[DAY_OF_YEAR] = false; + 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; + 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_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[DAY_OF_MONTH] = 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; break; case AM_PM: + isSet[HOUR] = true; isSet[HOUR_OF_DAY] = false; break; case HOUR_OF_DAY: @@ -648,12 +750,15 @@ public abstract class Calendar implements Serializable, Cloneable isSet[HOUR] = false; break; case HOUR: + isSet[AM_PM] = true; isSet[HOUR_OF_DAY] = false; break; + case DST_OFFSET: + explicitDSTOffset = true; } // 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; } @@ -675,8 +780,10 @@ public abstract class Calendar implements Serializable, Cloneable isSet[WEEK_OF_MONTH] = false; isSet[DAY_OF_WEEK] = 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 * @param minute the minute. * @param second the second. */ - public final void set(int year, int month, int date, - int hour, int minute, int second) + public final void set(int year, int month, int date, int hour, int minute, + int second) { set(year, month, date, hour, minute); fields[SECOND] = second; @@ -721,11 +828,15 @@ public abstract class Calendar implements Serializable, Cloneable { isTimeSet = 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++) - { - isSet[i] = false; - fields[i] = 0; - } + isSet[i] = false; } /** @@ -737,10 +848,15 @@ public abstract class Calendar implements Serializable, Cloneable */ 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; areFieldsSet = false; isSet[field] = false; - fields[field] = 0; + fields[field] = tempFields[field]; } /** @@ -757,18 +873,18 @@ public abstract class Calendar implements Serializable, Cloneable /** * 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() { - if (!isTimeSet) + if (! isTimeSet) computeTime(); - if (!areFieldsSet) + if (! areFieldsSet) computeFields(); } /** - * Compares the given calendar with this. + * Compares the given calendar with this. * @param o the object to that we should compare. * @return true, if the given object is a calendar, that represents * the same time (but doesn't necessary have the same fields). @@ -776,12 +892,12 @@ public abstract class Calendar implements Serializable, Cloneable public boolean equals(Object o) { return (o instanceof Calendar) - && getTimeInMillis() == ((Calendar) o).getTimeInMillis(); + && getTimeInMillis() == ((Calendar) o).getTimeInMillis(); } /** * 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> */ public int hashCode() @@ -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. * @return true, if the given object is a calendar, and this calendar * represents a smaller time than the calendar o. @@ -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. * @return true, if the given object is a calendar, and this calendar * represents a bigger time than the calendar o. @@ -831,11 +947,11 @@ public abstract class Calendar implements Serializable, Cloneable /** * Rolls the specified time field up or down. This means add one * 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> * * <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. * @param field the time field. One of the time field constants. * @param up the direction, true for up, false for down. @@ -854,7 +970,7 @@ public abstract class Calendar implements Serializable, Cloneable * * @param field the time field. One of the time field constants. * @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 * the valid range. The value of field must be >= 0 and * <= <code>FIELD_COUNT</code>. @@ -874,7 +990,6 @@ public abstract class Calendar implements Serializable, Cloneable } } - /** * Sets the time zone to the specified value. * @param zone the new time zone @@ -918,7 +1033,7 @@ public abstract class Calendar implements Serializable, Cloneable /** * 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. */ public void setFirstDayOfWeek(int value) @@ -928,7 +1043,7 @@ public abstract class Calendar implements Serializable, Cloneable /** * 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. */ public int getFirstDayOfWeek() @@ -972,7 +1087,6 @@ public abstract class Calendar implements Serializable, Cloneable */ public abstract int getMaximum(int field); - /** * Gets the greatest minimum value that is allowed for the specified field. * @param field the time field. One of the time field constants. @@ -984,7 +1098,7 @@ public abstract class Calendar implements Serializable, Cloneable * Gets the smallest maximum value that is allowed for the * specified field. For example this is 28 for DAY_OF_MONTH. * @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); @@ -1000,16 +1114,15 @@ public abstract class Calendar implements Serializable, Cloneable */ 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 end = tmp.getMinimum(field); tmp.set(field, 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) - break; // Done if not successful - + break; // Done if not successful } return min; } @@ -1018,7 +1131,7 @@ public abstract class Calendar implements Serializable, Cloneable * Gets the actual maximum value that is allowed for the specified field. * This value is dependent on the values of the other fields. * @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 * the valid range. The value of field must be >= 0 and * <= <code>FIELD_COUNT</code>. @@ -1026,7 +1139,7 @@ public abstract class Calendar implements Serializable, Cloneable */ 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 end = tmp.getMaximum(field); tmp.set(field, max); @@ -1048,7 +1161,7 @@ public abstract class Calendar implements Serializable, Cloneable { Calendar cal = (Calendar) super.clone(); cal.fields = (int[]) fields.clone(); - cal.isSet = (boolean[])isSet.clone(); + cal.isSet = (boolean[]) isSet.clone(); return cal; } catch (CloneNotSupportedException ex) @@ -1057,16 +1170,19 @@ public abstract class Calendar implements Serializable, Cloneable } } - private static final String[] fieldNames = { - ",ERA=", ",YEAR=", ",MONTH=", - ",WEEK_OF_YEAR=", ",WEEK_OF_MONTH=", - ",DAY_OF_MONTH=", ",DAY_OF_YEAR=", ",DAY_OF_WEEK=", - ",DAY_OF_WEEK_IN_MONTH=", - ",AM_PM=", ",HOUR=", ",HOUR_OF_DAY=", - ",MINUTE=", ",SECOND=", ",MILLISECOND=", - ",ZONE_OFFSET=", ",DST_OFFSET=" - }; - + private static final String[] fieldNames = + { + ",ERA=", ",YEAR=", ",MONTH=", + ",WEEK_OF_YEAR=", + ",WEEK_OF_MONTH=", + ",DAY_OF_MONTH=", + ",DAY_OF_YEAR=", ",DAY_OF_WEEK=", + ",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 @@ -1109,7 +1225,7 @@ public abstract class Calendar implements Serializable, Cloneable * says, that it could be omitted. */ private void writeObject(ObjectOutputStream stream) throws IOException { - if (!isTimeSet) + if (! isTimeSet) computeTime(); stream.defaultWriteObject(); } @@ -1121,7 +1237,7 @@ public abstract class Calendar implements Serializable, Cloneable throws IOException, ClassNotFoundException { stream.defaultReadObject(); - if (!isTimeSet) + if (! isTimeSet) computeTime(); if (serialVersionOnStream > 1) @@ -1130,7 +1246,6 @@ public abstract class Calendar implements Serializable, Cloneable // Sun wants to remove all fields from the stream someday // and will then increase the serialVersion number again. // We prepare to be compatible. - fields = new int[FIELD_COUNT]; isSet = new boolean[FIELD_COUNT]; areFieldsSet = false; diff --git a/libjava/java/util/GregorianCalendar.java b/libjava/java/util/GregorianCalendar.java index d99a9f2c612..710dd56f58b 100644 --- a/libjava/java/util/GregorianCalendar.java +++ b/libjava/java/util/GregorianCalendar.java @@ -39,6 +39,7 @@ exception statement from your version. */ package java.util; + /** * <p> * This class represents the Gregorian calendar, that is used in most @@ -46,7 +47,7 @@ package java.util; * for dates smaller than the date of the change to the Gregorian calendar. * The Gregorian calendar differs from the Julian calendar by a different * leap year rule (no leap year every 100 years, except if year is divisible - * by 400). + * by 400). * </p> * <p> * This change date is different from country to country, and can be changed with @@ -136,7 +137,7 @@ public class GregorianCalendar extends Calendar * Constant representing the era BC (Before Christ). */ public static final int BC = 0; - + /** * Constant representing the era AD (Anno Domini). */ @@ -164,43 +165,46 @@ public class GregorianCalendar extends Calendar private static final String bundleName = "gnu.java.locale.Calendar"; /** - * Retrieves the resource bundle. The resources should be loaded - * via this method only. Iff an application uses this method, the - * resourcebundle is required. + * Days in the epoch. Relative Jan 1, year '0' which is not a leap year. + * (although there is no year zero, this does not matter.) + * 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. - * @return A resource bundle for the calendar for the specified locale. + * The gregorian cutover in 1582 was 10 days, so by calculating the + * 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) - { - return ResourceBundle.getBundle(bundleName, locale, - ClassLoader.getSystemClassLoader()); - } + private static final int EPOCH_DAYS = 719162; /** * 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() { this(TimeZone.getDefault(), Locale.getDefault()); } - + /** * 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. */ public GregorianCalendar(TimeZone zone) { this(zone, Locale.getDefault()); } - + /** * Constructs a new GregorianCalender representing the current * time, using the default time zone and the specified locale. - * + * * @param locale a locale. */ public GregorianCalendar(Locale locale) @@ -212,15 +216,30 @@ public class GregorianCalendar extends Calendar * Constructs a new GregorianCalender representing the current * time with the given time zone and the given locale. * - * @param zone a time zone. - * @param locale a locale. + * @param zone a time zone. + * @param locale a 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); - ResourceBundle rb = getBundle(locale); + ResourceBundle rb = ResourceBundle.getBundle(bundleName, locale, + ClassLoader + .getSystemClassLoader()); gregorianCutover = ((Date) rb.getObject("gregorianCutOver")).getTime(); - setTimeInMillis(System.currentTimeMillis()); } /** @@ -232,7 +251,7 @@ public class GregorianCalendar extends Calendar */ public GregorianCalendar(int year, int month, int day) { - super(); + this(TimeZone.getDefault(), Locale.getDefault(), false); set(year, month, day); } @@ -248,7 +267,7 @@ public class GregorianCalendar extends Calendar */ 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); } @@ -264,10 +283,10 @@ public class GregorianCalendar extends Calendar * @param minute corresponds to the MINUTE time field. * @param second corresponds to the SECOND time field. */ - public GregorianCalendar(int year, int month, int day, - int hour, int minute, int second) + public GregorianCalendar(int year, int month, int day, int hour, int minute, + int second) { - super(); + this(TimeZone.getDefault(), Locale.getDefault(), false); set(year, month, day, hour, minute, second); } @@ -308,71 +327,23 @@ public class GregorianCalendar extends Calendar * </p> * * @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) { + // Only years divisible by 4 can be leap years if ((year & 3) != 0) - // Only years divisible by 4 can be leap years return false; - // compute the linear day of the 29. February of that year. - // The 13 is the number of days, that were omitted in the Gregorian - // 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) + // Is the leap-day a Julian date? Then it's a leap year + if (! isGregorian(year, 31 + 29 - 1)) return true; + // Apply gregorian rules otherwise 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 * day of the specified year. * @@ -382,8 +353,8 @@ public class GregorianCalendar extends Calendar */ private int getWeekDay(int year, int dayOfYear) { - int day = - (int) (getLinearTime(year, dayOfYear, 0) / (24 * 60 * 60 * 1000L)); + boolean greg = isGregorian(year, dayOfYear); + int day = (int) getLinearDay(year, dayOfYear, greg); // The epoch was a thursday. int weekday = (day + THURSDAY) % 7; @@ -393,235 +364,360 @@ public class GregorianCalendar extends Calendar } /** - * <p> - * 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. + * Returns the day of the week for the first day of a given month (0..11) */ - private int[] getDayOfYear(int year) + private int getFirstDayOfMonth(int year, int month) { - if (isSet[MONTH]) - { - int dayOfYear; - if (fields[MONTH] > FEBRUARY) - { + int[] dayCount = { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 }; - // The months after February are regular: - // 9 is an offset found by try and error. - dayOfYear = (fields[MONTH] * (31 + 30 + 31 + 30 + 31) - 9) / 5; - if (isLeapYear(year)) - dayOfYear++; - } - else - dayOfYear = 31 * fields[MONTH]; + if (month > 11) + { + year += (month / 12); + month = month % 12; + } - if (isSet[DAY_OF_MONTH]) + if (month < 0) + { + year += (int) month / 12; + month = month % 12; + if (month < 0) { - return new int[] - { - dayOfYear + fields[DAY_OF_MONTH], 0}; + month += 12; + year--; } - 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[] - { - 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_MONTH] - + (fields[DAY_OF_WEEK] < getFirstDayOfWeek()? 0 : -1) - + (weekday < getFirstDayOfWeek()? -1 : 0))}; - } - if (isSet[DAY_OF_WEEK] && isSet[DAY_OF_WEEK_IN_MONTH]) - { - // the weekday of the first day in that month is: - int weekday = getWeekDay(year, ++dayOfYear); - return new int[] { - dayOfYear, - fields[DAY_OF_WEEK] - weekday + - 7 * (fields[DAY_OF_WEEK_IN_MONTH] - + (fields[DAY_OF_WEEK] < weekday ? 0 : -1))}; - } + int dayOfYear = dayCount[month] + 1; + if (month > 1) + if (isLeapYear(year)) + dayOfYear++; + + boolean greg = isGregorian(year, dayOfYear); + int day = (int) getLinearDay(year, dayOfYear, greg); + + // The epoch was a thursday. + int weekday = (day + THURSDAY) % 7; + if (weekday <= 0) + weekday += 7; + return weekday; + } + + /** + * Takes a year, and a (zero based) day of year and determines + * if it is gregorian or not. + */ + private boolean isGregorian(int year, int dayOfYear) + { + 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[DAY_OF_YEAR]) + if (isSet[WEEK_OF_MONTH]) { - 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(); - // the weekday of the day, that begins the first week - // in that year is: - int weekday = getWeekDay(year, dayOfYear); - - 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))}; + int weeks = (month == 1 && leap == 0) ? 4 : 5; + if (fields[DAY_OF_WEEK_IN_MONTH] < -weeks + || fields[DAY_OF_WEEK_IN_MONTH] > weeks) + throw new IllegalArgumentException("Illegal DAY_OF_WEEK_IN_MONTH."); } - // As last resort return Jan, 1st. - return new int[] {1, 0}; + if (isSet[AM_PM] && fields[AM_PM] != AM && fields[AM_PM] != PM) + 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 - * milliseconds since the epoch UTC (<code>time</code>). + * milliseconds since the epoch UTC (<code>time</code>). * * @throws IllegalArgumentException if any calendar fields * are invalid. */ protected synchronized void computeTime() { - int era = isSet[ERA] ? fields[ERA] : AD; - int year = isSet[YEAR] ? fields[YEAR] : 1970; - if (era == BC) - year = 1 - year; + int millisInDay = 0; + int era = fields[ERA]; + int year = fields[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[HOUR_OF_DAY]) - hour = fields[HOUR_OF_DAY]; - else if (isSet[HOUR]) + if (! isSet[MONTH] && (! isSet[DAY_OF_WEEK] || isSet[WEEK_OF_YEAR])) + { + // 5: YEAR + DAY_OF_WEEK + WEEK_OF_YEAR + 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]; - if (isSet[AM_PM] && fields[AM_PM] == PM) + if (fields[AM_PM] == PM) if (hour != 12) /* not Noon */ - hour += 12; + hour += 12; /* 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; } + else + hour = fields[HOUR_OF_DAY]; - int minute = isSet[MINUTE] ? fields[MINUTE] : 0; - int second = isSet[SECOND] ? fields[SECOND] : 0; - int millis = isSet[MILLISECOND] ? fields[MILLISECOND] : 0; - int millisInDay; + // Read the era,year,month,day fields and convert as appropriate. + // Calculate number of milliseconds into the day + // This takes care of both h, m, s, ms over/underflows. + 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 - long allMillis = (((hour * 60L) + minute) * 60L + second) * 1000L - + millis; - daysOfYear[1] += allMillis / (24 * 60 * 60 * 1000L); - millisInDay = (int) (allMillis % (24 * 60 * 60 * 1000L)); + year += (int) month / 12; + month = month % 12; + if (month < 0) + { + month += 12; + year--; + } } - else + if (month > 11) { - if (hour < 0 || hour >= 24 || minute < 0 || minute > 59 - || second < 0 || second > 59 || millis < 0 || millis >= 1000) - throw new IllegalArgumentException(); - millisInDay = (((hour * 60) + minute) * 60 + second) * 1000 + millis; + year += (month / 12); + month = month % 12; } - time = getLinearTime(year, daysOfYear[0], millisInDay); - // Add the relative days after calculating the linear time, to - // get right behaviour when jumping over the gregorianCutover. - time += daysOfYear[1] * (24 * 60 * 60 * 1000L); + month_days[1] = isLeapYear(year) ? 29 : 28; + 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(); - int rawOffset = isSet[ZONE_OFFSET] - ? fields[ZONE_OFFSET] : zone.getRawOffset(); - - int dayOfYear = daysOfYear[0] + daysOfYear[1]; - // This formula isn't right, so check for month as a quick fix. - // It doesn't compensate for leap years and puts day 30 in month 1 - // instead of month 0. - int month = isSet[MONTH] - ? fields[MONTH] : (dayOfYear * 5 + 3) / (31 + 30 + 31 + 30 + 31); - // This formula isn't right, so check for day as a quick fix. It - // doesn't compensate for leap years, either. - int day = isSet[DAY_OF_MONTH] ? fields[DAY_OF_MONTH] - : (6 + (dayOfYear * 5 + 3) % (31 + 30 + 31 + 30 + 31)) / 5; - int weekday = ((int) (time / (24 * 60 * 60 * 1000L)) + THURSDAY) % 7; + // ok, by here we have valid day,month,year,era and millisinday + int dayOfYear = dayCount[month] + day - 1; // (day starts on 1) + if (isLeapYear(year) && month > 1) + dayOfYear++; + + 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.); + + if ((relativeDay + gregFactor) * 60L * 60L * 24L * 1000L >= gregorianCutover) + relativeDay += gregFactor; + else + relativeDay -= 2; + + time = relativeDay * (24 * 60 * 60 * 1000L) + millisInDay; + + // the epoch was a Thursday. + int weekday = (int) (relativeDay + THURSDAY) % 7; if (weekday <= 0) weekday += 7; - int dstOffset = isSet[DST_OFFSET] - ? 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; - } + fields[DAY_OF_WEEK] = weekday; - /** - * <p> - * Determines if the given year is a leap year. - * </p> - * <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; + // Time zone corrections. + TimeZone zone = getTimeZone(); + int rawOffset = isSet[ZONE_OFFSET] ? fields[ZONE_OFFSET] + : zone.getRawOffset(); - if (!gregorian) - return true; + int dstOffset = isSet[DST_OFFSET] ? fields[DST_OFFSET] + : (zone.getOffset((year < 0) ? BC : AD, + (year < 0) ? 1 - year + : year, + month, day, weekday, + millisInDay) + - zone.getRawOffset()); - // We rely on AD area here. - return ((year % 100) != 0 || (year % 400) == 0); + time -= rawOffset + dstOffset; + + isTimeSet = true; } /** * Get the linear day in days since the epoch, using the * Julian or Gregorian calendar as specified. If you specify a * 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 dayOfYear the day of year of the date; 1 based. * @param gregorian <code>true</code>, if we should use the Gregorian rules. * @return the days since the epoch, may be negative. */ - private int getLinearDay(int year, int dayOfYear, boolean gregorian) - { - // The 13 is the number of days, that were omitted in the Gregorian - // Calender 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); - + public long getLinearDay(int year, int dayOfYear, boolean gregorian) + { + // The 13 is the number of days, that were omitted in the Gregorian + // Calender 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). + long julianDay = (year - 1) * 365L + ((year - 1) >> 2) + (dayOfYear - 1) + - EPOCH_DAYS; // gregorian days from 1 to epoch. + if (gregorian) { // subtract the days that are missing in gregorian calendar @@ -633,11 +729,13 @@ public class GregorianCalendar extends Calendar // // The additional leap year factor accounts for the fact that // a leap day is not seen on Jan 1 of the leap year. - int gregOffset = (year / 400) - (year / 100) + 2; - if (isLeapYear (year, true) && dayOfYear < 31 + 29) - --gregOffset; - julianDay += gregOffset; + int gregOffset = (int) Math.floor((double) (year - 1) / 400.) + - (int) Math.floor((double) (year - 1) / 100.); + + return julianDay + gregOffset; } + else + julianDay -= 2; return julianDay; } @@ -646,26 +744,27 @@ public class GregorianCalendar extends Calendar * day_of_year, day_of_month, day_of_week, and writes the result * into the fields array. * - * @param day the linear day. + * @param day the linear day. * @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. - int weekday = (day + THURSDAY) % 7; + // the epoch was a Thursday. + int weekday = (int) (day + THURSDAY) % 7; if (weekday <= 0) weekday += 7; fields[DAY_OF_WEEK] = weekday; // get a first approximation of the year. This may be one // year too big. - int year = 1970 + (gregorian - ? ((day - 100) * 400) / (365 * 400 + 100 - 4 + 1) - : ((day - 100) * 4) / (365 * 4 + 1)); + int year = 1970 + + (int) (gregorian + ? ((day - 100L) * 400L) / (365L * 400L + 100L - 4L + + 1L) : ((day - 100L) * 4L) / (365L * 4L + 1L)); if (day >= 0) year++; - int firstDayOfYear = getLinearDay(year, 1, gregorian); + long firstDayOfYear = getLinearDay(year, 1, gregorian); // Now look in which year day really lies. if (day < firstDayOfYear) @@ -674,9 +773,9 @@ public class GregorianCalendar extends Calendar 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) { fields[ERA] = BC; @@ -688,16 +787,16 @@ public class GregorianCalendar extends Calendar fields[YEAR] = year; } - int leapday = isLeapYear(year, gregorian) ? 1 : 0; + int leapday = isLeapYear(year) ? 1 : 0; if (day <= 31 + 28 + leapday) { - fields[MONTH] = day / 32; // 31->JANUARY, 32->FEBRUARY - fields[DAY_OF_MONTH] = day - 31 * fields[MONTH]; + fields[MONTH] = (int) day / 32; // 31->JANUARY, 32->FEBRUARY + fields[DAY_OF_MONTH] = (int) day - 31 * fields[MONTH]; } else { // 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[DAY_OF_MONTH] = (scaledDay % (31 + 30 + 31 + 30 + 31)) / 5 + 1; } @@ -716,25 +815,26 @@ public class GregorianCalendar extends Calendar fields[ZONE_OFFSET] = zone.getRawOffset(); 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)); + if (millisInDay < 0) { millisInDay += (24 * 60 * 60 * 1000); day--; } - calculateDay(day, gregorian); - fields[DST_OFFSET] = - zone.getOffset(fields[ERA], fields[YEAR], fields[MONTH], - fields[DAY_OF_MONTH], fields[DAY_OF_WEEK], - millisInDay) - fields[ZONE_OFFSET]; + calculateDay(fields, day, gregorian); + fields[DST_OFFSET] = zone.getOffset(fields[ERA], fields[YEAR], + fields[MONTH], fields[DAY_OF_MONTH], + fields[DAY_OF_WEEK], millisInDay) + - fields[ZONE_OFFSET]; millisInDay += fields[DST_OFFSET]; if (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; @@ -749,13 +849,12 @@ public class GregorianCalendar extends Calendar // Do the Correction: getMinimalDaysInFirstWeek() is always in the // first week. int minDays = getMinimalDaysInFirstWeek(); - int firstWeekday = - (7 + getWeekDay(fields[YEAR], minDays) - getFirstDayOfWeek()) % 7; + int firstWeekday = (7 + getWeekDay(fields[YEAR], minDays) + - getFirstDayOfWeek()) % 7; if (minDays - firstWeekday < 1) weekOfYear++; fields[WEEK_OF_YEAR] = weekOfYear; - int hourOfDay = millisInDay / (60 * 60 * 1000); fields[AM_PM] = (hourOfDay < 12) ? AM : PM; int hour = hourOfDay % 12; @@ -767,14 +866,7 @@ public class GregorianCalendar extends Calendar fields[SECOND] = 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 * equivalent to this if it is also a <code>GregorianCalendar</code> * with the same time since the epoch under the same conditions * (same change date and same time zone). - * + * * @param o the object to that we should compare. * @return true, if the given object is a calendar, that represents * the same time (but doesn't necessarily have the same fields). @@ -794,48 +886,20 @@ public class GregorianCalendar extends Calendar */ public boolean equals(Object o) { - if (!(o instanceof GregorianCalendar)) + 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 smaller time than the calender o. -// */ -// public boolean before(Object o) { -// if (!(o instanceof GregorianCalendar)) -// return false; - -// GregorianCalendar cal = (GregorianCalendar) o; -// return (cal.getTimeInMillis() < getTimeInMillis()); -// } - -// /** -// * Compares the given calender with this. -// * @param o the object to that we should compare. -// * @return true, if the given object is a calendar, and this calendar -// * represents a bigger time than the calender o. -// */ -// public boolean after(Object o) { -// if (!(o instanceof GregorianCalendar)) -// return false; - -// GregorianCalendar cal = (GregorianCalendar) o; -// return (cal.getTimeInMillis() > getTimeInMillis()); -// } - /** * Adds the specified amount of time to the given time field. The * amount may be negative to subtract the time. If the field overflows * it does what you expect: Jan, 25 + 10 Days is Feb, 4. * @param field one of the time field constants. * @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 * if <code>amount</code> contains an out-of-range value and the calendar * is not in lenient mode. @@ -859,18 +923,18 @@ public class GregorianCalendar extends Calendar fields[MONTH] += 12; fields[YEAR]--; } - isTimeSet = false; int maxDay = getActualMaximum(DAY_OF_MONTH); if (fields[DAY_OF_MONTH] > maxDay) { fields[DAY_OF_MONTH] = maxDay; - isTimeSet = false; } + set(YEAR, fields[YEAR]); + set(MONTH, fields[MONTH]); break; case DAY_OF_MONTH: case DAY_OF_YEAR: case DAY_OF_WEEK: - if (!isTimeSet) + if (! isTimeSet) computeTime(); time += amount * (24 * 60 * 60 * 1000L); areFieldsSet = false; @@ -878,59 +942,57 @@ public class GregorianCalendar extends Calendar case WEEK_OF_YEAR: case WEEK_OF_MONTH: case DAY_OF_WEEK_IN_MONTH: - if (!isTimeSet) + if (! isTimeSet) computeTime(); time += amount * (7 * 24 * 60 * 60 * 1000L); areFieldsSet = false; break; case AM_PM: - if (!isTimeSet) + if (! isTimeSet) computeTime(); time += amount * (12 * 60 * 60 * 1000L); areFieldsSet = false; break; case HOUR: case HOUR_OF_DAY: - if (!isTimeSet) + if (! isTimeSet) computeTime(); time += amount * (60 * 60 * 1000L); areFieldsSet = false; break; case MINUTE: - if (!isTimeSet) + if (! isTimeSet) computeTime(); time += amount * (60 * 1000L); areFieldsSet = false; break; case SECOND: - if (!isTimeSet) + if (! isTimeSet) computeTime(); time += amount * (1000L); areFieldsSet = false; break; case MILLISECOND: - if (!isTimeSet) + if (! isTimeSet) computeTime(); time += amount; areFieldsSet = false; break; case ZONE_OFFSET: - case DST_OFFSET: - default: + case DST_OFFSET:default: throw new IllegalArgumentException("Invalid or unknown field"); } } - /** * Rolls the specified time field up or down. This means add one * to the specified field, but don't change the other fields. If - * the maximum for this field is reached, start over with the - * minimum value. + * the maximum for this field is reached, start over with the + * minimum value. * * <strong>Note:</strong> There may be situation, where the other - * fields must be changed, e.g rolling the month on May, 31. - * The date June, 31 is automatically converted to July, 1. + * fields must be changed, e.g rolling the month on May, 31. + * The date June, 31 is automatically converted to July, 1. * This requires lenient settings. * * @param field the time field. One of the time field constants. @@ -972,7 +1034,6 @@ public class GregorianCalendar extends Calendar isSet[DAY_OF_YEAR] = false; isSet[WEEK_OF_YEAR] = false; break; - case DAY_OF_MONTH: isSet[WEEK_OF_MONTH] = false; isSet[DAY_OF_WEEK] = false; @@ -981,7 +1042,6 @@ public class GregorianCalendar extends Calendar isSet[WEEK_OF_YEAR] = false; time += delta * (24 * 60 * 60 * 1000L); break; - case WEEK_OF_MONTH: isSet[DAY_OF_MONTH] = false; isSet[DAY_OF_WEEK_IN_MONTH] = false; @@ -1013,7 +1073,6 @@ public class GregorianCalendar extends Calendar isSet[DAY_OF_YEAR] = false; time += delta * (7 * 24 * 60 * 60 * 1000L); break; - case AM_PM: isSet[HOUR_OF_DAY] = false; time += delta * (12 * 60 * 60 * 1000L); @@ -1027,7 +1086,6 @@ public class GregorianCalendar extends Calendar isSet[AM_PM] = false; time += delta * (60 * 60 * 1000L); break; - case MINUTE: time += delta * (60 * 1000L); break; @@ -1047,7 +1105,7 @@ public class GregorianCalendar extends Calendar * with the minimum value and vice versa for negative amounts. * * <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. * * @param field the time field. One of the time field constants. @@ -1084,16 +1142,23 @@ public class GregorianCalendar extends Calendar /** * The minimum values for the calendar fields. */ - private static final int[] minimums = - { BC, 1, 0, 0, 1, 1, 1, SUNDAY, 1, - AM, 1, 0, 1, 1, 1, -(12*60*60*1000), 0 }; + private static final int[] minimums = + { + 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. */ - 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) }; + private static final int[] maximums = + { + AD, 5000000, 11, 53, 5, 31, 366, + SATURDAY, 5, PM, 12, 23, 59, 59, 999, + +(12 * 60 * 60 * 1000), + (12 * 60 * 60 * 1000) + }; /** * Gets the smallest value that is allowed for the specified field. @@ -1117,7 +1182,6 @@ public class GregorianCalendar extends Calendar return maximums[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> @@ -1142,7 +1206,7 @@ public class GregorianCalendar extends Calendar * 28 days). * * @param field the time field. One of the time field constants. - * @return the least maximum value. + * @return the least maximum value. * @see #getActualMaximum(int) * @since 1.2 */ @@ -1182,7 +1246,7 @@ public class GregorianCalendar extends Calendar int min = getMinimalDaysInFirstWeek(); if (min == 0) return 1; - if (!areFieldsSet || !isSet[ERA] || !isSet[YEAR]) + if (! areFieldsSet || ! isSet[ERA] || ! isSet[YEAR]) complete(); int year = fields[ERA] == AD ? fields[YEAR] : 1 - fields[YEAR]; @@ -1203,45 +1267,46 @@ public class GregorianCalendar extends Calendar * 29, rather than 28. * * @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) { switch (field) { case WEEK_OF_YEAR: - { - if (!areFieldsSet || !isSet[ERA] || !isSet[YEAR]) + { + if (! areFieldsSet || ! isSet[ERA] || ! isSet[YEAR]) complete(); + // 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 // year in that case. int year = fields[ERA] == AD ? fields[YEAR] : 1 - fields[YEAR]; int lastDay = isLeapYear(year) ? 366 : 365; int weekday = getWeekDay(year, lastDay); - int week = (lastDay + 6 - - (7 + weekday - getFirstDayOfWeek()) % 7) / 7; + int week = (lastDay + 6 - (7 + weekday - getFirstDayOfWeek()) % 7) / 7; int minimalDays = getMinimalDaysInFirstWeek(); int firstWeekday = getWeekDay(year, minimalDays); - /* + /* * Is there a set of days at the beginning of the year, before the * first day of the week, equal to or greater than the minimum number * of days required in the first week? */ if (minimalDays - (7 + firstWeekday - getFirstDayOfWeek()) % 7 < 1) return week + 1; /* Add week 1: firstWeekday through to firstDayOfWeek */ - } - case DAY_OF_MONTH: - { - if (!areFieldsSet || !isSet[MONTH]) + } + case DAY_OF_MONTH: + { + if (! areFieldsSet || ! isSet[MONTH]) complete(); int month = fields[MONTH]; + // If you change this, you should also change // SimpleTimeZone.getDaysInMonth(); if (month == FEBRUARY) { - if (!isSet[YEAR] || !isSet[ERA]) + if (! isSet[YEAR] || ! isSet[ERA]) complete(); int year = fields[ERA] == AD ? fields[YEAR] : 1 - fields[YEAR]; return isLeapYear(year) ? 29 : 28; @@ -1250,33 +1315,31 @@ public class GregorianCalendar extends Calendar return 31 - (month & 1); else return 30 + (month & 1); - } + } case DAY_OF_YEAR: - { - if (!areFieldsSet || !isSet[ERA] || !isSet[YEAR]) + { + if (! areFieldsSet || ! isSet[ERA] || ! isSet[YEAR]) complete(); int year = fields[ERA] == AD ? fields[YEAR] : 1 - fields[YEAR]; return isLeapYear(year) ? 366 : 365; - } + } case DAY_OF_WEEK_IN_MONTH: - { + { // This is wrong for the month that contains the gregorian change. int daysInMonth = getActualMaximum(DAY_OF_MONTH); + // That's black magic, I know return (daysInMonth - (fields[DAY_OF_MONTH] - 1) % 7 + 6) / 7; - } + } case WEEK_OF_MONTH: - { + { int daysInMonth = getActualMaximum(DAY_OF_MONTH); int weekday = (daysInMonth - fields[DAY_OF_MONTH] - + fields[DAY_OF_WEEK] - SUNDAY) % 7 + SUNDAY; - return (daysInMonth + 6 - - (7 + weekday - getFirstDayOfWeek()) % 7) / 7; - } + + fields[DAY_OF_WEEK] - SUNDAY) % 7 + SUNDAY; + return (daysInMonth + 6 - (7 + weekday - getFirstDayOfWeek()) % 7) / 7; + } default: return maximums[field]; } } - - } diff --git a/libjava/java/util/SimpleTimeZone.java b/libjava/java/util/SimpleTimeZone.java index 7d0e2019861..e50d92fab49 100644 --- a/libjava/java/util/SimpleTimeZone.java +++ b/libjava/java/util/SimpleTimeZone.java @@ -38,6 +38,7 @@ exception statement from your version. */ package java.util; + /** * This class represents a simple time zone offset and handles * daylight savings. It can only handle one daylight savings rule, so @@ -49,14 +50,14 @@ package java.util; * lying in the AD era. * * @see Calendar - * @see GregorianCalender + * @see GregorianCalender * @author Jochen Hoenicke */ public class SimpleTimeZone extends TimeZone { /** * The raw time zone offset in milliseconds to GMT, ignoring - * daylight savings. + * daylight savings. * @serial */ private int rawOffset; @@ -70,23 +71,22 @@ public class SimpleTimeZone extends TimeZone /** * The daylight savings offset. This is a positive offset in * 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 * @since JDK1.1.4 */ 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 */ private int startYear; - private static final int DOM_MODE = 1; private static final int DOW_IN_MONTH_MODE = 2; private static final int DOW_GE_DOM_MODE = 3; private static final int DOW_LE_DOM_MODE = 4; - + /** * The mode of the start rule. This takes one of the following values: * <dl> @@ -119,7 +119,7 @@ public class SimpleTimeZone extends TimeZone /** * The month in which daylight savings start. This is one of the - * constants Calendar.JANUARY, ..., Calendar.DECEMBER. + * constants Calendar.JANUARY, ..., Calendar.DECEMBER. * @serial */ private int startMonth; @@ -128,21 +128,21 @@ public class SimpleTimeZone extends TimeZone * This variable can have different meanings. See startMode for details * @see #startMode; * @serial - */ + */ 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. * @serial * @see #startMode; - */ + */ private int startDayOfWeek; - + /** * This variable specifies the time of change to daylight savings. * This time is given in milliseconds after midnight local - * standard time. + * standard time. * @serial */ private int startTime; @@ -157,9 +157,9 @@ public class SimpleTimeZone extends TimeZone /** * The month in which daylight savings ends. This is one of the - * constants Calendar.JANUARY, ..., Calendar.DECEMBER. + * constants Calendar.JANUARY, ..., Calendar.DECEMBER. * @serial - */ + */ private int endMonth; /** @@ -167,7 +167,7 @@ public class SimpleTimeZone extends TimeZone * It can take the same values as startMode. * @serial * @see #startMode - */ + */ private int endMode; /** @@ -176,19 +176,19 @@ public class SimpleTimeZone extends TimeZone * @see #startMode; */ 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. * @serial * @see #startMode; */ private int endDayOfWeek; - + /** * This variable specifies the time of change back to standard time. * This time is given in milliseconds after midnight local - * standard time. + * standard time. * @serial */ private int endTime; @@ -210,8 +210,11 @@ public class SimpleTimeZone extends TimeZone * @serial */ private byte[] monthLength = monthArr; - private static final byte[] monthArr = - {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; + private static final byte[] monthArr = + { + 31, 28, 31, 30, 31, 30, 31, 31, 30, + 31, 30, 31 + }; /** * The version of the serialized data on the stream. @@ -232,10 +235,9 @@ public class SimpleTimeZone extends TimeZone * When streaming out this class it is always written in the latest * version. * @serial - * @since JDK1.1.4 + * @since JDK1.1.4 */ private int serialVersionOnStream = 2; - private static final long serialVersionUID = -403250971215465050L; /** @@ -257,9 +259,9 @@ public class SimpleTimeZone extends TimeZone /** * 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 id The identifier of this time zone. + * @param id The identifier of this time zone. */ public SimpleTimeZone(int rawOffset, String id) { @@ -273,7 +275,7 @@ public class SimpleTimeZone extends TimeZone * Create a <code>SimpleTimeZone</code> with the given time offset * from GMT and with daylight savings. The start/end parameters * 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. * * <dl> @@ -296,12 +298,12 @@ public class SimpleTimeZone extends TimeZone * must make sure that this day lies in the same month. </dd> * </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. * * The start rule must have a different month than the end rule. * This restriction shouldn't hurt for all possible time zones. - * + * * @param rawOffset The time offset from GMT in milliseconds. * @param id The identifier of this time zone. * @param startMonth The start month of daylight savings; use the @@ -312,29 +314,26 @@ public class SimpleTimeZone extends TimeZone * @param startTime A time in millis in standard time. * @param endMonth The end month of daylight savings; use the * 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. * @param endDayOfWeek The end rule day of week; see above. * @param endTime A time in millis in standard time. * @throws IllegalArgumentException if parameters are invalid or out of * range. */ - public SimpleTimeZone(int rawOffset, String id, - int startMonth, int startDayOfWeekInMonth, - int startDayOfWeek, int startTime, - int endMonth, int endDayOfWeekInMonth, - int endDayOfWeek, int endTime) + public SimpleTimeZone(int rawOffset, String id, int startMonth, + int startDayOfWeekInMonth, int startDayOfWeek, + int startTime, int endMonth, int endDayOfWeekInMonth, + int endDayOfWeek, int endTime) { this.rawOffset = rawOffset; setID(id); useDaylight = true; - setStartRule(startMonth, startDayOfWeekInMonth, - startDayOfWeek, startTime); + setStartRule(startMonth, startDayOfWeekInMonth, startDayOfWeek, startTime); setEndRule(endMonth, endDayOfWeekInMonth, endDayOfWeek, endTime); if (startMonth == endMonth) - throw new IllegalArgumentException - ("startMonth and endMonth must be different"); + throw new IllegalArgumentException("startMonth and endMonth must be different"); this.startYear = 0; } @@ -347,15 +346,13 @@ public class SimpleTimeZone extends TimeZone * time in milliseconds. This must be positive. * @since 1.2 */ - public SimpleTimeZone(int rawOffset, String id, - int startMonth, int startDayOfWeekInMonth, - int startDayOfWeek, int startTime, - int endMonth, int endDayOfWeekInMonth, - int endDayOfWeek, int endTime, int dstSavings) + public SimpleTimeZone(int rawOffset, String id, int startMonth, + int startDayOfWeekInMonth, int startDayOfWeek, + int startTime, int endMonth, int endDayOfWeekInMonth, + int endDayOfWeek, int endTime, int dstSavings) { - this(rawOffset, id, - startMonth, startDayOfWeekInMonth, startDayOfWeek, startTime, - endMonth, endDayOfWeekInMonth, endDayOfWeek, endTime); + this(rawOffset, id, startMonth, startDayOfWeekInMonth, startDayOfWeek, + startTime, endMonth, endDayOfWeekInMonth, endDayOfWeek, endTime); this.dstSavings = dstSavings; } @@ -376,12 +373,11 @@ public class SimpleTimeZone extends TimeZone * range. * @since 1.4 */ - public SimpleTimeZone(int rawOffset, String id, - int startMonth, int startDayOfWeekInMonth, - int startDayOfWeek, int startTime, int startTimeMode, - int endMonth, int endDayOfWeekInMonth, - int endDayOfWeek, int endTime, int endTimeMode, - int dstSavings) + public SimpleTimeZone(int rawOffset, String id, int startMonth, + int startDayOfWeekInMonth, int startDayOfWeek, + int startTime, int startTimeMode, int endMonth, + int endDayOfWeekInMonth, int endDayOfWeek, + int endTime, int endTimeMode, int dstSavings) { this.rawOffset = rawOffset; setID(id); @@ -394,12 +390,10 @@ public class SimpleTimeZone extends TimeZone this.startTimeMode = startTimeMode; this.endTimeMode = endTimeMode; - setStartRule(startMonth, startDayOfWeekInMonth, - startDayOfWeek, startTime); + setStartRule(startMonth, startDayOfWeekInMonth, startDayOfWeek, startTime); setEndRule(endMonth, endDayOfWeekInMonth, endDayOfWeek, endTime); if (startMonth == endMonth) - throw new IllegalArgumentException - ("startMonth and endMonth must be different"); + throw new IllegalArgumentException("startMonth and endMonth must be different"); this.startYear = 0; this.dstSavings = dstSavings; @@ -432,6 +426,7 @@ public class SimpleTimeZone extends TimeZone { if (month < 0 || month > 11) throw new IllegalArgumentException("month out of range"); + int daysInMonth = getDaysInMonth(month, 1); if (dayOfWeek == 0) { @@ -460,7 +455,6 @@ public class SimpleTimeZone extends TimeZone } } - /** * Sets the daylight savings start rule. You must also set the * end rule with <code>setEndRule</code> or the result of @@ -514,14 +508,16 @@ public class SimpleTimeZone extends TimeZone * @since 1.2 * @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 // or before mode. this.startDay = after ? Math.abs(day) : -Math.abs(day); this.startDayOfWeek = after ? Math.abs(dayOfWeek) : -Math.abs(dayOfWeek); - this.startMode = (dayOfWeek != 0) ? (after ? DOW_GE_DOM_MODE : DOW_LE_DOM_MODE) - : checkRule(month, day, dayOfWeek); + this.startMode = (dayOfWeek != 0) + ? (after ? DOW_GE_DOM_MODE : DOW_LE_DOM_MODE) + : checkRule(month, day, dayOfWeek); this.startDay = Math.abs(this.startDay); this.startDayOfWeek = Math.abs(this.startDayOfWeek); @@ -591,7 +587,7 @@ public class SimpleTimeZone extends TimeZone * * Note that this API isn't incredibly well specified. It appears that the * after flag must override the parameters, since normally, the day and - * dayofweek can select this. I.e., if day < 0 and dayOfWeek < 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 * 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 @@ -606,14 +602,16 @@ public class SimpleTimeZone extends TimeZone * @since 1.2 * @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 // or before mode. this.endDay = after ? Math.abs(day) : -Math.abs(day); this.endDayOfWeek = after ? Math.abs(dayOfWeek) : -Math.abs(dayOfWeek); - this.endMode = (dayOfWeek != 0) ? (after ? DOW_GE_DOM_MODE : DOW_LE_DOM_MODE) - : checkRule(month, day, dayOfWeek); + this.endMode = (dayOfWeek != 0) + ? (after ? DOW_GE_DOM_MODE : DOW_LE_DOM_MODE) + : checkRule(month, day, dayOfWeek); this.endDay = Math.abs(this.endDay); this.endDayOfWeek = Math.abs(endDayOfWeek); @@ -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 * time. * @@ -674,8 +672,8 @@ public class SimpleTimeZone extends TimeZone * @return the time zone offset in milliseconds. * @throws IllegalArgumentException if arguments are incorrect. */ - public int getOffset(int era, int year, int month, - int day, int dayOfWeek, int millis) + public int getOffset(int era, int year, int month, int day, int dayOfWeek, + int millis) { int daysInMonth = getDaysInMonth(month, year); if (day < 1 || day > daysInMonth) @@ -683,7 +681,7 @@ public class SimpleTimeZone extends TimeZone if (dayOfWeek < Calendar.SUNDAY || dayOfWeek > Calendar.SATURDAY) throw new IllegalArgumentException("dayOfWeek out of range"); 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. int daylightSavings = 0; @@ -691,27 +689,22 @@ public class SimpleTimeZone extends TimeZone { // This does only work for Gregorian calendars :-( // This is mainly because setStartYear doesn't take an era. - - boolean afterStart = !isBefore(year, month, day, dayOfWeek, millis, - startMode, startMonth, - startDay, startDayOfWeek, startTime); + boolean afterStart = ! isBefore(year, month, day, dayOfWeek, millis, + startMode, startMonth, startDay, + startDayOfWeek, startTime); boolean beforeEnd = isBefore(year, month, day, dayOfWeek, millis + dstSavings, - endMode, endMonth, - endDay, endDayOfWeek, endTime); + endMode, endMonth, endDay, endDayOfWeek, + endTime); if (startMonth < endMonth) - { - // use daylight savings, if the date is after the start of - // savings, and before the end of savings. - daylightSavings = afterStart && beforeEnd ? dstSavings : 0; - } + // use daylight savings, if the date is after the start of + // savings, and before the end of savings. + daylightSavings = afterStart && beforeEnd ? dstSavings : 0; else - { - // use daylight savings, if the date is before the end of - // savings, or after the start of savings. - daylightSavings = beforeEnd || afterStart ? dstSavings : 0; - } + // use daylight savings, if the date is before the end of + // savings, or after the start of savings. + daylightSavings = beforeEnd || afterStart ? dstSavings : 0; } return rawOffset + daylightSavings; } @@ -740,7 +733,7 @@ public class SimpleTimeZone extends TimeZone * milliseconds with respect to standard time. Typically this * is one hour, but for some time zones this may be half an our. * @return the daylight savings offset in milliseconds. - * + * * @since 1.2 */ public int getDSTSavings() @@ -760,7 +753,7 @@ public class SimpleTimeZone extends TimeZone { if (dstSavings <= 0) throw new IllegalArgumentException("illegal value for dstSavings"); - + this.dstSavings = dstSavings; } @@ -774,23 +767,28 @@ public class SimpleTimeZone extends TimeZone } /** - * Returns the number of days in the given month. It does always - * use the Gregorian leap year rule. + * Returns the number of days in the given month. + * Uses gregorian rules prior to 1582 (The default and earliest cutover) * @param month The month, zero based; use one of the Calendar constants. * @param year The year. */ private int getDaysInMonth(int month, int year) { - // Most of this is copied from GregorianCalendar.getActualMaximum() if (month == Calendar.FEBRUARY) { - return ((year & 3) == 0 && (year % 100 != 0 || year % 400 == 0)) - ? 29 : 28; + if ((year & 3) != 0) + 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 - return 30 + (month & 1); + return monthArr[month]; } /** @@ -804,23 +802,19 @@ public class SimpleTimeZone extends TimeZone * @param mode the change mode; same semantic as startMode. * @param month the change month; same semantic as startMonth. * @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. * same semantic as startDayOfWeek. * @return true, if cal is before the change, false if cal is on * or after the change. */ - private boolean isBefore(int calYear, - int calMonth, int calDayOfMonth, int calDayOfWeek, - int calMillis, int mode, int month, - int day, int dayOfWeek, int millis) + private boolean isBefore(int calYear, int calMonth, int calDayOfMonth, + int calDayOfWeek, int calMillis, int mode, + int month, int day, int dayOfWeek, int millis) { - // This method is called by Calendar, so we mustn't use that class. // We have to do all calculations by hand. - // check the months: - // XXX - this is not correct: // for the DOW_GE_DOM and DOW_LE_DOM modes the change date may // be in a different month. @@ -835,7 +829,7 @@ public class SimpleTimeZone extends TimeZone return calDayOfMonth < day; break; case DOW_IN_MONTH_MODE: - { + { // This computes the day of month of the day of type // "dayOfWeek" that lies in the same (sunday based) week as cal. calDayOfMonth += (dayOfWeek - calDayOfWeek); @@ -844,7 +838,6 @@ public class SimpleTimeZone extends TimeZone // after dividing by 7). If we count from the end of the // month, we get want a -7 based number counting the days from // the end: - if (day < 0) calDayOfMonth -= getDaysInMonth(calMonth, calYear) + 7; else @@ -857,9 +850,9 @@ public class SimpleTimeZone extends TimeZone // 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 // 34 35 36 -9 -8 -7 - // Now we calculate the day of week in month: int week = calDayOfMonth / 7; + // day > 0 day < 0 // 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 @@ -867,7 +860,6 @@ public class SimpleTimeZone extends TimeZone // 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 // 4 5 5 -1 -1 -1 - if (week != day) return week < day; @@ -876,26 +868,25 @@ public class SimpleTimeZone extends TimeZone // daylight savings starts/ends on the given day. break; - } - + } case DOW_LE_DOM_MODE: // The greatest sunday before or equal December, 12 // is the same as smallest sunday after or equal December, 6. day = Math.abs(day) - 6; - case DOW_GE_DOM_MODE: - // Calculate the day of month of the day of type // "dayOfWeek" that lies before (or on) the given date. - calDayOfMonth -= (calDayOfWeek < dayOfWeek ? 7 : 0) - + calDayOfWeek - dayOfWeek; + calDayOfMonth -= (calDayOfWeek < dayOfWeek ? 7 : 0) + calDayOfWeek + - dayOfWeek; if (calDayOfMonth < day) return true; if (calDayOfWeek != dayOfWeek || calDayOfMonth >= day + 7) return false; + // now we have the same day break; } + // the millis decides: return (calMillis < millis); } @@ -914,40 +905,35 @@ public class SimpleTimeZone extends TimeZone /** * Generates the hashCode for the SimpleDateFormat object. It is * the rawOffset, possibly, if useDaylightSavings is true, xored - * with startYear, startMonth, startDayOfWeekInMonth, ..., endTime. + * with startYear, startMonth, startDayOfWeekInMonth, ..., endTime. */ public synchronized int hashCode() { - return rawOffset ^ - (useDaylight ? - startMonth ^ startDay ^ startDayOfWeek ^ startTime - ^ endMonth ^ endDay ^ endDayOfWeek ^ endTime : 0); + return rawOffset + ^ (useDaylight + ? startMonth ^ startDay ^ startDayOfWeek ^ startTime ^ endMonth + ^ endDay ^ endDayOfWeek ^ endTime : 0); } public synchronized boolean equals(Object o) { if (this == o) return true; - if (!(o instanceof SimpleTimeZone)) + if (! (o instanceof SimpleTimeZone)) return false; SimpleTimeZone zone = (SimpleTimeZone) o; - if (zone.hashCode() != hashCode() - || !getID().equals(zone.getID()) - || rawOffset != zone.rawOffset || useDaylight != zone.useDaylight) + if (zone.hashCode() != hashCode() || ! getID().equals(zone.getID()) + || rawOffset != zone.rawOffset || useDaylight != zone.useDaylight) return false; - if (!useDaylight) + if (! useDaylight) return true; - return (startYear == zone.startYear - && startMonth == zone.startMonth - && startDay == zone.startDay - && startDayOfWeek == zone.startDayOfWeek - && startTime == zone.startTime - && startTimeMode == zone.startTimeMode - && endMonth == zone.endMonth - && endDay == zone.endDay - && endDayOfWeek == zone.endDayOfWeek - && endTime == zone.endTime - && endTimeMode == zone.endTimeMode); + return (startYear == zone.startYear && startMonth == zone.startMonth + && startDay == zone.startDay + && startDayOfWeek == zone.startDayOfWeek + && startTime == zone.startTime + && startTimeMode == zone.startTimeMode && endMonth == zone.endMonth + && endDay == zone.endDay && endDayOfWeek == zone.endDayOfWeek + && endTime == zone.endTime && endTimeMode == zone.endTimeMode); } /** @@ -962,25 +948,21 @@ public class SimpleTimeZone extends TimeZone { if (this == other) return true; - if (!(other instanceof SimpleTimeZone)) + if (! (other instanceof SimpleTimeZone)) return false; SimpleTimeZone zone = (SimpleTimeZone) other; - if (zone.hashCode() != hashCode() - || rawOffset != zone.rawOffset || useDaylight != zone.useDaylight) + if (zone.hashCode() != hashCode() || rawOffset != zone.rawOffset + || useDaylight != zone.useDaylight) return false; - if (!useDaylight) + if (! useDaylight) return true; - return (startYear == zone.startYear - && startMonth == zone.startMonth - && startDay == zone.startDay - && startDayOfWeek == zone.startDayOfWeek - && startTime == zone.startTime - && startTimeMode == zone.startTimeMode - && endMonth == zone.endMonth - && endDay == zone.endDay - && endDayOfWeek == zone.endDayOfWeek - && endTime == zone.endTime - && endTimeMode == zone.endTimeMode); + return (startYear == zone.startYear && startMonth == zone.startMonth + && startDay == zone.startDay + && startDayOfWeek == zone.startDayOfWeek + && startTime == zone.startTime + && startTimeMode == zone.startTimeMode && endMonth == zone.endMonth + && endDay == zone.endDay && endDayOfWeek == zone.endDayOfWeek + && endTime == zone.endTime && endTimeMode == zone.endTimeMode); } /** @@ -991,26 +973,17 @@ public class SimpleTimeZone extends TimeZone { // the test for useDaylight is an incompatibility to jdk1.2, but // I think this shouldn't hurt. - return getClass().getName() + "[" - + "id=" + getID() - + ",offset=" + rawOffset - + ",dstSavings=" + dstSavings - + ",useDaylight=" + useDaylight - + (useDaylight ? - ",startYear=" + startYear - + ",startMode=" + startMode - + ",startMonth=" + startMonth - + ",startDay=" + startDay - + ",startDayOfWeek=" + startDayOfWeek - + ",startTime=" + startTime - + ",startTimeMode=" + startTimeMode - + ",endMode=" + endMode - + ",endMonth=" + endMonth - + ",endDay=" + endDay - + ",endDayOfWeek=" + endDayOfWeek - + ",endTime=" + endTime - + ",endTimeMode=" + endTimeMode - : "") + "]"; + return getClass().getName() + "[" + "id=" + getID() + ",offset=" + + rawOffset + ",dstSavings=" + dstSavings + ",useDaylight=" + + useDaylight + + (useDaylight + ? ",startYear=" + startYear + ",startMode=" + startMode + + ",startMonth=" + startMonth + ",startDay=" + startDay + + ",startDayOfWeek=" + startDayOfWeek + ",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 startMode = DOW_IN_MONTH_MODE; startTimeMode = WALL_TIME; endTimeMode = WALL_TIME; - serialVersionOnStream = 2; } + serialVersionOnStream = 2; + } else { int length = input.readInt(); @@ -1054,29 +1028,31 @@ public class SimpleTimeZone extends TimeZone * <code>start/endDay(OfWeek)</code>-Fields are written in the * DOW_IN_MONTH_MODE rule, since this was the only supported rule * in 1.1. - * + * * In the optional section, we write first the length of an byte * array as int and afterwards the byte array itself. The byte * array contains in this release four elements, namely the real * startDay, startDayOfWeek endDay, endDayOfWeek in that Order. * These fields are needed, because for compatibility reasons only * approximative values are written to the required section, as - * described above. + * described above. */ private void writeObject(java.io.ObjectOutputStream output) throws java.io.IOException { byte[] byteArray = new byte[] - { - (byte) startDay, (byte) startDayOfWeek, - (byte) endDay, (byte) endDayOfWeek}; + { + (byte) startDay, (byte) startDayOfWeek, (byte) endDay, + (byte) endDayOfWeek + }; /* calculate the approximation for JDK 1.1 */ switch (startMode) { case DOM_MODE: - startDayOfWeek = Calendar.SUNDAY; // random day of week - // fall through + startDayOfWeek = Calendar.SUNDAY; // random day of week + + // fall through case DOW_GE_DOM_MODE: case DOW_LE_DOM_MODE: startDay = (startDay + 6) / 7; @@ -1085,7 +1061,8 @@ public class SimpleTimeZone extends TimeZone { case DOM_MODE: endDayOfWeek = Calendar.SUNDAY; - // fall through + + // fall through case DOW_GE_DOM_MODE: case DOW_LE_DOM_MODE: endDay = (endDay + 6) / 7; diff --git a/libjava/java/util/TimeZone.java b/libjava/java/util/TimeZone.java index 0685e604600..7c811795790 100644 --- a/libjava/java/util/TimeZone.java +++ b/libjava/java/util/TimeZone.java @@ -447,6 +447,7 @@ public abstract class TimeZone implements java.io.Serializable, Cloneable Calendar.MARCH, -1, Calendar.SUNDAY, 2000 * 3600, Calendar.OCTOBER, -1, Calendar.SUNDAY, 2000 * 3600); timezones0.put("CET", tz); + timezones0.put("CEST", tz); timezones0.put("ECT", tz); timezones0.put("MET", tz); timezones0.put("Africa/Ceuta", tz); |