13

I have a String representing a date (with or without time) like 13/12/2017 or 13/12/2017 15:39:51

So i'm trying to use java 8 DateTimeFormatter with optional part.

That code works

LocalDateTime localDateTime = LocalDateTime.parse("13/12/2017 15:39:51",DateTimeFormatter.ofPattern("dd/MM/yyyy[ HH:mm:ss]"));
System.out.println(localDateTime.format(DateTimeFormatter.ofPattern("dd/MM/yyyy")));
System.out.println(localDateTime.format(DateTimeFormatter.ofPattern("HH:mm:ss")));

13/12/2017
15:39:51

But I don't understand why that one does not

LocalDateTime localDateTime = LocalDateTime.parse("13/12/2017",DateTimeFormatter.ofPattern("dd/MM/yyyy[ HH:mm:ss]"));

giving me

Exception in thread "main" java.time.format.DateTimeParseException: Text '13/12/2017' could not be parsed: Unable to obtain LocalDateTime from TemporalAccessor: {},ISO resolved to 2017-12-13 of type java.time.format.Parsed
at java.time.format.DateTimeFormatter.createError(DateTimeFormatter.java:1920)
at java.time.format.DateTimeFormatter.parse(DateTimeFormatter.java:1855)
at java.time.LocalDateTime.parse(LocalDateTime.java:492)
...

And even with

LocalDateTime localDateTime = LocalDateTime.parse("13/12/2017",DateTimeFormatter.ofPattern("dd/MM/yyyy"));

it does not work with the same exception.

Olivier Grégoire
  • 33,839
  • 23
  • 96
  • 137
tweetysat
  • 2,187
  • 14
  • 38
  • 75

1 Answers1

28

Use parseBest

When you use an optional component, you should parse using parseBest. Your application may be working using only parse, but then it's only by luck (because you're only parsing full inputs, not partial ones). With parseBest, you can properly handle various TemporalAccessor, which is the whole reason to use optional.

The decision of which TemporalAccessor is returned is rather simple: parseBest will try to match each TemporalQuery in order of argument. When any matches, the method returns that one. So make sure to go from most precise to less precise. Also, if none were matched, an exception will be thrown.

LocalDateTime dateTime;
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd/MM/yyyy[ HH:mm:ss]");
TemporalAccessor temporalAccessor = formatter.parseBest("13/12/2017", LocalDateTime::from, LocalDate::from);
if (temporalAccessor instanceof LocalDateTime) {
  dateTime = (LocalDateTime)temporalAccessor;
} else {
  dateTime = ((LocalDate)temporalAccessor).atStartOfDay();
}
Olivier Grégoire
  • 33,839
  • 23
  • 96
  • 137
  • I use that solution for a while now but I noticed a little problem. If the input date seconds are 00 (`17/01/2018 08:30:00`) it gives me `2018-01-17T08:30` and not `2018-01-17T08:30:00`. Is there a solution ? – tweetysat May 13 '19 at 08:27
  • @tweetysat what you say here above is a formatting issue, not a parsing one. To fix it, check [this answer](https://stackoverflow.com/a/37025877/180719). – Olivier Grégoire May 13 '19 at 09:00
  • Thanks @Olivier Grégoire. I fixed it. – tweetysat May 13 '19 at 09:21