Java SimpleDateFormat throwing ParseException: Unp

2020-07-25 09:13发布

问题:

I have the following method:

    public static Date convertFromWowInterface(String wowinterfaceFormat){
        Date date = null;
        try {
            SimpleDateFormat dateFormat = new SimpleDateFormat("MM-dd-yy hh:mm a");
            date = dateFormat.parse(wowinterfaceFormat);
        } catch (ParseException e) {
            e.printStackTrace();
        }
        return date;
    }

The string being passed in is of the format:

"08-11-19 07:00 AM"

without the quotes obviously. Now the above method works just fine on my mac, however when some of my users use the program (on Windows) they get the exception:

java.text.ParseException: Unparseable date: "08-11-19 07:00 AM"
        at java.base/java.text.DateFormat.parse(DateFormat.java:395)

Does the OS make a difference here? Or is there something else at play? As far as I can tell the SimpleDateFormat match the input string exactly?

回答1:

I can reproduce the problem when I switch to a different locale.

Here's my demo program:

import java.util.*;
import java.text.*;

public class YourCode {
    public static void main(String[] args) throws Exception {
        for (String a : args) {
            SimpleDateFormat dateFormat = new SimpleDateFormat("MM-dd-yy hh:mm a");
            Date d = dateFormat.parse(a);
            System.out.println(d);
        }
    }
}

It works, when I run it under US English locale:

robert@saaz:~$ LC_ALL="en_US.UTF-8" java YourCode "08-11-19 07:00 AM" 
Sun Aug 11 07:00:00 CDT 2019

I get the exception you see when I switch to e.g., German locale, which uses 24h time, not AM/PM.

robert@saaz:~$ LC_ALL="de_DE.UTF-8" java YourCode "08-11-19 07:00 AM" 
Exception in thread "main" java.text.ParseException: Unparseable date: "08-11-19 07:00 AM"
    at java.base/java.text.DateFormat.parse(DateFormat.java:395)
    at YourCode.main(YourCode.java:8)

To get around this, specify the locale when you create the SimpleDateFormat. Change

SimpleDateFormat dateFormat = new SimpleDateFormat("MM-dd-yy hh:mm a");

to

SimpleDateFormat dateFormat = new SimpleDateFormat("MM-dd-yy hh:mm a", Locale.US);


回答2:

tl;dr

➡ Specify a language to use in translating AM/PM.

Use modern java.time classes. Never use Date & SimpleDateFormat.

LocalDateTime
.parse(
    "08-11-19 07:00 AM" ,
    DateTimeFormatter.ofPattern( "MM-dd-uu hh:mm a" ).withLocale( Locale.US )
)

java.time

You are using terrible date-time classes that were supplanted years ago by the modern java.time classes defined in JSR 310.

Define a formatting pattern to match your input. Specify a Locale to determine the human language and cultural norms needed to translate "AM"/"PM".

String input = "08-11-19 07:00 AM" ;
Locale locale = Locale.US ;
DateTimeFormatter f = DateTimeFormatter.ofPattern( "MM-dd-uu hh:mm a" ).withLocale( locale ) ;

Parse input as a LocalDateTime as it lacks an indicator of time zone or offset-from-UTC. As such, it has no real meaning. We do not know if the publisher of this data meant 7 AM in Tokyo, 7 AM in Casablanca, or 7 AM in Montréal — all very different moments, several hours apart.

LocalDateTime ldt = LocalDateTime.parse( input , f ) ;

Run this code live at IdeOne.com.

ldt.toString(): 2019-08-11T07:00

Finally, educate the publisher of your data about the ISO 8601 standard formats for exchanging date-time values as text. That current pattern you are using is a poor choice.