Skip to content

Commit 7d77ecc

Browse files
authored
Reject trailing text after the date in Converter.DATE (#435)
* reject trailing text after the date in Converter.DATE * Only fall back to English locale when the date fails to parse Restrict the English retry to when the default-locale parse matches nothing; a partial match is a trailing-text failure and now throws with the parse position instead of retrying and reporting a misleading offset.
1 parent 4dec263 commit 7d77ecc

2 files changed

Lines changed: 35 additions & 5 deletions

File tree

src/main/java/org/apache/commons/cli/Converter.java

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ Licensed to the Apache Software Foundation (ASF) under one or more
2222
import java.nio.file.InvalidPathException;
2323
import java.nio.file.Path;
2424
import java.nio.file.Paths;
25+
import java.text.ParsePosition;
2526
import java.text.SimpleDateFormat;
2627
import java.util.Date;
2728
import java.util.Locale;
@@ -82,15 +83,26 @@ public interface Converter<T, E extends Exception> {
8283
final SimpleDateFormat format = new SimpleDateFormat(pattern);
8384
// reject out-of-range fields (for example "Feb 30") instead of silently rolling them over.
8485
format.setLenient(false);
85-
try {
86-
return format.parse(s);
87-
} catch (final java.text.ParseException e) {
86+
// SimpleDateFormat.parse(String) stops at the first character it cannot use and ignores any
87+
// trailing text, so "<valid date> garbage" would be accepted. Parse from an explicit position
88+
// and reject the value unless the whole string is consumed.
89+
final ParsePosition pos = new ParsePosition(0);
90+
Date date = format.parse(s, pos);
91+
if (date == null) {
8892
// Date.toString() always emits English month/day names, so fall back to Locale.ENGLISH
89-
// when the default locale rejects the documented format.
93+
// when the default locale rejects the documented format. Only retry when the default
94+
// locale matched nothing; a partial match is a trailing-text failure, handled below.
9095
final SimpleDateFormat englishFormat = new SimpleDateFormat(pattern, Locale.ENGLISH);
9196
englishFormat.setLenient(false);
92-
return englishFormat.parse(s);
97+
pos.setIndex(0);
98+
pos.setErrorIndex(-1);
99+
date = englishFormat.parse(s, pos);
93100
}
101+
if (date == null || pos.getIndex() != s.length()) {
102+
final int errorIndex = pos.getErrorIndex() >= 0 ? pos.getErrorIndex() : pos.getIndex();
103+
throw new java.text.ParseException(String.format("Unparseable date: \"%s\"", s), errorIndex);
104+
}
105+
return date;
94106
};
95107

96108
/**

src/test/java/org/apache/commons/cli/ConverterTests.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,24 @@ void testDate() throws Exception {
8686
assertThrows(java.text.ParseException.class, () -> Converter.DATE.apply("Jun 06 17:48:57 EDT 2002"));
8787
}
8888

89+
@Test
90+
void testDateRejectsTrailingText() throws Exception {
91+
final Date expected = new Date(1023400137000L);
92+
final String formatted = new SimpleDateFormat("EEE MMM dd HH:mm:ss zzz yyyy").format(expected);
93+
assertThrows(java.text.ParseException.class, () -> Converter.DATE.apply(formatted + " trailing"));
94+
}
95+
96+
@Test
97+
@DefaultLocale(language = "de", country = "DE")
98+
void testDateRejectsTrailingTextLocaleDe() throws Exception {
99+
// Trailing text must be rejected even when a non-English default locale parses the date, and
100+
// the reported error position should point past the parsed date rather than at index 0.
101+
final Date expected = new Date(1023400137000L);
102+
final String formatted = new SimpleDateFormat("EEE MMM dd HH:mm:ss zzz yyyy").format(expected);
103+
final java.text.ParseException e = assertThrows(java.text.ParseException.class, () -> Converter.DATE.apply(formatted + " trailing"));
104+
assertEquals(formatted.length(), e.getErrorOffset());
105+
}
106+
89107
@Test
90108
@DefaultLocale(language = "de", country = "DE")
91109
void testDateLocaleDe() throws Exception {

0 commit comments

Comments
 (0)