summaryrefslogtreecommitdiff
path: root/libjava/java/text
diff options
context:
space:
mode:
authorwarrenl <warrenl@138bc75d-0d04-0410-961f-82ee72b054a4>2000-11-28 03:09:22 +0000
committerwarrenl <warrenl@138bc75d-0d04-0410-961f-82ee72b054a4>2000-11-28 03:09:22 +0000
commit1fbe90973cde7a3c75bf3d48a0a533c218a635d3 (patch)
tree1662df7a9a7114295c1d93c1195edd57cd72c465 /libjava/java/text
parente878834be90739f89574a37d8303c85207eaefbc (diff)
downloadgcc-1fbe90973cde7a3c75bf3d48a0a533c218a635d3.tar.gz
* Makefile.am: Added natTimeZone.cc.
* Makefile.in: Rebuilt. * gnu/gcj/text/LocaleData_en.java: Added DateFormat entries. * java/text/DateFormatSymbols.java (ampms): Made package private. (eras): Made package private. (months): Made package private. (shortMonths): Made package private. (shortWeekdays): Made package private. (weekdays): Made package private. (formatPrefixes): New private field. (localPatternCharsDefault): Made private. (dateFormats): New package private field. (timeFormats): New package private field. (formatsForKey): New private method. (DateFormatSymbols(Locale)): Set dateFormats and timeFormats. (DateFormatSymbols(DateFormatSymbols)): Ditto. * java/text/SimpleDateFormat.java: Merged with Classpath. * java/util/TimeZone.java: Merged with Classpath. * java/util/natTimeZone.cc: New file. git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@37808 138bc75d-0d04-0410-961f-82ee72b054a4
Diffstat (limited to 'libjava/java/text')
-rw-r--r--libjava/java/text/DateFormatSymbols.java45
-rw-r--r--libjava/java/text/SimpleDateFormat.java1481
2 files changed, 1084 insertions, 442 deletions
diff --git a/libjava/java/text/DateFormatSymbols.java b/libjava/java/text/DateFormatSymbols.java
index b63bf369209..c8250c9344a 100644
--- a/libjava/java/text/DateFormatSymbols.java
+++ b/libjava/java/text/DateFormatSymbols.java
@@ -24,21 +24,29 @@ import java.util.ResourceBundle;
public class DateFormatSymbols extends Object
implements java.io.Serializable, Cloneable
{
- private String[] ampms;
- private String[] eras;
+ String[] ampms;
+ String[] eras;
private String localPatternChars;
- private String[] months;
- private String[] shortMonths;
- private String[] shortWeekdays;
- private String[] weekdays;
+ String[] months;
+ String[] shortMonths;
+ String[] shortWeekdays;
+ String[] weekdays;
private String[][] zoneStrings;
private static final long serialVersionUID = -5987973545549424702L;
+ // The order of these prefixes must be the same as in DateFormat
+ // FIXME: XXX: Note that this differs from the Classpath implemention
+ // in that there is no "default" entry; that is due to differing
+ // implementations where DateFormat.DEFAULT is MEDIUM here but 4 in
+ // Classpath (the JCL says it should be MEDIUM). That will need to be
+ // resolved in the merge.
+ private static final String[] formatPrefixes = { "full", "long", "medium", "short" };
+
private static final String[] ampmsDefault = {"AM", "PM" };
private static final String[] erasDefault = {"BC", "AD" };
// localPatternCharsDefault is used by SimpleDateFormat.
- protected static final String localPatternCharsDefault
+ private static final String localPatternCharsDefault
= "GyMdkHmsSEDFwWahKz";
private static final String[] monthsDefault = {
"January", "February", "March", "April", "May", "June",
@@ -77,6 +85,24 @@ public class DateFormatSymbols extends Object
/**/ "Alaska Daylight Time", "ADT", "Anchorage" }
};
+ // These are each arrays with a value for SHORT, MEDIUM, LONG, FULL,
+ // and DEFAULT (constants defined in java.text.DateFormat). While
+ // not part of the official spec, we need a way to get at locale-specific
+ // default formatting patterns. They are declared package scope so
+ // as to be easily accessible where needed (DateFormat, SimpleDateFormat).
+ transient String[] dateFormats;
+ transient String[] timeFormats;
+
+ private String[] formatsForKey(ResourceBundle res, String key)
+ {
+ String[] values = new String [formatPrefixes.length];
+ for (int i = 0; i < formatPrefixes.length; i++)
+ {
+ values[i] = res.getString(formatPrefixes[i]+key);
+ }
+ return values;
+ }
+
private final Object safeGetResource (ResourceBundle res,
String key, Object def)
{
@@ -116,6 +142,9 @@ public class DateFormatSymbols extends Object
weekdays = (String[]) safeGetResource (res, "weekdays", weekdaysDefault);
zoneStrings = (String[][]) safeGetResource (res, "zoneStrings",
zoneStringsDefault);
+
+ dateFormats = formatsForKey(res, "DateFormat");
+ timeFormats = formatsForKey(res, "TimeFormat");
}
public DateFormatSymbols ()
@@ -134,6 +163,8 @@ public class DateFormatSymbols extends Object
shortWeekdays = old.shortWeekdays;
weekdays = old.weekdays;
zoneStrings = old.zoneStrings;
+ dateFormats = old.dateFormats;
+ timeFormats = old.timeFormats;
}
public String[] getAmPmStrings()
diff --git a/libjava/java/text/SimpleDateFormat.java b/libjava/java/text/SimpleDateFormat.java
index feb64f0d02d..e2f70cddcc6 100644
--- a/libjava/java/text/SimpleDateFormat.java
+++ b/libjava/java/text/SimpleDateFormat.java
@@ -1,107 +1,268 @@
-/* Copyright (C) 1998, 1999, 2000 Free Software Foundation
+/* SimpleDateFormat.java -- A class for parsing/formating simple
+ date constructs
+ Copyright (C) 1998, 1999, 2000 Free Software Foundation, Inc.
- This file is part of libgcj.
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+02111-1307 USA.
+
+As a special exception, if you link this library with other files to
+produce an executable, this library does not by itself cause the
+resulting executable to be covered by the GNU General Public License.
+This exception does not however invalidate any other reasons why the
+executable file might be covered by the GNU General Public License. */
-This software is copyrighted work licensed under the terms of the
-Libgcj License. Please consult the file "LIBGCJ_LICENSE" for
-details. */
package java.text;
-import java.util.*;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.Enumeration;
+import java.util.GregorianCalendar;
+import java.util.Locale;
+import java.util.TimeZone;
+import java.util.SimpleTimeZone;
+import java.util.Vector;
import java.io.ObjectInputStream;
import java.io.IOException;
/**
- * @author Per Bothner <bothner@cygnus.com>
- * @date October 25, 1998.
+ * SimpleDateFormat provides convenient methods for parsing and formatting
+ * dates using Gregorian calendars (see java.util.GregorianCalendar).
*/
-/* Written using "Java Class Libraries", 2nd edition, plus online
- * API docs for JDK 1.2 beta from http://www.javasoft.com.
- * Status: Believed complete and correct to 1.2.
- */
-
-public class SimpleDateFormat extends DateFormat
+public class SimpleDateFormat extends DateFormat
{
- // Serialization fields.
- private Date defaultCenturyStart = new Date();
- private DateFormatSymbols formatData;
+ /** A pair class used by SimpleDateFormat as a compiled representation
+ * of a format string.
+ */
+ private class FieldSizePair
+ {
+ public int field;
+ public int size;
+
+ /** Constructs a pair with the given field and size values */
+ public FieldSizePair(int f, int s) {
+ field = f;
+ size = s;
+ }
+ }
+
+ private transient Vector tokens;
+ private DateFormatSymbols formatData; // formatData
+ private Date defaultCenturyStart =
+ new Date(System.currentTimeMillis() - (80*365*24*60*60*1000));
private String pattern;
- private int serialVersionOnStream = 1;
+ private int serialVersionOnStream = 1; // 0 indicates JDK1.1.3 or earlier
private static final long serialVersionUID = 4774881970558875024L;
- // Serialization method.
+ // 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 = "GyMdkHmsSEDFwWahKz";
+
private void readObject(ObjectInputStream stream)
throws IOException, ClassNotFoundException
{
stream.defaultReadObject();
if (serialVersionOnStream < 1)
{
- defaultCenturyStart = new Date();
+ defaultCenturyStart =
+ new Date(System.currentTimeMillis() - (80*365*24*60*60*1000));
serialVersionOnStream = 1;
}
+
+ // Set up items normally taken care of by the constructor.
+ tokens = new Vector();
+ compileFormat(pattern);
}
- public SimpleDateFormat ()
+ private void compileFormat(String pattern)
{
- this("dd/MM/yy HH:mm", Locale.getDefault());
- }
+ // Any alphabetical characters are treated as pattern characters
+ // unless enclosed in single quotes.
- public SimpleDateFormat (String pattern)
+ char thisChar;
+ int pos;
+ int field;
+ FieldSizePair current = null;
+
+ for (int i=0; i<pattern.length(); i++) {
+ thisChar = pattern.charAt(i);
+ field = formatData.getLocalPatternChars().indexOf(thisChar);
+ if (field == -1) {
+ current = null;
+ if (Character.isLetter(thisChar)) {
+ // Not a valid letter
+ tokens.addElement(new FieldSizePair(-1,0));
+ } 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.addElement(new FieldSizePair(-1,0));
+ }
+ if ((pos+1 < pattern.length()) && (pattern.charAt(pos+1) == '\'')) {
+ tokens.addElement(pattern.substring(i+1,pos+1));
+ } else {
+ tokens.addElement(pattern.substring(i+1,pos));
+ }
+ i = pos;
+ } else {
+ // A special character
+ tokens.addElement(new Character(thisChar));
+ }
+ } else {
+ // A valid field
+ if ((current != null) && (field == current.field)) {
+ current.size++;
+ } else {
+ current = new FieldSizePair(field,1);
+ tokens.addElement(current);
+ }
+ }
+ }
+ }
+
+ public String toString()
{
- this(pattern, Locale.getDefault());
+ StringBuffer output = new StringBuffer();
+ Enumeration e = tokens.elements();
+ while (e.hasMoreElements()) {
+ output.append(e.nextElement().toString());
+ }
+ return output.toString();
}
-
- public SimpleDateFormat (String pattern, Locale locale)
+
+ /**
+ * Constructs a SimpleDateFormat using the default pattern for
+ * the default locale.
+ */
+ public SimpleDateFormat()
{
- this.pattern = pattern;
- this.calendar = Calendar.getInstance(locale);
- this.numberFormat = NumberFormat.getInstance(locale);
- numberFormat.setGroupingUsed(false);
- this.formatData = new DateFormatSymbols (locale);
+ /*
+ * There does not appear to be a standard API for determining
+ * what the default pattern for a locale is, so use package-scope
+ * variables in DateFormatSymbols to encapsulate this.
+ */
+ super();
+ Locale locale = Locale.getDefault();
+ calendar = new GregorianCalendar(locale);
+ tokens = new Vector();
+ formatData = new DateFormatSymbols(locale);
+ pattern = formatData.dateFormats[DEFAULT]+' '+formatData.timeFormats[DEFAULT];
+ compileFormat(pattern);
+ numberFormat = NumberFormat.getInstance(locale);
}
-
- public SimpleDateFormat (String pattern, DateFormatSymbols formatData)
+
+ /**
+ * Creates a date formatter using the specified pattern, with the default
+ * DateFormatSymbols for the default locale.
+ */
+ public SimpleDateFormat(String pattern)
{
- this.pattern = pattern;
- this.formatData = formatData;
- this.calendar = Calendar.getInstance();
- this.numberFormat = NumberFormat.getInstance();
- numberFormat.setGroupingUsed(false);
+ this(pattern, Locale.getDefault());
}
- public Date get2DigitYearStart()
+ /**
+ * Creates a date formatter using the specified pattern, with the default
+ * DateFormatSymbols for the given locale.
+ */
+ public SimpleDateFormat(String pattern, Locale locale)
{
- return defaultCenturyStart;
+ super();
+ calendar = new GregorianCalendar(locale);
+ tokens = new Vector();
+ formatData = new DateFormatSymbols(locale);
+ compileFormat(pattern);
+ this.pattern = pattern;
+ numberFormat = NumberFormat.getInstance(locale);
}
- public void set2DigitYearStart(Date startDate)
- {
- defaultCenturyStart = startDate;
+ /**
+ * Creates a date formatter using the specified pattern. The
+ * specified DateFormatSymbols will be used when formatting.
+ */
+ public SimpleDateFormat(String pattern, DateFormatSymbols formatData) {
+ super();
+ calendar = new GregorianCalendar();
+ // FIXME: XXX: Is it really necessary to set the timezone?
+ // The Calendar constructor is supposed to take care of this.
+ calendar.setTimeZone(TimeZone.getDefault());
+ tokens = new Vector();
+ this.formatData = formatData;
+ compileFormat(pattern);
+ this.pattern = pattern;
+ numberFormat = NumberFormat.getInstance();
}
- public DateFormatSymbols getDateFormatSymbols ()
+ // 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.
+ *
+ * @return The format string.
+ */
+ public String toPattern()
{
- return formatData;
+ return pattern;
}
- public void setDateFormatSymbols (DateFormatSymbols value)
+ /**
+ * This method returns a string with the formatting pattern being used
+ * by this object. This string is localized.
+ *
+ * @return The format string.
+ */
+ public String toLocalizedPattern()
{
- formatData = value;
+ String localChars = formatData.getLocalPatternChars();
+ return applyLocalizedPattern (pattern, standardChars, localChars);
}
- public String toPattern ()
+ /**
+ * This method sets the formatting pattern that should be used by this
+ * object. This string is not localized.
+ *
+ * @param pattern The new format pattern.
+ */
+ public void applyPattern(String pattern)
{
- return pattern;
+ tokens = new Vector();
+ compileFormat(pattern);
+ this.pattern = pattern;
}
- public void applyPattern (String pattern)
+ /**
+ * This method sets the formatting pattern that should be used by this
+ * object. This string is localized.
+ *
+ * @param pattern The new format pattern.
+ */
+ public void applyLocalizedPattern(String pattern)
{
- this.pattern = pattern;
+ String localChars = formatData.getLocalPatternChars();
+ pattern = applyLocalizedPattern (pattern, localChars, standardChars);
+ applyPattern(pattern);
}
- private String applyLocalizedPattern (String pattern,
- String oldChars, String newChars)
+ private String applyLocalizedPattern(String pattern,
+ String oldChars, String newChars)
{
int len = pattern.length();
StringBuffer buf = new StringBuffer(len);
@@ -122,424 +283,874 @@ public class SimpleDateFormat extends DateFormat
return buf.toString();
}
- public void applyLocalizedPattern (String pattern)
+ /**
+ * Returns the start of the century used for two digit years.
+ *
+ * @return A <code>Date</code> representing the start of the century
+ * for two digit years.
+ */
+ public Date get2DigitYearStart()
{
- String localChars = formatData.getLocalPatternChars();
- String standardChars = DateFormatSymbols.localPatternCharsDefault;
- pattern = applyLocalizedPattern (pattern, localChars, standardChars);
- applyPattern(pattern);
+ return defaultCenturyStart;
}
- public String toLocalizedPattern ()
+ /**
+ * Sets the start of the century used for two digit years.
+ *
+ * @param date A <code>Date</code> representing the start of the century for
+ * two digit years.
+ */
+ public void set2DigitYearStart(Date date)
{
- String localChars = formatData.getLocalPatternChars();
- String standardChars = DateFormatSymbols.localPatternCharsDefault;
- return applyLocalizedPattern (pattern, standardChars, localChars);
+ defaultCenturyStart = date;
}
- private final void append (StringBuffer buf, int value, int numDigits)
+ /**
+ * This method returns the format symbol information used for parsing
+ * and formatting dates.
+ *
+ * @return The date format symbols.
+ */
+ public DateFormatSymbols getDateFormatSymbols()
{
- numberFormat.setMinimumIntegerDigits(numDigits);
- numberFormat.format(value, buf, null);
+ return formatData;
}
- public StringBuffer format (Date date, StringBuffer buf, FieldPosition pos)
+ /**
+ * This method sets the format symbols information used for parsing
+ * and formatting dates.
+ *
+ * @param formatData The date format symbols.
+ */
+ public void setDateFormatSymbols(DateFormatSymbols formatData)
+ {
+ this.formatData = formatData;
+ }
+
+ /**
+ * This methods tests whether the specified object is equal to this
+ * object. This will be true if and only if the specified object:
+ * <p>
+ * <ul>
+ * <li>Is not <code>null</code>.
+ * <li>Is an instance of <code>SimpleDateFormat</code>.
+ * <li>Is equal to this object at the superclass (i.e., <code>DateFormat</code>)
+ * level.
+ * <li>Has the same formatting pattern.
+ * <li>Is using the same formatting symbols.
+ * <li>Is using the same century for two digit years.
+ * </ul>
+ *
+ * @param obj The object to compare for equality against.
+ *
+ * @return <code>true</code> if the specified object is equal to this object,
+ * <code>false</code> otherwise.
+ */
+ public boolean equals(Object o)
{
- Calendar calendar = (Calendar) this.calendar.clone();
- calendar.setTime(date);
- int len = pattern.length();
- int quoteStart = -1;
- for (int i = 0; i < len; i++)
- {
- char ch = pattern.charAt(i);
- if (ch == '\'')
- {
- // We must do a little lookahead to see if we have two
- // single quotes embedded in quoted text.
- if (i < len - 1 && pattern.charAt(i + 1) == '\'')
- {
- ++i;
- buf.append(ch);
- }
- else
- quoteStart = quoteStart < 0 ? i : -1;
- }
- // From JCL: any characters in the pattern that are not in
- // the ranges of [a..z] and [A..Z] are treated as quoted
- // text.
- else if (quoteStart != -1
- || ((ch < 'a' || ch > 'z')
- && (ch < 'A' || ch > 'Z')))
- buf.append(ch);
- else
+ if (o == null)
+ return false;
+
+ if (!super.equals(o))
+ return false;
+
+ if (!(o instanceof SimpleDateFormat))
+ return false;
+
+ SimpleDateFormat sdf = (SimpleDateFormat)o;
+
+ if (!toPattern().equals(sdf.toPattern()))
+ return false;
+
+ if (!get2DigitYearStart().equals(sdf.get2DigitYearStart()))
+ return false;
+
+ if (!getDateFormatSymbols().equals(sdf.getDateFormatSymbols()))
+ return false;
+
+ return true;
+ }
+
+
+ /**
+ * Formats the date input according to the format string in use,
+ * appending to the specified StringBuffer. The input StringBuffer
+ * is returned as output for convenience.
+ */
+ public StringBuffer format(Date date, StringBuffer buffer, FieldPosition pos) {
+ String temp;
+ Calendar theCalendar = (Calendar) calendar.clone();
+ theCalendar.setTime(date);
+
+ // go through vector, filling in fields where applicable, else toString
+ Enumeration e = tokens.elements();
+ while (e.hasMoreElements()) {
+ Object o = e.nextElement();
+ if (o instanceof FieldSizePair) {
+ FieldSizePair p = (FieldSizePair) o;
+ int beginIndex = buffer.length();
+ switch (p.field) {
+ case ERA_FIELD:
+ buffer.append(formatData.eras[theCalendar.get(Calendar.ERA)]);
+ break;
+ case YEAR_FIELD:
+ temp = String.valueOf(theCalendar.get(Calendar.YEAR));
+ if (p.size < 4)
+ buffer.append(temp.substring(temp.length()-2));
+ else
+ buffer.append(temp);
+ break;
+ case MONTH_FIELD:
+ if (p.size < 3)
+ withLeadingZeros(theCalendar.get(Calendar.MONTH)+1,p.size,buffer);
+ else if (p.size < 4)
+ buffer.append(formatData.shortMonths[theCalendar.get(Calendar.MONTH)]);
+ else
+ buffer.append(formatData.months[theCalendar.get(Calendar.MONTH)]);
+ break;
+ case DATE_FIELD:
+ withLeadingZeros(theCalendar.get(Calendar.DATE),p.size,buffer);
+ break;
+ case HOUR_OF_DAY1_FIELD: // 1-12
+ withLeadingZeros(theCalendar.get(Calendar.HOUR),p.size,buffer);
+ break;
+ case HOUR_OF_DAY0_FIELD: // 0-23
+ withLeadingZeros(theCalendar.get(Calendar.HOUR_OF_DAY),p.size,buffer);
+ break;
+ case MINUTE_FIELD:
+ withLeadingZeros(theCalendar.get(Calendar.MINUTE),p.size,buffer);
+ break;
+ case SECOND_FIELD:
+ withLeadingZeros(theCalendar.get(Calendar.SECOND),p.size,buffer);
+ break;
+ case MILLISECOND_FIELD:
+ withLeadingZeros(theCalendar.get(Calendar.MILLISECOND),p.size,buffer);
+ break;
+ case DAY_OF_WEEK_FIELD:
+ if (p.size < 4)
+ buffer.append(formatData.shortWeekdays[theCalendar.get(Calendar.DAY_OF_WEEK)]);
+ else
+ buffer.append(formatData.weekdays[theCalendar.get(Calendar.DAY_OF_WEEK)]);
+ break;
+ case DAY_OF_YEAR_FIELD:
+ withLeadingZeros(theCalendar.get(Calendar.DAY_OF_YEAR),p.size,buffer);
+ break;
+ case DAY_OF_WEEK_IN_MONTH_FIELD:
+ withLeadingZeros(theCalendar.get(Calendar.DAY_OF_WEEK_IN_MONTH),p.size,buffer);
+ break;
+ case WEEK_OF_YEAR_FIELD:
+ withLeadingZeros(theCalendar.get(Calendar.WEEK_OF_YEAR),p.size,buffer);
+ break;
+ case WEEK_OF_MONTH_FIELD:
+ withLeadingZeros(theCalendar.get(Calendar.WEEK_OF_MONTH),p.size,buffer);
+ break;
+ case AM_PM_FIELD:
+ buffer.append(formatData.ampms[theCalendar.get(Calendar.AM_PM)]);
+ break;
+ case HOUR1_FIELD: // 1-24
+ withLeadingZeros(theCalendar.get(Calendar.HOUR_OF_DAY)+1,p.size,buffer);
+ break;
+ case HOUR0_FIELD: // 0-11
+ withLeadingZeros(theCalendar.get(Calendar.HOUR)-1,p.size,buffer);
+ break;
+ case TIMEZONE_FIELD:
+ // TODO
+ break;
+ default:
+ throw new IllegalArgumentException("Illegal pattern character");
+ }
+ if (pos != null && p.field == pos.getField())
{
- int first = i;
- int value;
- while (++i < len && pattern.charAt(i) == ch) ;
- int count = i - first; // Number of repetions of ch in pattern.
- int beginIndex = buf.length();
- int field;
- i--; // Skip all but last instance of ch in pattern.
- switch (ch)
- {
- case 'd':
- append(buf, calendar.get(Calendar.DATE), count);
- field = DateFormat.DATE_FIELD;
- break;
- case 'D':
- append(buf, calendar.get(Calendar.DAY_OF_YEAR), count);
- field = DateFormat.DAY_OF_YEAR_FIELD;
- break;
- case 'F':
- append(buf, calendar.get(Calendar.DAY_OF_WEEK_IN_MONTH),count);
- field = DateFormat.DAY_OF_WEEK_IN_MONTH_FIELD;
- break;
- case 'E':
- value = calendar.get(calendar.DAY_OF_WEEK);
- buf.append(count <= 3 ? formatData.getShortWeekdays()[value]
- : formatData.getWeekdays()[value]);
- field = DateFormat.DAY_OF_WEEK_FIELD;
- break;
- case 'w':
- append(buf, calendar.get(Calendar.WEEK_OF_YEAR), count);
- field = DateFormat.WEEK_OF_YEAR_FIELD;
- break;
- case 'W':
- append(buf, calendar.get(Calendar.WEEK_OF_MONTH), count);
- field = DateFormat.WEEK_OF_MONTH_FIELD;
- break;
- case 'M':
- value = calendar.get(Calendar.MONTH);
- if (count <= 2)
- append(buf, value + 1, count);
- else
- buf.append(count <= 3 ? formatData.getShortMonths()[value]
- : formatData.getMonths()[value]);
- field = DateFormat.MONTH_FIELD;
- break;
- case 'y':
- value = calendar.get(Calendar.YEAR);
- append(buf, count <= 2 ? value % 100 : value, count);
- field = DateFormat.YEAR_FIELD;
- break;
- case 'K':
- append(buf, calendar.get(Calendar.HOUR), count);
- field = DateFormat.HOUR0_FIELD;
- break;
- case 'h':
- value = ((calendar.get(Calendar.HOUR) + 11) % 12) + 1;
- append(buf, value, count);
- field = DateFormat.HOUR1_FIELD;
- break;
- case 'H':
- append(buf, calendar.get(Calendar.HOUR_OF_DAY), count);
- field = DateFormat.HOUR_OF_DAY0_FIELD;
- break;
- case 'k':
- value = ((calendar.get(Calendar.HOUR_OF_DAY) + 23) % 24) + 1;
- append(buf, value, count);
- field = DateFormat.HOUR_OF_DAY1_FIELD;
- break;
- case 'm':
- append(buf, calendar.get(Calendar.MINUTE), count);
- field = DateFormat.MINUTE_FIELD;
- break;
- case 's':
- append(buf, calendar.get(Calendar.SECOND), count);
- field = DateFormat.SECOND_FIELD;
- break;
- case 'S':
- append(buf, calendar.get(Calendar.MILLISECOND), count);
- field = DateFormat.MILLISECOND_FIELD;
- break;
- case 'a':
- value = calendar.get(calendar.AM_PM);
- buf.append(formatData.getAmPmStrings()[value]);
- field = DateFormat.AM_PM_FIELD;
- break;
- case 'z':
- String zoneID = calendar.getTimeZone().getID();
- String[][] zoneStrings = formatData.getZoneStrings();
- int zoneCount = zoneStrings.length;
- for (int j = 0; j < zoneCount; j++)
- {
- String[] strings = zoneStrings[j];
- if (zoneID.equals(strings[0]))
- {
- j = count > 3 ? 2 : 1;
- if (calendar.get(Calendar.DST_OFFSET) != 0)
- j+=2;
- zoneID = strings[j];
- break;
- }
- }
- buf.append(zoneID);
- field = DateFormat.TIMEZONE_FIELD;
- break;
- default:
- // Note that the JCL is actually somewhat
- // contradictory here. It defines the pattern letters
- // to be a particular list, but also says that a
- // pattern containing an invalid pattern letter must
- // throw an exception. It doesn't describe what an
- // invalid pattern letter might be, so we just assume
- // it is any letter in [a-zA-Z] not explicitly covered
- // above.
- throw new RuntimeException("bad format string");
- }
- if (pos != null && field == pos.getField())
- {
- pos.setBeginIndex(beginIndex);
- pos.setEndIndex(buf.length());
- }
+ pos.setBeginIndex(beginIndex);
+ pos.setEndIndex(buffer.length());
}
+ } else {
+ buffer.append(o.toString());
}
- return buf;
+ }
+ return buffer;
+ }
+
+ private void withLeadingZeros(int value, int length, StringBuffer buffer) {
+ String valStr = String.valueOf(value);
+ for (length -= valStr.length(); length > 0; length--)
+ buffer.append('0');
+ buffer.append(valStr);
+ }
+
+ private int indexInArray(String dateStr, int index, String[] values) {
+ int l1 = dateStr.length()-index;
+ int l2;
+
+ for (int i=0; i < values.length; i++) {
+ if (values[i] == null)
+ continue;
+
+ l2 = values[i].length();
+ //System.err.println(values[i] + " " + dateStr.substring(index,index+l2));
+ if ((l1 >= l2) && (dateStr.substring(index,index+l2).equals(values[i])))
+ return i;
+ }
+ return -1;
}
- private final boolean expect (String source, ParsePosition pos,
- char ch)
+ /*
+ * Get the actual year value, converting two digit years if necessary.
+ */
+ private int processYear(int val)
{
- int x = pos.getIndex();
- boolean r = x < source.length() && source.charAt(x) == ch;
- if (r)
- pos.setIndex(x + 1);
- else
- pos.setErrorIndex(x);
- return r;
+ if (val > 100)
+ return val;
+
+ Date d = get2DigitYearStart();
+ Calendar c = Calendar.getInstance();
+ c.setTime(d);
+ int y = c.get(YEAR_FIELD);
+
+ return ((y / 100) * 100) + val;
}
- public Date parse (String source, ParsePosition pos)
+ /*
+ * Ok, we ignore the format string and just try to parse what we can
+ * out of the string. We need, month, day, year at a minimum. The real
+ * killer is stuff like XX/XX/XX. How do we interpret that? Is is the
+ * US style MM/DD/YY or the European style DD/MM/YY. Or is it YYYY/MM/DD?
+ * I'm an American, so I guess you know which one I'm choosing....
+ */
+ private Date parseLenient(String dateStr, ParsePosition pos)
{
- int fmt_index = 0;
- int fmt_max = pattern.length();
+ int month = -1;
+ int day = -1;
+ int year = -1;
+ int era = -1;
+ int hour = -1;
+ int hour24 = -1;
+ int minute = -1;
+ int second = -1;
+ int millis = -1;
+ int ampm = -1;
+ int last = -1;
+ TimeZone tz = null;
+ char lastsep = ' ';
+ char nextchar = ' ';
+
+ Calendar cal = (Calendar)calendar.clone();
+ cal.clear();
+ cal.setTime(new Date(0));
+
+ int index = pos.getIndex();
+ String buf = dateStr.substring(index, dateStr.length());
- calendar.clear();
- int quote_start = -1;
- for (; fmt_index < fmt_max; ++fmt_index)
+ top:
+ for(;;)
{
- 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 (source, 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 (source, pos, ch))
- return null;
- continue;
- }
+ // Are we at the end of the string? If so, make sure we have
+ // enough data and return. // FIXME: Also detect sufficient data
+ // and return by setting buf to "" on an unparsible string.
+ if (buf.equals(""))
+ {
+ pos.setIndex(index);
- // 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 count = fmt_index - first;
- --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;
- int zone_number = 0;
- 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 = (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 (count <= 2)
- ;
- else
- {
- is_numeric = false;
- match = (count <= 3
- ? formatData.getShortMonths()
- : formatData.getMonths());
- }
- break;
- case 'y':
- calendar_field = Calendar.YEAR;
- if (count <= 2)
- offset = 1900;
- break;
- case 'K':
- calendar_field = Calendar.HOUR;
- break;
- case 'h':
- calendar_field = Calendar.HOUR;
- offset = -1;
- break;
- case 'H':
- calendar_field = Calendar.HOUR_OF_DAY;
- break;
- case 'k':
- calendar_field = Calendar.HOUR_OF_DAY;
- offset = -1;
- 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++)
- {
- String[] strings = zoneStrings[j];
- int k;
- for (k = 1; k < strings.length; ++k)
- {
- if (source.startsWith(strings[k], index))
- break;
- }
- if (k != strings.length)
- {
- if (k > 2)
- ; // FIXME: dst.
- zone_number = 0; // FIXME: dst.
- // FIXME: raw offset to SimpleTimeZone const.
- calendar.setTimeZone(new SimpleTimeZone (1, strings[0]));
- pos.setIndex(index + strings[k].length());
- break;
- }
- }
- if (! found_zone)
- {
- pos.setErrorIndex(pos.getIndex());
- return null;
- }
- break;
- default:
+ // This is the minimum we need
+ if ((month == -1) || (day == -1) || (year == -1))
+ {
+ pos.setErrorIndex(index);
+ return null;
+ }
+
+ if (tz != null)
+ cal.setTimeZone(tz);
+
+ cal.set(Calendar.YEAR, year);
+ cal.set(Calendar.MONTH, month - 1);
+ cal.set(Calendar.DATE, day);
+
+ if (ampm == 0)
+ cal.set(Calendar.AM_PM, Calendar.AM);
+ else if (ampm == 1)
+ cal.set(Calendar.AM_PM, Calendar.PM);
+
+ // If am/pm not set, we assume 24 hour day
+ if (hour != -1)
+ {
+ if (ampm == -1)
+ cal.set(Calendar.HOUR_OF_DAY, hour);
+ else
+ {
+ if (ampm == 0)
+ {
+ if (hour == 12)
+ hour = 0;
+ }
+ else
+ {
+ if (hour != 12)
+ hour += 12;
+ }
+
+ cal.set(Calendar.HOUR_OF_DAY, hour);
+ }
+ }
+
+ if (minute != -1)
+ cal.set(Calendar.MINUTE, minute);
+
+ if (second != -1)
+ cal.set(Calendar.SECOND, second);
+
+ if (millis != -1)
+ cal.set(Calendar.MILLISECOND, millis);
+
+ if (era == 0)
+ cal.set(Calendar.ERA, GregorianCalendar.BC);
+ else if (era == 1)
+ cal.set(Calendar.ERA, GregorianCalendar.AD);
+
+ return cal.getTime();
+ }
+
+ // Skip over whitespace and expected punctuation
+ char c = buf.charAt(0);
+ boolean comma_found = false;
+ while(Character.isWhitespace(c) || (c == ':') ||
+ (c == ',') || (c == '.') || (c == '/'))
+ {
+ lastsep = c;
+ if (c == ',') // This is a total and utter crock
+ comma_found = true;
+ buf = buf.substring(1);
+ if (buf.equals(""))
+ continue;
+ c = buf.charAt(0);
+ }
+
+ if (comma_found == true)
+ lastsep = ',';
+
+ // Is it a month name?
+ for (int i = 0; i < formatData.months.length; i++)
+ if ((formatData.months[i] != null)
+ && buf.startsWith(formatData.months[i]))
+ {
+ month = i + 1;
+ buf = buf.substring(formatData.months[i].length());
+ index += formatData.months[i].length();
+ last = MONTH_FIELD;
+ continue top;
+ }
+
+ // Is it a short month name?
+ for (int i = 0; i < formatData.shortMonths.length; i++)
+ if ((formatData.shortMonths[i] != null)
+ && buf.startsWith(formatData.shortMonths[i]))
+ {
+ month = i + 1;
+ buf = buf.substring(formatData.shortMonths[i].length());
+ index += formatData.shortMonths[i].length();
+ last = MONTH_FIELD;
+ continue top;
+ }
+
+ // Is it a weekday name?
+ for (int i = 0; i < formatData.weekdays.length; i++)
+ if ((formatData.weekdays[i] != null)
+ && buf.startsWith(formatData.weekdays[i]))
+ {
+ buf = buf.substring(formatData.weekdays[i].length());
+ index += formatData.weekdays[i].length();
+ last = DAY_OF_WEEK_FIELD;
+ continue top;
+ }
+
+ // Is it a short weekday name?
+ for (int i = 0; i < formatData.shortWeekdays.length; i++)
+ if ((formatData.shortWeekdays[i] != null)
+ && buf.startsWith(formatData.shortWeekdays[i]))
+ {
+ buf = buf.substring(formatData.shortWeekdays[i].length());
+ index += formatData.shortWeekdays[i].length();
+ last = DAY_OF_WEEK_FIELD;
+ continue top;
+ }
+
+ // Is this an am/pm string?
+ for (int i = 0; i < formatData.ampms.length; i++) {
+ if ((formatData.ampms[i] != null)
+ && buf.toLowerCase().startsWith(formatData.ampms[i].toLowerCase()))
+ {
+ ampm = i;
+ buf = buf.substring(formatData.ampms[i].length());
+ index += formatData.ampms[i].length();
+ last = AM_PM_FIELD;
+ continue top;
+ }
+ }
+
+ // See if we have a number
+ c = buf.charAt(0);
+ String nbrstr = "";
+ while (Character.isDigit(c))
+ {
+ nbrstr = nbrstr + c;
+ buf = buf.substring(1);
+ if (buf.equals(""))
+ break;
+ c = buf.charAt(0);
+ }
+
+ // If we didn't get a number, try for a timezone, otherwise set buf
+ // to "" and loop to see if we are done.
+ if (nbrstr.equals(""))
+ {
+ // Ok, try for a timezone name
+ while(!Character.isWhitespace(c) && (c != ',') && (c != '.') &&
+ (c != ':') && (c != '/'))
+ {
+ nbrstr = nbrstr + c;
+ buf = buf.substring(1);
+ if (buf.equals(""))
+ break;
+ c = buf.charAt(0);
+ }
+ TimeZone tmptz = TimeZone.getTimeZone(nbrstr);
+
+ // We get GMT on failure, so be sure we asked for it.
+ if (tmptz.getID().equals("GMT"))
+ {
+ if (!nbrstr.equals("GMT"))
+ {
+ buf = "";
+ continue top;
+ }
+ }
+
+ tz = tmptz;
+ last = TIMEZONE_FIELD;
+ index += nbrstr.length();
+ continue top;
+ }
+
+ // Convert to integer
+ int val = 0;
+ try
+ {
+ val = Integer.parseInt(nbrstr);
+ }
+ catch(Exception e)
+ {
+ return null; // Shouldn't happen
+ }
+
+ if (!buf.equals(""))
+ nextchar = buf.charAt(0);
+ else
+ nextchar = ' ';
+
+ // Figure out which value to assign to
+ // I make bad US assumptions about MM/DD/YYYY
+ if (last == DAY_OF_WEEK_FIELD)
+ {
+ day = val;
+ last = DATE_FIELD;
+ }
+ else if ((last == MONTH_FIELD) && (day != -1))
+ {
+ year = processYear(val);
+ last = YEAR_FIELD;
+ }
+ else if (last == MONTH_FIELD)
+ {
+ day = val;
+ last = DATE_FIELD;
+ }
+ else if (last == -1)
+ {
+ // Assume month
+ if ((val < 13) && (val > 0))
+ {
+ month = val;
+ last = MONTH_FIELD;
+ }
+ // Assume year. This only works for two digit years that aren't
+ // between 01 and 12
+ else
+ {
+ year = processYear(val);
+ last = YEAR_FIELD;
+ }
+ }
+ else if ((last == YEAR_FIELD) && ((nextchar == '/') ||
+ (nextchar == '.')))
+ {
+ month = val;
+ last = MONTH_FIELD;
+ }
+ else if (last == YEAR_FIELD)
+ {
+ hour = val;
+ last = HOUR0_FIELD;
+ }
+ else if ((last == DATE_FIELD) && ((nextchar == '/') ||
+ (nextchar == '.') || buf.equals("")))
+ {
+ year = processYear(val);
+ last = YEAR_FIELD;
+ }
+ else if ((last == DATE_FIELD) && ((lastsep == '/') ||
+ (lastsep == '.') || (lastsep == ',')))
+ {
+ year = processYear(val);
+ last = YEAR_FIELD;
+ }
+ else if (last == DATE_FIELD)
+ {
+ hour = val;
+ last = HOUR0_FIELD;
+ }
+ else if (last == HOUR0_FIELD)
+ {
+ minute = val;
+ last = MINUTE_FIELD;
+ }
+ else if (last == MINUTE_FIELD)
+ {
+ second = val;
+ last = SECOND_FIELD;
+ }
+ else if (lastsep == '.')
+ {
+ ; // This is milliseconds or something. Ignore it
+ last = WEEK_OF_YEAR_FIELD; // Just a random value
+ }
+ else // It is year. I have spoken!
+ {
+ year = processYear(val);
+ last = YEAR_FIELD;
+ }
+ }
+ }
+
+ private int parseLeadingZeros(String dateStr, ParsePosition pos,
+ FieldSizePair p)
+ {
+ int value;
+ int index = pos.getIndex();
+ String buf = null;
+
+ if (p.size == 1)
+ {
+ char c = dateStr.charAt(index+1);
+ if ((dateStr.charAt(index) == '1') &&
+ Character.isDigit(dateStr.charAt(index+1)))
+ buf = dateStr.substring(index, index+2);
+ else
+ buf = dateStr.substring(index, index+1);
+ pos.setIndex(index + buf.length());
+ }
+ else if (p.size == 2)
+ {
+ buf = dateStr.substring(index, index+2);
+ pos.setIndex(index+2);
+ }
+ else if (p.size == 3)
+ {
+ buf = dateStr.substring(index, index+3);
+ pos.setIndex(index+3);
+ }
+ else
+ {
+ buf = dateStr.substring(index, index+4);
+ pos.setIndex(index+4);
+ }
+ try
+ {
+ value = Integer.parseInt(buf);
+ }
+ catch(NumberFormatException nfe)
+ {
+ pos.setIndex(index);
+ pos.setErrorIndex(index);
+ return -1;
+ }
+
+ return value;
+ }
+
+ /*
+ * Note that this method doesn't properly protect against
+ * StringIndexOutOfBoundsException. FIXME
+ */
+ private Date parseStrict(String dateStr, ParsePosition pos)
+ {
+ // start looking at position pos.index
+ Enumeration e = tokens.elements();
+ Calendar theCalendar = (Calendar) calendar.clone();
+ theCalendar.clear();
+ theCalendar.setTime(new Date(0));
+
+ int value, index, hour = -1;
+ String buf;
+ while (pos.getIndex() < dateStr.length()) {
+ Object o = e.nextElement();
+ if (o instanceof FieldSizePair) {
+ FieldSizePair p = (FieldSizePair) o;
+ switch (p.field) {
+
+ case ERA_FIELD:
+ value = indexInArray(dateStr,pos.getIndex(),formatData.eras);
+ if (value == -1) {
pos.setErrorIndex(pos.getIndex());
return null;
}
+ pos.setIndex(pos.getIndex() + formatData.eras[value].length());
+ theCalendar.set(Calendar.ERA,value);
+ break;
- // Compute the value we should assign to the field.
- int value;
- if (is_numeric)
- {
- numberFormat.setMinimumIntegerDigits(count);
- Number n = numberFormat.parse(source, pos);
- if (pos == null || ! (n instanceof Long))
- return null;
- value = n.intValue() + offset;
- }
- else if (match != null)
- {
- int index = pos.getIndex();
- int i;
- for (i = offset; i < match.length; ++i)
- {
- if (source.startsWith(match[i], index))
- break;
- }
- if (i == match.length)
- {
- pos.setErrorIndex(index);
- return null;
- }
- pos.setIndex(index + match[i].length());
- value = i;
- }
- else
- value = zone_number;
+ case YEAR_FIELD:
+ String y;
+ if (p.size < 4)
+ y = dateStr.substring(pos.getIndex(), pos.getIndex() + 2);
+ else
+ y = dateStr.substring(pos.getIndex(), pos.getIndex() + 4);
+
+ int year;
+ try
+ {
+ year = Integer.parseInt(y);
+ }
+ catch(NumberFormatException nfe)
+ {
+ pos.setErrorIndex(pos.getIndex());
+ return null;
+ }
- // Assign the value and move on.
- try
- {
- calendar.set(calendar_field, value);
+ if (p.size < 4)
+ year += get2DigitYearStart().getYear();
+
+ theCalendar.set(Calendar.YEAR, year);
+ if (p.size < 4)
+ pos.setIndex(pos.getIndex()+2);
+ else
+ pos.setIndex(pos.getIndex()+4);
+ break;
+
+ case MONTH_FIELD:
+ if (p.size > 2)
+ {
+ index = pos.getIndex();
+
+ value = indexInArray(dateStr,pos.getIndex(),
+ (p.size == 3) ? formatData.shortMonths : formatData.months);
+ if (value == -1)
+ {
+ pos.setErrorIndex(pos.getIndex());
+ return null;
+ }
+ if (p.size == 3)
+ pos.setIndex(index + formatData.shortMonths[value].length());
+ else
+ pos.setIndex(index + formatData.months[value].length());
+ theCalendar.set(Calendar.MONTH, value);
+ break;
+ }
+
+ value = parseLeadingZeros(dateStr, pos, p);
+ if (value == -1)
+ return null;
+
+ theCalendar.set(Calendar.MONTH, value);
+ break;
+
+ case DATE_FIELD:
+ value = parseLeadingZeros(dateStr, pos, p);
+ if (value == -1)
+ return null;
+
+ theCalendar.set(Calendar.DATE, value);
+ break;
+
+ case HOUR_OF_DAY1_FIELD:
+ case HOUR_OF_DAY0_FIELD:
+ index = pos.getIndex();
+ buf = dateStr.substring(index, index+2);
+ try
+ {
+ value = Integer.parseInt(buf);
+ }
+ catch(NumberFormatException nfe)
+ {
+ return null;
+ }
+ if (p.field == HOUR_OF_DAY0_FIELD)
+ // theCalendar.set(Calendar.HOUR_OF_DAY, value);
+ hour = value + 1;
+ else
+ // theCalendar.set(Calendar.HOUR_OF_DAY, value-1);
+ hour = value;
+ pos.setIndex(index+2);
+
+ break;
+
+ case MINUTE_FIELD:
+ value = parseLeadingZeros(dateStr, pos, p);
+ if (value == -1)
+ return null;
+
+ theCalendar.set(Calendar.MINUTE, value);
+ break;
+
+ case SECOND_FIELD:
+ value = parseLeadingZeros(dateStr, pos, p);
+ if (value == -1)
+ return null;
+
+ theCalendar.set(Calendar.SECOND, value);
+ break;
+
+ case MILLISECOND_FIELD:
+ value = parseLeadingZeros(dateStr, pos, p);
+ if (value == -1)
+ return null;
+
+ theCalendar.set(Calendar.MILLISECOND, value);
+ break;
+
+ case DAY_OF_WEEK_FIELD:
+ value = indexInArray(dateStr,pos.getIndex(),(p.size < 4) ? formatData.shortWeekdays : formatData.weekdays);
+ if (value == -1) {
+ pos.setErrorIndex(pos.getIndex());
+ return null;
}
- // FIXME: what exception is thrown on an invalid
- // non-lenient set?
- catch (IllegalArgumentException x)
- {
+ pos.setIndex(pos.getIndex() + ((p.size < 4) ? formatData.shortWeekdays[value].length()
+ : formatData.weekdays[value].length()));
+ // Note: Calendar.set(Calendar.DAY_OF_WEEK,value) does not work
+ // as implemented in jdk1.1.5 (possibly DAY_OF_WEEK is meant to
+ // be read-only). Instead, calculate number of days offset.
+ theCalendar.add(Calendar.DATE,value
+ - theCalendar.get(Calendar.DAY_OF_WEEK));
+ // in JDK, this seems to clear the hours, so we'll do the same.
+ theCalendar.set(Calendar.HOUR_OF_DAY,0);
+ break;
+
+ case DAY_OF_YEAR_FIELD:
+ value = parseLeadingZeros(dateStr, pos, p);
+ if (value == -1)
+ return null;
+
+ theCalendar.set(Calendar.DAY_OF_YEAR, value);
+ break;
+
+ // Just parse and ignore
+ case DAY_OF_WEEK_IN_MONTH_FIELD:
+ value = parseLeadingZeros(dateStr, pos, p);
+ if (value == -1)
+ return null;
+
+ break;
+
+ // Just parse and ignore
+ case WEEK_OF_YEAR_FIELD:
+ value = parseLeadingZeros(dateStr, pos, p);
+ if (value == -1)
+ return null;
+
+ break;
+
+ // Just parse and ignore
+ case WEEK_OF_MONTH_FIELD:
+ value = parseLeadingZeros(dateStr, pos, p);
+ if (value == -1)
+ return null;
+
+ break;
+
+ case AM_PM_FIELD:
+ value = indexInArray(dateStr,pos.getIndex(),formatData.ampms);
+ if (value == -1) {
pos.setErrorIndex(pos.getIndex());
return null;
}
+ pos.setIndex(pos.getIndex() + formatData.ampms[value].length());
+ theCalendar.set(Calendar.AM_PM,value);
+ break;
+
+ case HOUR1_FIELD:
+ case HOUR0_FIELD:
+ value = parseLeadingZeros(dateStr, pos, p);
+ if (value == -1)
+ return null;
+ if (p.field == HOUR1_FIELD)
+ theCalendar.set(Calendar.HOUR, value);
+ if (p.field == HOUR0_FIELD)
+ theCalendar.set(Calendar.HOUR, value+1);
+ break;
+
+ /*
+ case TIMEZONE_FIELD:
+ // TODO
+ break;
+ */
+
+ default:
+ throw new IllegalArgumentException("Illegal pattern character: " +
+ p.field);
+ } // end switch
+ } else if (o instanceof String) {
+ String ostr = (String) o;
+ if (dateStr.substring(pos.getIndex(),pos.getIndex()+ostr.length()).equals(ostr)) {
+ pos.setIndex(pos.getIndex() + ostr.length());
+ } else {
+ pos.setErrorIndex(pos.getIndex());
+ return null;
+ }
+ } else if (o instanceof Character) {
+ Character ochar = (Character) o;
+ if (dateStr.charAt(pos.getIndex()) == ochar.charValue()) {
+ pos.setIndex(pos.getIndex() + 1);
+ } else {
+ pos.setErrorIndex(pos.getIndex());
+ return null;
+ }
}
+ }
- return calendar.getTime();
- }
+ if (hour != -1)
+ {
+ if (theCalendar.get(Calendar.AM_PM) == Calendar.PM)
+ {
+ if (hour == 12)
+ theCalendar.set(Calendar.HOUR_OF_DAY, 12);
+ else
+ theCalendar.set(Calendar.HOUR_OF_DAY, hour + 12);
+ }
+ else
+ {
+ if (hour == 12)
+ theCalendar.set(Calendar.HOUR_OF_DAY, 0);
+ else
+ theCalendar.set(Calendar.HOUR_OF_DAY, hour);
+ }
+ }
- public boolean equals (Object obj)
- {
- if (! (obj instanceof SimpleDateFormat) || ! super.equals(obj) )
- return false;
- SimpleDateFormat other = (SimpleDateFormat) obj;
- return (DateFormatSymbols.equals(pattern, other.pattern)
- && DateFormatSymbols.equals(formatData, other.formatData)
- && DateFormatSymbols.equals(defaultCenturyStart,
- other.defaultCenturyStart));
+ return theCalendar.getTime();
}
- public Object clone ()
- {
- // We know the superclass just call's Object's generic cloner.
- return super.clone ();
- }
+ /**
+ * This method parses the specified string into a date.
+ *
+ * @param dateStr The date string to parse.
+ * @param pos The input and output parse position
+ *
+ * @return The parsed date, or <code>null</code> if the string cannot be
+ * parsed.
+ */
+ public Date parse(String dateStr, ParsePosition pos) {
+ if (isLenient())
+ return parseLenient(dateStr, pos);
+ else
+ return parseStrict(dateStr, pos);
- public int hashCode ()
- {
- int hash = super.hashCode();
- if (pattern != null)
- hash ^= pattern.hashCode();
- return hash;
}
}
+