Description
The Format::parseObject (which ClassicFormat implements) explicitly says that it returns `null` in case of error:
* @return An <code>Object</code> parsed from the string. In case of error, returns null.
And it may throw NullPointerException if position is `null`:
* @throws NullPointerException if {@code source} or {@code pos} is null.
However, ClassicFormat::parseObject does not conform to this specification and may leak DateTimeException, reproducer below:
final DateTimeFormatter formatter = new DateTimeFormatterBuilder()
.parseCaseInsensitive()
.append(DateTimeFormatter.ISO_LOCAL_DATE)
.appendLiteral('T')
.optionalStart()
.appendValue(HOUR_OF_DAY, 2, 2, SignStyle.NOT_NEGATIVE)
.optionalStart()
.appendLiteral(':')
.appendValue(MINUTE_OF_HOUR, 2, 2, SignStyle.NOT_NEGATIVE)
.optionalStart()
.appendLiteral(':')
.appendValue(SECOND_OF_MINUTE, 2, 2, SignStyle.NOT_NEGATIVE)
.optionalEnd()
.optionalEnd()
.optionalStart()
.appendZoneOrOffsetId()
.optionalEnd()
.optionalStart()
.appendOffset("+HHmm", "Z")
.optionalEnd()
.optionalEnd()
.toFormatter(Locale.ROOT)
.withResolverStyle(ResolverStyle.STRICT);
var object = formatter.toFormat().parseObject("2018-03-30T17-30-28.842Z", new ParsePosition(0));
assert object == null;
The snippet fails with:
java.time.DateTimeException: Value out of range: Hour[0-23], Minute[0-59], Second[0-59]
at java.base/java.time.format.DateTimeFormatterBuilder$OffsetIdPrinterParser.parse(DateTimeFormatterBuilder.java:3708)
at java.base/java.time.format.DateTimeFormatterBuilder$CompositePrinterParser.parse(DateTimeFormatterBuilder.java:2360)
at java.base/java.time.format.DateTimeFormatterBuilder$CompositePrinterParser.parse(DateTimeFormatterBuilder.java:2360)
at java.base/java.time.format.DateTimeFormatterBuilder$CompositePrinterParser.parse(DateTimeFormatterBuilder.java:2370)
at java.base/java.time.format.DateTimeFormatter.parseUnresolved0(DateTimeFormatter.java:2107)
at java.base/java.time.format.DateTimeFormatter$ClassicFormat.parseObject(DateTimeFormatter.java:2236)
* @return An <code>Object</code> parsed from the string. In case of error, returns null.
And it may throw NullPointerException if position is `null`:
* @throws NullPointerException if {@code source} or {@code pos} is null.
However, ClassicFormat::parseObject does not conform to this specification and may leak DateTimeException, reproducer below:
final DateTimeFormatter formatter = new DateTimeFormatterBuilder()
.parseCaseInsensitive()
.append(DateTimeFormatter.ISO_LOCAL_DATE)
.appendLiteral('T')
.optionalStart()
.appendValue(HOUR_OF_DAY, 2, 2, SignStyle.NOT_NEGATIVE)
.optionalStart()
.appendLiteral(':')
.appendValue(MINUTE_OF_HOUR, 2, 2, SignStyle.NOT_NEGATIVE)
.optionalStart()
.appendLiteral(':')
.appendValue(SECOND_OF_MINUTE, 2, 2, SignStyle.NOT_NEGATIVE)
.optionalEnd()
.optionalEnd()
.optionalStart()
.appendZoneOrOffsetId()
.optionalEnd()
.optionalStart()
.appendOffset("+HHmm", "Z")
.optionalEnd()
.optionalEnd()
.toFormatter(Locale.ROOT)
.withResolverStyle(ResolverStyle.STRICT);
var object = formatter.toFormat().parseObject("2018-03-30T17-30-28.842Z", new ParsePosition(0));
assert object == null;
The snippet fails with:
java.time.DateTimeException: Value out of range: Hour[0-23], Minute[0-59], Second[0-59]
at java.base/java.time.format.DateTimeFormatterBuilder$OffsetIdPrinterParser.parse(DateTimeFormatterBuilder.java:3708)
at java.base/java.time.format.DateTimeFormatterBuilder$CompositePrinterParser.parse(DateTimeFormatterBuilder.java:2360)
at java.base/java.time.format.DateTimeFormatterBuilder$CompositePrinterParser.parse(DateTimeFormatterBuilder.java:2360)
at java.base/java.time.format.DateTimeFormatterBuilder$CompositePrinterParser.parse(DateTimeFormatterBuilder.java:2370)
at java.base/java.time.format.DateTimeFormatter.parseUnresolved0(DateTimeFormatter.java:2107)
at java.base/java.time.format.DateTimeFormatter$ClassicFormat.parseObject(DateTimeFormatter.java:2236)