package org.caudexorigo.time;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.NoSuchElementException;
import java.util.StringTokenizer;
import java.util.TimeZone;
public class ISO8601
{
private static final TimeZone timeZone = TimeZone.getTimeZone("UTC");
private static final TimeZone localTimeZone = TimeZone.getDefault();
private static final ThreadLocal<SimpleDateFormat> sdf_local = new ThreadLocal<SimpleDateFormat>()
{
@Override
protected SimpleDateFormat initialValue()
{
final String pattern = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'";
SimpleDateFormat _format;
_format = new SimpleDateFormat(pattern);
_format.setTimeZone(localTimeZone);
return _format;
}
};
private static final ThreadLocal<SimpleDateFormat> sdf = new ThreadLocal<SimpleDateFormat>()
{
@Override
protected SimpleDateFormat initialValue()
{
final String pattern = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'";
SimpleDateFormat _format;
_format = new SimpleDateFormat(pattern);
_format.setTimeZone(timeZone);
return _format;
}
};
private static boolean check(StringTokenizer st, String token) throws IllegalArgumentException
{
try
{
if (st.nextToken().equals(token))
{
return true;
}
else
{
throw new IllegalArgumentException("Missing [" + token + "]");
}
}
catch (NoSuchElementException ex)
{
return false;
}
}
private static Calendar getCalendar(String isodate) throws IllegalArgumentException
{
// YYYY-MM-DDThh:mm:ss.sTZD
StringTokenizer st = new StringTokenizer(isodate, "-T:.+Z", true);
Calendar calendar = new GregorianCalendar(TimeZone.getTimeZone("UTC"));
calendar.setLenient(false);
calendar.clear();
try
{
// Year
if (st.hasMoreTokens())
{
int year = Integer.parseInt(st.nextToken());
calendar.set(Calendar.YEAR, year);
}
else
{
return calendar;
}
// Month
if (check(st, "-") && (st.hasMoreTokens()))
{
int month = Integer.parseInt(st.nextToken()) - 1;
calendar.set(Calendar.MONTH, month);
}
else
{
return calendar;
}
// Day
if (check(st, "-") && (st.hasMoreTokens()))
{
int day = Integer.parseInt(st.nextToken());
calendar.set(Calendar.DAY_OF_MONTH, day);
}
else
{
return calendar;
}
// Hour
if (check(st, "T") && (st.hasMoreTokens()))
{
int hour = Integer.parseInt(st.nextToken());
calendar.set(Calendar.HOUR_OF_DAY, hour);
}
else
{
calendar.set(Calendar.HOUR_OF_DAY, 0);
calendar.set(Calendar.MINUTE, 0);
calendar.set(Calendar.SECOND, 0);
calendar.set(Calendar.MILLISECOND, 0);
return calendar;
}
// Minutes
if (check(st, ":") && (st.hasMoreTokens()))
{
int minutes = Integer.parseInt(st.nextToken());
calendar.set(Calendar.MINUTE, minutes);
}
else
{
calendar.set(Calendar.MINUTE, 0);
calendar.set(Calendar.SECOND, 0);
calendar.set(Calendar.MILLISECOND, 0);
return calendar;
}
//
// Not mandatory now
//
// Secondes
if (!st.hasMoreTokens())
{
return calendar;
}
String tok = st.nextToken();
if (tok.equals(":"))
{ // secondes
if (st.hasMoreTokens())
{
int secondes = Integer.parseInt(st.nextToken());
calendar.set(Calendar.SECOND, secondes);
if (!st.hasMoreTokens())
{
return calendar;
}
// frac sec
tok = st.nextToken();
if (tok.equals("."))
{
// bug fixed, thx to Martin Bottcher
String nt = st.nextToken();
while (nt.length() < 3)
{
nt += "0";
}
nt = nt.substring(0, 3); // Cut trailing chars..
int millisec = Integer.parseInt(nt);
// int millisec = Integer.parseInt(st.nextToken()) * 10;
calendar.set(Calendar.MILLISECOND, millisec);
if (!st.hasMoreTokens())
{
return calendar;
}
tok = st.nextToken();
}
else
{
calendar.set(Calendar.MILLISECOND, 0);
}
}
else
{
throw new IllegalArgumentException("No secondes specified");
}
}
else
{
calendar.set(Calendar.SECOND, 0);
calendar.set(Calendar.MILLISECOND, 0);
}
// Timezone
if (!tok.equals("Z"))
{ // UTC
if (!(tok.equals("+") || tok.equals("-")))
{
throw new IllegalArgumentException("only Z, + or - allowed");
}
boolean plus = tok.equals("+");
if (!st.hasMoreTokens())
{
throw new IllegalArgumentException("Missing hour field");
}
int tzhour = Integer.parseInt(st.nextToken());
int tzmin = 0;
if (check(st, ":") && (st.hasMoreTokens()))
{
tzmin = Integer.parseInt(st.nextToken());
}
else
{
throw new IllegalArgumentException("Missing minute field");
}
if (plus)
{
calendar.add(Calendar.HOUR, -tzhour);
calendar.add(Calendar.MINUTE, -tzmin);
}
else
{
calendar.add(Calendar.HOUR, tzhour);
calendar.add(Calendar.MINUTE, tzmin);
}
}
}
catch (NumberFormatException ex)
{
throw new IllegalArgumentException("[" + ex.getMessage() + "] is not an integer");
}
return calendar;
}
/**
* Parse the given string in ISO 8601 format and build a Date object.
*
* @param isodate
* the date in ISO 8601 format
* @return a Date instance
* @exception IllegalArgumentException
* if the date is not valid
*/
public static Date parse(String isodate) throws IllegalArgumentException
{
Calendar calendar = getCalendar(isodate);
return calendar.getTime();
}
/**
* Generate a date string in ISO 8601 format
*
* @param date
* a Date instance
* @return a string representing the date in the ISO 8601 format
*/
public static String format(Date date)
{
return sdf.get().format(date);
}
public static String formatLocalTz(Date date)
{
return sdf_local.get().format(date);
}
public static void test(String isodate)
{
System.out.println("----------------------------------");
try
{
Date date = parse(isodate);
System.out.println(">> " + isodate);
System.out.println(">> " + date.toString() + " [" + date.getTime() + "]");
System.out.println(">> " + format(date));
}
catch (IllegalArgumentException ex)
{
System.err.println(isodate + " is invalid");
System.err.println(ex.getMessage());
}
System.out.println("----------------------------------");
}
public static void test(Date date)
{
String isodate = null;
System.out.println("----------------------------------");
try
{
System.out.println(">> " + date.toString() + " [" + date.getTime() + "]");
isodate = format(date);
System.out.println(">> " + isodate);
date = parse(isodate);
System.out.println(">> " + date.toString() + " [" + date.getTime() + "]");
}
catch (IllegalArgumentException ex)
{
System.err.println(isodate + " is invalid");
System.err.println(ex.getMessage());
}
System.out.println("----------------------------------");
}
public static void main(String args[])
{
test("2007-12-14T21:47:35.538680Z");
test("2007-12-14T21:47:35.538Z");
test("2007-12-14T21:47:35Z");
test("1997-07-16T19:20:30.45+01:00");
test("1997-07-16T19:20:30.45123+01:00");
test("1997-07-16T19:20:30.45-02:00");
test("1997-07-16T19:20:30+01:00");
test("1997-07-16T19:20:30+01:00");
test("1997-07-16T19:20");
test("1997-07-16");
test("1997-07");
test("1997");
test(new Date());
}
}