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
This commit is contained in:
Chas Honton 2015-04-08 02:19:02 +00:00
parent c9d3c58e44
commit 86c76dd5ea
4 changed files with 42 additions and 36 deletions

View File

@ -22,7 +22,7 @@
<body> <body>
<release version="3.5" date="tba" description="tba"> <release version="3.5" date="tba" description="tba">
<action issue="LANG-1107" type="update" dev="chas">Fix parsing edge cases in FastDateParser</action>
</release> </release>
<release version="3.4" date="2014-04-06" description="Feature and bugfix release"> <release version="3.4" date="2014-04-06" description="Feature and bugfix release">

View File

@ -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 * A strategy that handles a timezone field in the parsing pattern
*/ */
private static class TimeZoneStrategy extends Strategy { 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 String validTimeZoneChars;
private final SortedMap<String, TimeZone> tzNames= new TreeMap<String, TimeZone>(String.CASE_INSENSITIVE_ORDER); private final SortedMap<String, TimeZone> tzNames= new TreeMap<String, TimeZone>(String.CASE_INSENSITIVE_ORDER);
/** /**
@ -777,9 +781,6 @@ private static class TimeZoneStrategy extends Strategy {
TimeZoneStrategy(final Locale locale) { TimeZoneStrategy(final Locale locale) {
final String[][] zones = DateFormatSymbols.getInstance(locale).getZoneStrings(); final String[][] zones = DateFormatSymbols.getInstance(locale).getZoneStrings();
for (final String[] zone : zones) { for (final String[] zone : zones) {
if (zone[ID].startsWith("GMT")) {
continue;
}
final TimeZone tz = TimeZone.getTimeZone(zone[ID]); final TimeZone tz = TimeZone.getTimeZone(zone[ID]);
if (!tzNames.containsKey(zone[LONG_STD])){ if (!tzNames.containsKey(zone[LONG_STD])){
tzNames.put(zone[LONG_STD], tz); tzNames.put(zone[LONG_STD], tz);
@ -796,15 +797,6 @@ private static class TimeZoneStrategy extends Strategy {
} }
} }
} }
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 @Override
boolean addRegex(final FastDateParser parser, final StringBuilder regex) { boolean addRegex(final FastDateParser parser, final StringBuilder regex) {
regex.append(validTimeZoneChars); regex.append(VALID_TZ);
return true; return true;
} }
@ -825,8 +817,8 @@ void setCalendar(final FastDateParser parser, final Calendar cal, final String v
if(value.charAt(0)=='+' || value.charAt(0)=='-') { if(value.charAt(0)=='+' || value.charAt(0)=='-') {
tz= TimeZone.getTimeZone("GMT"+value); tz= TimeZone.getTimeZone("GMT"+value);
} }
else if(value.startsWith("GMT")) { else if(value.regionMatches(true, 0, "GMT", 0, 3)) {
tz= TimeZone.getTimeZone(value); tz= TimeZone.getTimeZone(value.toUpperCase());
} }
else { else {
tz= tzNames.get(value); tz= tzNames.get(value);

View File

@ -27,7 +27,6 @@
import java.util.Locale; import java.util.Locale;
import java.util.TimeZone; import java.util.TimeZone;
import org.junit.Ignore;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.junit.runners.Parameterized; import org.junit.runners.Parameterized;
@ -138,13 +137,11 @@ public void testUpperCasePP() throws Exception {
} }
@Test @Test
@Ignore // not currently supported
public void testLowerCase() throws Exception { public void testLowerCase() throws Exception {
checkParse(input.toLowerCase(locale)); checkParse(input.toLowerCase(locale));
} }
@Test @Test
@Ignore // not currently supported
public void testLowerCasePP() throws Exception { public void testLowerCasePP() throws Exception {
checkParsePosition(input.toLowerCase(locale)); checkParsePosition(input.toLowerCase(locale));
} }

View File

@ -21,6 +21,7 @@
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
import java.io.Serializable; import java.io.Serializable;
import java.text.DateFormatSymbols;
import java.text.ParseException; import java.text.ParseException;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.util.Calendar; import java.util.Calendar;
@ -30,6 +31,7 @@
import java.util.Locale; import java.util.Locale;
import java.util.Map; import java.util.Map;
import java.util.TimeZone; import java.util.TimeZone;
import java.util.regex.Pattern;
import org.apache.commons.lang3.SerializationUtils; import org.apache.commons.lang3.SerializationUtils;
import org.junit.Assert; import org.junit.Assert;
@ -260,21 +262,21 @@ public void testParses() throws Exception {
@Test @Test
public void testTzParses() throws Exception { public void testTzParses() throws Exception {
// Check that all Locales can parse the time formats we use // Check that all Locales can parse the time formats we use
for(final Locale locale : Locale.getAvailableLocales()) { for(final Locale locale : Locale.getAvailableLocales()) {
final FastDateParser fdp= new FastDateParser("yyyy/MM/dd z", TimeZone.getDefault(), locale); final FastDateParser fdp= new FastDateParser("yyyy/MM/dd z", TimeZone.getDefault(), locale);
for(final TimeZone tz : new TimeZone[]{NEW_YORK, REYKJAVIK, GMT}) { for(final TimeZone tz : new TimeZone[]{NEW_YORK, REYKJAVIK, GMT}) {
final Calendar cal= Calendar.getInstance(tz, locale); final Calendar cal= Calendar.getInstance(tz, locale);
cal.clear(); cal.clear();
cal.set(Calendar.YEAR, 2000); cal.set(Calendar.YEAR, 2000);
cal.set(Calendar.MONTH, 1); cal.set(Calendar.MONTH, 1);
cal.set(Calendar.DAY_OF_MONTH, 10); cal.set(Calendar.DAY_OF_MONTH, 10);
final Date expected= cal.getTime(); final Date expected= cal.getTime();
final Date actual = fdp.parse("2000/02/10 "+tz.getDisplayName(locale)); final Date actual = fdp.parse("2000/02/10 "+tz.getDisplayName(locale));
Assert.assertEquals("tz:"+tz.getID()+" locale:"+locale.getDisplayName(), expected, actual); 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());
}
}
}
}
} }