Added missing support for ISO 8601 parsing and printing using "ZZ" pattern (code now matches Javadoc). Addresses LANG-1000.

git-svn-id: https://svn.apache.org/repos/asf/commons/proper/lang/trunk@1628061 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Duncan Jones 2014-09-28 10:11:17 +00:00
parent 2aae22de23
commit 22b6781a0b
5 changed files with 77 additions and 8 deletions

View File

@ -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>

View File

@ -513,6 +513,10 @@ private Strategy getStrategy(final String formatField, final Calendar definingCa
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 @@ else if(value.startsWith("GMT")) {
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 @@ int modify(final int iValue) {
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();
}

View File

@ -274,6 +274,8 @@ protected List<Rule> parsePattern() {
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 void appendTo(final StringBuffer buffer, final Calendar calendar) {
* 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 int estimateLength() {
*/
@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) {

View File

@ -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 void testFormatUTC() {
}
@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 void testDateTimeISO(){
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 void testLang312() {
}
*/
@Test
public void testLANG1000() throws Exception {
String date = "2013-11-18T12:48:05Z";
DateFormatUtils.ISO_DATETIME_TIME_ZONE_FORMAT.parse(date);
}
}

View File

@ -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 void testCalendarTimezoneRespected() {
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));
}
}