JDK-8046416 : Unable to parse an Instant from fields
  • Type: Bug
  • Component: core-libs
  • Sub-Component: java.time
  • Affected Version: 8u20
  • Priority: P3
  • Status: Resolved
  • Resolution: Fixed
  • Submitted: 2014-06-10
  • Updated: 2014-07-29
  • Resolved: 2014-06-19
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
8u20 b20Fixed 9Fixed
The fix for JDK-8033662 enabled a ZonedDateTime to be parsed when withZone() is used. Unfortunately, the fix did not allow an Instant to be parsed. More generally, there is no current way to parse an instant except using the appendInstant() method of the formatter builder.

    public void test_parse_Instant_withZone1() {
        DateTimeFormatter fmt = new DateTimeFormatterBuilder().appendPattern("yyyy-MM-dd HH:mm:ss ").appendZoneId().toFormatter();
        TemporalAccessor acc = fmt.parse("2014-06-30 01:02:03 Europe/Paris");
        Instant actual = Instant.from(acc);
        assertEquals(actual, ZonedDateTime.of(2014, 6, 30, 1, 2, 3, 0, PARIS).toInstant());

    public void test_parse_Instant_withZone2() {
        DateTimeFormatter fmt = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").withZone(PARIS);
        TemporalAccessor acc = fmt.parse("2014-06-30 01:02:03");
        Instant actual = Instant.from(acc);
        assertEquals(actual, ZonedDateTime.of(2014, 6, 30, 1, 2, 3, 0, PARIS).toInstant());

Tests fail in the from() method because it does not make available INSTANT_SECONDS even though sufficient data is available.

Thanks for the detailed explanation of the bug and fix.

The patch includes code to fix the TemporalAccessor returned from parsing to match the spec in ChronoField: "Implementations of TemporalAccessor should provide a value for this field if they can return a value for SECOND_OF_MINUTE, SECOND_OF_DAY or INSTANT_SECONDS filling unknown precision with zero." The patch includes code to fix the implementation of appendInstant() which ignores anything else added after appendInstant(). This is spec'ed by implication - nowhere does it suggest that appendInstant() must be the last element in a DateTimeFormatterBuilder. The patch includes code to ensure that a parsed or injected zone or offset is correctly used to interpret the result. "The withZone method returns a new formatter that overrides the zone. If overridden, the date-time value is converted to a ZonedDateTime with the requested ZoneId before formatting. During parsing the ZoneId is applied before the value is returned." "When parsing, there are two distinct cases to consider. If a zone has been parsed directly from the text, perhaps because DateTimeFormatterBuilder.appendZoneId() was used, then this override zone has no effect. If no zone has been parsed, then this override zone will be included in the result of the parse where it can be used to build instants and date-times." "An Instant represents an instantaneous point on the time-line. On their own, an instant has insufficient information to allow a local date-time to be obtained. Only when paired with an offset or time-zone can the local date or time be calculated." The change is very definitely what was intended of the API. Any reasonable end-user would expect the code to behave as per the change, and would describe the current behaviour as a bug. The change is also additive - it fixes code that would otherwise be broken. This change is also very similar to the change made for ZonedDateTime in JDK-8033662.

Patch attached that solves the issue and adds lots of tests.

Issue JDK-8033659 was closed as it was assumed fixed by JDK-8033662. Unfortunately, it wasn't actually fixed. In evaluating this bug it was found that nothing can be parsed after calling appendInstant() in a formatter builder (the code moves the position after a successful parse to the end of the text rather than the end of the instant text).