[kaffe] CVS kaffe (robilad): Resynced with GNU Classpath: Various Calendar and DateFormat patches

Kaffe CVS cvs-commits at kaffe.org
Thu Feb 3 09:05:41 PST 2005


PatchSet 5966 
Date: 2005/02/03 17:01:23
Author: robilad
Branch: HEAD
Tag: (none) 
Log:
Resynced with GNU Classpath: Various Calendar and DateFormat patches

2005-02-03  Dalibor Topic  <robilad at kaffe.org>

Resynced with GNU Classpath.

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

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

2005-02-02  Andrew John Hughes  <gnu_andrew at 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-01  Sven de Marothy  <sven at physto.se>

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

2005-02-01  Sven de Marothy  <sven at 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.

Members: 
	ChangeLog:1.3505->1.3506 
	libraries/javalib/java/text/SimpleDateFormat.java:1.42->1.43 
	libraries/javalib/java/util/Calendar.java:1.30->1.31 
	libraries/javalib/java/util/GregorianCalendar.java:1.33->1.34 

Index: kaffe/ChangeLog
diff -u kaffe/ChangeLog:1.3505 kaffe/ChangeLog:1.3506
--- kaffe/ChangeLog:1.3505	Thu Feb  3 04:10:10 2005
+++ kaffe/ChangeLog	Thu Feb  3 17:01:23 2005
@@ -1,5 +1,47 @@
 2005-02-03  Dalibor Topic  <robilad at kaffe.org>
 
+	Resynced with GNU Classpath.
+	
+	2005-02-02  Sven de Marothy  <sven at physto.se>
+
+	* java/util/Calendar.java 
+	(set) Invalidate all fields on first call to set().
+
+	2005-02-02  Andrew John Hughes  <gnu_andrew at 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-01  Sven de Marothy  <sven at physto.se>
+	
+	* java/util/GregorianCalendar.java
+        (computeTime): Fixed handling of time zones.
+
+	2005-02-01  Sven de Marothy  <sven at 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-03  Dalibor Topic  <robilad at kaffe.org>
+
 	* configure.ac: Abort if zip can't be found.
 
 2005-02-03  Dalibor Topic  <robilad at kaffe.org>
Index: kaffe/libraries/javalib/java/text/SimpleDateFormat.java
diff -u kaffe/libraries/javalib/java/text/SimpleDateFormat.java:1.42 kaffe/libraries/javalib/java/text/SimpleDateFormat.java:1.43
--- kaffe/libraries/javalib/java/text/SimpleDateFormat.java:1.42	Sat Jan 29 15:47:08 2005
+++ kaffe/libraries/javalib/java/text/SimpleDateFormat.java	Thu Feb  3 17:01:25 2005
@@ -45,6 +45,7 @@
 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;
@@ -68,14 +69,12 @@
    * 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 or invalid characters in the format
-   * pattern.
+   * of pattern characters.
    */
   private class CompiledField
   {
     /**
      * The ID of the field within the local pattern characters,
-     * or -1 if the sequence is invalid.
      */
     private int field;
 
@@ -106,8 +105,7 @@
 
     /**
      * Retrieves the ID of the field relative to
-     * the local pattern characters, or -1 if
-     * the sequence is invalid.
+     * the local pattern characters.
      */
     public int getField()
     {
@@ -154,12 +152,81 @@
     }
   }
 
