Author: djones Date: Sun Sep 28 10:11:17 2014 New Revision: 1628061 URL: http://svn.apache.org/r1628061 Log: Added missing support for ISO 8601 parsing and printing using "ZZ" pattern (code now matches Javadoc). Addresses LANG-1000.
Modified: commons/proper/lang/trunk/src/changes/changes.xml commons/proper/lang/trunk/src/main/java/org/apache/commons/lang3/time/FastDateParser.java commons/proper/lang/trunk/src/main/java/org/apache/commons/lang3/time/FastDatePrinter.java commons/proper/lang/trunk/src/test/java/org/apache/commons/lang3/time/DateFormatUtilsTest.java commons/proper/lang/trunk/src/test/java/org/apache/commons/lang3/time/FastDatePrinterTest.java Modified: commons/proper/lang/trunk/src/changes/changes.xml URL: http://svn.apache.org/viewvc/commons/proper/lang/trunk/src/changes/changes.xml?rev=1628061&r1=1628060&r2=1628061&view=diff ============================================================================== --- commons/proper/lang/trunk/src/changes/changes.xml [utf-8] (original) +++ commons/proper/lang/trunk/src/changes/changes.xml [utf-8] Sun Sep 28 10:11:17 2014 @@ -22,6 +22,7 @@ <body> <release version="3.4" date="tba" description="tba"> + <action issue="LANG-1000" type="fix" dev="djones">ParseException when trying to parse UTC dates with Z as zone designator using DateFormatUtils.ISO_DATETIME_TIME_ZONE_FORMAT</action> <action issue="LANG-1035" type="fix" dev="djones">Javadoc for EqualsBuilder.reflectionEquals() is unclear</action> <action issue="LANG-1020" type="update" dev="britter" due-to="Libor Ondrusek">Improve performance of normalize space</action> <action issue="LANG-1033" type="add" dev="ggregory">Add StringUtils.countMatches(CharSequence, char)</action> Modified: commons/proper/lang/trunk/src/main/java/org/apache/commons/lang3/time/FastDateParser.java URL: http://svn.apache.org/viewvc/commons/proper/lang/trunk/src/main/java/org/apache/commons/lang3/time/FastDateParser.java?rev=1628061&r1=1628060&r2=1628061&view=diff ============================================================================== --- commons/proper/lang/trunk/src/main/java/org/apache/commons/lang3/time/FastDateParser.java (original) +++ commons/proper/lang/trunk/src/main/java/org/apache/commons/lang3/time/FastDateParser.java Sun Sep 28 10:11:17 2014 @@ -513,6 +513,10 @@ public class FastDateParser implements D case 'y': return formatField.length()>2 ?LITERAL_YEAR_STRATEGY :ABBREVIATED_YEAR_STRATEGY; case 'Z': + if (formatField.equals("ZZ")) { + return ISO_8601_STRATEGY; + } + //$FALL-THROUGH$ case 'z': return getLocaleSpecificStrategy(Calendar.ZONE_OFFSET, definingCalendar); } @@ -814,6 +818,32 @@ public class FastDateParser implements D cal.setTimeZone(tz); } } + + private static class ISO8601TimeZoneStrategy extends Strategy { + // Z, +hh, -hh, +hhmm, -hhmm, +hh:mm or -hh:mm + private static final String PATTERN = "(Z|(?:[+-]\\d{2}(?::?\\d{2})?))"; + + /** + * {@inheritDoc} + */ + @Override + boolean addRegex(FastDateParser parser, StringBuilder regex) { + regex.append(PATTERN); + return true; + } + + /** + * {@inheritDoc} + */ + @Override + void setCalendar(FastDateParser parser, Calendar cal, String value) { + if (value.equals("Z")) { + cal.setTimeZone(TimeZone.getTimeZone("UTC")); + } else { + cal.setTimeZone(TimeZone.getTimeZone("GMT" + value)); + } + } + } private static final Strategy NUMBER_MONTH_STRATEGY = new NumberStrategy(Calendar.MONTH) { @Override @@ -844,4 +874,5 @@ public class FastDateParser implements D private static final Strategy MINUTE_STRATEGY = new NumberStrategy(Calendar.MINUTE); private static final Strategy SECOND_STRATEGY = new NumberStrategy(Calendar.SECOND); private static final Strategy MILLISECOND_STRATEGY = new NumberStrategy(Calendar.MILLISECOND); + private static final Strategy ISO_8601_STRATEGY = new ISO8601TimeZoneStrategy(); } Modified: commons/proper/lang/trunk/src/main/java/org/apache/commons/lang3/time/FastDatePrinter.java URL: http://svn.apache.org/viewvc/commons/proper/lang/trunk/src/main/java/org/apache/commons/lang3/time/FastDatePrinter.java?rev=1628061&r1=1628060&r2=1628061&view=diff ============================================================================== --- commons/proper/lang/trunk/src/main/java/org/apache/commons/lang3/time/FastDatePrinter.java (original) +++ commons/proper/lang/trunk/src/main/java/org/apache/commons/lang3/time/FastDatePrinter.java Sun Sep 28 10:11:17 2014 @@ -274,6 +274,8 @@ public class FastDatePrinter implements case 'Z': // time zone (value) if (tokenLen == 1) { rule = TimeZoneNumberRule.INSTANCE_NO_COLON; + } else if (tokenLen == 2) { + rule = TimeZoneNumberRule.INSTANCE_ISO_8601; } else { rule = TimeZoneNumberRule.INSTANCE_COLON; } @@ -1173,18 +1175,22 @@ public class FastDatePrinter implements * or {@code +/-HH:MM}.</p> */ private static class TimeZoneNumberRule implements Rule { - static final TimeZoneNumberRule INSTANCE_COLON = new TimeZoneNumberRule(true); - static final TimeZoneNumberRule INSTANCE_NO_COLON = new TimeZoneNumberRule(false); + static final TimeZoneNumberRule INSTANCE_COLON = new TimeZoneNumberRule(true, false); + static final TimeZoneNumberRule INSTANCE_NO_COLON = new TimeZoneNumberRule(false, false); + static final TimeZoneNumberRule INSTANCE_ISO_8601 = new TimeZoneNumberRule(true, true); final boolean mColon; + final boolean mISO8601; /** * Constructs an instance of {@code TimeZoneNumberRule} with the specified properties. * * @param colon add colon between HH and MM in the output if {@code true} + * @param iso8601 create an ISO 8601 format output */ - TimeZoneNumberRule(final boolean colon) { + TimeZoneNumberRule(final boolean colon, final boolean iso8601) { mColon = colon; + mISO8601 = iso8601; } /** @@ -1200,6 +1206,11 @@ public class FastDatePrinter implements */ @Override public void appendTo(final StringBuffer buffer, final Calendar calendar) { + if (mISO8601 && calendar.getTimeZone().getID().equals("UTC")) { + buffer.append("Z"); + return; + } + int offset = calendar.get(Calendar.ZONE_OFFSET) + calendar.get(Calendar.DST_OFFSET); if (offset < 0) { Modified: commons/proper/lang/trunk/src/test/java/org/apache/commons/lang3/time/DateFormatUtilsTest.java URL: http://svn.apache.org/viewvc/commons/proper/lang/trunk/src/test/java/org/apache/commons/lang3/time/DateFormatUtilsTest.java?rev=1628061&r1=1628060&r2=1628061&view=diff ============================================================================== --- commons/proper/lang/trunk/src/test/java/org/apache/commons/lang3/time/DateFormatUtilsTest.java (original) +++ commons/proper/lang/trunk/src/test/java/org/apache/commons/lang3/time/DateFormatUtilsTest.java Sun Sep 28 10:11:17 2014 @@ -17,10 +17,13 @@ package org.apache.commons.lang3.time; import org.junit.Test; + import static org.junit.Assert.*; + import java.lang.reflect.Constructor; import java.lang.reflect.Modifier; import java.util.Calendar; +import java.util.Date; import java.util.Locale; import java.util.TimeZone; @@ -103,7 +106,7 @@ public class DateFormatUtilsTest { } @Test - public void testDateTimeISO(){ + public void testDateTimeISO() throws Exception { final TimeZone timeZone = TimeZone.getTimeZone("GMT-3"); final Calendar cal = Calendar.getInstance(timeZone); cal.set(2002,1,23,9,11,12); @@ -124,6 +127,14 @@ public class DateFormatUtilsTest { assertEquals("2002-02-23T09:11:12-03:00", text); text = DateFormatUtils.ISO_DATETIME_TIME_ZONE_FORMAT.format(cal); assertEquals("2002-02-23T09:11:12-03:00", text); + + Calendar utcCal = Calendar.getInstance(TimeZone.getTimeZone("UTC")); + utcCal.set(2002, 1, 23, 9, 11, 12); + utcCal.set(Calendar.MILLISECOND, 0); + text = DateFormatUtils.ISO_DATETIME_TIME_ZONE_FORMAT.format(utcCal); + assertEquals("2002-02-23T09:11:12Z", text); + Date date = DateFormatUtils.ISO_DATETIME_TIME_ZONE_FORMAT.parse(text); + assertEquals(utcCal.getTime(), date); } @Test @@ -249,4 +260,9 @@ public class DateFormatUtilsTest { } */ + @Test + public void testLANG1000() throws Exception { + String date = "2013-11-18T12:48:05Z"; + DateFormatUtils.ISO_DATETIME_TIME_ZONE_FORMAT.parse(date); + } } Modified: commons/proper/lang/trunk/src/test/java/org/apache/commons/lang3/time/FastDatePrinterTest.java URL: http://svn.apache.org/viewvc/commons/proper/lang/trunk/src/test/java/org/apache/commons/lang3/time/FastDatePrinterTest.java?rev=1628061&r1=1628060&r2=1628061&view=diff ============================================================================== --- commons/proper/lang/trunk/src/test/java/org/apache/commons/lang3/time/FastDatePrinterTest.java (original) +++ commons/proper/lang/trunk/src/test/java/org/apache/commons/lang3/time/FastDatePrinterTest.java Sun Sep 28 10:11:17 2014 @@ -16,10 +16,7 @@ */ package org.apache.commons.lang3.time; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; +import static org.junit.Assert.*; import java.io.Serializable; import java.text.SimpleDateFormat; @@ -286,4 +283,17 @@ public class FastDatePrinterTest { final String actualValue = FastDateFormat.getInstance(pattern).format(cal); assertEquals(expectedValue, actualValue); } + + @Test + public void testTimeZoneAsZ() throws Exception { + Calendar c = Calendar.getInstance(TimeZone.getTimeZone("UTC")); + FastDateFormat noColonFormat = FastDateFormat.getInstance("Z"); + assertEquals("+0000", noColonFormat.format(c)); + + FastDateFormat isoFormat = FastDateFormat.getInstance("ZZ"); + assertEquals("Z", isoFormat.format(c)); + + FastDateFormat colonFormat = FastDateFormat.getInstance("ZZZ"); + assertEquals("+00:00", colonFormat.format(c)); + } }