JDK-8281302 : Fatal parse errors: shortname for September has been changed in Locale.UK.
  • Type: Bug
  • Component: core-libs
  • Sub-Component: java.time
  • Affected Version: 16,17,18,19
  • Priority: P3
  • Status: New
  • Resolution: Unresolved
  • OS: generic
  • CPU: generic
  • Submitted: 2022-01-31
  • Updated: 2022-02-06
Description
ADDITIONAL SYSTEM INFORMATION :
With Java 1.8.0_292 and Java 11.0.11 on Linux this all works fine.
With Java 17 it fails.

A DESCRIPTION OF THE PROBLEM :
In Java 8 and 11 formatting and parsing a date using the Locale.UK used the shortname "Sep" for September.
With Java 17 this has changed to "Sept".
Only September has been changed to 4 letters, all other months remain at the expected 3 letters.

This change causes fatal parsing errors when trying to parse historical logging.

Changing to a different Locale like Locale.US or Locale.ENGLISH is not an option as these do not follow the ISO standard about weekdays (i.e. the Monday/Sunday thing) which are fields I'm looking for in my code.
I have not been able to find a locale that is "ENGLISH" and conforms to the ISO standards. Both Locale.US and Locale.ENGLISH do it the US way.

Background: The tests in my project failed when trying to build it with Java 17
https://github.com/nielsbasjes/logparser/blob/master/httpdlog/httpdlog-parser/src/test/java/nl/basjes/parse/httpdlog/dissectors/TestTimeStampDissector.java#L158


REGRESSION : Last worked in version 11.0.14

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Create a file September.java with the content below and then using Java 17 simply do:  javac September.java && java September.

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
Specific lines from the output of the provided reproduction code:

en_GB 01/Sep/2021:12:11:12 +0200
en_GB Test case: 30/sep/2016:00:00:06 +0000 : Passed: {InstantSeconds=1475193606, OffsetSeconds=0},ISO,Europe/Amsterdam resolved to 2016-09-30T00:00:06


ACTUAL -
Specific lines from the output of the provided reproduction code:

en_GB 01/Sept/2021:12:11:12 +0200
en_GB Test case: 30/sep/2016:00:00:06 +0000 : Failed: Text '30/sep/2016:00:00:06 +0000' could not be parsed at index 3


---------- BEGIN SOURCE ----------
import java.time.Instant;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeFormatterBuilder;
import java.time.temporal.WeekFields;
import java.util.Arrays;
import java.util.List;
import java.util.Locale;

public class September {
    public static void main( String[] args ) {
        show(Locale.UK); // The default Locale that follows the ISO-8601 WeekFields
        show(Locale.US);
        show(Locale.ENGLISH);
    }

    public static void show(Locale locale) {
        System.err.println("===============: With Locale " + locale.toString());
        showWeekfields(locale);

        // The default parser to what we find in the Apache httpd Logfiles
        //                                    [05/Sep/2010:11:27:50 +0200]
        String defaultApacheDateTimePattern = "dd/MMM/yyyy:HH:mm:ss ZZ";

        DateTimeFormatter formatter = new DateTimeFormatterBuilder()
            .parseCaseInsensitive()
            .appendPattern(defaultApacheDateTimePattern)
            .toFormatter()
            .withLocale(locale)
            .withZone(ZoneId.of("Europe/Amsterdam"));

        List<String> printCases = Arrays.asList(
            "2021-01-01T10:11:12.00Z",
            "2021-02-01T10:11:12.00Z",
            "2021-03-01T10:11:12.00Z",
            "2021-04-01T10:11:12.00Z",
            "2021-05-01T10:11:12.00Z",
            "2021-06-01T10:11:12.00Z",
            "2021-07-01T10:11:12.00Z",
            "2021-08-01T10:11:12.00Z",
            "2021-09-01T10:11:12.00Z",
            "2021-10-01T10:11:12.00Z",
            "2021-11-01T10:11:12.00Z",
            "2021-12-01T10:11:12.00Z");

        for (String testCase: printCases) {
            System.err.println(locale.toString() + " " + formatter.format(Instant.parse(testCase)));
        }

        String timestamp = "30/sep/2016:00:00:06 +0000";
        System.err.print(locale + " Test case: " + timestamp + " : ");
        try {
            System.err.println("Passed: " + formatter.parse(timestamp));
        } catch (Exception e) {
            System.err.println("Failed: " + e.getMessage());
        }
    }

    public static void showWeekfields(Locale locale) {
        WeekFields localeWeekFields = WeekFields.of(locale);
        WeekFields isoWeekFields = WeekFields.ISO;
        if (localeWeekFields.getFirstDayOfWeek().equals(isoWeekFields.getFirstDayOfWeek())) {
            System.err.println(locale + " has ISO FirstDayOfWeek");
        } else {
            System.err.println(locale + " DOES NOT HAVE ISO FirstDayOfWeek");
        }
        if (localeWeekFields.getMinimalDaysInFirstWeek() == isoWeekFields.getMinimalDaysInFirstWeek()) {
            System.err.println(locale + " has ISO MinimalDaysInFirstWeek");
        } else {
            System.err.println(locale + " DOES NOT HAVE ISO MinimalDaysInFirstWeek");
        }
    }

}

---------- END SOURCE ----------

CUSTOMER SUBMITTED WORKAROUND :
The hack that seems to work in some specific cases is to catch the exception and using a regular expression replace "sep" with "sept" and then try again.


FREQUENCY : always



Comments
The observations on Windows 10: JDK 16ea+25: Passed. JDK 16ea+26: Failed, Text '30/sep/2016:00:00:06 +0000' could not be parsed at index 3 JDK 17: Failed. JDK 18ea+29: Failed. JDK 19ea+3: Failed.
06-02-2022