+  /**
+   * 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 root of the CLDR.  We set it here
@@ -167,6 +234,20 @@
   // 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
   {
@@ -182,9 +263,25 @@
 
     // 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
@@ -197,20 +294,21 @@
 
     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 CompiledField(-1,0,thisChar));
+ 	  // 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 CompiledField(-1,0,thisChar));
+	    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));
@@ -234,13 +332,31 @@
     }
   }
 
+  /**
+   * 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());
-    }
+    StringBuilder output = new StringBuilder(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();
   }
 
@@ -271,8 +387,12 @@
   }
   
   /**
-   * 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) 
   {
@@ -280,8 +400,13 @@
   }
 
   /**
-   * 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) 
   {
@@ -299,8 +424,14 @@
   }
 
   /**
-   * 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 is null.
+   * @throws IllegalArgumentException if the pattern is invalid.
    */
   public SimpleDateFormat(String pattern, DateFormatSymbols formatData)
   {
@@ -317,9 +448,6 @@
     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.
@@ -340,7 +468,7 @@
   public String toLocalizedPattern()
   {
     String localChars = formatData.getLocalPatternChars();
-    return applyLocalizedPattern (pattern, standardChars, localChars);
+    return translateLocalizedPattern(pattern, standardChars, localChars);
   }
 
   /**
@@ -348,6 +476,8 @@
    * 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)
   {
@@ -361,16 +491,34 @@
    * 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);
Index: kaffe/libraries/javalib/java/util/Calendar.java
diff -u kaffe/libraries/javalib/java/util/Calendar.java:1.30 kaffe/libraries/javalib/java/util/Calendar.java:1.31
--- kaffe/libraries/javalib/java/util/Calendar.java:1.30	Mon Jan 24 15:59:45 2005
+++ kaffe/libraries/javalib/java/util/Calendar.java	Thu Feb  3 17:01:25 2005
@@ -676,21 +676,72 @@
    */
   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 validity
+    // 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:
@@ -698,6 +749,7 @@
 	isSet[HOUR] = false;
 	break;
       case HOUR:
+	isSet[AM_PM] = true;
 	isSet[HOUR_OF_DAY] = false;
 	break;
       case DST_OFFSET:
@@ -775,11 +827,23 @@
   {
     isTimeSet = false;
     areFieldsSet = false;
-    for (int i = 0; i < FIELD_COUNT; i++)
-      {
-	isSet[i] = false;
-	fields[i] = 0;
-      }
+
+    int hour = fields[ZONE_OFFSET] / (60 * 60 * 1000);
+    int minute = (fields[ZONE_OFFSET] - 60 * 60 * 1000 * hour) / (60 * 1000);
+    int seconds = (fields[ZONE_OFFSET] - 60 * 60 * 1000 * hour
+                  - 60 * 1000 * minute) / 1000;
+    int millis = fields[ZONE_OFFSET] - 60 * 60 * 1000 * hour
+                 - 60 * 1000 * minute - seconds * 1000;
+    int[] tempFields = 
+                       {
+                         1, 1970, JANUARY, 1, 1, 1, 1, THURSDAY, 1, AM, hour,
+                         hour, minute, seconds, millis, fields[ZONE_OFFSET],
+                         fields[DST_OFFSET]
+                       };
+    fields = tempFields;
+    for (int i = 0; i < FIELD_COUNT - 2; i++)
+      isSet[i] = false;
+    isSet[ZONE_OFFSET] = isSet[DST_OFFSET] = true;
   }
 
   /**
Index: kaffe/libraries/javalib/java/util/GregorianCalendar.java
diff -u kaffe/libraries/javalib/java/util/GregorianCalendar.java:1.33 kaffe/libraries/javalib/java/util/GregorianCalendar.java:1.34
--- kaffe/libraries/javalib/java/util/GregorianCalendar.java:1.33	Sat Jan 29 15:27:42 2005
+++ kaffe/libraries/javalib/java/util/GregorianCalendar.java	Thu Feb  3 17:01:25 2005
@@ -182,20 +182,6 @@
   private static final int EPOCH_DAYS = 719162;
 
   /**
-   * Retrieves the resource bundle.  The resources should be loaded
-   * via this method only. Iff an application uses this method, the
-   * resourcebundle is required.
-   *
-   * @param locale the locale in use for this calendar.
-   * @return A resource bundle for the calendar for the specified locale.
-   */
-  private static ResourceBundle getBundle(Locale locale)
-  {
-    return ResourceBundle.getBundle(bundleName, locale,
-                                    ClassLoader.getSystemClassLoader());
-  }
-
-  /**
    * Constructs a new GregorianCalender representing the current
    * time, using the default time zone and the default locale.
    */
@@ -250,7 +236,9 @@
   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();
   }
 
