0

I'm attempting to migrate code from Java 8 to the OpenJDK 11. All was going amazingly smoothly until I attempted to parse date strings. I have a large number of these date strings from the database (and no, I can't change the database) that parse fine in Java 7 but fail in Java 11.

Some notes:

  1. The locale is set to the same default value.
  2. The date was generated originally using the date formatter with LONG, LONG settings.
  3. I ran this on Java 8 to get the time in mills. Then on Java 11, I converted that time in mills to a date. That seemed to work fine.
  4. However, the system seemed to insert an "at" between the date and time.
  5. I attempted to add in the "at" text to the date and it still fails.

Can someone point me in the right direction? I need to eventually do date calculations on these strings.

Thanks!

Bruce

package utility;

import java.text.DateFormat;
import java.text.ParseException;
import java.util.Date;
import java.util.Iterator;
import java.util.Locale;


public class OpenJDK11DateDisaster {
    public static void main(String args[])
    {
        /*
         * Example of the date string that works with Java 8 but fails to
         * parse in OpenJDK 11.
         * 
         * Note:  The Locale is identical between the two versions (US, English)
         * 
         */
        
        /* Java 8 Output...
        Java Version = 1.8.0_265
        Broken Date = September 1, 2015 3:13:29 AM MDT
        Convert mills to a date.
        From mills date = September 1, 2015 3:13:29 AM MDT
        Broken Date = September 1, 2015 3:13:29 AM MDT
        Attempting to parse broken date.
        Date in mills = 1441098809000
        From mills date = September 1, 2015 3:13:29 AM MDT
        
        Java 11 output...
        Java Version = 11.0.8
        Broken Date = September 1, 2015 3:13:29 AM MDT
        Convert mills to a date.
        From mills date = September 1, 2015 at 3:13:29 AM MDT
        Broken Date = September 1, 2015 3:13:29 AM MDT
        Attempting to parse broken date.
        java.text.ParseException: Unparseable date: "September 1, 2015 3:13:29 AM MDT"

        Unable to parse broken date.
        Adding in the at...
            at java.base/java.text.DateFormat.parse(DateFormat.java:395)
            at utility.OpenJDK11DateDisaster.main(OpenJDK11DateDisaster.java:60)
        Fixed date =  September 1, 2015 at 3:13:29 AM MDT
            java.text.ParseException: Unparseable date: " September 1, 2015 at 3:13:29 AM MDT"
            at java.base/java.text.DateFormat.parse(DateFormat.java:395)
            at utility.OpenJDK11DateDisaster.main(OpenJDK11DateDisaster.java:89)

        Unable to parse fixed date.

        
        */
        System.out.println("Java Version = " + System.getProperty("java.version"));
        // This is the date that will parse in Java 7 but fails in Java 11
        String BrokenDate = "September 1, 2015 3:13:29 AM MDT";
        System.out.println("Broken Date = " + BrokenDate);
        
        // Broken date converted to mills in Java 7.
        long mills = 1441098809000L;
        
            
        System.out.println("Convert mills to a date.");
        DateFormat df2 = DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.LONG, Locale.US);
        
        Date millDate = new Date(mills);
        System.out.println("From mills date = " + df2.format(millDate));
        
        System.out.println("Broken Date = " + BrokenDate);
        
        
        /*
         * Parse these old dates.
         */
        System.out.println("Attempting to parse broken date.");
        df2 = DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.LONG, Locale.US);
        Date date = null;
        try {
            date = df2.parse(BrokenDate) ;
        } catch (ParseException e) {
            e.printStackTrace();
            System.out.println();
            System.out.println("Unable to parse broken date.");
            
            // For some reason, they now put an "at" word between the date and the time.
            // Here we put the "at" back in and see.
            
            // See if we can fix it.
            System.out.println("Adding in the at...");
            StringList fields = Acl.split(BrokenDate, ' ');
            String FixedDate = "";
            Iterator iter = fields.iterator();
            int count = 0;
            while (iter.hasNext() ) {
                String field = (String)iter.next();
                count++;
                FixedDate += " " + field;
                if (count == 3) {
                    // add an at
                    FixedDate += " at";
                }
                
            }
            System.out.println("Fixed date = " + FixedDate);
            // Now see if it will parse
        
            try {
                date = df2.parse(FixedDate);
            } catch (ParseException e2) {
                e2.printStackTrace();
                System.out.println();
                System.out.println("Unable to parse fixed date.");
                System.exit(-1);
            }
            System.out.println("Was able to parse Fixed Date " + date);
        }
        mills = date.getTime();  // From the BrokenDate
        // Should be -62112243600000 as computed on a Java 7 machine
        System.out.println("Date in mills = " + mills);
        
        // Convert the mills to a date.
        millDate = new Date(mills);
        System.out.println("From mills date = " + df2.format(millDate));
    }
}
Arvind Kumar Avinash
  • 71,965
  • 6
  • 74
  • 110
