Parse date or datetime both as LocalDateTime in Java 8
Asked Answered
R

4

13

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.

Roster answered 16/3, 2018 at 14:22 Comment(2)
Possibly related: #49027414Sprawl
LocalDate.parse("1986-04-08", DateTimeFormatter.ofPattern("yyyy-MM-dd")).atStartOfDay();Energumen
C
29

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
Cannae answered 16/3, 2018 at 15:16 Comment(0)
L
5

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.

Limiting answered 16/3, 2018 at 18:36 Comment(0)
B
0

I know its late for the answer, but it can help others...

Since the LocalDateTime you need to set a time, otherwise you can use just the LocalDate, I searched for LocalDateTime built-in solution to handle this. I didn't found, however I used this following approach:

// For specific date (the same as the question)
LocalDateTime specificDate LocalDateTime.of(LocalDate.of(1986, 4, 8), LocalTime.MIN);

Other examples:

// For the start of day
LocalDateTime startToday = LocalDateTime.of(LocalDate.now(), LocalTime.MIN));

// For the end of day
LocalDateTime endOfToday = LocalDateTime.of(LocalDate.now(), LocalTime.MAX));

This way you don't need to use a formatter. :)

Boner answered 25/5, 2020 at 20:0 Comment(0)
T
0

Based on Jose Da Silva's answer:

DateTimeFormatter formatter = new DateTimeFormatterBuilder()
    .parseCaseInsensitive()
    .append(DateTimeFormatter.ISO_LOCAL_DATE)
    .optionalStart()
    .appendLiteral(' ') // might be 'T' in your case
    .append(DateTimeFormatter.ISO_LOCAL_TIME)
    .optionalEnd()
    .parseDefaulting(ChronoField.HOUR_OF_DAY, 0)
    .parseDefaulting(ChronoField.MINUTE_OF_HOUR, 0)
    .parseDefaulting(ChronoField.SECOND_OF_MINUTE, 0)
    .toFormatter()
    .withResolverStyle(ResolverStyle.STRICT); // parse STRICT instead of SMART!

Usage:

LocalDateTime localDateTime1 = LocalDateTime.parse("1994-05-13", formatter);
LocalDateTime localDateTime2 = LocalDateTime.parse("1994-05-13 23:00:00", formatter);
Tristichous answered 31/8, 2022 at 10:38 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.