diff --git a/classpath/java/text/ParseException.java b/classpath/java/text/ParseException.java new file mode 100644 index 0000000000..c300758e0a --- /dev/null +++ b/classpath/java/text/ParseException.java @@ -0,0 +1,24 @@ +/* Copyright (c) 2008-2013, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.text; + +public class ParseException extends Exception { + private int errorOffset; + + public ParseException(String message, int errorOffset) { + super(message); + this.errorOffset = errorOffset; + } + + public int getErrorOffset() { + return errorOffset; + } +} diff --git a/classpath/java/text/ParsePosition.java b/classpath/java/text/ParsePosition.java new file mode 100644 index 0000000000..2677f00b6b --- /dev/null +++ b/classpath/java/text/ParsePosition.java @@ -0,0 +1,39 @@ +/* Copyright (c) 2008-2013, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.text; + +public class ParsePosition { + private int index, errorIndex = -1; + + public ParsePosition(int index) { + this.index = index; + } + + public int getErrorIndex() { + return errorIndex; + } + + public int getIndex() { + return index; + } + + public void setErrorIndex(int i) { + errorIndex = i; + } + + public void setIndex(int i) { + index = i; + } + + public String toString() { + return "index: " + index + "(error index: " + errorIndex + ")"; + } +} diff --git a/classpath/java/text/SimpleDateFormat.java b/classpath/java/text/SimpleDateFormat.java new file mode 100644 index 0000000000..6086eaa561 --- /dev/null +++ b/classpath/java/text/SimpleDateFormat.java @@ -0,0 +1,98 @@ +/* Copyright (c) 2008-2013, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.text; + +import java.util.Calendar; +import java.util.Date; + +public class SimpleDateFormat { + private String pattern; + + public SimpleDateFormat(String pattern) { + this.pattern = pattern; + if (! "yyyy-MM-dd'T'HH:mm:ss".equals(pattern)) { + throw new UnsupportedOperationException("Unsupported pattern: " + pattern); + } + } + + public StringBuffer format(Date date, StringBuffer buffer, FieldPosition position) { + Calendar calendar = Calendar.getInstance(); + calendar.setTime(date); + pad(buffer, calendar.get(Calendar.YEAR), 4); + buffer.append('-'); + pad(buffer, calendar.get(Calendar.MONTH) + 1, 2); + buffer.append('-'); + pad(buffer, calendar.get(Calendar.DAY_OF_MONTH), 2); + buffer.append("T"); + pad(buffer, calendar.get(Calendar.HOUR_OF_DAY), 2); + buffer.append(':'); + pad(buffer, calendar.get(Calendar.MINUTE), 2); + buffer.append(':'); + pad(buffer, calendar.get(Calendar.SECOND), 2); + return buffer; + } + + public Date parse(String text) { + return parse(text, new ParsePosition(0)); + } + + public Date parse(String text, ParsePosition position) { + int index = position.getIndex(); + try { + Calendar calendar = Calendar.getInstance(); + index = parseField(text, index, 4, calendar, Calendar.YEAR, 0); + index = expectPrefix(text, index, "-"); + index = parseField(text, index, 2, calendar, Calendar.MONTH, -1); + index = expectPrefix(text, index, "-"); + index = parseField(text, index, 2, calendar, Calendar.DAY_OF_MONTH, 0); + index = expectPrefix(text, index, "T"); + index = parseField(text, index, 2, calendar, Calendar.HOUR_OF_DAY, 0); + index = expectPrefix(text, index, ":"); + index = parseField(text, index, 2, calendar, Calendar.MINUTE, 0); + index = expectPrefix(text, index, ":"); + index = parseField(text, index, 2, calendar, Calendar.SECOND, 0); + position.setIndex(index); + return calendar.getTime(); + } catch (ParseException e) { + position.setErrorIndex(index); + return null; + } + } + + private static void pad(StringBuffer buffer, int value, int digits) { + int i = value == 0 ? 1 : value; + while (i > 0) { + i /= 10; + --digits; + } + while (digits-- > 0) { + buffer.append('0'); + } + buffer.append(value); + } + + private static int parseField(String text, int offset, int length, Calendar calendar, int field, int adjustment) throws ParseException { + if (text.length() < offset + length) throw new ParseException("Short date: " + text, offset); + try { + int value = Integer.parseInt(text.substring(offset, offset + length), 10); + calendar.set(field, value + adjustment); + } catch (NumberFormatException e) { + throw new ParseException("Not a number: " + text, offset); + } + return offset + length; + } + + private static int expectPrefix(String text, int offset, String prefix) throws ParseException { + if (text.length() <= offset) throw new ParseException("Short date: " + text, offset); + if (! text.substring(offset).startsWith(prefix)) throw new ParseException("Parse error: " + text, offset); + return offset + prefix.length(); + } +} diff --git a/classpath/java/util/Calendar.java b/classpath/java/util/Calendar.java index b0b8dd1dde..c6c552dff2 100644 --- a/classpath/java/util/Calendar.java +++ b/classpath/java/util/Calendar.java @@ -55,6 +55,10 @@ public abstract class Calendar { time = date.getTime(); } + public Date getTime() { + return new Date(time); + } + public abstract void roll(int field, boolean up); public abstract void add(int field, int amount); @@ -102,6 +106,23 @@ public abstract class Calendar { parseIntoFields(this.time); } + public Date getTime() { + long days = fields[DAY_OF_MONTH] - 1; + long years = fields[YEAR] - EPOCH_LEAP_YEAR; + days += years * 365 + years / 4 + 1 - DAYS_TO_EPOCH; + for (int month = 0; month < fields[MONTH]; month++) { + days += DAYS_IN_MONTH[0][month]; + } + if (fields[MONTH] < 2 && isLeapYear(fields[YEAR])) { + days--; + } + long time = MILLIS_PER_DAY * days + + MILLIS_PER_HOUR * fields[HOUR_OF_DAY] + + MILLIS_PER_MINUTE * fields[MINUTE] + + MILLIS_PER_SECOND * fields[SECOND]; + return new Date(time); + } + private static boolean isLeapYear(int year) { return (year%4 == 0) && (year%100 != 0) || (year%400 == 0); } diff --git a/test/Dates.java b/test/Dates.java new file mode 100644 index 0000000000..9ee66ff27b --- /dev/null +++ b/test/Dates.java @@ -0,0 +1,25 @@ +import java.text.FieldPosition; +import java.text.SimpleDateFormat; +import java.util.Date; + +public class Dates { + private final static long EPOCH = 1234567890; + private final static String TEXT = "2009-02-13T23:31:30"; + + private static void expect(boolean v) { + if (! v) throw new RuntimeException(); + } + + public static void main(String[] args) throws Exception { + SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss"); + Date date = format.parse("1970-01-01T00:00:00"); + expect(0 == date.getTime()); + + date = new Date(EPOCH * 1000l); + String actual = format.format(date, new StringBuffer(), new FieldPosition(0)).toString(); + expect(TEXT.equals(actual)); + + date = format.parse(TEXT); + expect(EPOCH == date.getTime() / 1000l); + } +}