4

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?

Community
  • 1
  • 1
user1676688
  • 414
  • 4
  • 15
  • 3
    the formatter is supposed to put it in that format, not to be able to read that format. – Stultuske Jan 23 '18 at 14:03
  • 3
    @Stultuske why does `DateTimeFormatter` has a `parse(CharSequence text)` method then? – XtremeBaumer Jan 23 '18 at 14:08
  • @XtremeBaumer might be wrong about it. – Stultuske Jan 23 '18 at 14:12
  • 2
    @user1676688 this might explain a bit: https://stackoverflow.com/questions/30518954/datetimeformatter-month-pattern-letter-l-fails – Stultuske Jan 23 '18 at 14:12
  • 1
    Next to the above comments, without the year supplied, how should the date be determined? Can't guess the year. – Hans Schreuder Jan 23 '18 at 14:20
  • 1
    I have this input coming from log of an application. Is it even possible to parse it I wonder? – user1676688 Jan 23 '18 at 14:25
  • 3
    Locale problem? “Jul” is not the same in all languages. 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. – Ole V.V. Jan 23 '18 at 14:29
  • You can find some references [here](https://stackoverflow.com/questions/30518954/datetimeformatter-month-pattern-letter-l-fails) – Dipak Pawar Jan 23 '18 at 14:49

2 Answers2

7

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);
Juan Carlos Mendoza
  • 5,736
  • 7
  • 25
  • 50
  • 2
    Nitpick: Actually that is an offset-from-UTC rather than a time zone. A zone is a history of changes in offset used by the people of a particular region. – Basil Bourque Jan 23 '18 at 22:30
2

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.

Ole V.V.
  • 81,772
  • 15
  • 137
  • 161