/***************************************************
*
* cismet GmbH, Saarbruecken, Germany
*
* ... and it just works.
*
****************************************************/
package de.cismet.cids.custom.switchon.utils;
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.
*
* <p>Found on <a href="http://www.java2s.com/Code/Java/Data-Type/ISO8601DateFormat.htm">java2s</a>.</p>
*
* @author skaringa
* @version $Revision$, $Date$
*/
public class ISO8601DateFormat extends ISO8601DateTimeFormat {
//~ Constructors -----------------------------------------------------------
/**
* 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(final TimeZone tz) {
setCalendar(Calendar.getInstance(tz));
}
//~ Methods ----------------------------------------------------------------
@Override
public Date parse(final String text, final ParsePosition pos) {
int i = pos.getIndex();
try {
final int year = Integer.parseInt(text.substring(i, i + 4));
i += 4;
if (text.charAt(i) != '-') {
throw new NumberFormatException();
}
i++;
final int month = Integer.parseInt(text.substring(i, i + 2)) - 1;
i += 2;
if (text.charAt(i) != '-') {
throw new NumberFormatException();
}
i++;
final int day = Integer.parseInt(text.substring(i, i + 2));
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();
}
@Override
public StringBuffer format(final Date date, final StringBuffer sbuf, final 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.
*
* @version $Revision$, $Date$
*/
class ISO8601DateTimeFormat extends DateFormat {
//~ Constructors -----------------------------------------------------------
/**
* 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(final TimeZone tz) {
setCalendar(Calendar.getInstance(tz));
}
//~ Methods ----------------------------------------------------------------
/**
* @see DateFormat#parse(String, ParsePosition)
*/
@Override
public Date parse(final String text, final ParsePosition pos) {
int i = pos.getIndex();
try {
final int year = Integer.parseInt(text.substring(i, i + 4));
i += 4;
if (text.charAt(i) != '-') {
throw new NumberFormatException();
}
i++;
final int month = Integer.parseInt(text.substring(i, i + 2)) - 1;
i += 2;
if (text.charAt(i) != '-') {
throw new NumberFormatException();
}
i++;
final int day = Integer.parseInt(text.substring(i, i + 2));
i += 2;
if (text.charAt(i) != 'T') {
throw new NumberFormatException();
}
i++;
final int hour = Integer.parseInt(text.substring(i, i + 2));
i += 2;
if (text.charAt(i) != ':') {
throw new NumberFormatException();
}
i++;
final int mins = Integer.parseInt(text.substring(i, i + 2));
i += 2;
int secs = 0;
if ((i < text.length()) && (text.charAt(i) == ':')) {
// handle seconds flexible
i++;
secs = Integer.parseInt(text.substring(i, i + 2));
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.
*
* @throws NumberFormatException DOCUMENT ME!
*/
protected final int parseTZ(int i, final 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++;
final int offsetHour = Integer.parseInt(text.substring(i, i + 2));
i += 2;
if (text.charAt(i) != ':') {
throw new NumberFormatException();
}
i++;
final int offsetMin = Integer.parseInt(text.substring(i, i + 2));
i += 2;
offset = ((offsetHour * 60) + offsetMin) * 60000 * sign;
}
final 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)
*/
@Override
public StringBuffer format(final Date date, final StringBuffer sbuf, final 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(final StringBuffer sbuf) {
final 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(final StringBuffer sbuf) {
final int hour = calendar.get(Calendar.HOUR_OF_DAY);
appendInt(sbuf, hour, 2);
sbuf.append(':');
final int mins = calendar.get(Calendar.MINUTE);
appendInt(sbuf, mins, 2);
sbuf.append(':');
final 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(final StringBuffer sbuf) {
final 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);
final 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(final StringBuffer buf, final int value, final int length) {
final int len1 = buf.length();
buf.append(value);
final int len2 = buf.length();
for (int i = len2; i < (len1 + length); ++i) {
buf.insert(len1, '0');
}
}
}