diff options
Diffstat (limited to 'libjava/java/text/SimpleDateFormat.java')
-rw-r--r-- | libjava/java/text/SimpleDateFormat.java | 522 |
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; + } +} |