aboutsummaryrefslogtreecommitdiff
path: root/libjava
diff options
context:
space:
mode:
authorWarren Levy <warrenl@cygnus.com>2000-11-28 03:09:22 +0000
committerWarren Levy <warrenl@gcc.gnu.org>2000-11-28 03:09:22 +0000
commita22add726841748f6c4051f8d9b4278ba5ddbce9 (patch)
tree1662df7a9a7114295c1d93c1195edd57cd72c465 /libjava
parent9734e80cade2a318334a7b3f83ec787d07df80c4 (diff)
downloadgcc-a22add726841748f6c4051f8d9b4278ba5ddbce9.zip
gcc-a22add726841748f6c4051f8d9b4278ba5ddbce9.tar.gz
gcc-a22add726841748f6c4051f8d9b4278ba5ddbce9.tar.bz2
Makefile.am: Added natTimeZone.cc.
* 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. From-SVN: r37808
Diffstat (limited to 'libjava')
-rw-r--r--libjava/ChangeLog22
-rw-r--r--libjava/Makefile.am1
-rw-r--r--libjava/Makefile.in10
-rw-r--r--libjava/gnu/gcj/text/LocaleData_en.java10
-rw-r--r--libjava/java/text/DateFormatSymbols.java45
-rw-r--r--libjava/java/text/SimpleDateFormat.java1481
-rw-r--r--libjava/java/util/TimeZone.java1140
-rw-r--r--libjava/java/util/natTimeZone.cc72
8 files changed, 2221 insertions, 560 deletions
diff --git a/libjava/ChangeLog b/libjava/ChangeLog
index d21d0a3..8176ba9 100644
--- a/libjava/ChangeLog
+++ b/libjava/ChangeLog
@@ -1,3 +1,25 @@
+2000-11-27 Warren Levy <warrenl@cygnus.com>
+
+ * 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.
+
2000-11-27 Bryce McKinlay <bryce@albatross.co.nz>
* java/util/Vector.java (ensureCapacity): Don't increment modCount.
diff --git a/libjava/Makefile.am b/libjava/Makefile.am
index 69131e3..5cd6203 100644
--- a/libjava/Makefile.am
+++ b/libjava/Makefile.am
@@ -1229,6 +1229,7 @@ java/net/natPlainDatagramSocketImpl.cc \
java/net/natPlainSocketImpl.cc \
java/text/natCollator.cc \
java/util/natGregorianCalendar.cc \
+java/util/natTimeZone.cc \
java/util/zip/natDeflater.cc \
java/util/zip/natInflater.cc
diff --git a/libjava/Makefile.in b/libjava/Makefile.in
index 279ec6e..da16e04 100644
--- a/libjava/Makefile.in
+++ b/libjava/Makefile.in
@@ -991,6 +991,7 @@ java/net/natPlainDatagramSocketImpl.cc \
java/net/natPlainSocketImpl.cc \
java/text/natCollator.cc \
java/util/natGregorianCalendar.cc \
+java/util/natTimeZone.cc \
java/util/zip/natDeflater.cc \
java/util/zip/natInflater.cc
@@ -1142,8 +1143,8 @@ java/lang/reflect/natArray.lo java/lang/reflect/natConstructor.lo \
java/lang/reflect/natField.lo java/lang/reflect/natMethod.lo \
java/net/natInetAddress.lo java/net/natPlainDatagramSocketImpl.lo \
java/net/natPlainSocketImpl.lo java/text/natCollator.lo \
-java/util/natGregorianCalendar.lo java/util/zip/natDeflater.lo \
-java/util/zip/natInflater.lo
+java/util/natGregorianCalendar.lo java/util/natTimeZone.lo \
+java/util/zip/natDeflater.lo java/util/zip/natInflater.lo
libgcjx_la_OBJECTS = gnu/gcj/xlib/natClip.lo \
gnu/gcj/xlib/natColormap.lo gnu/gcj/xlib/natDisplay.lo \
gnu/gcj/xlib/natDrawable.lo gnu/gcj/xlib/natFont.lo \
@@ -1684,8 +1685,9 @@ DEP_FILES = .deps/$(srcdir)/$(CONVERT_DIR)/gen-from-JIS.P \
.deps/java/util/jar/JarException.P .deps/java/util/jar/JarFile.P \
.deps/java/util/jar/JarInputStream.P \
.deps/java/util/jar/JarOutputStream.P .deps/java/util/jar/Manifest.P \
-.deps/java/util/natGregorianCalendar.P .deps/java/util/zip/Adler32.P \
-.deps/java/util/zip/CRC32.P .deps/java/util/zip/CheckedInputStream.P \
+.deps/java/util/natGregorianCalendar.P .deps/java/util/natTimeZone.P \
+.deps/java/util/zip/Adler32.P .deps/java/util/zip/CRC32.P \
+.deps/java/util/zip/CheckedInputStream.P \
.deps/java/util/zip/CheckedOutputStream.P \
.deps/java/util/zip/Checksum.P \
.deps/java/util/zip/DataFormatException.P \
diff --git a/libjava/gnu/gcj/text/LocaleData_en.java b/libjava/gnu/gcj/text/LocaleData_en.java
index cd13db0..c24c1121 100644
--- a/libjava/gnu/gcj/text/LocaleData_en.java
+++ b/libjava/gnu/gcj/text/LocaleData_en.java
@@ -68,6 +68,16 @@ public final class LocaleData_en extends ListResourceBundle
{ "shortWeekdays", shortWeekdaysDefault },
{ "weekdays", weekdaysDefault },
+ // These are for DateFormat.
+ { "shortDateFormat", "M/d/yy" }, // Java's Y2K bug.
+ { "mediumDateFormat", "d-MMM-yy" },
+ { "longDateFormat", "MMMM d, yyyy" },
+ { "fullDateFormat", "EEEE MMMM d, yyyy G" },
+ { "shortTimeFormat", "h:mm a" },
+ { "mediumTimeFormat", "h:mm:ss a" },
+ { "longTimeFormat", "h:mm:ss a z" },
+ { "fullTimeFormat", "h:mm:ss;S 'o''clock' a z" },
+
// For RuleBasedCollator.
// FIXME: this is nowhere near complete.
// In particular we must mark accents as ignorable,
diff --git a/libjava/java/text/DateFormatSymbols.java b/libjava/java/text/DateFormatSymbols.java
index b63bf36..c8250c9 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 feb64f0..e2f70cd 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;
}
}
+
diff --git a/libjava/java/util/TimeZone.java b/libjava/java/util/TimeZone.java
index 0145d72..45c26f6 100644
--- a/libjava/java/util/TimeZone.java
+++ b/libjava/java/util/TimeZone.java
@@ -1,189 +1,1101 @@
-/* Copyright (C) 1998, 1999, 2000 Free Software Foundation
+/* java.util.TimeZone
+ 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.util;
+import java.text.DateFormatSymbols;
/**
- * @author Per Bothner <bothner@cygnus.com>
- * @date October 24, 1998.
- */
-
-/* Written using "Java Class Libraries", 2nd edition, ISBN 0-201-31002-3.
- * Status: getAvailableIDs, getDefault, getTimeZone only know about GMT.
+ * This class represents a time zone offset and handles daylight savings.
+ *
+ * You can get the default time zone with <code>getDefault</code>.
+ * This represents the time zone where program is running.
+ *
+ * Another way to create a time zone is <code>getTimeZone</code>, where
+ * you can give an identifier as parameter. For instance, the identifier
+ * of the Central European Time zone is "CET".
+ *
+ * With the <code>getAvailableIDs</code> method, you can get all the
+ * supported time zone identifiers.
+ *
+ * @see Calendar
+ * @see SimpleTimeZone
+ * @author Jochen Hoenicke
*/
-
public abstract class TimeZone implements java.io.Serializable, Cloneable
{
+
+ /**
+ * Constant used to indicate that a short timezone abbreviation should
+ * be returned, such as "EST"
+ */
public static final int SHORT = 0;
+
+ /**
+ * Constant used to indicate that a long timezone name should be
+ * returned, such as "Eastern Standard Time".
+ */
public static final int LONG = 1;
- // The fields are as specified in Sun's "Serialized Form"
- // in the JDK 1.2 beta 4 API specification.
- String ID;
+ /**
+ * The time zone identifier, e.g. PST.
+ */
+ private String ID;
- static final TimeZone zoneGMT = new SimpleTimeZone(0, "GMT");
-
- private static TimeZone zoneDefault;
+ /**
+ * The default time zone, as returned by getDefault.
+ */
+ private static TimeZone defaultZone;
private static final long serialVersionUID = 3581463369166924961L;
- public TimeZone ()
+ /**
+ * Hashtable for timezones by ID.
+ */
+ private static final Hashtable timezones = new Hashtable();
+
+ static
{
+ TimeZone tz;
+ // Automatically generated by scripts/timezones.pl
+ // XXX - Should we read this data from a file?
+ tz = new SimpleTimeZone(-11000 * 3600, "Pacific/Niue");
+ timezones.put("Pacific/Niue", tz);
+ timezones.put("Pacific/Apia", tz);
+ timezones.put("Pacific/Midway", tz);
+ timezones.put("Pacific/Pago_Pago", tz);
+ tz = new SimpleTimeZone
+ (-10000 * 3600, "America/Adak",
+ Calendar.APRIL, 1, Calendar.SUNDAY, 2000 * 3600,
+ Calendar.OCTOBER, -1, Calendar.SUNDAY, 2000 * 3600);
+ timezones.put("America/Adak", tz);
+ tz = new SimpleTimeZone(-10000 * 3600, "HST");
+ timezones.put("HST", tz);
+ timezones.put("Pacific/Fakaofo", tz);
+ timezones.put("Pacific/Honolulu", tz);
+ timezones.put("Pacific/Johnston", tz);
+ timezones.put("Pacific/Rarotonga", tz);
+ timezones.put("Pacific/Tahiti", tz);
+ tz = new SimpleTimeZone
+ (-9000 * 3600, "America/Juneau",
+ Calendar.APRIL, 1, Calendar.SUNDAY, 2000 * 3600,
+ Calendar.OCTOBER, -1, Calendar.SUNDAY, 2000 * 3600);
+ timezones.put("America/Juneau", tz);
+ timezones.put("America/Anchorage", tz);
+ timezones.put("America/Nome", tz);
+ timezones.put("America/Yakutat", tz);
+ tz = new SimpleTimeZone(-9000 * 3600, "Pacific/Gambier");
+ timezones.put("Pacific/Gambier", tz);
+ tz = new SimpleTimeZone(-8500 * 3600, "Pacific/Marquesas");
+ timezones.put("Pacific/Marquesas", tz);
+ tz = new SimpleTimeZone
+ (-8000 * 3600, "PST8PDT",
+ Calendar.APRIL, 1, Calendar.SUNDAY, 2000 * 3600,
+ Calendar.OCTOBER, -1, Calendar.SUNDAY, 2000 * 3600);
+ timezones.put("PST8PDT", tz);
+ timezones.put("America/Dawson", tz);
+ timezones.put("America/Los_Angeles", tz);
+ timezones.put("America/Tijuana", tz);
+ timezones.put("America/Vancouver", tz);
+ timezones.put("America/Whitehorse", tz);
+ timezones.put("US/Pacific-New", tz);
+ tz = new SimpleTimeZone(-8000 * 3600, "Pacific/Pitcairn");
+ timezones.put("Pacific/Pitcairn", tz);
+ tz = new SimpleTimeZone(-7000 * 3600, "MST");
+ timezones.put("MST", tz);
+ timezones.put("America/Dawson_Creek", tz);
+ timezones.put("America/Phoenix", tz);
+ tz = new SimpleTimeZone
+ (-7000 * 3600, "MST7MDT",
+ Calendar.APRIL, 1, Calendar.SUNDAY, 2000 * 3600,
+ Calendar.OCTOBER, -1, Calendar.SUNDAY, 2000 * 3600);
+ timezones.put("MST7MDT", tz);
+ timezones.put("America/Boise", tz);
+ timezones.put("America/Chihuahua", tz);
+ timezones.put("America/Denver", tz);
+ timezones.put("America/Edmonton", tz);
+ timezones.put("America/Inuvik", tz);
+ timezones.put("America/Mazatlan", tz);
+ timezones.put("America/Shiprock", tz);
+ timezones.put("America/Yellowknife", tz);
+ tz = new SimpleTimeZone(-6000 * 3600, "America/Regina");
+ timezones.put("America/Regina", tz);
+ timezones.put("America/Belize", tz);
+ timezones.put("America/Costa_Rica", tz);
+ timezones.put("America/El_Salvador", tz);
+ timezones.put("America/Guatemala", tz);
+ timezones.put("America/Managua", tz);
+ timezones.put("America/Swift_Current", tz);
+ timezones.put("America/Tegucigalpa", tz);
+ timezones.put("Pacific/Galapagos", tz);
+ tz = new SimpleTimeZone
+ (-6000 * 3600, "CST6CDT",
+ Calendar.APRIL, 1, Calendar.SUNDAY, 2000 * 3600,
+ Calendar.OCTOBER, -1, Calendar.SUNDAY, 2000 * 3600);
+ timezones.put("CST6CDT", tz);
+ timezones.put("America/Cambridge_Bay", tz);
+ timezones.put("America/Cancun", tz);
+ timezones.put("America/Chicago", tz);
+ timezones.put("America/Iqaluit", tz);
+ timezones.put("America/Menominee", tz);
+ timezones.put("America/Mexico_City", tz);
+ timezones.put("America/Pangnirtung", tz);
+ timezones.put("America/Rainy_River", tz);
+ timezones.put("America/Rankin_Inlet", tz);
+ timezones.put("America/Winnipeg", tz);
+ tz = new SimpleTimeZone
+ (-6000 * 3600, "Pacific/Easter",
+ Calendar.OCTOBER, 9, -Calendar.SUNDAY, 0 * 3600,
+ Calendar.MARCH, 9, -Calendar.SUNDAY, 0 * 3600);
+ timezones.put("Pacific/Easter", tz);
+ tz = new SimpleTimeZone
+ (-5000 * 3600, "America/Grand_Turk",
+ Calendar.APRIL, 1, Calendar.SUNDAY, 0 * 3600,
+ Calendar.OCTOBER, -1, Calendar.SUNDAY, 0 * 3600);
+ timezones.put("America/Grand_Turk", tz);
+ tz = new SimpleTimeZone
+ (-5000 * 3600, "America/Havana",
+ Calendar.APRIL, 1, 0, 0 * 3600,
+ Calendar.OCTOBER, -1, Calendar.SUNDAY, 0 * 3600);
+ timezones.put("America/Havana", tz);
+ tz = new SimpleTimeZone(-5000 * 3600, "EST");
+ timezones.put("EST", tz);
+ timezones.put("America/Bogota", tz);
+ timezones.put("America/Cayman", tz);
+ timezones.put("America/Guayaquil", tz);
+ timezones.put("America/Indiana/Indianapolis", tz);
+ timezones.put("America/Indiana/Knox", tz);
+ timezones.put("America/Indiana/Marengo", tz);
+ timezones.put("America/Indiana/Vevay", tz);
+ timezones.put("America/Indianapolis", tz);
+ timezones.put("America/Jamaica", tz);
+ timezones.put("America/Lima", tz);
+ timezones.put("America/Panama", tz);
+ timezones.put("America/Port-au-Prince", tz);
+ timezones.put("America/Porto_Acre", tz);
+ tz = new SimpleTimeZone
+ (-5000 * 3600, "EST5EDT",
+ Calendar.APRIL, 1, Calendar.SUNDAY, 2000 * 3600,
+ Calendar.OCTOBER, -1, Calendar.SUNDAY, 2000 * 3600);
+ timezones.put("EST5EDT", tz);
+ timezones.put("America/Detroit", tz);
+ timezones.put("America/Louisville", tz);
+ timezones.put("America/Montreal", tz);
+ timezones.put("America/Nassau", tz);
+ timezones.put("America/New_York", tz);
+ timezones.put("America/Nipigon", tz);
+ timezones.put("America/Thunder_Bay", tz);
+ tz = new SimpleTimeZone(-4000 * 3600, "America/Anguilla");
+ timezones.put("America/Anguilla", tz);
+ timezones.put("America/Antigua", tz);
+ timezones.put("America/Aruba", tz);
+ timezones.put("America/Barbados", tz);
+ timezones.put("America/Caracas", tz);
+ timezones.put("America/Curacao", tz);
+ timezones.put("America/Dominica", tz);
+ timezones.put("America/Grenada", tz);
+ timezones.put("America/Guadeloupe", tz);
+ timezones.put("America/Guyana", tz);
+ timezones.put("America/La_Paz", tz);
+ timezones.put("America/Manaus", tz);
+ timezones.put("America/Martinique", tz);
+ timezones.put("America/Montserrat", tz);
+ timezones.put("America/Port_of_Spain", tz);
+ timezones.put("America/Porto_Velho", tz);
+ timezones.put("America/Puerto_Rico", tz);
+ timezones.put("America/Santo_Domingo", tz);
+ timezones.put("America/St_Kitts", tz);
+ timezones.put("America/St_Lucia", tz);
+ timezones.put("America/St_Thomas", tz);
+ timezones.put("America/St_Vincent", tz);
+ timezones.put("America/Tortola", tz);
+ tz = new SimpleTimeZone
+ (-4000 * 3600, "America/Cuiaba",
+ Calendar.OCTOBER, 1, Calendar.SUNDAY, 0 * 3600,
+ Calendar.FEBRUARY, -1, Calendar.SUNDAY, 0 * 3600);
+ timezones.put("America/Cuiaba", tz);
+ timezones.put("America/Asuncion", tz);
+ timezones.put("America/Boa_Vista", tz);
+ tz = new SimpleTimeZone
+ (-4000 * 3600, "America/Thule",
+ Calendar.APRIL, 1, Calendar.SUNDAY, 2000 * 3600,
+ Calendar.OCTOBER, -1, Calendar.SUNDAY, 2000 * 3600);
+ timezones.put("America/Thule", tz);
+ timezones.put("America/Glace_Bay", tz);
+ timezones.put("America/Goose_Bay", tz);
+ timezones.put("America/Halifax", tz);
+ timezones.put("Atlantic/Bermuda", tz);
+ tz = new SimpleTimeZone
+ (-4000 * 3600, "Antarctica/Palmer",
+ Calendar.OCTOBER, 9, -Calendar.SUNDAY, 0 * 3600,
+ Calendar.MARCH, 9, -Calendar.SUNDAY, 0 * 3600);
+ timezones.put("Antarctica/Palmer", tz);
+ timezones.put("America/Santiago", tz);
+ tz = new SimpleTimeZone
+ (-4000 * 3600, "Atlantic/Stanley",
+ Calendar.SEPTEMBER, 2, Calendar.SUNDAY, 0 * 3600,
+ Calendar.APRIL, 16, -Calendar.SUNDAY, 0 * 3600);
+ timezones.put("Atlantic/Stanley", tz);
+ tz = new SimpleTimeZone(-3000 * 3600, "America/Buenos_Aires");
+ timezones.put("America/Buenos_Aires", tz);
+ timezones.put("America/Belem", tz);
+ timezones.put("America/Catamarca", tz);
+ timezones.put("America/Cayenne", tz);
+ timezones.put("America/Cordoba", tz);
+ timezones.put("America/Jujuy", tz);
+ timezones.put("America/Mendoza", tz);
+ timezones.put("America/Montevideo", tz);
+ timezones.put("America/Paramaribo", tz);
+ timezones.put("America/Rosario", tz);
+ tz = new SimpleTimeZone
+ (-3000 * 3600, "America/Fortaleza",
+ Calendar.OCTOBER, 1, Calendar.SUNDAY, 0 * 3600,
+ Calendar.FEBRUARY, -1, Calendar.SUNDAY, 0 * 3600);
+ timezones.put("America/Fortaleza", tz);
+ timezones.put("America/Araguaina", tz);
+ timezones.put("America/Maceio", tz);
+ timezones.put("America/Sao_Paulo", tz);
+ tz = new SimpleTimeZone
+ (-3000 * 3600, "America/Godthab",
+ Calendar.MARCH, 30, -Calendar.SATURDAY, 22000 * 3600,
+ Calendar.OCTOBER, 30, -Calendar.SATURDAY, 22000 * 3600);
+ timezones.put("America/Godthab", tz);
+ tz = new SimpleTimeZone
+ (-3000 * 3600, "America/Miquelon",
+ Calendar.APRIL, 1, Calendar.SUNDAY, 2000 * 3600,
+ Calendar.OCTOBER, -1, Calendar.SUNDAY, 2000 * 3600);
+ timezones.put("America/Miquelon", tz);
+ tz = new SimpleTimeZone
+ (-2500 * 3600, "America/St_Johns",
+ Calendar.APRIL, 1, Calendar.SUNDAY, 2000 * 3600,
+ Calendar.OCTOBER, -1, Calendar.SUNDAY, 2000 * 3600);
+ timezones.put("America/St_Johns", tz);
+ tz = new SimpleTimeZone(-2000 * 3600, "America/Noronha");
+ timezones.put("America/Noronha", tz);
+ timezones.put("Atlantic/South_Georgia", tz);
+ tz = new SimpleTimeZone
+ (-1000 * 3600, "America/Scoresbysund",
+ Calendar.MARCH, -1, Calendar.SUNDAY, 0 * 3600,
+ Calendar.OCTOBER, -1, Calendar.SUNDAY, 0 * 3600);
+ timezones.put("America/Scoresbysund", tz);
+ timezones.put("Atlantic/Azores", tz);
+ tz = new SimpleTimeZone(-1000 * 3600, "Atlantic/Cape_Verde");
+ timezones.put("Atlantic/Cape_Verde", tz);
+ timezones.put("Atlantic/Jan_Mayen", tz);
+ tz = new SimpleTimeZone(0 * 3600, "GMT");
+ timezones.put("GMT", tz);
+ timezones.put("Africa/Abidjan", tz);
+ timezones.put("Africa/Accra", tz);
+ timezones.put("Africa/Bamako", tz);
+ timezones.put("Africa/Banjul", tz);
+ timezones.put("Africa/Bissau", tz);
+ timezones.put("Africa/Casablanca", tz);
+ timezones.put("Africa/Conakry", tz);
+ timezones.put("Africa/Dakar", tz);
+ timezones.put("Africa/El_Aaiun", tz);
+ timezones.put("Africa/Freetown", tz);
+ timezones.put("Africa/Lome", tz);
+ timezones.put("Africa/Monrovia", tz);
+ timezones.put("Africa/Nouakchott", tz);
+ timezones.put("Africa/Ouagadougou", tz);
+ timezones.put("Africa/Sao_Tome", tz);
+ timezones.put("Africa/Timbuktu", tz);
+ timezones.put("Atlantic/Reykjavik", tz);
+ timezones.put("Atlantic/St_Helena", tz);
+ timezones.put("Europe/Belfast", tz);
+ timezones.put("Europe/Dublin", tz);
+ timezones.put("Europe/London", tz);
+ timezones.put("UTC", tz);
+ tz = new SimpleTimeZone
+ (0 * 3600, "WET",
+ Calendar.MARCH, -1, Calendar.SUNDAY, 1000 * 3600,
+ Calendar.OCTOBER, -1, Calendar.SUNDAY, 1000 * 3600);
+ timezones.put("WET", tz);
+ timezones.put("Atlantic/Canary", tz);
+ timezones.put("Atlantic/Faeroe", tz);
+ timezones.put("Atlantic/Madeira", tz);
+ timezones.put("Europe/Lisbon", tz);
+ tz = new SimpleTimeZone(1000 * 3600, "Africa/Algiers");
+ timezones.put("Africa/Algiers", tz);
+ timezones.put("Africa/Bangui", tz);
+ timezones.put("Africa/Brazzaville", tz);
+ timezones.put("Africa/Douala", tz);
+ timezones.put("Africa/Kinshasa", tz);
+ timezones.put("Africa/Lagos", tz);
+ timezones.put("Africa/Libreville", tz);
+ timezones.put("Africa/Luanda", tz);
+ timezones.put("Africa/Malabo", tz);
+ timezones.put("Africa/Ndjamena", tz);
+ timezones.put("Africa/Niamey", tz);
+ timezones.put("Africa/Porto-Novo", tz);
+ timezones.put("Africa/Tunis", tz);
+ tz = new SimpleTimeZone
+ (1000 * 3600, "Africa/Windhoek",
+ Calendar.SEPTEMBER, 1, Calendar.SUNDAY, 2000 * 3600,
+ Calendar.APRIL, 1, Calendar.SUNDAY, 2000 * 3600);
+ timezones.put("Africa/Windhoek", tz);
+ tz = new SimpleTimeZone
+ (1000 * 3600, "CET",
+ Calendar.MARCH, -1, Calendar.SUNDAY, 2000 * 3600,
+ Calendar.OCTOBER, -1, Calendar.SUNDAY, 2000 * 3600);
+ timezones.put("CET", tz);
+ timezones.put("Africa/Ceuta", tz);
+ timezones.put("Arctic/Longyearbyen", tz);
+ timezones.put("Europe/Amsterdam", tz);
+ timezones.put("Europe/Andorra", tz);
+ timezones.put("Europe/Belgrade", tz);
+ timezones.put("Europe/Berlin", tz);
+ timezones.put("Europe/Bratislava", tz);
+ timezones.put("Europe/Brussels", tz);
+ timezones.put("Europe/Budapest", tz);
+ timezones.put("Europe/Copenhagen", tz);
+ timezones.put("Europe/Gibraltar", tz);
+ timezones.put("Europe/Ljubljana", tz);
+ timezones.put("Europe/Luxembourg", tz);
+ timezones.put("Europe/Madrid", tz);
+ timezones.put("Europe/Malta", tz);
+ timezones.put("Europe/Monaco", tz);
+ timezones.put("Europe/Oslo", tz);
+ timezones.put("Europe/Paris", tz);
+ timezones.put("Europe/Prague", tz);
+ timezones.put("Europe/Rome", tz);
+ timezones.put("Europe/San_Marino", tz);
+ timezones.put("Europe/Sarajevo", tz);
+ timezones.put("Europe/Skopje", tz);
+ timezones.put("Europe/Stockholm", tz);
+ timezones.put("Europe/Tirane", tz);
+ timezones.put("Europe/Vaduz", tz);
+ timezones.put("Europe/Vatican", tz);
+ timezones.put("Europe/Vienna", tz);
+ timezones.put("Europe/Warsaw", tz);
+ timezones.put("Europe/Zagreb", tz);
+ timezones.put("Europe/Zurich", tz);
+ timezones.put("MET", tz);
+ tz = new SimpleTimeZone
+ (2000 * 3600, "Africa/Cairo",
+ Calendar.APRIL, -1, Calendar.FRIDAY, 0 * 3600,
+ Calendar.SEPTEMBER, -1, Calendar.THURSDAY, 23000 * 3600);
+ timezones.put("Africa/Cairo", tz);
+ tz = new SimpleTimeZone(2000 * 3600, "Africa/Gaborone");
+ timezones.put("Africa/Gaborone", tz);
+ timezones.put("Africa/Blantyre", tz);
+ timezones.put("Africa/Bujumbura", tz);
+ timezones.put("Africa/Harare", tz);
+ timezones.put("Africa/Johannesburg", tz);
+ timezones.put("Africa/Khartoum", tz);
+ timezones.put("Africa/Kigali", tz);
+ timezones.put("Africa/Lubumbashi", tz);
+ timezones.put("Africa/Lusaka", tz);
+ timezones.put("Africa/Maputo", tz);
+ timezones.put("Africa/Maseru", tz);
+ timezones.put("Africa/Mbabane", tz);
+ timezones.put("Africa/Tripoli", tz);
+ timezones.put("Europe/Tallinn", tz);
+ tz = new SimpleTimeZone
+ (2000 * 3600, "Asia/Amman",
+ Calendar.APRIL, 1, 0, 0 * 3600, Calendar.OCTOBER, 1, 0, 0 * 3600);
+ timezones.put("Asia/Amman", tz);
+ timezones.put("Asia/Damascus", tz);
+ tz = new SimpleTimeZone
+ (2000 * 3600, "Asia/Beirut",
+ Calendar.MARCH, -1, Calendar.SUNDAY, 0 * 3600,
+ Calendar.OCTOBER, -1, Calendar.SUNDAY, 0 * 3600);
+ timezones.put("Asia/Beirut", tz);
+ tz = new SimpleTimeZone
+ (2000 * 3600, "Asia/Gaza",
+ Calendar.APRIL, 3, Calendar.FRIDAY, 0 * 3600,
+ Calendar.OCTOBER, 3, Calendar.FRIDAY, 0 * 3600);
+ timezones.put("Asia/Gaza", tz);
+ tz = new SimpleTimeZone
+ (2000 * 3600, "Asia/Jerusalem",
+ Calendar.APRIL, 1, Calendar.FRIDAY, 2000 * 3600,
+ Calendar.SEPTEMBER, 1, Calendar.FRIDAY, 2000 * 3600);
+ timezones.put("Asia/Jerusalem", tz);
+ tz = new SimpleTimeZone
+ (2000 * 3600, "EET",
+ Calendar.MARCH, -1, Calendar.SUNDAY, 3000 * 3600,
+ Calendar.OCTOBER, -1, Calendar.SUNDAY, 3000 * 3600);
+ timezones.put("EET", tz);
+ timezones.put("Asia/Istanbul", tz);
+ timezones.put("Asia/Nicosia", tz);
+ timezones.put("Europe/Athens", tz);
+ timezones.put("Europe/Bucharest", tz);
+ timezones.put("Europe/Chisinau", tz);
+ timezones.put("Europe/Helsinki", tz);
+ timezones.put("Europe/Istanbul", tz);
+ timezones.put("Europe/Kiev", tz);
+ timezones.put("Europe/Riga", tz);
+ timezones.put("Europe/Simferopol", tz);
+ timezones.put("Europe/Sofia", tz);
+ timezones.put("Europe/Uzhgorod", tz);
+ timezones.put("Europe/Vilnius", tz);
+ timezones.put("Europe/Zaporozhye", tz);
+ tz = new SimpleTimeZone
+ (2000 * 3600, "Europe/Minsk",
+ Calendar.MARCH, -1, Calendar.SUNDAY, 2000 * 3600,
+ Calendar.OCTOBER, -1, Calendar.SUNDAY, 2000 * 3600);
+ timezones.put("Europe/Minsk", tz);
+ timezones.put("Europe/Kaliningrad", tz);
+ tz = new SimpleTimeZone
+ (3000 * 3600, "Asia/Baghdad",
+ Calendar.APRIL, 1, 0, 3000 * 3600,
+ Calendar.OCTOBER, 1, 0, 3000 * 3600);
+ timezones.put("Asia/Baghdad", tz);
+ tz = new SimpleTimeZone
+ (3000 * 3600, "Europe/Tiraspol",
+ Calendar.MARCH, -1, Calendar.SUNDAY, 2000 * 3600,
+ Calendar.OCTOBER, -1, Calendar.SUNDAY, 2000 * 3600);
+ timezones.put("Europe/Tiraspol", tz);
+ timezones.put("Europe/Moscow", tz);
+ tz = new SimpleTimeZone(3000 * 3600, "Indian/Comoro");
+ timezones.put("Indian/Comoro", tz);
+ timezones.put("Africa/Addis_Ababa", tz);
+ timezones.put("Africa/Asmera", tz);
+ timezones.put("Africa/Dar_es_Salaam", tz);
+ timezones.put("Africa/Djibouti", tz);
+ timezones.put("Africa/Kampala", tz);
+ timezones.put("Africa/Mogadishu", tz);
+ timezones.put("Africa/Nairobi", tz);
+ timezones.put("Antarctica/Syowa", tz);
+ timezones.put("Asia/Aden", tz);
+ timezones.put("Asia/Bahrain", tz);
+ timezones.put("Asia/Kuwait", tz);
+ timezones.put("Asia/Qatar", tz);
+ timezones.put("Asia/Riyadh", tz);
+ timezones.put("Indian/Antananarivo", tz);
+ timezones.put("Indian/Mayotte", tz);
+ tz = new SimpleTimeZone(3500 * 3600, "Asia/Tehran");
+ timezones.put("Asia/Tehran", tz);
+ tz = new SimpleTimeZone
+ (4000 * 3600, "Asia/Baku",
+ Calendar.MARCH, -1, Calendar.SUNDAY, 1000 * 3600,
+ Calendar.OCTOBER, -1, Calendar.SUNDAY, 1000 * 3600);
+ timezones.put("Asia/Baku", tz);
+ tz = new SimpleTimeZone
+ (4000 * 3600, "Asia/Tbilisi",
+ Calendar.MARCH, -1, Calendar.SUNDAY, 0 * 3600,
+ Calendar.OCTOBER, -1, Calendar.SUNDAY, 0 * 3600);
+ timezones.put("Asia/Tbilisi", tz);
+ timezones.put("Asia/Aqtau", tz);
+ tz = new SimpleTimeZone
+ (4000 * 3600, "Asia/Yerevan",
+ Calendar.MARCH, -1, Calendar.SUNDAY, 2000 * 3600,
+ Calendar.OCTOBER, -1, Calendar.SUNDAY, 2000 * 3600);
+ timezones.put("Asia/Yerevan", tz);
+ timezones.put("Europe/Samara", tz);
+ tz = new SimpleTimeZone(4000 * 3600, "Indian/Mauritius");
+ timezones.put("Indian/Mauritius", tz);
+ timezones.put("Asia/Dubai", tz);
+ timezones.put("Asia/Muscat", tz);
+ timezones.put("Indian/Mahe", tz);
+ timezones.put("Indian/Reunion", tz);
+ tz = new SimpleTimeZone(4500 * 3600, "Asia/Kabul");
+ timezones.put("Asia/Kabul", tz);
+ tz = new SimpleTimeZone
+ (5000 * 3600, "Asia/Aqtobe",
+ Calendar.MARCH, -1, Calendar.SUNDAY, 0 * 3600,
+ Calendar.OCTOBER, -1, Calendar.SUNDAY, 0 * 3600);
+ timezones.put("Asia/Aqtobe", tz);
+ tz = new SimpleTimeZone
+ (5000 * 3600, "Asia/Bishkek",
+ Calendar.MARCH, -1, Calendar.SUNDAY, 2500 * 3600,
+ Calendar.OCTOBER, -1, Calendar.SUNDAY, 2500 * 3600);
+ timezones.put("Asia/Bishkek", tz);
+ tz = new SimpleTimeZone
+ (5000 * 3600, "Asia/Yekaterinburg",
+ Calendar.MARCH, -1, Calendar.SUNDAY, 2000 * 3600,
+ Calendar.OCTOBER, -1, Calendar.SUNDAY, 2000 * 3600);
+ timezones.put("Asia/Yekaterinburg", tz);
+ tz = new SimpleTimeZone(5000 * 3600, "Indian/Kerguelen");
+ timezones.put("Indian/Kerguelen", tz);
+ timezones.put("Asia/Ashkhabad", tz);
+ timezones.put("Asia/Dushanbe", tz);
+ timezones.put("Asia/Karachi", tz);
+ timezones.put("Asia/Samarkand", tz);
+ timezones.put("Asia/Tashkent", tz);
+ timezones.put("Indian/Chagos", tz);
+ timezones.put("Indian/Maldives", tz);
+ tz = new SimpleTimeZone(5500 * 3600, "Asia/Calcutta");
+ timezones.put("Asia/Calcutta", tz);
+ tz = new SimpleTimeZone(5750 * 3600, "Asia/Katmandu");
+ timezones.put("Asia/Katmandu", tz);
+ tz = new SimpleTimeZone(6000 * 3600, "Antarctica/Mawson");
+ timezones.put("Antarctica/Mawson", tz);
+ timezones.put("Asia/Colombo", tz);
+ timezones.put("Asia/Dacca", tz);
+ timezones.put("Asia/Thimbu", tz);
+ tz = new SimpleTimeZone
+ (6000 * 3600, "Asia/Almaty",
+ Calendar.MARCH, -1, Calendar.SUNDAY, 0 * 3600,
+ Calendar.OCTOBER, -1, Calendar.SUNDAY, 0 * 3600);
+ timezones.put("Asia/Almaty", tz);
+ tz = new SimpleTimeZone
+ (6000 * 3600, "Asia/Omsk",
+ Calendar.MARCH, -1, Calendar.SUNDAY, 2000 * 3600,
+ Calendar.OCTOBER, -1, Calendar.SUNDAY, 2000 * 3600);
+ timezones.put("Asia/Omsk", tz);
+ timezones.put("Asia/Novosibirsk", tz);
+ tz = new SimpleTimeZone(6500 * 3600, "Asia/Rangoon");
+ timezones.put("Asia/Rangoon", tz);
+ timezones.put("Indian/Cocos", tz);
+ tz = new SimpleTimeZone(7000 * 3600, "Antarctica/Davis");
+ timezones.put("Antarctica/Davis", tz);
+ timezones.put("Asia/Bangkok", tz);
+ timezones.put("Asia/Jakarta", tz);
+ timezones.put("Asia/Phnom_Penh", tz);
+ timezones.put("Asia/Saigon", tz);
+ timezones.put("Asia/Vientiane", tz);
+ timezones.put("Indian/Christmas", tz);
+ tz = new SimpleTimeZone
+ (7000 * 3600, "Asia/Krasnoyarsk",
+ Calendar.MARCH, -1, Calendar.SUNDAY, 2000 * 3600,
+ Calendar.OCTOBER, -1, Calendar.SUNDAY, 2000 * 3600);
+ timezones.put("Asia/Krasnoyarsk", tz);
+ tz = new SimpleTimeZone(8000 * 3600, "Antarctica/Casey");
+ timezones.put("Antarctica/Casey", tz);
+ timezones.put("Asia/Brunei", tz);
+ timezones.put("Asia/Chungking", tz);
+ timezones.put("Asia/Dili", tz);
+ timezones.put("Asia/Harbin", tz);
+ timezones.put("Asia/Hong_Kong", tz);
+ timezones.put("Asia/Kashgar", tz);
+ timezones.put("Asia/Kuala_Lumpur", tz);
+ timezones.put("Asia/Kuching", tz);
+ timezones.put("Asia/Macao", tz);
+ timezones.put("Asia/Manila", tz);
+ timezones.put("Asia/Shanghai", tz);
+ timezones.put("Asia/Singapore", tz);
+ timezones.put("Asia/Taipei", tz);
+ timezones.put("Asia/Ujung_Pandang", tz);
+ timezones.put("Asia/Urumqi", tz);
+ timezones.put("Australia/Perth", tz);
+ tz = new SimpleTimeZone
+ (8000 * 3600, "Asia/Irkutsk",
+ Calendar.MARCH, -1, Calendar.SUNDAY, 2000 * 3600,
+ Calendar.OCTOBER, -1, Calendar.SUNDAY, 2000 * 3600);
+ timezones.put("Asia/Irkutsk", tz);
+ tz = new SimpleTimeZone
+ (8000 * 3600, "Asia/Ulan_Bator",
+ Calendar.MARCH, -1, Calendar.SUNDAY, 0 * 3600,
+ Calendar.SEPTEMBER, -1, Calendar.SUNDAY, 0 * 3600);
+ timezones.put("Asia/Ulan_Bator", tz);
+ tz = new SimpleTimeZone(9000 * 3600, "Asia/Jayapura");
+ timezones.put("Asia/Jayapura", tz);
+ timezones.put("Asia/Pyongyang", tz);
+ timezones.put("Asia/Seoul", tz);
+ timezones.put("Asia/Tokyo", tz);
+ timezones.put("Pacific/Palau", tz);
+ tz = new SimpleTimeZone
+ (9000 * 3600, "Asia/Yakutsk",
+ Calendar.MARCH, -1, Calendar.SUNDAY, 2000 * 3600,
+ Calendar.OCTOBER, -1, Calendar.SUNDAY, 2000 * 3600);
+ timezones.put("Asia/Yakutsk", tz);
+ tz = new SimpleTimeZone
+ (9500 * 3600, "Australia/Adelaide",
+ Calendar.OCTOBER, -1, Calendar.SUNDAY, 2000 * 3600,
+ Calendar.MARCH, -1, Calendar.SUNDAY, 2000 * 3600);
+ timezones.put("Australia/Adelaide", tz);
+ timezones.put("Australia/Broken_Hill", tz);
+ tz = new SimpleTimeZone(9500 * 3600, "Australia/Darwin");
+ timezones.put("Australia/Darwin", tz);
+ tz = new SimpleTimeZone(10000 * 3600, "Antarctica/DumontDUrville");
+ timezones.put("Antarctica/DumontDUrville", tz);
+ timezones.put("Australia/Brisbane", tz);
+ timezones.put("Australia/Lindeman", tz);
+ timezones.put("Pacific/Guam", tz);
+ timezones.put("Pacific/Port_Moresby", tz);
+ timezones.put("Pacific/Saipan", tz);
+ timezones.put("Pacific/Truk", tz);
+ timezones.put("Pacific/Yap", tz);
+ tz = new SimpleTimeZone
+ (10000 * 3600, "Asia/Vladivostok",
+ Calendar.MARCH, -1, Calendar.SUNDAY, 2000 * 3600,
+ Calendar.OCTOBER, -1, Calendar.SUNDAY, 2000 * 3600);
+ timezones.put("Asia/Vladivostok", tz);
+ tz = new SimpleTimeZone
+ (10000 * 3600, "Australia/Hobart",
+ Calendar.OCTOBER, 1, Calendar.SUNDAY, 2000 * 3600,
+ Calendar.MARCH, -1, Calendar.SUNDAY, 2000 * 3600);
+ timezones.put("Australia/Hobart", tz);
+ tz = new SimpleTimeZone
+ (10000 * 3600, "Australia/Melbourne",
+ Calendar.OCTOBER, -1, Calendar.SUNDAY, 2000 * 3600,
+ Calendar.MARCH, -1, Calendar.SUNDAY, 2000 * 3600);
+ timezones.put("Australia/Melbourne", tz);
+ timezones.put("Australia/Sydney", tz);
+/******************************************************************
+ * FIXME: XXX: Not yet available in libgcj. Need new jdk 1.2
+ * SimpleTimeZone constructor.
+ tz = new SimpleTimeZone
+ (10500 * 3600, "Australia/Lord_Howe",
+ Calendar.OCTOBER, -1, Calendar.SUNDAY, 2000 * 3600,
+ Calendar.MARCH, -1, Calendar.SUNDAY, 2000 * 3600, 500 * 3600);
+ timezones.put("Australia/Lord_Howe", tz);
+ ******************************************************************/
+ tz = new SimpleTimeZone
+ (11000 * 3600, "Asia/Magadan",
+ Calendar.MARCH, -1, Calendar.SUNDAY, 2000 * 3600,
+ Calendar.OCTOBER, -1, Calendar.SUNDAY, 2000 * 3600);
+ timezones.put("Asia/Magadan", tz);
+ tz = new SimpleTimeZone(11000 * 3600, "Pacific/Ponape");
+ timezones.put("Pacific/Ponape", tz);
+ timezones.put("Pacific/Efate", tz);
+ timezones.put("Pacific/Guadalcanal", tz);
+ timezones.put("Pacific/Kosrae", tz);
+ timezones.put("Pacific/Noumea", tz);
+ tz = new SimpleTimeZone(11500 * 3600, "Pacific/Norfolk");
+ timezones.put("Pacific/Norfolk", tz);
+ tz = new SimpleTimeZone
+ (12000 * 3600, "Antarctica/McMurdo",
+ Calendar.OCTOBER, 1, Calendar.SUNDAY, 2000 * 3600,
+ Calendar.MARCH, 3, Calendar.SUNDAY, 2000 * 3600);
+ timezones.put("Antarctica/McMurdo", tz);
+ timezones.put("Antarctica/South_Pole", tz);
+ timezones.put("Pacific/Auckland", tz);
+ tz = new SimpleTimeZone
+ (12000 * 3600, "Asia/Kamchatka",
+ Calendar.MARCH, -1, Calendar.SUNDAY, 2000 * 3600,
+ Calendar.OCTOBER, -1, Calendar.SUNDAY, 2000 * 3600);
+ timezones.put("Asia/Kamchatka", tz);
+ timezones.put("Asia/Anadyr", tz);
+ tz = new SimpleTimeZone
+ (12000 * 3600, "Pacific/Fiji",
+ Calendar.NOVEMBER, 1, Calendar.SUNDAY, 2000 * 3600,
+ Calendar.FEBRUARY, -1, Calendar.SUNDAY, 3000 * 3600);
+ timezones.put("Pacific/Fiji", tz);
+ tz = new SimpleTimeZone(12000 * 3600, "Pacific/Tarawa");
+ timezones.put("Pacific/Tarawa", tz);
+ timezones.put("Pacific/Funafuti", tz);
+ timezones.put("Pacific/Kwajalein", tz);
+ timezones.put("Pacific/Majuro", tz);
+ timezones.put("Pacific/Nauru", tz);
+ timezones.put("Pacific/Wake", tz);
+ timezones.put("Pacific/Wallis", tz);
+ tz = new SimpleTimeZone
+ (12750 * 3600, "Pacific/Chatham",
+ Calendar.OCTOBER, 1, Calendar.SUNDAY, 2750 * 3600,
+ Calendar.MARCH, 3, Calendar.SUNDAY, 2750 * 3600);
+ timezones.put("Pacific/Chatham", tz);
+ tz = new SimpleTimeZone(13000 * 3600, "Pacific/Enderbury");
+ timezones.put("Pacific/Enderbury", tz);
+ tz = new SimpleTimeZone
+ (13000 * 3600, "Pacific/Tongatapu",
+ Calendar.OCTOBER, 1, Calendar.SATURDAY, 2000 * 3600,
+ Calendar.APRIL, 16, -Calendar.SUNDAY, 2000 * 3600);
+ timezones.put("Pacific/Tongatapu", tz);
+ tz = new SimpleTimeZone(14000 * 3600, "Pacific/Kiritimati");
+ timezones.put("Pacific/Kiritimati", tz);
}
- public abstract int getOffset (int era, int year, int month,
- int day, int dayOfWeek, int milliseconds);
- public abstract void setRawOffset (int offsetMillis);
+ /* Look up default timezone */
+ static
+ {
+ // System.loadLibrary("javautil");
- public abstract int getRawOffset ();
+ String tzid = System.getProperty("user.timezone");
- public String getID () { return ID; }
+ if (tzid == null)
+ tzid = getDefaultTimeZoneId();
- public void setID (String ID) { this.ID = ID; }
+ if (tzid == null)
+ tzid = "GMT";
- public final String getDisplayName()
+ defaultZone = getTimeZone(tzid);
+ }
+
+ /* This method returns us a time zone id string which is in the
+ form <standard zone name><GMT offset><daylight time zone name>.
+ The GMT offset is in seconds, except where it is evenly divisible
+ by 3600, then it is in hours. If the zone does not observe
+ daylight time, then the daylight zone name is omitted. Examples:
+ in Chicago, the timezone would be CST6CDT. In Indianapolis
+ (which does not have Daylight Savings Time) the string would
+ be EST5
+ */
+ private static native String getDefaultTimeZoneId();
+
+ /**
+ * 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.
+ * @param era the era of the given date
+ * @param year the year of the given date
+ * @param month the month of the given date, 0 for January.
+ * @param day the day of month
+ * @param dayOfWeek the day of week
+ * @param milliseconds the millis in the day (in local standard time)
+ * @return the time zone offset in milliseconds.
+ */
+ public abstract int getOffset(int era, int year, int month,
+ int day, int dayOfWeek, int milliseconds);
+
+ /**
+ * Gets the time zone offset, ignoring daylight savings. This is
+ * the offset to add to UTC to get the local time.
+ * @return the time zone offset in milliseconds.
+ */
+ public abstract int getRawOffset();
+
+ /**
+ * Sets the time zone offset, ignoring daylight savings. This is
+ * the offset to add to UTC to get the local time.
+ * @param offsetMillis the time zone offset to GMT.
+ */
+ public abstract void setRawOffset(int offsetMillis);
+
+ /**
+ * Gets the identifier of this time zone. For instance, PST for
+ * Pacific Standard Time.
+ * @returns the ID of this time zone.
+ */
+ public String getID()
{
- return ID; // FIXME
+ return ID;
}
- // public final String getDisplayName (Local locale) { ... } FIXME
+ /**
+ * Sets the identifier of this time zone. For instance, PST for
+ * Pacific Standard Time.
+ * @param id the new time zone ID.
+ */
+ public void setID(String id)
+ {
+ this.ID = id;
+ }
- public final String getDisplayName (boolean daylight, int style)
+ /**
+ * This method returns a string name of the time zone suitable
+ * for displaying to the user. The string returned will be the long
+ * description of the timezone in the current locale. The name
+ * displayed will assume daylight savings time is not in effect.
+ *
+ * @return The name of the time zone.
+ */
+ public final String getDisplayName()
{
- return ID; // FIXME
+ return (getDisplayName(false, LONG, Locale.getDefault()));
}
- /*
- public final String getDisplayName (boolean daylight, int style, Locale locale)
+ /**
+ * This method returns a string name of the time zone suitable
+ * for displaying to the user. The string returned will be the long
+ * description of the timezone in the specified locale. The name
+ * displayed will assume daylight savings time is not in effect.
+ *
+ * @param locale The locale for this timezone name.
+ *
+ * @return The name of the time zone.
+ */
+ public final String getDisplayName(Locale locale)
{
- return ID; // FIXME
+ return (getDisplayName(false, LONG, locale));
}
- */
- public abstract boolean useDaylightTime();
+ /**
+ * This method returns a string name of the time zone suitable
+ * for displaying to the user. The string returned will be of the
+ * specified type in the current locale.
+ *
+ * @param dst Whether or not daylight savings time is in effect.
+ * @param style <code>LONG</code> for a long name, <code>SHORT</code> for
+ * a short abbreviation.
+ *
+ * @return The name of the time zone.
+ */
+ public final String getDisplayName(boolean dst, int style)
+ {
+ return (getDisplayName(dst, style, Locale.getDefault()));
+ }
- public abstract boolean inDaylightTime (Date date);
- public static synchronized TimeZone getTimeZone (String ID)
+ /**
+ * This method returns a string name of the time zone suitable
+ * for displaying to the user. The string returned will be of the
+ * specified type in the specified locale.
+ *
+ * @param dst Whether or not daylight savings time is in effect.
+ * @param style <code>LONG</code> for a long name, <code>SHORT</code> for
+ * a short abbreviation.
+ * @param locale The locale for this timezone name.
+ *
+ * @return The name of the time zone.
+ */
+ public String getDisplayName(boolean dst, int style, Locale locale)
{
- int i;
- for (i = 0; i < tzIDs.length; ++i)
+ DateFormatSymbols dfs;
+ try
{
- if (ID.equals(tzIDs[i]))
- break;
- }
- if (i == tzIDs.length)
- return null;
+ dfs = new DateFormatSymbols(locale);
- if (timeZones[i] == null)
+ // The format of the value returned is defined by us.
+ String[][]zoneinfo = dfs.getZoneStrings();
+ for (int i = 0; i < zoneinfo.length; i++)
+ {
+ if (zoneinfo[i][0].equals(getID()))
+ {
+ if (!dst)
+ {
+ if (style == SHORT)
+ return (zoneinfo[i][2]);
+ else
+ return (zoneinfo[i][1]);
+ }
+ else
+ {
+ if (style == SHORT)
+ return (zoneinfo[i][4]);
+ else
+ return (zoneinfo[i][3]);
+ }
+ }
+ }
+ }
+ catch (MissingResourceException e)
{
- if (ID.equals("GMT"))
- timeZones[i] = zoneGMT;
- else
- timeZones[i] = new SimpleTimeZone (rawOffsets[i], tzIDs[i]);
}
- return timeZones[i];
+ return getDefaultDisplayName(dst);
}
- public static String[] getAvailableIDs()
+ private String getDefaultDisplayName(boolean dst)
{
- return (String[]) tzIDs.clone();
+ int offset = getRawOffset();
+ if (dst && this instanceof SimpleTimeZone)
+ {
+ // ugly, but this is a design failure of the API:
+ // getDisplayName takes a dst parameter even though
+ // TimeZone knows nothing about daylight saving offsets.
+ offset += ((SimpleTimeZone) this).getDSTSavings();
+ }
+
+ StringBuffer sb = new StringBuffer(9);
+ sb.append("GMT");
+ sb.append(offset >= 0 ? '+' : '-');
+
+ offset = Math.abs(offset) / (1000 * 60);
+ int hours = offset / 60;
+ int minutes = offset % 60;
+
+ sb.append('0' + hours / 10).append('0' + hours % 10).append(':');
+ sb.append('0' + minutes / 10).append('0' + minutes % 10);
+ return sb.toString();
}
- public static String[] getAvailableIDs(int rawOffset)
- {
- int first, last;
+ /**
+ * Returns true, if this time zone uses Daylight Savings Time.
+ */
+ public abstract boolean useDaylightTime();
- for (first = 0; first < rawOffsets.length; ++first)
- {
- if (rawOffset == rawOffsets[first])
- break;
- }
- if (first == rawOffsets.length)
- return new String[0];
- for (last = first + 1; last < rawOffsets.length; ++last)
+ /**
+ * Returns true, if the given date is in Daylight Savings Time in this
+ * time zone.
+ * @param date the given Date.
+ */
+ public abstract boolean inDaylightTime(Date date);
+
+ /**
+ * Gets the TimeZone for the given ID.
+ * @param ID the time zone identifier.
+ * @return The time zone for the identifier or GMT, if no such time
+ * zone exists.
+ */
+ // FIXME: XXX: JCL indicates this and other methods are synchronized.
+ public static TimeZone getTimeZone(String ID)
+ {
+ // First check timezones hash
+ TimeZone tz = (TimeZone) timezones.get(ID);
+ if (tz != null)
{
- if (rawOffset != rawOffsets[last])
- break;
+ if (tz.getID().equals(ID))
+ return tz;
+
+ // We always return a timezone with the requested ID.
+ // This is the same behaviour as with JDK1.2.
+ tz = (TimeZone) tz.clone();
+ tz.setID(ID);
+ // We also save the alias, so that we return the same
+ // object again if getTimeZone is called with the same
+ // alias.
+ timezones.put(ID, tz);
+ return tz;
}
- String[] r = new String[last - first];
- for (int i = first; i < last; ++i)
+ // See if the ID is really a GMT offset form.
+ // Note that GMT is in the table so we know it is different.
+ if (ID.startsWith("GMT"))
{
- r[i - first] = tzIDs[i];
- }
+ int pos = 3;
+ int offset_direction = 1;
- return r;
- }
+ if (ID.charAt(pos) == '-')
+ {
+ offset_direction = -1;
+ pos++;
+ }
+ else if (ID.charAt(pos) == '+')
+ {
+ pos++;
+ }
- private static synchronized TimeZone setDefault()
- {
- if (zoneDefault == null)
- {
try
{
- String id = System.getProperty("user.timezone");
- if (id != null && ! id.equals("GMT"))
- zoneDefault = getTimeZone(id);
+ int hour, minute;
+
+ String offset_str = ID.substring(pos);
+ int idx = offset_str.indexOf(":");
+ if (idx != -1)
+ {
+ hour = Integer.parseInt(offset_str.substring(0, idx));
+ minute = Integer.parseInt(offset_str.substring(idx + 1));
+ }
+ else
+ {
+ int offset_length = offset_str.length();
+ if (offset_length <= 2)
+ {
+ // Only hour
+ hour = Integer.parseInt(offset_str);
+ minute = 0;
+ }
+ else
+ {
+ // hour and minute, not separated by colon
+ hour = Integer.parseInt
+ (offset_str.substring(0, offset_length - 2));
+ minute = Integer.parseInt
+ (offset_str.substring(offset_length - 2));
+ }
+ }
+
+ return new SimpleTimeZone((hour * (60 * 60 * 1000) +
+ minute * (60 * 1000))
+ * offset_direction, ID);
}
- catch (Exception ex)
+ catch (NumberFormatException e)
{
}
- if (zoneDefault == null)
- zoneDefault = zoneGMT;
}
- return zoneDefault;
+
+ // Finally, return GMT per spec
+ return getTimeZone("GMT");
}
- public static TimeZone getDefault()
+ /**
+ * Gets the available IDs according to the given time zone
+ * offset.
+ * @param rawOffset the given time zone GMT offset.
+ * @return An array of IDs, where the time zone has the specified GMT
+ * offset. For example <code>{"Phoenix", "Denver"}</code>, since both have
+ * GMT-07:00, but differ in daylight savings behaviour.
+ */
+/******************************************************************
+ * FIXME: XXX: Not yet available in libgcj. Need jdk 1.2 Iterator and Map.
+ public static String[] getAvailableIDs(int rawOffset)
{
- return zoneDefault == null ? setDefault() : zoneDefault;
+ int count = 0;
+ Iterator iter = timezones.entrySet().iterator();
+ while (iter.hasNext())
+ {
+ // Don't iterate the values, since we want to count
+ // doubled values (aliases)
+ Map.Entry entry = (Map.Entry) iter.next();
+ if (((TimeZone) entry.getValue()).getRawOffset() == rawOffset)
+ count++;
+ }
+
+ String[] ids = new String[count];
+ count = 0;
+ iter = timezones.entrySet().iterator();
+ while (iter.hasNext())
+ {
+ Map.Entry entry = (Map.Entry) iter.next();
+ if (((TimeZone) entry.getValue()).getRawOffset() == rawOffset)
+ ids[count++] = (String) entry.getKey();
+ }
+ return ids;
}
+ ******************************************************************/
- public static void setDefault (TimeZone zone) { zoneDefault = zone; }
+ /**
+ * Gets all available IDs.
+ * @return An array of all supported IDs.
+ */
+/******************************************************************
+ * FIXME: XXX: Not yet available in libgcj. Need jdk 1.2 java.util.Map.
+ public static String[] getAvailableIDs()
+ {
+ return (String[])
+ timezones.keySet().toArray(new String[timezones.size()]);
+ }
+ ******************************************************************/
- public boolean hasSameRules (TimeZone other)
+ /**
+ * Returns the time zone under which the host is running. This
+ * can be changed with setDefault.
+ * @return the time zone for this host.
+ * @see #setDefault
+ */
+ public static TimeZone getDefault()
{
- return this == other;
+ return defaultZone;
}
- public Object clone ()
+ public static void setDefault(TimeZone zone)
{
- // Just use Object's generic cloner.
- return super.clone ();
+ defaultZone = zone;
}
- // Names of timezones. This array is kept in parallel with
- // rawOffsets. This list comes from the JCL 1.1 book.
- private static final String[] tzIDs =
+ /**
+ * Test if the other time zone uses the same rule and only
+ * possibly differs in ID. This implementation for this particular
+ * class will return true if the raw offsets are identical. Subclasses
+ * should override this method if they use daylight savings.
+ * @return true if this zone has the same raw offset
+ */
+ public boolean hasSameRules(TimeZone other)
{
- "MIT", "HST", "AST", "PST", "PNT",
- "MST", "CST", "EST", "IET", "PRT",
- "CNT", "AGT", "BET", "CAT", "GMT",
- "ECT", "EET", "ART", "EAT", "MET",
- "NET", "PLT", "IST", "BST", "VST",
- "CTT", "JST", "ACT", "AET", "SST",
- "NST"
- };
- // This holds raw offsets in milliseconds.
- // 3600000 == 60 * 60 * 1000
- private static final int[] rawOffsets =
+ return other.getRawOffset() == getRawOffset();
+ }
+
+ /**
+ * Returns a clone of this object. I can't imagine, why this is
+ * useful for a time zone.
+ */
+ public Object clone()
{
- -11 * 3600000, -10 * 3600000, -9 * 3600000, -8 * 3600000, -7 * 3600000,
- -7 * 3600000, -6 * 3600000, -5 * 3600000, -5 * 3600000, -4 * 3600000,
- -35 * 360000, -3 * 3600000, -3 * 3600000, -1 * 3600000, 0,
- 1 * 3600000, 1 * 3600000, 2 * 3600000, 3 * 3600000, 35 * 360000,
- 4 * 3600000, 5 * 3600000, 55 * 360000, 6 * 3600000, 7 * 3600000,
- 8 * 3600000, 9 * 3600000, 95 * 360000, 10 * 3600000, 11 * 3600000,
- 12 * 3600000
- };
- // This caches all the corresponding zone objects.
- private static TimeZone[] timeZones = new TimeZone[tzIDs.length];
+ try
+ {
+ return super.clone();
+ }
+ catch (CloneNotSupportedException ex)
+ {
+ return null;
+ }
+ }
+
+ static final TimeZone zoneGMT = new SimpleTimeZone(0, "GMT");
}
diff --git a/libjava/java/util/natTimeZone.cc b/libjava/java/util/natTimeZone.cc
new file mode 100644
index 0000000..61128c8
--- /dev/null
+++ b/libjava/java/util/natTimeZone.cc
@@ -0,0 +1,72 @@
+/* Copyright (C) 2000 Free Software Foundation
+
+ This file is part of libgcj.
+
+This software is copyrighted work licensed under the terms of the
+Libgcj License. Please consult the file "LIBGCJ_LICENSE" for
+details. */
+
+#include <config.h>
+
+#include <gcj/cni.h>
+#include <java/util/TimeZone.h>
+
+#include <stdio.h>
+#include <string.h>
+
+#if TIME_WITH_SYS_TIME
+# include <sys/time.h>
+# include <time.h>
+#else
+# if HAVE_SYS_TIME_H
+# include <sys/time.h>
+# else
+# include <time.h>
+# endif
+#endif
+
+/*
+ * This method returns a time zone string that is used by the static
+ * initializer in java.util.TimeZone to create the default timezone
+ * instance. This is a key into the timezone table used by
+ * that class.
+ */
+jstring
+java::util::TimeZone::getDefaultTimeZoneId (void)
+{
+ time_t current_time;
+ char **tzinfo, *tzid;
+ long tzoffset;
+ jstring retval;
+
+ current_time = time(0);
+
+ mktime(localtime(&current_time));
+ tzinfo = tzname;
+ tzoffset = timezone;
+
+ if ((tzoffset % 3600) == 0)
+ tzoffset = tzoffset / 3600;
+
+ if (!strcmp(tzinfo[0], tzinfo[1]))
+ {
+ tzid = (char*) _Jv_Malloc (strlen(tzinfo[0]) + 6);
+ if (!tzid)
+ return NULL;
+
+ sprintf(tzid, "%s%ld", tzinfo[0], tzoffset);
+ }
+ else
+ {
+ tzid = (char*) _Jv_Malloc (strlen(tzinfo[0]) + strlen(tzinfo[1]) + 6);
+ if (!tzid)
+ return NULL;
+
+ sprintf(tzid, "%s%ld%s", tzinfo[0], tzoffset, tzinfo[1]);
+ }
+
+ retval = JvNewStringUTF (tzid);
+ _Jv_Free (tzid);
+ return retval;
+}
+