package test;

import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;
import org.joda.time.format.DateTimeFormatterBuilder;
import org.joda.time.format.DateTimeParser;
import org.joda.time.format.ISODateTimeFormat;

/**
 * <p>An implementation of the W3C's specification of the ISO8601 date-time
 * format.</p>
 * 
 * <p>Optimally, these would be wrapped into their own DateTimeFormat and
 * DateTimeFormatter classes; however, I did not have time to do this and had
 * to use this for the time being.</p>
 *
 * @author John Jenkins
 */
public class Test {
	/**
	 * The date-time formatter for the W3C's specifications for date-only
	 * values.
	 */
	private static final DateTimeFormatter ISO_W3C_DATE_FORMATTER;
	static {
		// The W3C define 6 formats as valid ISO date/time values.
		DateTimeParser[] parsers = new DateTimeParser[3];
		
		// Just the year.
		parsers[0] = ISODateTimeFormat.year().getParser();
		
		// The year and month.
		parsers[1] = ISODateTimeFormat.yearMonth().getParser();
		
		// The year, month, and day.
		parsers[2] = 
			ISODateTimeFormat.yearMonthDay().getParser();
		
		// Build the formatter with the 6 parsers and the complete ISO 
		// date-time printer.
		DateTimeFormatterBuilder builder = new DateTimeFormatterBuilder();
		builder.append(ISODateTimeFormat.dateTime().getPrinter(), parsers);
		ISO_W3C_DATE_FORMATTER = builder.toFormatter().withZoneUTC();
	}
	
	/**
	 * The date-time formatter for the W3C's specifications for date-time 
	 * values.
	 */
	private static final DateTimeFormatter ISO_W3C_DATE_TIME_FORMATTER;
	static {
		// The W3C define 6 formats as valid ISO date/time values.
		DateTimeParser[] parsers = new DateTimeParser[3];
		
		// The year, month, day, hour, minute, and time zone.
		// Build the parser from the existing ISODateTimeParser for the year,
		// month, day, hour, and minute and add the time zone.
		DateTimeFormatterBuilder dateHourMinuteTimezone =
			new DateTimeFormatterBuilder();
		dateHourMinuteTimezone.append(ISODateTimeFormat.dateHourMinute());
		dateHourMinuteTimezone.append(DateTimeFormat.forPattern("ZZ"));
		parsers[0] = dateHourMinuteTimezone.toFormatter().getParser();
		
		// The year, month, day, hour, minute, second, and time zone.
		parsers[1] = ISODateTimeFormat.dateTimeNoMillis().getParser();
		
		// The year, month, day, hour, minute, and time zone.
		// Build the parser from the existing ISODateTimeParser for the year,
		// month, day, hour, and minute and add the time zone.
		parsers[2] = ISODateTimeFormat.dateTime().getParser();
		
		// Build the formatter with the 6 parsers and the complete ISO 
		// date-time printer.
		DateTimeFormatterBuilder builder = new DateTimeFormatterBuilder();
		builder.append(ISODateTimeFormat.dateTime().getPrinter(), parsers);
		ISO_W3C_DATE_TIME_FORMATTER = builder.toFormatter();
	}
	
	/**
	 * Parses a string into a DateTime object that represents the time as long
	 * as it conforms to one of the W3C's specifications of the ISO8601
	 * standard.
	 * 
	 * @param value The string value to be parsed.
	 * 
	 * @return A DateTime object that represents the parsed value.
	 * 
	 * @throws IllegalArgumentException The value does not conform to the W3C
	 * 									specification of the ISO8601 standard.
	 */
	public static DateTime parse(String value) {
		try {
			return ISO_W3C_DATE_FORMATTER.parseDateTime(value);
		}
		catch(IllegalArgumentException e) {
			return ISO_W3C_DATE_TIME_FORMATTER.parseDateTime(value);
		}
	}
	
	/**
	 * Tests that the parse() function works for all W3C date-time formats.
	 * 
	 * @param args Ignored.
	 * 
	 * @throws Exception Not thrown.
	 */
	public static void main(String args[]) throws Exception {
		DateTime year = parse("1970");
		DateTime yearMonth = parse("1970-01");
		DateTime yearMonthDay = parse("1970-01-01");
		DateTime yearMonthDayHourMinuteZone = parse("1969-12-31T17:00-07:00");
		DateTime yearMonthDayHourMinuteSecondZone = 
			parse("1969-12-31T17:00:00-07:00");
		DateTime yearMonthDayHourMinuteSecondMillisZone = 
			parse("1969-12-31T17:00:00.000-07:00");

		// Verify that the year parser parsed the value correctly.
		if(year.getMillis() != 0) {
			throw new IllegalStateException("The year parser is incorrect.");
		}
		// Verify that the year-month parser parsed the value correctly.
		if(yearMonth.getMillis() != 0) {
			throw 
				new IllegalStateException(
					"The year-month parser is incorrect.");
		}
		// Verify that the year-month-day parser parsed the value correctly.
		if(yearMonthDay.getMillis() != 0) {
			throw 
				new IllegalStateException(
					"The year-month-day parser is incorrect.");
		}
		// Verify that the year-month-day-hour-minute-zone parser parsed the
		// value correctly.
		if(yearMonthDayHourMinuteZone.getMillis() != 0) {
			throw 
				new IllegalStateException(
					"The year-month-day-hour-minute-zone parser is incorrect.");
		}
		// Verify that the time zone was preserved by the parser.
		if(! yearMonthDayHourMinuteZone
				.getZone().equals(DateTimeZone.forID("America/Los_Angeles"))) {
			
			throw 
				new IllegalStateException(
					"The time zone was lost by the year-month-day-hour-minute-zone parser.");
		}
		// Verify that the year-month-day-hour-minute-second-zone parser parsed
		// the value correctly.
		if(yearMonthDayHourMinuteSecondZone.getMillis() != 0) {
			throw 
				new IllegalStateException(
					"The year-month-day-hour-minute-second-zone parser is incorrect.");
		}
		// Verify that the time zone was preserved by the parser.
		if(! yearMonthDayHourMinuteSecondZone
			.getZone().equals(DateTimeZone.forID("America/Los_Angeles"))) {
		
			throw 
				new IllegalStateException(
					"The time zone was lost by the year-month-day-hour-minute-zone parser.");
		}
		// Verify that the year-month-day-hour-minute-second-millis-zone
		// parser parsed the value correctly.
		if(yearMonthDayHourMinuteSecondMillisZone.getMillis() != 0) {
			throw 
				new IllegalStateException(
					"The year-month-day-hour-minute-second-millis-zone parser is incorrect.");
		}
		// Verify that the time zone was preserved by the parser.
		if(! yearMonthDayHourMinuteSecondMillisZone
			.getZone().equals(DateTimeZone.forID("America/Los_Angeles"))) {
		
			throw 
				new IllegalStateException(
					"The time zone was lost by the year-month-day-hour-minute-zone parser.");
		}
		
		// The test completed successfully.
	}
}