diff --git a/src/changes/changes.xml b/src/changes/changes.xml index 7f1fdf334..6ba92b626 100644 --- a/src/changes/changes.xml +++ b/src/changes/changes.xml @@ -22,7 +22,7 @@ - Fix parsing edge cases in FastDateParser + diff --git a/src/main/java/org/apache/commons/lang3/time/FastDateParser.java b/src/main/java/org/apache/commons/lang3/time/FastDateParser.java index 2d469a6b3..d704f69ca 100644 --- a/src/main/java/org/apache/commons/lang3/time/FastDateParser.java +++ b/src/main/java/org/apache/commons/lang3/time/FastDateParser.java @@ -744,13 +744,9 @@ public class FastDateParser implements DateParser, Serializable { /** * A strategy that handles a timezone field in the parsing pattern */ - static class TimeZoneStrategy extends Strategy { - private static final String RFC_822_TIME_ZONE = "[+-]\\d{4}"; - private static final String GMT_OPTION= "GMT[+-]\\d{1,2}:\\d{2}"; - // see http://www.iana.org/time-zones and http://cldr.unicode.org/translation/timezones - static final String TZ_DATABASE= "(?:\\p{L}[\\p{L}\\p{Mc}\\p{Nd}\\p{Zs}\\p{P}&&[^-]]*-?\\p{Zs}?)*"; - private static final String VALID_TZ = "((?iu)"+RFC_822_TIME_ZONE+"|"+GMT_OPTION+"|"+TZ_DATABASE+")"; - + private static class TimeZoneStrategy extends Strategy { + + private final String validTimeZoneChars; private final SortedMap tzNames= new TreeMap(String.CASE_INSENSITIVE_ORDER); /** @@ -781,6 +777,9 @@ public class FastDateParser implements DateParser, Serializable { TimeZoneStrategy(final Locale locale) { final String[][] zones = DateFormatSymbols.getInstance(locale).getZoneStrings(); for (final String[] zone : zones) { + if (zone[ID].startsWith("GMT")) { + continue; + } final TimeZone tz = TimeZone.getTimeZone(zone[ID]); if (!tzNames.containsKey(zone[LONG_STD])){ tzNames.put(zone[LONG_STD], tz); @@ -796,7 +795,16 @@ public class FastDateParser implements DateParser, Serializable { tzNames.put(zone[SHORT_DST], tz); } } - } + } + + final StringBuilder sb= new StringBuilder(); + sb.append("(GMT[+-]\\d{1,2}:\\d{2}").append('|'); + sb.append("[+-]\\d{4}").append('|'); + for(final String id : tzNames.keySet()) { + escapeRegex(sb, id, false).append('|'); + } + sb.setCharAt(sb.length()-1, ')'); + validTimeZoneChars= sb.toString(); } /** @@ -804,7 +812,7 @@ public class FastDateParser implements DateParser, Serializable { */ @Override boolean addRegex(final FastDateParser parser, final StringBuilder regex) { - regex.append(VALID_TZ); + regex.append(validTimeZoneChars); return true; } @@ -817,8 +825,8 @@ public class FastDateParser implements DateParser, Serializable { if(value.charAt(0)=='+' || value.charAt(0)=='-') { tz= TimeZone.getTimeZone("GMT"+value); } - else if(value.regionMatches(true, 0, "GMT", 0, 3)) { - tz= TimeZone.getTimeZone(value.toUpperCase()); + else if(value.startsWith("GMT")) { + tz= TimeZone.getTimeZone(value); } else { tz= tzNames.get(value); diff --git a/src/test/java/org/apache/commons/lang3/reflect/FieldUtilsTest.java b/src/test/java/org/apache/commons/lang3/reflect/FieldUtilsTest.java index 284579dec..36c232c94 100644 --- a/src/test/java/org/apache/commons/lang3/reflect/FieldUtilsTest.java +++ b/src/test/java/org/apache/commons/lang3/reflect/FieldUtilsTest.java @@ -24,8 +24,8 @@ import org.junit.Test; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Modifier; +import java.util.ArrayList; import java.util.Arrays; -import java.util.Collections; import java.util.List; import static org.junit.Assert.*; @@ -146,36 +146,26 @@ public class FieldUtilsTest { FieldUtils.getField(PublicChild.class, " ", true); } - private Field[] allPublicChildFields() { - Class parentClass = PublicChild.class.getSuperclass(); - final Field[] fieldsParent = parentClass.getDeclaredFields(); - assertArrayEquals(fieldsParent, FieldUtils.getAllFields(parentClass)); - - final Field[] fieldsPublicChild = PublicChild.class.getDeclaredFields(); - return ArrayUtils.addAll(fieldsPublicChild, fieldsParent); - } - - private Field[] allIntegerFields() { - final Field[] fieldsNumber = Number.class.getDeclaredFields(); - assertArrayEquals(Number.class.getDeclaredFields(), FieldUtils.getAllFields(Number.class)); - final Field[] fieldsInteger = Integer.class.getDeclaredFields(); - return ArrayUtils.addAll(fieldsInteger, fieldsNumber); - } - @Test public void testGetAllFields() { assertArrayEquals(new Field[0], FieldUtils.getAllFields(Object.class)); - assertArrayEquals(allIntegerFields(), FieldUtils.getAllFields(Integer.class)); - - assertArrayEquals(allPublicChildFields(), FieldUtils.getAllFields(PublicChild.class)); + final Field[] fieldsNumber = Number.class.getDeclaredFields(); + assertArrayEquals(fieldsNumber, FieldUtils.getAllFields(Number.class)); + final Field[] fieldsInteger = Integer.class.getDeclaredFields(); + assertArrayEquals(ArrayUtils.addAll(fieldsInteger, fieldsNumber), FieldUtils.getAllFields(Integer.class)); + assertEquals(5, FieldUtils.getAllFields(PublicChild.class).length); } @Test public void testGetAllFieldsList() { - assertEquals(Collections.emptyList(), FieldUtils.getAllFieldsList(Object.class)); - assertEquals(Arrays.asList(allIntegerFields()), FieldUtils.getAllFieldsList(Integer.class)); - - assertEquals(Arrays.asList(allPublicChildFields()), FieldUtils.getAllFieldsList(PublicChild.class)); + assertEquals(0, FieldUtils.getAllFieldsList(Object.class).size()); + final List fieldsNumber = Arrays.asList(Number.class.getDeclaredFields()); + assertEquals(fieldsNumber, FieldUtils.getAllFieldsList(Number.class)); + final List fieldsInteger = Arrays.asList(Integer.class.getDeclaredFields()); + final List allFieldsInteger = new ArrayList(fieldsInteger); + allFieldsInteger.addAll(fieldsNumber); + assertEquals(allFieldsInteger, FieldUtils.getAllFieldsList(Integer.class)); + assertEquals(5, FieldUtils.getAllFieldsList(PublicChild.class).size()); } @Test diff --git a/src/test/java/org/apache/commons/lang3/time/FastDateParserSDFTest.java b/src/test/java/org/apache/commons/lang3/time/FastDateParserSDFTest.java index 75ca3428c..bdf5e27f8 100644 --- a/src/test/java/org/apache/commons/lang3/time/FastDateParserSDFTest.java +++ b/src/test/java/org/apache/commons/lang3/time/FastDateParserSDFTest.java @@ -27,6 +27,7 @@ import java.util.Date; import java.util.Locale; import java.util.TimeZone; +import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; @@ -137,11 +138,13 @@ public class FastDateParserSDFTest { } @Test + @Ignore // not currently supported public void testLowerCase() throws Exception { checkParse(input.toLowerCase(locale)); } @Test + @Ignore // not currently supported public void testLowerCasePP() throws Exception { checkParsePosition(input.toLowerCase(locale)); } diff --git a/src/test/java/org/apache/commons/lang3/time/FastDateParserTest.java b/src/test/java/org/apache/commons/lang3/time/FastDateParserTest.java index c2a387f48..b75d0861e 100644 --- a/src/test/java/org/apache/commons/lang3/time/FastDateParserTest.java +++ b/src/test/java/org/apache/commons/lang3/time/FastDateParserTest.java @@ -21,7 +21,6 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import java.io.Serializable; -import java.text.DateFormatSymbols; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Calendar; @@ -31,7 +30,6 @@ import java.util.HashMap; import java.util.Locale; import java.util.Map; import java.util.TimeZone; -import java.util.regex.Pattern; import org.apache.commons.lang3.SerializationUtils; import org.junit.Assert; @@ -262,21 +260,21 @@ public class FastDateParserTest { @Test public void testTzParses() throws Exception { // Check that all Locales can parse the time formats we use - for(final Locale locale : Locale.getAvailableLocales()) { - final FastDateParser fdp= new FastDateParser("yyyy/MM/dd z", TimeZone.getDefault(), locale); + for(final Locale locale : Locale.getAvailableLocales()) { + final FastDateParser fdp= new FastDateParser("yyyy/MM/dd z", TimeZone.getDefault(), locale); - for(final TimeZone tz : new TimeZone[]{NEW_YORK, REYKJAVIK, GMT}) { - final Calendar cal= Calendar.getInstance(tz, locale); - cal.clear(); - cal.set(Calendar.YEAR, 2000); - cal.set(Calendar.MONTH, 1); - cal.set(Calendar.DAY_OF_MONTH, 10); - final Date expected= cal.getTime(); + for(final TimeZone tz : new TimeZone[]{NEW_YORK, REYKJAVIK, GMT}) { + final Calendar cal= Calendar.getInstance(tz, locale); + cal.clear(); + cal.set(Calendar.YEAR, 2000); + cal.set(Calendar.MONTH, 1); + cal.set(Calendar.DAY_OF_MONTH, 10); + final Date expected= cal.getTime(); - final Date actual = fdp.parse("2000/02/10 "+tz.getDisplayName(locale)); - Assert.assertEquals("tz:"+tz.getID()+" locale:"+locale.getDisplayName(), expected, actual); - } - } + final Date actual = fdp.parse("2000/02/10 "+tz.getDisplayName(locale)); + Assert.assertEquals("tz:"+tz.getID()+" locale:"+locale.getDisplayName(), expected, actual); + } + } } @@ -642,19 +640,4 @@ public class FastDateParserTest { } } - @Test - public void testTimeZoneStrategyPattern() { - Pattern tz = Pattern.compile(FastDateParser.TimeZoneStrategy.TZ_DATABASE); - Assert.assertFalse(tz.matcher("GMT-1234").matches()); - - for (Locale locale : Locale.getAvailableLocales()) { - final String[][] zones = DateFormatSymbols.getInstance(locale).getZoneStrings(); - for (final String[] zone : zones) { - for (String zoneExpr : zone) { - Assert.assertTrue(locale.getDisplayName() + ":" + zoneExpr, tz.matcher(zoneExpr).matches()); - } - } - } - } - }