From 86c76dd5ea04d1881a52d79113d6dbb1d57c38a6 Mon Sep 17 00:00:00 2001 From: Chas Honton Date: Wed, 8 Apr 2015 02:19:02 +0000 Subject: [PATCH] LANG-1107 Fix parsing edge cases in FastDateParser git-svn-id: https://svn.apache.org/repos/asf/commons/proper/lang/trunk@1671992 13f79535-47bb-0310-9956-ffa450edef68 --- src/changes/changes.xml | 2 +- .../commons/lang3/time/FastDateParser.java | 30 +++++-------- .../lang3/time/FastDateParserSDFTest.java | 3 -- .../lang3/time/FastDateParserTest.java | 43 +++++++++++++------ 4 files changed, 42 insertions(+), 36 deletions(-) diff --git a/src/changes/changes.xml b/src/changes/changes.xml index 6ba92b626..7f1fdf334 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 d704f69ca..2d469a6b3 100644 --- a/src/main/java/org/apache/commons/lang3/time/FastDateParser.java +++ b/src/main/java/org/apache/commons/lang3/time/FastDateParser.java @@ -744,9 +744,13 @@ void setCalendar(final FastDateParser parser, final Calendar cal, final String v /** * A strategy that handles a timezone field in the parsing pattern */ - private static class TimeZoneStrategy extends Strategy { - - private final String validTimeZoneChars; + 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 final SortedMap tzNames= new TreeMap(String.CASE_INSENSITIVE_ORDER); /** @@ -777,9 +781,6 @@ private static class TimeZoneStrategy extends Strategy { 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); @@ -795,16 +796,7 @@ private static class TimeZoneStrategy extends Strategy { 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(); + } } /** @@ -812,7 +804,7 @@ private static class TimeZoneStrategy extends Strategy { */ @Override boolean addRegex(final FastDateParser parser, final StringBuilder regex) { - regex.append(validTimeZoneChars); + regex.append(VALID_TZ); return true; } @@ -825,8 +817,8 @@ void setCalendar(final FastDateParser parser, final Calendar cal, final String v if(value.charAt(0)=='+' || value.charAt(0)=='-') { tz= TimeZone.getTimeZone("GMT"+value); } - else if(value.startsWith("GMT")) { - tz= TimeZone.getTimeZone(value); + else if(value.regionMatches(true, 0, "GMT", 0, 3)) { + tz= TimeZone.getTimeZone(value.toUpperCase()); } else { tz= tzNames.get(value); 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 bdf5e27f8..75ca3428c 100644 --- a/src/test/java/org/apache/commons/lang3/time/FastDateParserSDFTest.java +++ b/src/test/java/org/apache/commons/lang3/time/FastDateParserSDFTest.java @@ -27,7 +27,6 @@ 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; @@ -138,13 +137,11 @@ public void testUpperCasePP() throws Exception { } @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 b75d0861e..c2a387f48 100644 --- a/src/test/java/org/apache/commons/lang3/time/FastDateParserTest.java +++ b/src/test/java/org/apache/commons/lang3/time/FastDateParserTest.java @@ -21,6 +21,7 @@ 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; @@ -30,6 +31,7 @@ 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; @@ -260,21 +262,21 @@ public void testParses() throws Exception { @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); + } + } } @@ -640,4 +642,19 @@ public void test1806() throws ParseException { } } + @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()); + } + } + } + } + }