Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java 8 Date time formatter misbehaving

I have the following piece of code that is throwing a DateTimeParseException:

String dateString = "Jul 20 09:32:46"
DateTimeFormatter formatter=DateTimeFormatter.ofPattern("L d HH:mm:ss");
return ZonedDateTime.parse(dateString, formatter);

According to the documentation, you will observe that Jul is the example for character L.

However, the exception message is:

java.time.format.DateTimeParseException: Text 'Jul' could not be parsed at index 0

What am I missing?

like image 611
user1676688 Avatar asked Jan 02 '23 22:01

user1676688


2 Answers

You have some issues here:

  1. To correctly parse 'Jul' you have to use MMM instead of L (here explains why).
  2. Your date string doesn't have a year. You can't create a ZonedDateTime without the year.
  3. If is a Zoned date time, it has to include the time zone information too, which is not in your date string. You can use a LocalDateTime if you don't want to work with time zones.

Here are some alternatives:

With timezone:

String dateString = "Jul 20 2018 09:32:46+0000";
DateTimeFormatter formatter= DateTimeFormatter.ofPattern("MMM dd y H:mm:ssZ");
return ZonedDateTime.parse(dateString, formatter);

Without timezone:

String dateString = "Jul 20 2018 09:32:46";
DateTimeFormatter formatter= DateTimeFormatter.ofPattern("MMM dd y H:mm:ss");
return LocalDateTime.parse(dateString, formatter);
like image 128
Juan Carlos Mendoza Avatar answered Jan 05 '23 11:01

Juan Carlos Mendoza


The answer by Juan Carlos Mendoza is correct. I will give my suggestions as a supplement: either improve your string to include year and time zone, or build a formatter that can parse your current string without them.

Improving your string

    String dateString = "Jul 20 2018 09:32:46 America/Argentina/La_Rioja";
    DateTimeFormatter formatter 
            = DateTimeFormatter.ofPattern("LLL d uuuu HH:mm:ss VV", Locale.ROOT);
    System.out.println(ZonedDateTime.parse(dateString, formatter));

This prints

2018-07-20T09:32:46-03:00[America/Argentina/La_Rioja]

The same formatter will also parse Jul 20 2018 09:32:46 -08:30 into a ZonedDateTime of 2018-07-20T09:32:46-08:30.

First potential issue is the locale. If “Jul” is in English, give an English-speaking locale, or parsing will likely fail on computers with a language where the month of July is called something else. I recommend you always specify locale with your formatter. Even if you end up going for Locale.getDefault(). It will still tell the reader (and yourself) that you have made a conscious choice.

Next the documentation says that both M and L can give month as number/text and gives examples 7; 07; Jul; July; J. So this line is clearly relevant: “Number/Text: If the count of pattern letters is 3 or greater, use the Text rules above. Otherwise use the Number rules above.” Since “Jul” is text, you need 3 pattern letters or greater. “Less than 4 pattern letters will use the short form.” “Jul” is short, so we need exactly three letters.

The code above works with Java 9.0.4 no matter if I use MMM or LLL in the format pattern string. In jdk1.8.0_131 it works with MMM but funnily fails with LLL, this may be a bug (tested on a Mac). See Juan Carlos Mendoza’s for a treatment of the intended difference between M and L.

Build a formatter that works

    String dateString = "Jul 20 09:32:46";
    ZoneId zone = ZoneId.of("America/Argentina/La_Rioja");
    DateTimeFormatter formatter = new DateTimeFormatterBuilder().appendPattern("LLL d HH:mm:ss")
            .parseDefaulting(ChronoField.YEAR, Year.now(zone).getValue())
            .toFormatter(Locale.ROOT)
            .withZone(zone);
    System.out.println(ZonedDateTime.parse(dateString, formatter));

This will parse the string from your question into 2018-07-20T09:32:46-03:00[America/Argentina/La_Rioja]. Please substitute your desired default time zone if it didn’t happen to coincide with the one I picked at random. Also substitute your desired year if you don’t want the current year.

Again my Java 8 requires MMM rather than LLL.

like image 34
Ole V.V. Avatar answered Jan 05 '23 11:01

Ole V.V.