This looks like a very simple thing to achieve, but I am failing to do so.
I have a string pattern which is yyyyMMddHH
and I am trying to parse 2021061104
into an instance of LocalDateTime
Here is the code:
import java.time.Duration; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatterBuilder; import java.time.format.DateTimeParseException; import java.time.temporal.ChronoField; import java.time.temporal.ChronoUnit; class Main { public static void main(String[] args) { String pattern = "yyyyMMddHH"; String date = "2021061104"; DateTimeFormatter formatter = new DateTimeFormatterBuilder() .parseDefaulting(ChronoField.HOUR_OF_DAY, 0) .parseDefaulting(ChronoField.MINUTE_OF_HOUR, 0) .parseDefaulting(ChronoField.SECOND_OF_MINUTE, 0) .parseLenient() .appendPattern(pattern) .toFormatter(); LocalDateTime ldt = LocalDateTime.parse(date, formatter); } }
It throws this exception:
Exception in thread "main" java.time.format.DateTimeParseException: Text '2021061104' could not be parsed at index 8 at java.time.format.DateTimeFormatter.parseResolved0(DateTimeFormatter.java:1949) at java.time.format.DateTimeFormatter.parse(DateTimeFormatter.java:1851) at java.time.LocalDateTime.parse(LocalDateTime.java:492) at Main.main(Main.java:22)
It is failing to parse the HH
field from the input.
I have looked at the javadoc here But that does not help.
Why do I have this exception? How to solve this?
EDIT:
I can not remove .parseDefaulting(ChronoField.HOUR_OF_DAY, 0)
The following are the constraints:
- The user will give the
pattern
anddate
( might be asargs[0]
andargs[1]
) - The pattern must always have date ( Year Month and Date )
- The time in the
pattern
is optional and it would be up to hour only. - Examples of a couple of valid patterns are:
yyyy-MM-dd HH
,yyyy MM dd
With these constraints, I can not remove .parseDefaulting(ChronoField.HOUR_OF_DAY, 0)
because if I do so, I would not be able to parse yyyy-MM-dd
into an instance of LocalDateTime
here
Advertisement
Answer
Well, I think this is caused by the line with .parseDefaulting(ChronoField.HOUR_OF_DAY, 0)
. The builder immediately inserts a default value, before any parsing has taken place. At the time of parsing, the hour component already has a value, so the parsing of HH
fails.
This behavior is actually mentioned in the JavaDocs:
During parsing, the current state of the parse is inspected. If the specified field has no associated value, because it has not been parsed successfully at that point, then the specified value is injected into the parse result. Injection is immediate, thus the field-value pair will be visible to any subsequent elements in the formatter. As such, this method is normally called at the end of the builder.
Emphasis mine.
So a possible fix would be to move the parseDefaulting
lines to the end of the formatter builder:
DateTimeFormatter formatter = new DateTimeFormatterBuilder() .parseLenient() .appendPattern(pattern) .parseDefaulting(ChronoField.HOUR_OF_DAY, 0) .parseDefaulting(ChronoField.MINUTE_OF_HOUR, 0) .parseDefaulting(ChronoField.SECOND_OF_MINUTE, 0) .toFormatter();
Here is a working example with those lines moved to the end of the builder.