JDK-8250514 : parseDefaulting results in a conflict when parsing Hour related fields
  • Type: Bug
  • Component: core-libs
  • Sub-Component: java.time
  • Affected Version: 8,11,15
  • Priority: P4
  • Status: Closed
  • Resolution: Not an Issue
  • Submitted: 2020-07-22
  • Updated: 2020-09-11
  • Resolved: 2020-07-24
Description
ADDITIONAL SYSTEM INFORMATION :
macos/JDK14

A DESCRIPTION OF THE PROBLEM :
Hour can be represented as an ChronoField.CLOCK_HOUR_OF_DAY, CLOCK_HOUR_OF_AMPM, HOUR_OF_AMPM and HOUR_OF_DAY
all these are resolved to HOUR_OF_DAY during resolving phase (see Parsed.resolveTimeFIelds)

using a pattern with parseDefaulting leads to a an exception suggesting a conflict being thrown.

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
 DateTimeFormatter dateTimeFormatterWithDefault = new DateTimeFormatterBuilder()
            .appendPattern("dd-MM-uuuu hh:mm a")
            .parseDefaulting(ChronoField.HOUR_OF_DAY, 23L)
            .toFormatter();
        TemporalAccessor parsingFails = dateTimeFormatterWithDefault.parse("01-01-2020 01:01 AM");

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
assertEquals(01, parsingFails.get(ChronoField.HOUR_OF_DAY));
assertEquals(01, parsingFails.get(ChronoField.HOUR_OF_AMPM));
ACTUAL -
java.time.format.DateTimeParseException: Text '01-01-2020 01:01 AM' could not be parsed: Conflict found: HourOfDay 23 differs from HourOfDay 1 while resolving  AmPmOfDay
	at java.base/java.time.format.DateTimeFormatter.createError(DateTimeFormatter.java:2020)
	at java.base/java.time.format.DateTimeFormatter.parse(DateTimeFormatter.java:1881)

---------- BEGIN SOURCE ----------
public void testX(){
        DateTimeFormatter dateTimeFormatter = new DateTimeFormatterBuilder()
            .appendPattern("dd-MM-uuuu hh:mm a")
            .toFormatter();
        TemporalAccessor parse = dateTimeFormatter.parse("01-01-2020 01:01 AM");
        assertTrue(parse.isSupported(ChronoField.HOUR_OF_DAY));
        assertTrue(parse.isSupported(ChronoField.HOUR_OF_AMPM));
        assertEquals(01, parse.get(ChronoField.HOUR_OF_DAY));
        assertEquals(01, parse.get(ChronoField.HOUR_OF_AMPM));
        //Parsed.resolveTimeFields() resolves hour field to HOUR_OF_DAY

        DateTimeFormatter dateTimeFormatterWithDefault = new DateTimeFormatterBuilder()
            .appendPattern("dd-MM-uuuu hh:mm a")
            .parseDefaulting(ChronoField.HOUR_OF_DAY, 23L)
            .toFormatter();
        TemporalAccessor parsingFails = dateTimeFormatterWithDefault.parse("01-01-2020 01:01 AM");
        /*

java.time.format.DateTimeParseException: Text '01-01-2020 01:01 AM' could not be parsed: Conflict found: HourOfDay 23 differs from HourOfDay 1 while resolving  AmPmOfDay
	at java.base/java.time.format.DateTimeFormatter.createError(DateTimeFormatter.java:2020)
	at java.base/java.time.format.DateTimeFormatter.parse(DateTimeFormatter.java:1881)
         */
    }
---------- END SOURCE ----------

FREQUENCY : always



Comments
There are two ways to resolve HOUR_OF_DAY field in this formatter, i.e., parseDefaulting(23L) and CLOCK_HOUR_OF_AMPM/AMPM_OF_DAY. In such a case, the test case has to choose the method to resolve the field to avoid possible conflict (like this case). To do this, the formatter should be created with either: .withResolverFields(<all other fields>, ChronoField.HOUR_OF_DAY) or .withResolverFields(<all other fields>, ChronoField.CLOCK_HOUR_OF_AMPM, ChronoField.AMPM_OF_DAY) The former produces 23 and the latter produces 1 for HOUR_OF_DAY.
24-07-2020

The observations on Windows 10: JDK 8: Fail JDK 11: Fail JDK 15: Fail
24-07-2020