Bruce
  • 1
  • 3
  • 1
    There is a compatibility flag: https://stackoverflow.com/questions/57049592/simpledateformat-format-gives-different-results-in-java8-vs-java11 – Joop Eggen Oct 13 '20 at 15:12
  • Which default locale is that? UK, US, New Zealand English, …? Asking partly because the three mentioned locales behave differently when it comes to formatting and parsing dates and times. – Ole V.V. Oct 14 '20 at 12:41
  • Ops. Sorry. US. – Bruce Oct 14 '20 at 23:31

2 Answers2

0

According to the documentation https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/text/DateFormat.html it seems you need the FULL format

LONG is longer, such as January 12, 1952 or 3:30:32pm
FULL is pretty completely specified, such as Tuesday, April 12, 1952 AD or 3:30:42pm PST.

Put a breakpoint on DateFormat.java in the parse method (around line 395) if you can, to understand what is happening.

Jihed Amine
  • 2,198
  • 19
  • 32
  • As a quick test, I set the format to FULL FULL (and about every combination). Still got the unparseable date exception. Looks like I'm going to have to dig into the source to see what is happening. Was trying to avoid that.... – Bruce Oct 13 '20 at 15:59
  • Well, have a partial solution: https://bugs.openjdk.java.net/browse/JDK-8211750 Says to add the JRE option -Djava.locale.providers=COMPAT and keep the format LONG, LONG. Interesting. Not sure this is a completely viable solution for my situation but at least it will actually parse the date! – Bruce Oct 13 '20 at 16:10
  • Jihed, thanks for pointing me in the right direction! – Bruce Oct 13 '20 at 16:29
  • You are welcome Bruce! – Jihed Amine Oct 13 '20 at 21:19
0

A safe option will be to replace MDT with the corresponding time-zone specifier, Mountain Standard Time.

import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Locale;

public class Main {
    public static void main(String[] args) {
        String dateTimeStr = "September 1, 2015 3:13:29 AM MDT".replace("MDT", "Mountain Standard Time");
        ZonedDateTime zdt = ZonedDateTime.parse(dateTimeStr,
                DateTimeFormatter.ofPattern("MMMM d, u h:m:s a zzzz", Locale.ENGLISH));
        System.out.println(zdt);

        // Custom format
        String formatted = zdt.format(DateTimeFormatter.ofPattern("EEEE MMMM dd uuuu 'at' hh:mm:ss a", Locale.ENGLISH));
        System.out.println(formatted);
    }
}

Output:

2015-09-01T03:13:29-06:00[America/Denver]
Tuesday September 01 2015 at 03:13:29 AM

Note: The legacy java.util date-time classes are outdated and error-prone and so is their formatting API, SimpleDateFormat. I suggest you should stop using them completely and switch to the modern date-time API. Learn more about the modern date-time API at Trail: Date Time.

If you are doing it for your Android project and your Android API level is still not compliant with Java-8, check Java 8+ APIs available through desugaring and How to use ThreeTenABP in Android Project.

With legacy API:

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

public class Main {
    public static void main(String[] args) throws ParseException {
        String dateTimeStr = "September 1, 2015 3:13:29 AM MDT".replace("MDT", "Mountain Standard Time");
        Date date = new SimpleDateFormat("MMMM d, yyyy h:m:s a zzzz", Locale.ENGLISH).parse(dateTimeStr);
        System.out.println(date);

        // Custom format
        String formatted = new SimpleDateFormat("EEEE MMMM dd yyyy 'at' hh:mm:ss a", Locale.ENGLISH).format(date);
        System.out.println(formatted);
    }
}
Arvind Kumar Avinash
  • 71,965
  • 6
  • 74
  • 110