2

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?

erik p
  • 360
  • 3
  • 15
  • Have you confirmed that the wowinterfaceFormat value is the same in both cases? I think the likeliest explanation is that it is not. – Alex Broadwin Aug 22 '19 at 19:31
  • @AlexBroadwin I will verify now, however, the String being reported as Unparseable date is from windows and matches the format exactly, does it not? So why would that be reported as unparseable? – erik p Aug 22 '19 at 19:39
  • Is it the same version of Java on both machines? – jalynn2 Aug 22 '19 at 19:39
  • @erikp, apologies, I misread part of your question. It does seem to match the format. – Alex Broadwin Aug 22 '19 at 19:41
  • have you tried the format 'MM-dd-yy hh:mm aa'? – jalynn2 Aug 22 '19 at 19:42
  • @jalynn2 they are both Java 12.0.2 however one is obviously the Mac distribution and the other the windows. – erik p Aug 22 '19 at 19:44
  • no problem to parse at my windows, @jalynn2 letter count has almost no meaning when parsing (only important if missing field separator) – user85421 Aug 22 '19 at 19:44
  • @jalynn2 no, is that necessary for am pm? Was under the impression it wasn't but will give it a try real quick. – erik p Aug 22 '19 at 19:45
  • @Carlos Heuberger: Agreed, just looking for a straw to grasp at. – jalynn2 Aug 22 '19 at 19:45
  • 2
    FYI, the terribly troublesome date-time classes such as [`java.util.Date`](https://docs.oracle.com/javase/10/docs/api/java/util/Date.html), [`java.util.Calendar`](https://docs.oracle.com/javase/10/docs/api/java/util/Calendar.html), and `java.text.SimpleDateFormat` are now [legacy](https://en.wikipedia.org/wiki/Legacy_system), supplanted by the [*java.time*](https://docs.oracle.com/javase/10/docs/api/java/time/package-summary.html) classes built into Java 8 and later. See [*Tutorial* by Oracle](https://docs.oracle.com/javase/tutorial/datetime/TOC.html). – Basil Bourque Aug 23 '19 at 00:03
  • 1
    I recommend you don’t use `SimpleDateFormat` and `Date`. Those classes are poorly designed and long outdated, the former in particular notoriously troublesome. Instead use `LocalDateTime` and `DateTimeFormatter`, both from [java.time, the modern Java date and time API](https://docs.oracle.com/javase/tutorial/datetime/). – Ole V.V. Aug 25 '19 at 10:54
  • 1
    Some locales use the strings `AM` and `PM`, while other locales uses other variants (like lowercase or with dots) or some completely different strings (and while most locales don’t use AM and PM at all in practice, Java still includes some strings for them and not necessarily `AM` and `PM`). So you need to specify a locale for your code to work across locales. – Ole V.V. Aug 25 '19 at 10:57

2 Answers2

5

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);
Robert
  • 7,394
  • 40
  • 45
  • 64
  • strange, works nice with GERMAN for me.... but not for e.g. CHINA – user85421 Aug 22 '19 at 19:52
  • I believe you, but that's messed up: The format is explicitly specifying the components. Default German format would be DMY, so i wonder if "08-13-19" would fail without the locale? – jalynn2 Aug 22 '19 at 19:54
  • I believe the problem with another Locale is that `a` is not AM/PM – user85421 Aug 22 '19 at 19:55
  • @Carlos Heuberger That makes sense – jalynn2 Aug 22 '19 at 19:55
  • 1
    probably related to what is documented as "SimpleDateFormat also supports localized date and time pattern strings. In these strings, the pattern letters described above may be replaced with other, locale dependent, pattern letters" – user85421 Aug 22 '19 at 20:01
  • 3
    *"German locale, which uses 24h time"* That's not the issue. The issue is that `AM/PM` is spelled different in German. Run this statement: `System.out.println(Arrays.toString(DateFormatSymbols.getInstance(Locale.GERMANY).getAmPmStrings()))` --- It'll show `[vorm., nachm.]`, which means that parsing would have worked if the input has been `08-11-19 07:00 vorm.` – Andreas Aug 22 '19 at 20:08
  • @Andreas strange, my system is returning AM/PM....(jshell 13.0) - disregard, it's just java 13, 12 is gives *correct* answer – user85421 Aug 22 '19 at 20:11
  • 1
    @CarlosHeuberger Depends on Java version: My jdk1.5.0_22 and jdk1.8.0_181 returns `[AM, PM]` for Germany, but my jdk-11.0.1 returns `[vorm., nachm.]` – Andreas Aug 22 '19 at 20:14
  • it is the Java version, 12 (and 11) gives the german text – user85421 Aug 22 '19 at 20:15
  • This actually seems to work for both me and my users. The issue seems to be Locale related as you suggest. Im on En US and they are Swedish and German. Specifying US locale fixed the issues with parsing! – erik p Aug 22 '19 at 20:17
3

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.

Basil Bourque
  • 303,325
  • 100
  • 852
  • 1,154