Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Parse date or datetime both as LocalDateTime in Java 8

I need to parse a field which is sometimes given as a date and sometimes as a date/time. Is it possible to use single datatype for this using Java 8 time API? Currently, I attempted to use a LocalDateTime for it, but for following invocation LocalDateTime.parse("1986-04-08", DateTimeFormatter.ofPattern("yyyy-MM-dd")) I get a

java.time.DateTimeException: Unable to obtain LocalDateTime from TemporalAccessor: {},ISO resolved to 1986-04-08 of type java.time.format.Parsed

This is part of some generic parser accepting a date/datetime parse pattern as configuration option. So e.g. following solution with hardcoded parsing pattern

if ("yyyy-MM-dd".equals(pattern)) {
    LocalDate.parse(value, DateTimeFormatter.ofPattern("yyyy-MM-dd"))).atStartOfDay()
}

is not an option for me.

Any other suggestions how to code it in a clean way are welcome.

like image 851
Kamil Roman Avatar asked Mar 16 '18 14:03

Kamil Roman


2 Answers

Just create custom formatter with the builder DateTimeFormatterBuilder

DateTimeFormatter formatter = new DateTimeFormatterBuilder()
        .appendPattern("yyyy-MM-dd[ HH:mm:ss]")
        .parseDefaulting(ChronoField.HOUR_OF_DAY, 0)
        .parseDefaulting(ChronoField.MINUTE_OF_HOUR, 0)
        .parseDefaulting(ChronoField.SECOND_OF_MINUTE, 0)
        .toFormatter();

This formatter uses the [] brackets to allow optional parts in the format, and adds the default values for hour HOUR_OF_DAY, minute MINUTE_OF_HOUR and second SECOND_OF_MINUTE.

note: you can ommit, minutes and seconds, just providing the hour is enough.

And use it as usual.

LocalDateTime localDateTime1 = LocalDateTime.parse("1994-05-13", formatter);
LocalDateTime localDateTime2 = LocalDateTime.parse("1994-05-13 23:00:00", formatter);

This outputs the correct date time with default hours of 0 (starting of the day).

System.out.println(localDateTime1); // 1994-05-13T00:00
System.out.println(localDateTime2); // 1994-05-13T23:00
like image 96
Jose Da Silva Avatar answered Oct 22 '22 07:10

Jose Da Silva


Jose's answer using parseDefaulting is nice. There's also another alternative, if you don't want to use a DateTimeFormatterBuilder.

First you create your formatter with an optional section - in this case, the time-of-day part, delimited by []:

DateTimeFormatter fmt = DateTimeFormatter.ofPattern("yyyy-MM-dd[ HH:mm:ss]");

Then you call parseBest, providing the String to be parsed and a list of method references:

TemporalAccessor parsed = fmt.parseBest("1986-04-08", LocalDateTime::from, LocalDate::from);

In this case, it'll first try to create a LocalDateTime, and if it's not possible, it'll try to create a LocalDate (if none is possible, it'll throw an exception).

Then, you can check which type is returned, and act accordingly:

LocalDateTime dt;
if (parsed instanceof LocalDateTime) {
    // it's a LocalDateTime, just assign it
    dt = (LocalDateTime) parsed;
} else if (parsed instanceof LocalDate) {
    // it's a LocalDate, set the time to whatever you want
    dt = ((LocalDate) parsed).atTime(LocalTime.MIDNIGHT);
}

If the result is a LocalDate, you can choose to call atStartOfDay(), as suggested by others, or change to a specific time-of-day, such as atTime(LocalTime.of(10, 30)) for 10:30 AM, for example.

like image 21
iamwert Avatar answered Oct 22 '22 07:10

iamwert