Skip to content

java.time.Instant.parse and kotlinx.datetime.Instant.parse behave differently #332

Closed as not planned
@dkhalanskyjb

Description

@dkhalanskyjb

Consider the following code:

import java.time.*

fun main() {
    for (str in listOf(
        "2020-02-02T23:59:60Z",
        "2020-02-02T23:59:60+00:00",
        "2020-02-02T23:59:60+01:00",
        "2020-02-03T00:59:60+01:00",
        "2020-02-03T22:59:60+01:00",
    )) {
        println(runCatching { Instant.parse(str) })
    }
}

play.kotlinlang.org outputs:

Success(2020-02-02T23:59:59Z)
Success(2020-02-02T23:59:59Z)
Success(2020-02-02T22:59:59Z)
Failure(java.time.format.DateTimeParseException: Text '2020-02-03T00:59:60+01:00' could not be parsed at index 0)
Failure(java.time.format.DateTimeParseException: Text '2020-02-03T22:59:60+01:00' could not be parsed at index 0)

In kotlinx-datetime, all these strings cause a parse failure.

The string 23:59:60 for LocalTime, regardless of the effective UTC offset, is treated differently by java-time when parsing an Instant, as it could represent a leap second. There's a way to query whether this leap second was parsed (parsedLeapSecond), but it's almost never used in practice (https://grep.app/search?q=parsedLeapSecond), and most of the known usages are erroneous.

This behavior on Java's side is inconsistent: citing Wikipedia,

Unlike leap days, which begin after 28 February, 23:59:59 local time, UTC leap seconds occur simultaneously worldwide; for example, the leap second on 31 December 2005, 23:59:60 UTC was 31 December 2005, 18:59:60 (6:59:60 p.m.) in U.S. Eastern Standard Time and 1 January 2006, 08:59:60 (a.m.) in Japan Standard Time.

Therefore, it's not exactly 23:59:60 that could represent a leap second but 23:59:60Z, which could theoretically be represented with different offsets, or the opposite, 23:59:60 with other offsets could represent a time that's never a valid leap second. Java Time's handling of leap seconds in this aspect seems weirdly situational. One possible explanation is that no one relies on this behavior and able to observe this inconsistency in practice.

Looks like we have three strategies to follow:

  • Support leap seconds more accurately, taking the offset into account and allowing exactly 23:59:60Z and its representations with other offsets;
  • Do exactly what Java is doing;
  • Don't support such anomalies other than by the more general mechanism of datetime parsing that allows dealing with out-of-bounds values.

Metadata

Metadata

Assignees

No one assigned

    Labels

    formattersRelated to parsing and formatting

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions