Home > Software design >  Generic date and time parsing in java 8
Generic date and time parsing in java 8

Time:06-10

I was recently trying to make a generic date and time parsing method with the java 8 time API, mainly for interfacing with older code using Date.

I wanted to do something like that:

public static Date parse(String dateStr, String pattern) {
    return Date.from(Instant.parse(dateStr, DateTimeFormatter.ofPattern(pattern)));
}

The problem is that with the time API, the class to use depends on the pattern DateTimeFormatter.parse will never fail but will return a TemporalAccessor which is horrible to work with and convert to a usable class.

And LocalDateTime.parse will fail if the pattern has no time information like "dd/MM/yyyy". Other classes like Instant, ZonedDateTime, etc. will all fail to parse if the pattern doesn't match the expected class.

Ideally, I'd like a way to parse leniently and return an Instant, with default values for missing fields, but I can't find a way to do that.

Any idea?

CodePudding user response:

You can use DateTimeFormatterBuilder::parseDefaulting to set default values.

var now = ZonedDateTime.now();
DateTimeFormatter formatter = new DateTimeFormatterBuilder()
    .appendPattern(pattern)
    .parseDefaulting(ChronoField.OFFSET_SECONDS, now.getOffset().getTotalSeconds())
    .parseDefaulting(ChronoField.YEAR, now.getYear())
    .parseDefaulting(ChronoField.MONTH_OF_YEAR, now.getMonthValue())
    .parseDefaulting(ChronoField.DAY_OF_MONTH, now.getDayOfMonth())
    .parseDefaulting(ChronoField.HOUR_OF_DAY, now.getHour())
    .parseDefaulting(ChronoField.MINUTE_OF_HOUR, now.getMinute())
    .parseDefaulting(ChronoField.SECOND_OF_MINUTE, now.getSecond())
    .toFormatter(Locale.ROOT);
Instant dt = Instant.from(formatter.parse(str));

Note that it's important to first append the pattern using appendPattern, and then set all your defaults using parseDefaulting.

Also note that I used the current time stamp to fill the defaults. So, for example, if you left out the year, it takes the current year (2022 at the time of writing). Of course, the defaults depend on your exact use case.

Examples:

At the time of writing, it's 2022-06-09T17:18:36 02:00.

System.out.println(parse("9-6", "d-M"));
System.out.println(parse("2023", "uuuu"));
System.out.println(parse("10:13", "H:m"));
System.out.println(parse("25 Dec, 16:22", "d MMM, H:mm"));

resolves to

2022-06-09T15:18:36Z
2023-06-09T15:18:36Z
2022-06-09T08:13:36Z
2022-12-25T14:22:36Z
  • Related