Java tutorial
//revised from skaringa import java.text.DateFormat; import java.text.FieldPosition; import java.text.ParsePosition; import java.util.Calendar; import java.util.Date; import java.util.TimeZone; /** * Format and parse an ISO 8601 DateFormat used in XML documents. * The lexical representation for date is the reduced (right truncated) * lexical representation for dateTime: CCYY-MM-DD. * No left truncation is allowed. * An optional following time zone qualifier is allowed as for dateTime. */ public class ISO8601DateFormat extends ISO8601DateTimeFormat { /** * Construct a new ISO8601DateFormat using the default time zone. * */ public ISO8601DateFormat() { setCalendar(Calendar.getInstance()); } /** * Construct a new ISO8601DateFormat using a specific time zone. * @param tz The time zone used to format and parse the date. */ public ISO8601DateFormat(TimeZone tz) { setCalendar(Calendar.getInstance(tz)); } /** * @see java.text.DateFormat#parse(String, ParsePosition) */ public Date parse(String text, ParsePosition pos) { int i = pos.getIndex(); try { int year = Integer.valueOf(text.substring(i, i + 4)).intValue(); i += 4; if (text.charAt(i) != '-') { throw new NumberFormatException(); } i++; int month = Integer.valueOf(text.substring(i, i + 2)).intValue() - 1; i += 2; if (text.charAt(i) != '-') { throw new NumberFormatException(); } i++; int day = Integer.valueOf(text.substring(i, i + 2)).intValue(); i += 2; calendar.set(year, month, day); calendar.set(Calendar.HOUR_OF_DAY, 0); calendar.set(Calendar.MINUTE, 0); calendar.set(Calendar.SECOND, 0); calendar.set(Calendar.MILLISECOND, 0); // no parts of a second i = parseTZ(i, text); } catch (NumberFormatException ex) { pos.setErrorIndex(i); return null; } catch (IndexOutOfBoundsException ex) { pos.setErrorIndex(i); return null; } finally { pos.setIndex(i); } return calendar.getTime(); } /** * @see java.text.DateFormat#format(Date, StringBuffer, FieldPosition) */ public StringBuffer format(Date date, StringBuffer sbuf, FieldPosition fieldPosition) { calendar.setTime(date); writeCCYYMM(sbuf); //writeTZ(sbuf); return sbuf; } } /** * Format and parse an ISO 8601 DateTimeFormat used in XML documents. * This lexical representation is the ISO 8601 * extended format CCYY-MM-DDThh:mm:ss * where "CC" represents the century, "YY" the year, "MM" the month * and "DD" the day, * preceded by an optional leading "-" sign to indicate a negative number. * If the sign is omitted, "+" is assumed. * The letter "T" is the date/time separator * and "hh", "mm", "ss" represent hour, minute and second respectively. * This representation may be immediately followed by a "Z" to indicate * Coordinated Universal Time (UTC) or, to indicate the time zone, * i.e. the difference between the local time and Coordinated Universal Time, * immediately followed by a sign, + or -, * followed by the difference from UTC represented as hh:mm. * */ class ISO8601DateTimeFormat extends DateFormat { /** * Construct a new ISO8601DateTimeFormat using the default time zone. * */ public ISO8601DateTimeFormat() { setCalendar(Calendar.getInstance()); } /** * Construct a new ISO8601DateTimeFormat using a specific time zone. * @param tz The time zone used to format and parse the date. */ public ISO8601DateTimeFormat(TimeZone tz) { setCalendar(Calendar.getInstance(tz)); } /** * @see DateFormat#parse(String, ParsePosition) */ public Date parse(String text, ParsePosition pos) { int i = pos.getIndex(); try { int year = Integer.valueOf(text.substring(i, i + 4)).intValue(); i += 4; if (text.charAt(i) != '-') { throw new NumberFormatException(); } i++; int month = Integer.valueOf(text.substring(i, i + 2)).intValue() - 1; i += 2; if (text.charAt(i) != '-') { throw new NumberFormatException(); } i++; int day = Integer.valueOf(text.substring(i, i + 2)).intValue(); i += 2; if (text.charAt(i) != 'T') { throw new NumberFormatException(); } i++; int hour = Integer.valueOf(text.substring(i, i + 2)).intValue(); i += 2; if (text.charAt(i) != ':') { throw new NumberFormatException(); } i++; int mins = Integer.valueOf(text.substring(i, i + 2)).intValue(); i += 2; int secs = 0; if (i < text.length() && text.charAt(i) == ':') { // handle seconds flexible i++; secs = Integer.valueOf(text.substring(i, i + 2)).intValue(); i += 2; } calendar.set(year, month, day, hour, mins, secs); calendar.set(Calendar.MILLISECOND, 0); // no parts of a second i = parseTZ(i, text); } catch (NumberFormatException ex) { pos.setErrorIndex(i); return null; } catch (IndexOutOfBoundsException ex) { pos.setErrorIndex(i); return null; } finally { pos.setIndex(i); } return calendar.getTime(); } /** * Parse the time zone. * @param i The position to start parsing. * @param text The text to parse. * @return The position after parsing has finished. */ protected final int parseTZ(int i, String text) { if (i < text.length()) { // check and handle the zone/dst offset int offset = 0; if (text.charAt(i) == 'Z') { offset = 0; i++; } else { int sign = 1; if (text.charAt(i) == '-') { sign = -1; } else if (text.charAt(i) != '+') { throw new NumberFormatException(); } i++; int offsetHour = Integer.valueOf(text.substring(i, i + 2)).intValue(); i += 2; if (text.charAt(i) != ':') { throw new NumberFormatException(); } i++; int offsetMin = Integer.valueOf(text.substring(i, i + 2)).intValue(); i += 2; offset = ((offsetHour * 60) + offsetMin) * 60000 * sign; } int offsetCal = calendar.get(Calendar.ZONE_OFFSET) + calendar.get(Calendar.DST_OFFSET); calendar.add(Calendar.MILLISECOND, offsetCal - offset); } return i; } /** * @see DateFormat#format(Date, StringBuffer, FieldPosition) */ public StringBuffer format(Date date, StringBuffer sbuf, FieldPosition fieldPosition) { calendar.setTime(date); writeCCYYMM(sbuf); sbuf.append('T'); writehhmmss(sbuf); writeTZ(sbuf); return sbuf; } /** * Write the time zone string. * @param sbuf The buffer to append the time zone. */ protected final void writeTZ(StringBuffer sbuf) { int offset = calendar.get(Calendar.ZONE_OFFSET) + calendar.get(Calendar.DST_OFFSET); if (offset == 0) { sbuf.append('Z'); } else { int offsetHour = offset / 3600000; int offsetMin = (offset % 3600000) / 60000; if (offset >= 0) { sbuf.append('+'); } else { sbuf.append('-'); offsetHour = 0 - offsetHour; offsetMin = 0 - offsetMin; } appendInt(sbuf, offsetHour, 2); sbuf.append(':'); appendInt(sbuf, offsetMin, 2); } } /** * Write hour, minutes, and seconds. * @param sbuf The buffer to append the string. */ protected final void writehhmmss(StringBuffer sbuf) { int hour = calendar.get(Calendar.HOUR_OF_DAY); appendInt(sbuf, hour, 2); sbuf.append(':'); int mins = calendar.get(Calendar.MINUTE); appendInt(sbuf, mins, 2); sbuf.append(':'); int secs = calendar.get(Calendar.SECOND); appendInt(sbuf, secs, 2); } /** * Write century, year, and months. * @param sbuf The buffer to append the string. */ protected final void writeCCYYMM(StringBuffer sbuf) { int year = calendar.get(Calendar.YEAR); appendInt(sbuf, year, 4); String month; switch (calendar.get(Calendar.MONTH)) { case Calendar.JANUARY: month = "-01-"; break; case Calendar.FEBRUARY: month = "-02-"; break; case Calendar.MARCH: month = "-03-"; break; case Calendar.APRIL: month = "-04-"; break; case Calendar.MAY: month = "-05-"; break; case Calendar.JUNE: month = "-06-"; break; case Calendar.JULY: month = "-07-"; break; case Calendar.AUGUST: month = "-08-"; break; case Calendar.SEPTEMBER: month = "-09-"; break; case Calendar.OCTOBER: month = "-10-"; break; case Calendar.NOVEMBER: month = "-11-"; break; case Calendar.DECEMBER: month = "-12-"; break; default: month = "-NA-"; break; } sbuf.append(month); int day = calendar.get(Calendar.DAY_OF_MONTH); appendInt(sbuf, day, 2); } /** * Write an integer value with leading zeros. * @param buf The buffer to append the string. * @param value The value to write. * @param length The length of the string to write. */ protected final void appendInt(StringBuffer buf, int value, int length) { int len1 = buf.length(); buf.append(value); int len2 = buf.length(); for (int i = len2; i < len1 + length; ++i) { buf.insert(len1, '0'); } } }