package esl.cuenet.algorithms.firstk.personal.Utils;
import java.text.DateFormat;
import java.text.FieldPosition;
import java.text.NumberFormat;
import java.text.ParsePosition;
import java.util.Calendar;
import java.util.Date;
import java.util.Locale;
import java.util.TimeZone;
/**
*
* Formatter, Parsers for RFC 3339 Dates
*
* Taken from http://mericleclerin.blogspot.jp/2013/07/internetdateformat-for-rfc3339.html
*
*
*/
public class RFC3339DateFormatter extends DateFormat {
/** Auto generated serialVersionUID. */
private static final long serialVersionUID = 1223084646264103173L;
/** Date/time formatting with time offset. */
private boolean offset = true;
/** Fractional seconds digits. */
private int fractionalSecondsDigits = 0;
/**
* Default constructor.
* Output time offset, and not output fractional seconds.
*/
public RFC3339DateFormatter() {
super();
setCalendar(Calendar.getInstance());
setNumberFormat(NumberFormat.getInstance());
}
/**
* Constructor.
* @param zone the given new time zone.
* @param aLocale the given locale.
*/
public RFC3339DateFormatter(TimeZone zone, Locale aLocale) {
super();
setCalendar(Calendar.getInstance(zone, aLocale));
setNumberFormat(NumberFormat.getInstance(aLocale));
}
/**
* Constructor.
* @param offset date/time formatting with time offset
* @param fractionalSecondsLength fractional seconds digits (0-3)
*/
public RFC3339DateFormatter(
boolean offset,
int fractionalSecondsLength) {
this();
setOffset(offset);
setFractionalSecondsDigits(fractionalSecondsLength);
}
/**
* Constructor.
* @param zone the given new time zone.
* @param aLocale the given locale.
* @param offset date/time formatting with time offset
* @param fractionalSecondsLength fractional seconds digits (0-3)
*/
public RFC3339DateFormatter(
TimeZone zone,
Locale aLocale,
boolean offset,
int fractionalSecondsLength) {
this(zone, aLocale);
setOffset(offset);
setFractionalSecondsDigits(fractionalSecondsLength);
}
/**
* Tell whether date/time formatting with time offset.
* The offset is used only format method.
* @return true is formatting with time offset; false otherwise.
*/
public boolean isOffset() {
return this.offset;
}
/**
* Specify whether or not date/time formatting with time offset.
* The offset is used only format method.
* @param anOffset true is formatting with time offset; false otherwise.
*/
public void setOffset(boolean anOffset) {
this.offset = anOffset;
}
/**
* Gets the number of fractional seconds digits.
* The digits is used only format method.
* @return fractional seconds digits (0-3)
*/
public int getFractionalSecondsDigits() {
return this.fractionalSecondsDigits;
}
/**
* Sets the number of fractional seconds digits.
* Digits range is 0 to 3.
* If digits over range, this method throws IllegalArgumentException.
* The digits is used only format method.
* @param aFractionalSecondsDigits fractional seconds digits (0-3)
*/
public void setFractionalSecondsDigits(int aFractionalSecondsDigits) {
if (aFractionalSecondsDigits < 0 || 3 < aFractionalSecondsDigits) {
throw new IllegalArgumentException(
"Fractional seconds digits range is 0 to 3. (" + aFractionalSecondsDigits+ ")");
}
this.fractionalSecondsDigits = aFractionalSecondsDigits;
}
/**
* Formats a Date into a date/time string.
* @param date a Date to be formatted into a date/time string.
* @param toAppendTo the string buffer for the returning time string.
* @param fieldPosition keeps track of the position of the field within the returned string.
*/
@Override
public StringBuffer format(
Date date,
StringBuffer toAppendTo,
FieldPosition fieldPosition) {
Calendar formatCalendar = (Calendar) getCalendar().clone();
formatCalendar.setTime(date);
if (isOffset()) {
int formatedOffset = formatCalendar.get(Calendar.ZONE_OFFSET);
toAppendTo.append(String.format(
"%1$tFT%1$tH:%1$tM:%1$tS", formatCalendar.getTime()));
toAppendTo.append(formatFractionalSeconds(formatCalendar));
toAppendTo.append((formatedOffset >= 0) ? "+" : "-");
toAppendTo.append(String.format(
"%1$02d:%2$02d",
Integer.valueOf(formatedOffset / (60 * 60 * 1000)),
Integer.valueOf(formatedOffset % (60 * 60 * 1000) / 1000)));
} else {
// Correct offset
formatCalendar.add(Calendar.MILLISECOND, -formatCalendar.get(Calendar.ZONE_OFFSET));
toAppendTo.append(String.format(
"%1$tFT%1$tH:%1$tM:%1$tS", formatCalendar.getTime()));
toAppendTo.append(formatFractionalSeconds(formatCalendar));
toAppendTo.append("Z");
}
fieldPosition.setBeginIndex(0);
fieldPosition.setEndIndex(toAppendTo.length());
return toAppendTo;
}
/***
* Formats a Date into a fractional seconds string.
* @param formatCalendar be formatted date
* @return fractional seconds string
*/
protected String formatFractionalSeconds(Calendar formatCalendar) {
String fractionalSeconds = "";
int digits = getFractionalSecondsDigits();
if (digits != 0) {
Integer milllisecond = Integer.valueOf(formatCalendar.get(Calendar.MILLISECOND));
fractionalSeconds = "." + String.format("%03d", milllisecond).substring(0, digits);
}
return fractionalSeconds;
}
/**
* Parse a date/time string according to the given parse position.
* @param source The date/time string to be parsed
* @param pos the parsing position
* the position at which parsing terminated, or the start position if the parse failed.
*/
@Override
public Date parse(String source, ParsePosition pos) {
try {
Calendar parseCalendar = (Calendar) getCalendar().clone();
parseCalendar.set(Calendar.YEAR, parseNumber(source, pos, 4));
checkSeparator(source, pos, "-");
parseCalendar.set(Calendar.MONTH, parseNumber(source, pos, 2) - 1);
checkSeparator(source, pos, "-");
parseCalendar.set(Calendar.DAY_OF_MONTH, parseNumber(source, pos, 2));
checkSeparator(source, pos, "T");
parseCalendar.set(Calendar.HOUR_OF_DAY, parseNumber(source, pos, 2));
checkSeparator(source, pos, ":");
parseCalendar.set(Calendar.MINUTE, parseNumber(source, pos, 2));
checkSeparator(source, pos, ":");
parseCalendar.set(Calendar.SECOND, parseNumber(source, pos, 2));
if (source.substring(pos.getIndex()).startsWith(".")) {
pos.setIndex(pos.getIndex() + 1);
parseCalendar.set(Calendar.MILLISECOND, parseFractionalSeconds(source, pos));
}
String next = source.substring(pos.getIndex());
if (next.equals("Z")) {
pos.setIndex(pos.getIndex() + 1);
// "Z" equals 00:00
parseCalendar.set(Calendar.ZONE_OFFSET, 0);
return parseCalendar.getTime();
} else if (next.startsWith("+")) {
pos.setIndex(pos.getIndex() + 1);
parseCalendar.set(Calendar.ZONE_OFFSET, parseOffset(source, pos));
return parseCalendar.getTime();
} else if (next.startsWith("-")) {
pos.setIndex(pos.getIndex() + 1);
parseCalendar.set(Calendar.ZONE_OFFSET, -parseOffset(source, pos));
return parseCalendar.getTime();
}
pos.setErrorIndex(pos.getIndex());
return null;
} catch (IndexOutOfBoundsException e) {
pos.setErrorIndex(pos.getIndex());
return null;
} catch (NumberFormatException e) {
pos.setErrorIndex(pos.getIndex());
return null;
}
}
/**
* Parse number, and increment parse position.
* @param source A String whose beginning should be parsed.
* @param pos the parsing position
* @param length parse length
* @return parsed number
*/
protected static int parseNumber(
String source,
ParsePosition pos,
int length) {
int index = pos.getIndex();
int number = Integer.parseInt(source.substring(index, index + length));
pos.setIndex(index + length);
return number;
}
/**
* Check separator string is valid.
* If separetaor is invalid, this method throws IndexOutOfBoundsException.
* @param source A String whose beginning should be parsed.
* @param pos the parsing position
* @param separator separator string
*/
protected static void checkSeparator(
String source,
ParsePosition pos,
String separator) {
int index = pos.getIndex();
int length = separator.length();
if (!source.substring(index, index + length).equals(separator)) {
throw new IndexOutOfBoundsException();
}
pos.setIndex(index + length);
}
/**
* Parse fractional seconds.
* @param source A String whose beginning should be parsed.
* @param pos the parsing position
* @return parsed fractional seconds
*/
protected static int parseFractionalSeconds(
String source,
ParsePosition pos) {
String milliSecond = source.substring(pos.getIndex()).split("\\D")[0];
int number = Integer.parseInt(milliSecond);
pos.setIndex(pos.getIndex() + milliSecond.length());
return number;
}
/**
* Parse offset.
* But it is not include "+" or "-".
* If this method is called, these operator was parsed.
* @param source A String whose beginning should be parsed.
* @param pos the parsing position
* @return offset millisecond
*/
protected static int parseOffset(
String source,
ParsePosition pos) {
int hour = parseNumber(source, pos, 2);
checkSeparator(source, pos, ":");
int minute = parseNumber(source, pos, 2);
return (hour * 60 + minute) * 60 * 1000;
}
}