Arqan Arqan - 1 month ago 16
Java Question

Java parsing String to LocalDateTime without providing time

Basically I have a field

private LocalDateTime date;


which is parsed from String. It works fine when provided String has time appended at the end but will throw an exception if it has not.

DateTimeFormatter formatter = null;
if (value.matches("\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}")) {
formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm");
} else if (value.matches("\\d{4}-\\d{2}-\\d{2}")) {
formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
}
// Throws an exception for pattern "yyyy-MM-dd"
date = LocalDateTime.parse(value, formatter);


And the exception itself:

java.time.format.DateTimeParseException: Text '2000-06-29' could not be parsed: Unable to obtain LocalDateTime from TemporalAccessor: {},ISO resolved to 2000-06-29 of type java.time.format.Parsed
at pl.empirica.swissborg.service.util.CsvBeanUtils.copyList(CsvBeanUtils.java:75) ~[classes/:na]
at pl.empirica.swissborg.service.csv.CsvReaderService.persistCsvData(CsvReaderService.java:101) ~[classes/:na]
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_91]
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source) ~[na:1.8.0_91]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source) ~[na:1.8.0_91]
at java.lang.reflect.Method.invoke(Unknown Source) ~[na:1.8.0_91]
at org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor$LifecycleElement.invoke(InitDestroyAnnotationBeanPostProcessor.java:354) ~[spring-beans-4.2.7.RELEASE.jar:4.2.7.RELEASE]
at org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor$LifecycleMetadata.invokeInitMethods(InitDestroyAnnotationBeanPostProcessor.java:305) ~[spring-beans-4.2.7.RELEASE.jar:4.2.7.RELEASE]
at org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor.postProcessBeforeInitialization(InitDestroyAnnotationBeanPostProcessor.java:133) ~[spring-beans-4.2.7.RELEASE.jar:4.2.7.RELEASE]
... 18 common frames omitted
Caused by: java.lang.RuntimeException: java.time.format.DateTimeParseException: Text '2000-06-29' could not be parsed: Unable to obtain LocalDateTime from TemporalAccessor: {},ISO resolved to 2000-06-29 of type java.time.format.Parsed
at pl.empirica.swissborg.service.util.CsvBeanUtils.copyFields(CsvBeanUtils.java:58) ~[classes/:na]
at pl.empirica.swissborg.service.util.CsvBeanUtils.copyList(CsvBeanUtils.java:70) ~[classes/:na]
... 26 common frames omitted
Caused by: java.time.format.DateTimeParseException: Text '2000-06-29' could not be parsed: Unable to obtain LocalDateTime from TemporalAccessor: {},ISO resolved to 2000-06-29 of type java.time.format.Parsed
at java.time.format.DateTimeFormatter.createError(Unknown Source) ~[na:1.8.0_91]
at java.time.format.DateTimeFormatter.parse(Unknown Source) ~[na:1.8.0_91]
at java.time.LocalDateTime.parse(Unknown Source) ~[na:1.8.0_91]
at pl.empirica.swissborg.service.util.CsvBeanUtils.copyFields(CsvBeanUtils.java:47) ~[classes/:na]
... 27 common frames omitted
Caused by: java.time.DateTimeException: Unable to obtain LocalDateTime from TemporalAccessor: {},ISO resolved to 2000-06-29 of type java.time.format.Parsed
at java.time.LocalDateTime.from(Unknown Source) ~[na:1.8.0_91]
at java.time.format.Parsed.query(Unknown Source) ~[na:1.8.0_91]
... 30 common frames omitted
Caused by: java.time.DateTimeException: Unable to obtain LocalTime from TemporalAccessor: {},ISO resolved to 2000-06-29 of type java.time.format.Parsed
at java.time.LocalTime.from(Unknown Source) ~[na:1.8.0_91]
... 32 common frames omitted


EDIT: for clarification, I'm 100% certain that formatter is set by the 2nd conditional branch.

Answer

Your issue is that a LocalDateTime needs a time!

You have two main options:

  • use two formatters like you do, but the second branch should be LocalDateTime d = LocalDate.parse(value, formatter).atStartOfDay() (for example)
  • create a formatter that can handle both formats

The second option could look like:

DateTimeFormatter fmt = new DateTimeFormatterBuilder()
        .appendPattern("yyyy-MM-dd")
        .optionalStart()
        .appendPattern(" HH:mm")
        .optionalEnd()
        .parseDefaulting(ChronoField.HOUR_OF_DAY, 0)
        .parseDefaulting(ChronoField.MINUTE_OF_HOUR, 0)
        .toFormatter();

which can parse 2016-10-01 and 2016-10-01 10:15.

Comments