Search code examples
javadateformatsimpledateformat

Java unable to parse date when using dots "." instead of dashes "-"


I have a confusing problem, consider this working code:

package com.mycompany.mavenproject2;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

public class Main {
  public static void main(String[] args) throws ParseException {
    SimpleDateFormat formatter = new SimpleDateFormat("dd-MMM-yyyy HH:mm:ss");
    
    Date date = formatter.parse("01-Jan-2017 00:47:13");
    System.out.println(date);
  }
}

which prints Sun Jan 01 00:47:13 CET 2017 when executing as expected.

However, when I replace each - with a . dot in the date:

package com.mycompany.mavenproject2;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

public class Main {
  public static void main(String[] args) throws ParseException {
    SimpleDateFormat formatter = new SimpleDateFormat("dd.MMM.yyyy HH:mm:ss");
    
    Date date = formatter.parse("01.Jan.2017 00:47:13");
    System.out.println(date);
  }
}

The code throws an exception on execution:

Exception in thread "main" java.text.ParseException: Unparseable date: "01.Jan.2017 00:47:13"
    at java.base/java.text.DateFormat.parse(DateFormat.java:395)
    at com.mycompany.mavenproject2.Main.main(Main.java:11)

I am using Java version 12 and German locale.

Has someone got an idea?


Solution

  • java.time

    I strongly agree with the comments recommending java.time, the modern Java date and time API, for your date and time work.

    Use this formatter:

    private static final DateTimeFormatter FORMATTER
            = DateTimeFormatter.ofPattern("dd.MMMuuuu HH:mm:ss", Locale.GERMAN);
    

    Do:

        LocalDateTime dateTime = LocalDateTime.parse("01.Jan.2017 00:47:13", FORMATTER);
        System.out.println(dateTime);
    

    Output when run on Java 11 with German locale:

    2017-01-01T00:47:13
    

    You may wonder why compared to your format pattern string I have left out the second dot? On Java 11 (and probably near Java versions too, possibly from Java 9 through 16) German month abbreviations are with a dot to signify abbreviation, so Jan. for Januar (January), etc. So in my format MMM matches Jan. and then uuuu matches 2017.

    For the fuller story Java gets its locale data including the month abbreviations used in different locales from up to four sources, and not all sources agree what German month abbreviations look like. Since Java 9 the default is CLDR,COMPAT meaning that locale data from CLDR, the Unicode Common Locale Data Repository, are preferred. And these include the dots I menitoned. You can get different results by setting the system property java.locale-providers to a value that does not begin with CLDR.

    What went wrong in your code?

    I have given you a hint already: In some Java versions German month abbreviations are with a dot. So in your example with dots as separators your SimpleDateFormat matched dd.MMM (without the second dot) to 01.Jan. (with the second dot). According to the format a dot should now come, but since that dot had already been consumed, SimpleDateFOrmat looked at 2017, decided it wasn’t a dot and threw the exception that you saw.

    The really surprising behaviour was in your first example where SimpleDateFormat was able to parse 01-Jan-2017 00:47:13 without any dots even though it believes that the month abbreviation should end in a dot. I have seen literally hundreds of examples of surprising behaviour of SimpleDateFormat before, but never any akin to this one.

    And all of these surprises are what make me say: By all means avoid using SimpleDateFormat.

    If you’re skeptical, I don’t blame you. So to demonstrate:

        SimpleDateFormat formatter = new SimpleDateFormat("MMMyyyy", Locale.GERMAN);
        
        System.out.println(formatter.format(0L));
        
        System.out.println(formatter.parse("Jan2017"));
        System.out.println(formatter.parse("Jan.2017"));
    

    Output, still on Java 11:

    Jan.1970
    Sun Jan 01 00:00:00 CET 2017
    Sun Jan 01 00:00:00 CET 2017
    

    We see that SimpleDateFormat formats the month with a dot, and is able to parse strings both with and without dots.

    This still isn’t the full story.

        SimpleDateFormat formatter = new SimpleDateFormat("MMM", Locale.GERMAN);
    
        System.out.println(formatter.format(0L));
    

    Output:

    Jan
    

    This time the month abbreviation was formatted without the dot. I got no idea what is going on. I repeat, forget about the confusing SimpleDateFormat class. It’s a notorious troublemaker.

    Links