summaryrefslogtreecommitdiff
path: root/libjava/java/text/SimpleDateFormat.java
diff options
context:
space:
mode:
Diffstat (limited to 'libjava/java/text/SimpleDateFormat.java')
-rw-r--r--libjava/java/text/SimpleDateFormat.java522
1 files changed, 522 insertions, 0 deletions
diff --git a/libjava/java/text/SimpleDateFormat.java b/libjava/java/text/SimpleDateFormat.java
new file mode 100644
index 00000000000..b4012479045
--- /dev/null
+++ b/libjava/java/text/SimpleDateFormat.java
@@ -0,0 +1,522 @@
+/* Copyright (C) 1998, 1999 Cygnus Solutions
+
+ 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. */
+
+package java.text;
+
+import java.util.*;
+
+/**
+ * @author Per Bothner <bothner@cygnus.com>
+ * @date October 25, 1998.
+ */
+/* Written using "Java Class Libraries", 2nd edition, plus online
+ * API docs for JDK 1.2 beta from http://www.javasoft.com.
+ * Status: parse is not implemented.
+ */
+
+public class SimpleDateFormat extends DateFormat
+{
+ private Date defaultCenturyStart;
+ private DateFormatSymbols formatData;
+ private String pattern;
+
+ public SimpleDateFormat ()
+ {
+ this("dd/MM/yy HH:mm", Locale.getDefault());
+ }
+
+ public SimpleDateFormat (String pattern)
+ {
+ this(pattern, Locale.getDefault());
+ }
+
+ public SimpleDateFormat (String pattern, Locale locale)
+ {
+ this.pattern = pattern;
+ this.calendar = Calendar.getInstance(locale);
+ this.numberFormat = NumberFormat.getInstance(locale);
+ numberFormat.setGroupingUsed(false);
+ this.formatData = new DateFormatSymbols (locale);
+ }
+
+ public SimpleDateFormat (String pattern, DateFormatSymbols formatData)
+ {
+ this.pattern = pattern;
+ this.formatData = formatData;
+ this.calendar = Calendar.getInstance();
+ this.numberFormat = NumberFormat.getInstance();
+ numberFormat.setGroupingUsed(false);
+ }
+
+ public Date get2DigitYearStart()
+ {
+ return defaultCenturyStart;
+ }
+
+ public void set2DigitYearStart(Date startDate)
+ {
+ defaultCenturyStart = startDate;
+ }
+
+ public DateFormatSymbols getDateFormatSymbols ()
+ {
+ return formatData;
+ }
+
+ public void setDateFormatSymbols (DateFormatSymbols value)
+ {
+ formatData = value;
+ }
+
+ public String toPattern ()
+ {
+ return pattern;
+ }
+
+ public void applyPattern (String pattern)
+ {
+ this.pattern = pattern;
+ }
+
+ private String applyLocalizedPattern (String pattern,
+ String oldChars, String newChars)
+ {
+ int len = pattern.length();
+ StringBuffer buf = new StringBuffer(len);
+ boolean quoted = false;
+ for (int i = 0; i < len; i++)
+ {
+ char ch = pattern.charAt(i);
+ if (ch == '\'')
+ quoted = ! quoted;
+ if (! quoted)
+ {
+ int j = oldChars.indexOf(ch);
+ if (j >= 0)
+ ch = newChars.charAt(j);
+ }
+ buf.append(ch);
+ }
+ return buf.toString();
+ }
+
+ public void applyLocalizedPattern (String pattern)
+ {
+ String localChars = formatData.getLocalPatternChars();
+ String standardChars = DateFormatSymbols.localPatternCharsDefault;
+ pattern = applyLocalizedPattern (pattern, localChars, standardChars);
+ applyPattern(pattern);
+ }
+
+ public String toLocalizedPattern ()
+ {
+ String localChars = formatData.getLocalPatternChars();
+ String standardChars = DateFormatSymbols.localPatternCharsDefault;
+ return applyLocalizedPattern (pattern, standardChars, localChars);
+ }
+
+ private final void append (StringBuffer buf, int value, int numDigits)
+ {
+ numberFormat.setMinimumIntegerDigits(numDigits);
+ numberFormat.format(value, buf, null);
+ }
+
+ public StringBuffer format (Date date, StringBuffer buf, FieldPosition pos)
+ {
+ 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
+ {
+ 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());
+ }
+ }
+ }
+ return buf;
+ }
+
+ private final boolean expect (String source, ParsePosition pos,
+ char ch)
+ {
+ 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;
+ }
+
+ public Date parse (String source, ParsePosition pos)
+ {
+ int fmt_index = 0;
+ int fmt_max = pattern.length();
+
+ calendar.clear();
+ int quote_start = -1;
+ for (; fmt_index < fmt_max; ++fmt_index)
+ {
+ 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;
+ }
+
+ // 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:
+ pos.setErrorIndex(pos.getIndex());
+ return null;
+ }
+
+ // 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;
+
+ // Assign the value and move on.
+ try
+ {
+ calendar.set(calendar_field, value);
+ }
+ // FIXME: what exception is thrown on an invalid
+ // non-lenient set?
+ catch (IllegalArgumentException x)
+ {
+ pos.setErrorIndex(pos.getIndex());
+ return null;
+ }
+ }
+
+ return calendar.getTime();
+ }
+
+ 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));
+ }
+
+ public int hashCode ()
+ {
+ int hash = super.hashCode();
+ if (pattern != null)
+ hash ^= pattern.hashCode();
+ return hash;
+ }
+}