/* * Copyright 2014 Attila Szegedi, Daniel Dekany, Jonathan Revusky * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package freemarker.template.utility; import java.text.DateFormat; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; import java.util.Locale; import java.util.TimeZone; import javax.xml.datatype.DatatypeConfigurationException; import javax.xml.datatype.DatatypeFactory; import javax.xml.datatype.XMLGregorianCalendar; import junit.framework.TestCase; import freemarker.template.utility.DateUtil.CalendarFieldsToDateConverter; import freemarker.template.utility.DateUtil.DateParseException; import freemarker.template.utility.DateUtil.DateToISO8601CalendarFactory; import freemarker.template.utility.DateUtil.TrivialCalendarFieldsToDateConverter; public class DateUtilTest extends TestCase { private final TimeZone originalDefaultTZ = TimeZone.getDefault(); @Override protected void setUp() throws Exception { TimeZone.setDefault(TimeZone.getTimeZone("Europe/Prague")); } @Override protected void tearDown() throws Exception { TimeZone.setDefault(originalDefaultTZ); } private final DateFormat df = new SimpleDateFormat("G yyyy-MM-dd HH:mm:ss:S Z", Locale.US); { df.setTimeZone(DateUtil.UTC); } private CalendarFieldsToDateConverter cf2dc = new TrivialCalendarFieldsToDateConverter(); private DateToISO8601CalendarFactory calendarFactory = new DateUtil.TrivialDateToISO8601CalendarFactory(); public DateUtilTest(String name) { super(name); } public void testDateToUTCString() throws ParseException { assertEquals( "1998-10-30T15:30:00.512Z", dateToISO8601UTCDateTimeMSString( df.parse("AD 1998-10-30 19:30:00:512 +0400"), true)); assertEquals( "1998-10-30T15:30:00.5Z", dateToISO8601UTCDateTimeMSString( df.parse("AD 1998-10-30 19:30:00:500 +0400"), true)); assertEquals( "1998-10-30T15:30:00.51Z", dateToISO8601UTCDateTimeMSString( df.parse("AD 1998-10-30 19:30:00:510 +0400"), true)); assertEquals( "1998-10-30T15:30:00.1Z", dateToISO8601UTCDateTimeMSString( df.parse("AD 1998-10-30 19:30:00:100 +0400"), true)); assertEquals( "1998-10-30T15:30:00.01Z", dateToISO8601UTCDateTimeMSString( df.parse("AD 1998-10-30 19:30:00:10 +0400"), true)); assertEquals( "1998-10-30T15:30:00.001Z", dateToISO8601UTCDateTimeMSString( df.parse("AD 1998-10-30 19:30:00:1 +0400"), true)); assertEquals( "2000-02-08T06:05:04Z", dateToISO8601UTCDateTimeMSString( df.parse("AD 2000-02-08 09:05:04:0 +0300"), true)); assertEquals( "0099-02-28T06:15:24Z", dateToISO8601UTCDateTimeString( df.parse( "AD 0099-03-02 09:15:24:0 +0300"), true)); assertEquals( "0010-02-28T06:15:24Z", dateToISO8601UTCDateTimeString( df.parse("AD 0010-03-02 09:15:24:0 +0300"), true)); assertEquals( "0001-02-28T06:15:24Z", dateToISO8601UTCDateTimeString( df.parse("AD 0001-03-02 09:15:24:0 +0300"), true)); assertEquals( "0000-02-29T06:15:24Z", dateToISO8601UTCDateTimeString( df.parse("BC 0001-03-02 09:15:24:0 +0300"), true)); assertEquals( "-1-02-28T06:15:24Z", dateToISO8601UTCDateTimeString( df.parse("BC 2-03-02 09:15:24:0 +0300"), true)); assertEquals( "10000-02-28T06:15:24Z", dateToISO8601UTCDateTimeString( df.parse("AD 10000-02-28 09:15:24:0 +0300"), true)); Date d = df.parse("AD 1998-10-30 19:30:00:512 +0400"); assertEquals( "1998-10-30", dateToISO8601UTCDateString(d)); assertEquals( "15:30:00.512Z", dateToISO8601UTCTimeMSString(d, true)); assertEquals( "15:30:00.512", dateToISO8601UTCTimeMSString(d, false)); assertEquals( "1998-10-30", dateToISO8601UTCDateString( new java.sql.Date(d.getTime()))); assertEquals( "15:30:00.512Z", dateToISO8601UTCTimeMSString( new java.sql.Time(d.getTime()), true)); } public void testLocalTime() throws ParseException { Date dsum = df.parse("AD 2010-05-09 20:00:00:0 UTC"); Date dwin = df.parse("AD 2010-01-01 20:00:00:0 UTC"); TimeZone tzRome = TimeZone.getTimeZone("Europe/Rome"); if (tzRome.getOffset(0) == 0) { throw new RuntimeException( "Can't get time zone for Europe/Rome!"); } assertEquals( "2010-05-09T22:00:00+02:00", dateToISO8601DateTimeString(dsum, tzRome)); assertEquals( "2010-01-01T21:00:00+01:00", dateToISO8601DateTimeString(dwin, tzRome)); assertEquals( "2010-05-09", dateToISO8601DateString(dsum, tzRome)); assertEquals( "2010-01-01", dateToISO8601DateString(dwin, tzRome)); assertEquals( "22:00:00+02:00", dateToISO8601TimeString(dsum, tzRome)); assertEquals( "21:00:00+01:00", dateToISO8601TimeString(dwin, tzRome)); TimeZone tzNY = TimeZone.getTimeZone("America/New_York"); if (tzNY.getOffset(0) == 0) { throw new RuntimeException( "Can't get time zone for America/New_York!"); } assertEquals( "2010-05-09T16:00:00-04:00", dateToISO8601DateTimeString(dsum, tzNY)); assertEquals( "2010-01-01T15:00:00-05:00", dateToISO8601DateTimeString(dwin, tzNY)); assertEquals( "2010-05-09", dateToISO8601DateString(dsum, tzNY)); assertEquals( "2010-01-01", dateToISO8601DateString(dwin, tzNY)); assertEquals( "16:00:00-04:00", dateToISO8601TimeString(dsum, tzNY)); assertEquals( "15:00:00-05:00", dateToISO8601TimeString(dwin, tzNY)); TimeZone tzFixed = TimeZone.getTimeZone("GMT+02:30"); assertEquals( "2010-05-09T22:30:00+02:30", dateToISO8601DateTimeString(dsum, tzFixed)); assertEquals( "2010-01-01T22:30:00+02:30", dateToISO8601DateTimeString(dwin, tzFixed)); } public void testGetTimeZone() throws Exception { assertTrue(DateUtil.getTimeZone("GMT") != DateUtil.UTC); assertTrue(DateUtil.getTimeZone("UT1") != DateUtil.UTC); assertEquals(DateUtil.getTimeZone("UTC"), DateUtil.UTC); assertEquals(DateUtil.getTimeZone("Europe/Rome"), TimeZone.getTimeZone("Europe/Rome")); assertEquals(DateUtil.getTimeZone("Iceland"), // GMT and no DST TimeZone.getTimeZone("Iceland")); try { DateUtil.getTimeZone("Europe/NoSuch"); fail(); } catch (UnrecognizedTimeZoneException e) { // good } } public void testTimeOnlyDate() throws UnrecognizedTimeZoneException { Date t = new Date(0L); SimpleDateFormat tf = new SimpleDateFormat("HH:mm:ss"); tf.setTimeZone(DateUtil.UTC); assertEquals("00:00:00", tf.format(t)); assertEquals("00:00:00", dateToISO8601UTCTimeString(t, false)); TimeZone gmt1 = DateUtil.getTimeZone("GMT+01"); tf.setTimeZone(gmt1); assertEquals("01:00:00", tf.format(t)); assertEquals("01:00:00+01:00", dateToISO8601TimeString(t, gmt1)); } public void testAccuracy() throws ParseException { Date d = df.parse("AD 2000-02-08 09:05:04:250 UTC"); assertEquals("2000-02-08T09:05:04Z", dateToISO8601UTCDateTimeString(d, true)); assertEquals("2000-02-08T09:05:04.25Z", dateToISO8601String(d, true, true, true, DateUtil.ACCURACY_MILLISECONDS, null)); assertEquals("2000-02-08T09:05:04Z", dateToISO8601String(d, true, true, true, DateUtil.ACCURACY_SECONDS, null)); assertEquals("2000-02-08T09:05Z", dateToISO8601String(d, true, true, true, DateUtil.ACCURACY_MINUTES, null)); assertEquals("2000-02-08T09Z", dateToISO8601String(d, true, true, true, DateUtil.ACCURACY_HOURS, null)); d = df.parse("AD 1998-10-30 19:30:00:000 +0400"); assertEquals( "15:30:00Z", dateToISO8601UTCTimeMSString(d, true)); assertEquals( "15:30:00.000Z", dateToISO8601UTCTimeMSFString(d, true)); assertEquals( "1998-10-30T15:30:00Z", dateToISO8601UTCDateTimeMSString(d, true)); assertEquals( "1998-10-30T15:30:00.000Z", dateToISO8601UTCDateTimeMSFString(d, true)); d = df.parse("AD 1998-10-30 19:30:00:100 +0400"); assertEquals( "15:30:00.1Z", dateToISO8601UTCTimeMSString(d, true)); assertEquals( "15:30:00.100Z", dateToISO8601UTCTimeMSFString(d, true)); assertEquals( "1998-10-30T15:30:00.1Z", dateToISO8601UTCDateTimeMSString(d, true)); assertEquals( "1998-10-30T15:30:00.100Z", dateToISO8601UTCDateTimeMSFString(d, true)); d = df.parse("AD 1998-10-30 19:30:00:010 +0400"); assertEquals( "15:30:00.01Z", dateToISO8601UTCTimeMSString(d, true)); assertEquals( "15:30:00.010Z", dateToISO8601UTCTimeMSFString(d, true)); assertEquals( "1998-10-30T15:30:00.01Z", dateToISO8601UTCDateTimeMSString(d, true)); assertEquals( "1998-10-30T15:30:00.010Z", dateToISO8601UTCDateTimeMSFString(d, true)); d = df.parse("AD 1998-10-30 19:30:00:001 +0400"); assertEquals( "15:30:00.001Z", dateToISO8601UTCTimeMSString(d, true)); assertEquals( "15:30:00.001Z", dateToISO8601UTCTimeMSFString(d, true)); assertEquals( "1998-10-30T15:30:00.001Z", dateToISO8601UTCDateTimeMSString(d, true)); assertEquals( "1998-10-30T15:30:00.001Z", dateToISO8601UTCDateTimeMSFString(d, true)); } public void testXSFormatISODeviations() throws ParseException, UnrecognizedTimeZoneException { Date dsum = df.parse("AD 2010-05-09 20:00:00:0 UTC"); Date dwin = df.parse("AD 2010-01-01 20:00:00:0 UTC"); TimeZone tzRome = DateUtil.getTimeZone("Europe/Rome"); assertEquals( "2010-01-01T21:00:00+01:00", DateUtil.dateToXSString(dwin, true, true, true, DateUtil.ACCURACY_SECONDS, tzRome, calendarFactory)); assertEquals( "2010-05-09T22:00:00+02:00", DateUtil.dateToXSString(dsum, true, true, true, DateUtil.ACCURACY_SECONDS, tzRome, calendarFactory)); assertEquals( "2010-01-01+01:00", // ISO doesn't allow date-only with TZ DateUtil.dateToXSString(dwin, true, false, true, DateUtil.ACCURACY_SECONDS, tzRome, calendarFactory)); assertEquals( "2010-05-09+02:00", // ISO doesn't allow date-only with TZ DateUtil.dateToXSString(dsum, true, false, true, DateUtil.ACCURACY_SECONDS, tzRome, calendarFactory)); assertEquals( "21:00:00+01:00", DateUtil.dateToXSString(dwin, false, true, true, DateUtil.ACCURACY_SECONDS, tzRome, calendarFactory)); assertEquals( "22:00:00+02:00", DateUtil.dateToXSString(dsum, false, true, true, DateUtil.ACCURACY_SECONDS, tzRome, calendarFactory)); assertEquals( "-1-02-29T06:15:24Z", // ISO uses 0 for BC 1 DateUtil.dateToXSString( df.parse("BC 0001-03-02 09:15:24:0 +0300"), true, true, true, DateUtil.ACCURACY_SECONDS, DateUtil.UTC, calendarFactory)); assertEquals( "-2-02-28T06:15:24Z", // ISO uses -1 for BC 2 DateUtil.dateToXSString( df.parse("BC 2-03-02 09:15:24:0 +0300"), true, true, true, DateUtil.ACCURACY_SECONDS, DateUtil.UTC, calendarFactory)); } private String dateToISO8601DateTimeString( Date date, TimeZone tz) { return dateToISO8601String(date, true, true, true, DateUtil.ACCURACY_SECONDS, tz); } private String dateToISO8601UTCDateTimeString( Date date, boolean offsetPart) { return dateToISO8601String(date, true, true, offsetPart, DateUtil.ACCURACY_SECONDS, DateUtil.UTC); } private String dateToISO8601UTCDateTimeMSString( Date date, boolean offsetPart) { return dateToISO8601String(date, true, true, offsetPart, DateUtil.ACCURACY_MILLISECONDS, DateUtil.UTC); } private String dateToISO8601UTCDateTimeMSFString( Date date, boolean offsetPart) { return dateToISO8601String(date, true, true, offsetPart, DateUtil.ACCURACY_MILLISECONDS_FORCED, DateUtil.UTC); } private String dateToISO8601DateString(Date date, TimeZone tz) { return dateToISO8601String(date, true, false, false, DateUtil.ACCURACY_SECONDS, tz); } private String dateToISO8601UTCDateString(Date date) { return dateToISO8601String(date, true, false, false, DateUtil.ACCURACY_SECONDS, DateUtil.UTC); } private String dateToISO8601TimeString( Date date, TimeZone tz) { return dateToISO8601String(date, false, true, true, DateUtil.ACCURACY_SECONDS, tz); } private String dateToISO8601UTCTimeString( Date date, boolean offsetPart) { return dateToISO8601String(date, false, true, offsetPart, DateUtil.ACCURACY_SECONDS, DateUtil.UTC); } private String dateToISO8601UTCTimeMSString( Date date, boolean offsetPart) { return dateToISO8601String(date, false, true, offsetPart, DateUtil.ACCURACY_MILLISECONDS, DateUtil.UTC); } private String dateToISO8601UTCTimeMSFString( Date date, boolean offsetPart) { return dateToISO8601String(date, false, true, offsetPart, DateUtil.ACCURACY_MILLISECONDS_FORCED, DateUtil.UTC); } private String dateToISO8601String( Date date, boolean datePart, boolean timePart, boolean offsetPart, int accuracy, TimeZone timeZone) { return DateUtil.dateToISO8601String( date, datePart, timePart, offsetPart, accuracy, timeZone, calendarFactory); } public void testParseDate() throws DateParseException { assertDateParsing( "AD 1998-10-29 20:00:00:0 +0000", null, "1998-10-30+04:00", DateUtil.UTC); assertDateParsing( "AD 1998-10-30 02:00:00:0 +0000", null, "1998-10-30-02:00", DateUtil.UTC); assertDateParsing( "AD 1998-10-30 02:00:00:0 +0000", "1998-10-30", DateUtil.parseXSTimeZone("-02:00")); assertDateParsing( null, "AD 1998-10-30 02:00:00:0 +0000", "19981030", DateUtil.parseXSTimeZone("-02:00")); assertDateParsing( "AD 1998-10-30 00:00:00:0 +0000", null, "1998-10-30Z", DateUtil.UTC); assertDateParsing( "AD 1998-10-30 00:00:00:0 +0000", "1998-10-30", DateUtil.UTC); assertDateParsing( null, "AD 1998-10-30 00:00:00:0 +0000", "19981030", DateUtil.UTC); assertDateParsing( "AD 1998-10-29 20:00:00:0 +0000", null, "1998-10-30+04:00", DateUtil.UTC); assertDateParsing( "AD 1998-10-30 04:00:00:0 +0000", null, "1998-10-30-04:00", DateUtil.UTC); assertDateParsing( "AD 1998-10-30 00:00:00:0 +0000", null, "1998-10-30Z", DateUtil.UTC); try { // XS doesn't have year 0 assertDateParsing( "BC 0000-02-05 00:00:00:0 +0000", null, "0000-02-03Z", DateUtil.UTC); fail(); } catch (DateParseException e) { echo(e); } assertDateParsing( null, "BC 0001-02-05 00:00:00:0 +0000", "0000-02-03", DateUtil.UTC); assertDateParsing( null, "BC 0001-02-05 00:00:00:0 +0000", "00000203", DateUtil.UTC); assertDateParsing( "BC 0001-02-05 00:00:00:0 +0000", // Julian "BC 0002-02-05 00:00:00:0 +0000", // Julian "-0001-02-03", DateUtil.UTC); // Proleptic Gregorian assertDateParsing( null, "BC 0002-02-05 00:00:00:0 +0000", // Julian "-00010203", DateUtil.UTC); // Proleptic Gregorian assertDateParsing( "AD 0001-02-05 00:00:00:0 +0000", // Julian null, "0001-02-03Z", DateUtil.UTC); // Proleptic Gregorian assertDateParsing( "AD 0001-02-05 00:00:00:0 +0000", // Julian "0001-02-03", DateUtil.UTC); // Proleptic Gregorian assertDateParsing( null, "AD 0001-02-05 00:00:00:0 +0000", // Julian "00010203", DateUtil.UTC); // Proleptic Gregorian assertDateParsing( "AD 1001-12-07 00:00:00:0 +0000", // Julian null, "1001-12-13Z", DateUtil.UTC); // Proleptic Gregorian assertDateParsing( "AD 1001-12-07 00:00:00:0 +0000", // Julian "1001-12-13", DateUtil.UTC); // Proleptic Gregorian assertDateParsing( "AD 2006-12-31 00:00:00:0 +0000", null, "2006-12-31Z", DateUtil.UTC); assertDateParsing( "AD 2006-12-31 00:00:00:0 +0000", "2006-12-31", DateUtil.UTC); assertDateParsing( "AD 2006-01-01 00:00:00:0 +0000", null, "2006-01-01Z", DateUtil.UTC); assertDateParsing( "AD 2006-01-01 00:00:00:0 +0000", "2006-01-01", DateUtil.UTC); assertDateParsing( "AD 12006-01-01 00:00:00:0 +0000", "12006-01-01", DateUtil.UTC); assertDateParsing( null, "AD 12006-01-01 00:00:00:0 +0000", "120060101", DateUtil.UTC); } public void testParseDateMalformed() { assertDateMalformed("1998-10-30x"); assertDateMalformed("+1998-10-30"); assertDateMalformed("1998-10-"); assertDateMalformed("1998-1-30"); assertDateMalformed("1998-10-30+01"); assertDateMalformed("1998-00-01"); assertDateMalformed("1998-13-01"); assertDateMalformed("1998-10-00"); assertDateMalformed("1998-10-32"); assertDateMalformed("1998-02-31"); assertISO8601DateMalformed("2100103"); assertISO8601DateMalformed("210-01-03"); assertISO8601DateMalformed("2012-0301"); assertISO8601DateMalformed("201203-01"); assertISO8601DateMalformed("2012-01-01+01:00"); } public void testParseTime() throws DateParseException { assertTimeParsing( "AD 1970-01-01 17:30:05:0 +0000", "17:30:05", DateUtil.UTC); assertTimeParsing( null, "AD 1970-01-01 17:30:05:0 +0000", "173005", DateUtil.UTC); assertTimeParsing( "AD 1970-01-01 07:30:00:100 +0000", "07:30:00.1", DateUtil.UTC); assertTimeParsing( "AD 1970-01-01 07:30:00:120 +0000", "07:30:00.12", DateUtil.UTC); assertTimeParsing( "AD 1970-01-01 07:30:00:123 +0000", "07:30:00.123", DateUtil.UTC); assertTimeParsing( "AD 1970-01-01 07:30:00:123 +0000", "07:30:00.1235", DateUtil.UTC); assertTimeParsing( "AD 1970-01-01 07:30:00:123 +0000", "07:30:00.12346", DateUtil.UTC); assertTimeParsing( null, "AD 1970-01-01 07:30:00:123 +0000", "073000.12346", DateUtil.UTC); assertTimeParsing( null, "AD 1970-01-01 07:30:00:123 +0000", "073000,12346", DateUtil.UTC); assertTimeParsing( "AD 1970-01-01 07:30:00:120 +0000", "07:30:00.12", DateUtil.UTC); assertTimeParsing( "AD 1970-01-01 07:30:00:500 +0000", "07:30:00.5", DateUtil.UTC); assertTimeParsing( "AD 1970-01-01 16:30:05:0 +0000", "17:30:05+01:00", DateUtil.UTC); assertTimeParsing( null, "AD 1970-01-01 16:30:05:0 +0000", "173005+01", DateUtil.UTC); assertTimeParsing( "AD 1970-01-01 19:00:05:0 +0000", "17:30:05-01:30", DateUtil.UTC); assertTimeParsing( null, "AD 1970-01-01 19:00:05:0 +0000", "173005-0130", DateUtil.UTC); assertTimeParsing( "AD 1970-01-01 16:30:05:500 +0000", "17:30:05.5+01:00", DateUtil.UTC); assertTimeParsing( null, "AD 1970-01-01 16:30:05:500 +0000", "173005.5+0100", DateUtil.UTC); assertTimeParsing( null, "AD 1970-01-01 16:30:05:500 +0000", "173005.5+01", DateUtil.UTC); assertTimeParsing( null, "AD 1970-01-01 16:00:00:0 +0000", "170000+01", DateUtil.UTC); assertTimeParsing( null, "AD 1970-01-01 16:00:00:0 +0000", "1700+01", DateUtil.UTC); assertTimeParsing( null, "AD 1970-01-01 16:00:00:0 +0000", "17+01", DateUtil.UTC); assertTimeParsing( "AD 1970-01-01 00:00:00:0 +0000", "00:00:00", DateUtil.UTC); assertTimeParsing( "AD 1970-01-02 00:00:00:0 +0000", "24:00:00", DateUtil.UTC); assertTimeParsing( null, "AD 1970-01-02 00:00:00:0 +0000", "240000", DateUtil.UTC); assertTimeParsing( null, "AD 1970-01-02 00:00:00:0 +0000", "2400", DateUtil.UTC); assertTimeParsing( null, "AD 1970-01-02 00:00:00:0 +0000", "24:00", DateUtil.UTC); assertTimeParsing( null, "AD 1970-01-02 00:00:00:0 +0000", "24", DateUtil.UTC); assertTimeParsing( "AD 1970-01-01 23:59:59:999 +0000", "23:59:59.999", DateUtil.UTC); } public void testParseTimeMalformed() { assertTimeMalformed("00:0000"); assertTimeMalformed("00:00:00-01:60"); assertTimeMalformed("24:00:01"); assertTimeMalformed("00:00:61"); assertTimeMalformed("00:60:00"); assertTimeMalformed("25:00:00"); assertTimeMalformed("2:00:00"); assertTimeMalformed("02:0:00"); assertTimeMalformed("02:00:0"); assertISO8601TimeMalformed("1010101"); assertISO8601TimeMalformed("10101"); assertISO8601TimeMalformed("101"); assertISO8601TimeMalformed("1"); assertISO8601TimeMalformed("101010-1"); assertISO8601TimeMalformed("101010-100"); assertISO8601TimeMalformed("101010-10000"); assertISO8601TimeMalformed("101010+1"); assertISO8601TimeMalformed("101010+100"); assertISO8601TimeMalformed("101010+10000"); } public void testParseDateTime() throws DateParseException { assertDateTimeParsing( "AD 1998-10-30 11:30:00:0 +0000", "1998-10-30T15:30:00+04:00", DateUtil.UTC); assertDateTimeParsing( null, "AD 1998-10-30 11:30:00:0 +0000", "19981030T153000+0400", DateUtil.UTC); assertDateTimeParsing( "AD 1998-10-30 11:30:00:500 +0000", "1998-10-30T15:30:00.5+04:00", DateUtil.UTC); assertDateTimeParsing( "AD 1998-10-30 15:30:00:0 +0000", "1998-10-30T15:30:00Z", DateUtil.UTC); assertDateTimeParsing( null, "AD 1998-10-30 15:30:00:0 +0000", "19981030T1530Z", DateUtil.UTC); assertDateTimeParsing( "AD 1998-10-30 15:30:00:500 +0000", "1998-10-30T15:30:00.5Z", DateUtil.UTC); assertDateTimeParsing( "AD 1998-10-30 11:30:00:0 +0000", "1998-10-30T15:30:00+04:00", DateUtil.UTC); assertDateTimeParsing( "AD 1998-10-30 15:30:00:0 +0000", "1998-10-30T15:30:00Z", DateUtil.UTC); assertDateTimeParsing( "AD 1998-10-30 15:30:00:0 +0000", "1998-10-30T15:30:00", DateUtil.UTC); assertDateTimeParsing( null, "AD 1998-10-30 15:30:00:0 +0000", "1998-10-30T15:30", DateUtil.UTC); assertDateTimeParsing( "AD 1998-10-29 20:00:00:0 +0000", "1998-10-30T00:00:00+04:00", DateUtil.UTC); assertDateTimeParsing( "AD 1998-10-30 02:00:00:0 +0000", "1998-10-30T00:00:00-02:00", DateUtil.UTC); assertDateTimeParsing( "AD 1998-10-30 00:00:00:0 +0000", "1998-10-30T00:00:00Z", DateUtil.UTC); assertDateTimeParsing( "AD 1998-10-29 20:00:00:0 +0000", "1998-10-30T00:00:00+04:00", DateUtil.UTC); assertDateTimeParsing( "AD 1998-10-30 00:00:00:0 +0000", "1998-10-30T00:00:00Z", DateUtil.UTC); assertDateTimeParsing( null, "AD 1998-10-30 00:00:00:0 +0000", "1998-10-30T00:00Z", DateUtil.UTC); assertDateTimeParsing( null, "AD 1998-10-30 00:00:00:0 +0000", "1998-10-30T00:00", DateUtil.UTC); assertDateTimeParsing( null, "AD 1998-10-30 00:00:00:0 +0000", "19981030T00Z", DateUtil.UTC); // BC years try { assertDateTimeParsing( "", null, "0000-02-03T00:00:00Z", DateUtil.UTC); fail(); } catch (DateParseException e) { echo(e); } assertDateTimeParsing( null, "BC 0001-02-05 00:00:00:0 +0000", "0000-02-03T00:00:00Z", DateUtil.UTC); assertDateTimeParsing( "BC 0001-02-05 00:00:00:0 +0000", // Julian "BC 0002-02-05 00:00:00:0 +0000", // Julian "-0001-02-03T00:00:00Z", DateUtil.UTC); // Proleptic Gregorian assertDateTimeParsing( "AD 0001-02-05 00:00:00:0 +0000", // Julian "0001-02-03T00:00:00Z", DateUtil.UTC); // Proleptic Gregorian assertDateTimeParsing( "AD 1001-12-07 00:00:00:0 +0000", // Julian "1001-12-13T00:00:00Z", DateUtil.UTC); // Proleptic Gregorian assertDateTimeParsing( "AD 11001-12-13 00:00:00:0 +0000", "11001-12-13T00:00:00Z", DateUtil.UTC); assertDateTimeParsing( null, "AD 11001-12-13 00:00:00:0 +0000", "110011213T00Z", DateUtil.UTC); assertDateTimeParsing( "AD 2006-12-31 00:00:00:0 +0000", "2006-12-31T00:00:00Z", DateUtil.UTC); assertDateTimeParsing( "AD 2006-01-01 00:00:00:0 +0000", "2006-01-01T00:00:00Z", DateUtil.UTC); assertDateTimeParsing( "AD 1970-01-01 07:30:00:123 +0000", "1970-01-01T07:30:00.123", DateUtil.UTC); assertDateTimeParsing( "AD 1970-01-01 07:30:00:123 +0000", "1970-01-01T07:30:00.1235", DateUtil.UTC); assertDateTimeParsing( "AD 1970-01-01 07:30:00:123 +0000", "1970-01-01T07:30:00.12346", DateUtil.UTC); assertDateTimeParsing( "AD 1970-01-01 07:30:00:120 +0000", "1970-01-01T07:30:00.12", DateUtil.UTC); assertDateTimeParsing( "AD 1970-01-01 07:30:00:500 +0000", "1970-01-01T07:30:00.5", DateUtil.UTC); assertDateTimeParsing( "AD 1970-01-01 16:30:05:0 +0000", "1970-01-01T17:30:05+01:00", DateUtil.UTC); assertDateTimeParsing( "AD 1970-01-01 16:30:05:500 +0000", "1970-01-01T17:30:05.5+01:00", DateUtil.UTC); assertDateTimeParsing( "AD 1970-01-01 00:00:00:0 +0000", "1970-01-01T00:00:00", DateUtil.UTC); assertDateTimeParsing( "AD 1970-01-02 00:00:00:0 +0000", "1970-01-01T24:00:00", DateUtil.UTC); assertDateTimeParsing( "AD 1970-01-01 23:59:59:999 +0000", "1970-01-01T23:59:59.999", DateUtil.UTC); } public void testParseDateTimeMalformed() throws DateParseException { assertDateTimeMalformed("1998-00-01T00:00:00"); assertDateTimeMalformed("1998-13-01T00:00:00"); assertDateTimeMalformed("1998-10-00T00:00:00"); assertDateTimeMalformed("1998-10-32T00:00:00"); assertDateTimeMalformed("1998-02-31T00:00:00"); assertDateTimeMalformed("1970-01-02T24:00:01"); assertDateTimeMalformed("1970-01-01T00:00:61"); assertDateTimeMalformed("1970-01-01T00:60:00"); assertDateTimeMalformed("1970-01-01T25:00:00"); assertISO8601DateTimeMalformed("197-01-01T20:00:00"); } public void testParseXSTimeZone() throws DateParseException { assertEquals(0, DateUtil.parseXSTimeZone("Z").getOffset(0)); assertEquals(0, DateUtil.parseXSTimeZone("-00:00").getOffset(0)); assertEquals(0, DateUtil.parseXSTimeZone("+00:00").getOffset(0)); assertEquals(90 * 60 * 1000, DateUtil.parseXSTimeZone("+01:30").getOffset(0)); assertEquals(-4 * 60 * 60 * 1000, DateUtil.parseXSTimeZone("-04:00").getOffset(0)); assertEquals(((-23 * 60) - 59) * 60 * 1000, DateUtil.parseXSTimeZone("-23:59").getOffset(0)); assertEquals(((23 * 60) + 59) * 60 * 1000, DateUtil.parseXSTimeZone("+23:59").getOffset(0)); } public void testParseXSTimeZoneWrong() { try { DateUtil.parseXSTimeZone("04:00").getOffset(0); fail(); } catch (DateParseException e) { echo(e); } try { DateUtil.parseXSTimeZone("-04:00x").getOffset(0); fail(); } catch (DateParseException e) { echo(e); } try { DateUtil.parseXSTimeZone("-04").getOffset(0); fail(); } catch (DateParseException e) { echo(e); } try { DateUtil.parseXSTimeZone("+24:00").getOffset(0); fail(); } catch (DateParseException e) { echo(e); } try { DateUtil.parseXSTimeZone("-24:00").getOffset(0); fail(); } catch (DateParseException e) { echo(e); } try { DateUtil.parseXSTimeZone("-01:60").getOffset(0); fail(); } catch (DateParseException e) { echo(e); } } public void testParseXSDateTimeFTLAndJavax() throws DateParseException { // Explicit time zone: assertJavaxAndFTLXSDateTimesSame("2014-01-01T13:35:08Z"); assertJavaxAndFTLXSDateTimesSame("2014-01-01T13:35:08+02:00"); // Default time zone: assertJavaxAndFTLXSDateTimesSame("2014-01-01T13:35:08"); // winter assertJavaxAndFTLXSDateTimesSame("2014-07-01T13:35:08"); // summer // Proleptic Gregorian assertJavaxAndFTLXSDateTimesSame("1500-01-01T13:35:08Z"); assertJavaxAndFTLXSDateTimesSame("0200-01-01T13:35:08Z"); assertJavaxAndFTLXSDateTimesSame("0001-01-01T00:00:00+05:00"); // BC assertJavaxAndFTLXSDateTimesSame("0001-01-01T13:35:08Z"); assertJavaxAndFTLXSDateTimesSame("-0001-01-01T13:35:08Z"); // Hour 24 assertJavaxAndFTLXSDateTimesSame("2014-01-01T23:59:59"); if (isAtLeastJava6()) { // Java 5 has broken parser that doesn't allow 24. assertJavaxAndFTLXSDateTimesSame("2014-01-31T24:00:00"); assertJavaxAndFTLXSDateTimesSame("2014-01-01T24:00:00"); } assertJavaxAndFTLXSDateTimesSame("2014-01-02T00:00:00"); // same as the previous assertJavaxAndFTLXSDateTimesSame("2014-02-01T00:00:00"); // same as the previous // Under ms assertJavaxAndFTLXSDateTimesSame("2014-01-01T23:59:59.123456789"); assertJavaxAndFTLXSDateTimesSame("2014-01-01T23:59:59.1235"); } private boolean isAtLeastJava6() { try { Class.forName("java.lang.management.LockInfo"); } catch (ClassNotFoundException e) { return false; } return true; } private final DatatypeFactory datetypeFactory; { try { datetypeFactory = DatatypeFactory.newInstance(); } catch (DatatypeConfigurationException e) { throw new RuntimeException(e); } } private void assertJavaxAndFTLXSDateTimesSame(String s) throws DateParseException { XMLGregorianCalendar xgc = datetypeFactory.newXMLGregorianCalendar(s); Date javaxDate = xgc.toGregorianCalendar().getTime(); Date ftlDate = DateUtil.parseXSDateTime(s, TimeZone.getDefault(), cf2dc); assertEquals(javaxDate, ftlDate); } private void assertDateParsing(String expected, String parsed, TimeZone tz) throws DateParseException { assertDateParsing(expected, expected, parsed, tz); } private void assertDateParsing(String expectedXS, String expectedISO8601, String parsed, TimeZone tz) throws DateParseException { if (expectedXS != null) { assertEquals( expectedXS, df.format(DateUtil.parseXSDate(parsed, tz, cf2dc))); } if (expectedISO8601 != null) { assertEquals( expectedISO8601, df.format(DateUtil.parseISO8601Date(parsed, tz, cf2dc))); } } private void assertDateTimeParsing(String expected, String parsed, TimeZone tz) throws DateParseException { assertDateTimeParsing(expected, expected, parsed, tz); } private void assertDateTimeParsing(String expectedXS, String expectedISO8601, String parsed, TimeZone tz) throws DateParseException { if (expectedXS != null) { assertEquals( expectedXS, df.format(DateUtil.parseXSDateTime(parsed, tz, cf2dc))); } if (expectedISO8601 != null) { assertEquals( expectedISO8601, df.format(DateUtil.parseISO8601DateTime(parsed, tz, cf2dc))); } } private void assertTimeParsing(String expected, String parsed, TimeZone tz) throws DateParseException { assertTimeParsing(expected, expected, parsed, tz); } private void assertTimeParsing(String expectedXS, String expectedISO8601, String parsed, TimeZone tz) throws DateParseException { if (expectedXS != null) { assertEquals( expectedXS, df.format(DateUtil.parseXSTime(parsed, tz, cf2dc))); } if (expectedISO8601 != null) { assertEquals( expectedISO8601, df.format(DateUtil.parseISO8601Time(parsed, tz, cf2dc))); } } private void assertDateMalformed(String parsed) { try { DateUtil.parseXSDate(parsed, DateUtil.UTC, cf2dc); fail(); } catch (DateParseException e) { // Expected echo(e); } try { DateUtil.parseISO8601Date(parsed, DateUtil.UTC, cf2dc); fail(); } catch (DateParseException e) { // Expected echo(e); } } private void assertTimeMalformed(String parsed) { try { DateUtil.parseXSTime(parsed, DateUtil.UTC, cf2dc); fail(); } catch (DateParseException e) { // Expected echo(e); } try { DateUtil.parseISO8601Time(parsed, DateUtil.UTC, cf2dc); fail(); } catch (DateParseException e) { // Expected echo(e); } } private void assertDateTimeMalformed(String parsed) { try { DateUtil.parseXSDateTime(parsed, DateUtil.UTC, cf2dc); fail(); } catch (DateParseException e) { // Expected echo(e); } try { DateUtil.parseISO8601DateTime(parsed, DateUtil.UTC, cf2dc); fail(); } catch (DateParseException e) { // Expected echo(e); } } private void assertISO8601DateMalformed(String parsed) { try { DateUtil.parseISO8601Date(parsed, DateUtil.UTC, cf2dc); fail(); } catch (DateParseException e) { // Expected echo(e); } } private void assertISO8601TimeMalformed(String parsed) { try { DateUtil.parseISO8601Time(parsed, DateUtil.UTC, cf2dc); fail(); } catch (DateParseException e) { // Expected echo(e); } } private void assertISO8601DateTimeMalformed(String parsed) { try { DateUtil.parseISO8601DateTime(parsed, DateUtil.UTC, cf2dc); fail(); } catch (DateParseException e) { // Expected echo(e); } } private void echo(@SuppressWarnings("unused") DateParseException e) { // System.out.println(e); } }