From a4804ed12c473b9d93ac1ad140e81032e17438ac Mon Sep 17 00:00:00 2001 From: Ilya Gorbunov Date: Thu, 17 Oct 2024 18:27:10 +0200 Subject: [PATCH] [stdlib] Fix a couple of issues in Duration.parse KT-72380 - `PT000000000000000000000000000H` expected to be zero, but parsed as an infinite duration - `PT000000000000000000000000001S` expected to be 1 second, but parsed as an infinite duration - `PT+-2H` expected to be an incorrect value, but parsed as "-2 hours" Co-authored-by: Dmitry Khalanskiy --- libraries/stdlib/src/kotlin/time/Duration.kt | 19 ++++++++++++++----- libraries/stdlib/test/time/DurationTest.kt | 11 ++++++----- 2 files changed, 20 insertions(+), 10 deletions(-) diff --git a/libraries/stdlib/src/kotlin/time/Duration.kt b/libraries/stdlib/src/kotlin/time/Duration.kt index 841f107f33ad2..ca798dac96425 100644 --- a/libraries/stdlib/src/kotlin/time/Duration.kt +++ b/libraries/stdlib/src/kotlin/time/Duration.kt @@ -998,12 +998,21 @@ private fun parseOverLongIsoComponent(value: String): Long { val length = value.length var startIndex = 0 if (length > 0 && value[0] in "+-") startIndex++ - if ((length - startIndex) > 16 && (startIndex..value.lastIndex).all { value[it] in '0'..'9' }) { - // all chars are digits, but more than ceiling(log10(MAX_MILLIS / 1000)) of them - return if (value[0] == '-') Long.MIN_VALUE else Long.MAX_VALUE + if (length - startIndex > 16) run { + var firstNonZero = startIndex + for (index in startIndex.. if (firstNonZero == index) firstNonZero++ + !in '1'..'9' -> return@run + } + } + if (length - firstNonZero > 16) { + // all chars are digits, but more than ceiling(log10(MAX_MILLIS / 1000)) of them + return if (value[0] == '-') Long.MIN_VALUE else Long.MAX_VALUE + } } - // TODO: replace with just toLong after min JDK becomes 8 - return if (value.startsWith("+")) value.drop(1).toLong() else value.toLong() + // TODO: replace with just toLong after the minimum supported Android SDK has the same behavior as JDK 8 + return if (value.startsWith("+") && length > 1 && value[1] in '0'..'9') value.drop(1).toLong() else value.toLong() } diff --git a/libraries/stdlib/test/time/DurationTest.kt b/libraries/stdlib/test/time/DurationTest.kt index bebccf6766c3f..54c68dd82ad25 100644 --- a/libraries/stdlib/test/time/DurationTest.kt +++ b/libraries/stdlib/test/time/DurationTest.kt @@ -553,13 +553,13 @@ class DurationTest { } // zero - test(Duration.ZERO, "PT0S", "P0D", "PT0H", "PT0M", "P0DT0H", "PT0H0M", "PT0H0S") + test(Duration.ZERO, "PT0S", "P0D", "PT0H", "PT0M", "P0DT0H", "PT0H0M", "PT0H0S", "PT000000000000000000000000H") // single unit test(1.days, "PT24H", "P1D", "PT1440M", "PT86400S") test(1.hours, "PT1H") test(1.minutes, "PT1M") - test(1.seconds, "PT1S") + test(1.seconds, "PT1S", "PT000000000000000000000001S") test(1.milliseconds, "PT0.001S") test(1.microseconds, "PT0.000001S") test(1.nanoseconds, "PT0.000000001S", "PT0.0000000009S") @@ -581,11 +581,11 @@ class DurationTest { // with sign test(-1.days + 15.minutes, "-PT23H45M", "PT-23H-45M", "+PT-24H+15M") test(-1.days - 15.minutes, "-PT24H15M", "PT-24H-15M", "-PT25H-45M") - test(Duration.ZERO, "PT0S", "P1DT-24H", "+PT-1H+60M", "-PT1M-60S") + test(Duration.ZERO, "PT0S", "P1DT-24H", "+PT-1H+60M", "-PT1M-60S", "PT-000000000000000000000000H") // infinite - test(Duration.INFINITE, "PT9999999999999H", "PT+10000000000000H", "-PT-9999999999999H", "-PT-1234567890123456789012S") - test(-Duration.INFINITE, "-PT9999999999999H", "-PT10000000000000H", "PT-1234567890123456789012S") + test(Duration.INFINITE, "PT9999999999999H", "PT+10000000000000H", "-PT-9999999999999H", "-PT-1234567890123456789012S", "PT+000000000000000001234567890123456789012H") + test(-Duration.INFINITE, "-PT9999999999999H", "-PT10000000000000H", "PT-1234567890123456789012S", "PT-000000000000000001234567890123456789012H") } @Test @@ -600,6 +600,7 @@ class DurationTest { "PT1S2S", "PT1S2H", "P9999999999999DT-9999999999999H", "PT1.5H", "PT0.5D", "PT.5S", "PT0.25.25S", + "PT+-2H", "PT-+2H", "PT+-01234567890123456S" )) { assertNull(Duration.parseIsoStringOrNull(invalidValue), invalidValue) assertFailsWith(invalidValue) { Duration.parseIsoString(invalidValue) }.let { e ->