@@ -376,103 +364,25 @@
   }
 
   /**
-   * <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)
-	  {
-	    // 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 (isSet[DAY_OF_MONTH])
-	  return new int[] { dayOfYear + fields[DAY_OF_MONTH], 0 };
-	if (isSet[WEEK_OF_MONTH] && isSet[DAY_OF_WEEK])
-	  {
-	    // the weekday of the first day in that month is:
-	    int weekday = getWeekDay(year, ++dayOfYear);
-
-	    return new int[]
-	           {
-	             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))
-	           };
-	  }
-      }
-
-    // MONTH + something did not succeed.
-    if (isSet[DAY_OF_YEAR])
-      return new int[] { 0, fields[DAY_OF_YEAR] };
+    int[] dayCount = { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 };
+    int dayOfYear = dayCount[month] + 1;
 
-    if (isSet[DAY_OF_WEEK] && isSet[WEEK_OF_YEAR])
-      {
-	int dayOfYear = getMinimalDaysInFirstWeek();
+    if (month > 1)
+      if (isLeapYear(year))
+	dayOfYear++;
 
-	// 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))
-	       };
-      }
+    boolean greg = isGregorian(year, dayOfYear);
+    int day = (int) getLinearDay(year, dayOfYear, greg);
 
-    // As last resort return Jan, 1st.
-    return new int[] { 1, 0 };
+    // The epoch was a thursday.
+    int weekday = (day + THURSDAY) % 7;
+    if (weekday <= 0)
+      weekday += 7;
+    return weekday;
   }
 
   /**
@@ -490,6 +400,90 @@
   }
 
   /**
+   * 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.");
+      }
+
+    if (isSet[WEEK_OF_MONTH])
+      {
+	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_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 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.");
+      }
+
+    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>).
    *
@@ -499,20 +493,73 @@
   protected synchronized void computeTime()
   {
     int millisInDay = 0;
-    int era = isSet[ERA] ? fields[ERA] : AD;
-    int year = isSet[YEAR] ? fields[YEAR] : 1970;
-    int month = isSet[MONTH] ? fields[MONTH] : 0;
-    int day = isSet[DAY_OF_MONTH] ? fields[DAY_OF_MONTH] : 1;
-    int minute = isSet[MINUTE] ? fields[MINUTE] : 0;
-    int second = isSet[SECOND] ? fields[SECOND] : 0;
-    int millis = isSet[MILLISECOND] ? fields[MILLISECOND] : 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;
 
+    if (! isLenient())
+      nonLeniencyCheck();
+
+    if (! isSet[MONTH])
+      {
+	// 5: YEAR + DAY_OF_WEEK + WEEK_OF_YEAR
+	if (isSet[DAY_OF_WEEK] || isSet[WEEK_OF_YEAR])
+	  {
+	    int first = getFirstDayOfMonth(year, 0);
+	    int offs;
+	    if ((8 - first) >= getMinimalDaysInFirstWeek())
+	      // start counting on first week
+	      offs = 1;
+	    else
+	      offs = 1 + (8 - first);
+
+	    month = 0;
+	    day = offs + 7 * (fields[WEEK_OF_YEAR] - 1);
+	    day += fields[DAY_OF_WEEK] - first;
+	  }
+	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
+		day = 1 + 7 * (fields[WEEK_OF_MONTH] - 1);
+		day += fields[DAY_OF_WEEK] - first;
+	      }
+	  }
+
+	// 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_OF_DAY])
@@ -528,71 +575,50 @@
 	  hour = 0;
       }
 
-    if (isLenient())
-      {
-	// 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));
+    // 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 (isSet[MONTH])
+    if (month < 0)
+      {
+	year += (int) month / 12;
+	month = month % 12;
+	if (month < 0)
 	  {
-	    if (month < 0)
-	      {
-		year += (int) month / 12;
-		month = month % 12;
-		if (month < 0)
-		  {
-		    month += 12;
-		    year--;
-		  }
-	      }
-	    if (month > 11)
-	      {
-		year += (month / 12);
-		month = month % 12;
-	      }
+	    month += 12;
+	    year--;
 	  }
+      }
+    if (month > 11)
+      {
+	year += (month / 12);
+	month = month % 12;
+      }
+
+    month_days[1] = isLeapYear(year) ? 29 : 28;
 
-	if (isSet[DAY_OF_MONTH])
+    while (day <= 0)
+      {
+	if (month == 0)
 	  {
+	    year--;
 	    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;
-		  }
-	      }
 	  }
+	month = (month + 11) % 12;
+	day += month_days[month];
       }
-    else
+    while (day > month_days[month])
       {
-	// non-lenient
-	if (month < 0 || month > 11 || hour < 0 || hour >= 24 || minute < 0
-	    || minute > 59 || second < 0 || second > 59 || millis < 0
-	    || millis >= 1000)
-	  throw new IllegalArgumentException();
-	if (day < 1 || day > month_days[month])
-	  throw new IllegalArgumentException();
-	millisInDay = (((hour * 60) + minute) * 60 + second) * 1000 + millis;
+	day -= (month_days[month]);
+	month = (month + 1) % 12;
+	if (month == 0)
+	  {
+	    year++;

*** Patch too long, truncated ***




More information about the kaffe mailing list