Duration: longer than long values in ISO components, test negative cases

This commit is contained in:
Ilya Gorbunov
2021-07-05 21:24:24 +03:00
committed by Space
parent 1c6ab08220
commit ca1a9e4ca3
2 changed files with 91 additions and 33 deletions
+26 -13
View File
@@ -7,10 +7,7 @@ package kotlin.time
import kotlin.contracts.*
import kotlin.jvm.JvmInline
import kotlin.math.abs
import kotlin.math.roundToInt
import kotlin.math.roundToLong
import kotlin.math.sign
import kotlin.math.*
/**
* Represents the amount of time one instant of time is away from another instant.
@@ -1080,13 +1077,13 @@ private fun parseDuration(value: String, strictIso: Boolean): Duration {
length <= index ->
throw IllegalArgumentException()
value[index] == 'P' -> {
index++
if (++index == length) throw IllegalArgumentException()
val signedDigits = "+-0123456789."
var isTimeComponent = false
var prevUnit: DurationUnit? = null
while (index < length) {
if (value[index] == 'T') {
if (isTimeComponent || index++ == length) throw IllegalArgumentException()
if (isTimeComponent || ++index == length) throw IllegalArgumentException()
isTimeComponent = true
continue
}
@@ -1098,11 +1095,13 @@ private fun parseDuration(value: String, strictIso: Boolean): Duration {
val unit = durationUnitByIsoChar(unitChar, isTimeComponent)
if (prevUnit != null && prevUnit <= unit) throw IllegalArgumentException("Unexpected order of duration components")
prevUnit = unit
if (unit == DurationUnit.SECONDS && '.' in component) {
// TODO: parse fractional part separately
result += component.toDouble().toDuration(unit)
val dotIndex = component.indexOf('.')
if (unit == DurationUnit.SECONDS && dotIndex > 0) {
val whole = component.substring(0, dotIndex)
result += parseOverLongIsoComponent(whole).toDuration(unit)
result += component.substring(dotIndex).toDouble().toDuration(unit)
} else {
result += component.toLong().toDuration(unit)
result += parseOverLongIsoComponent(component).toDuration(unit)
}
}
}
@@ -1129,9 +1128,11 @@ private fun parseDuration(value: String, strictIso: Boolean): Duration {
val unit = durationUnitByShortName(unitName)
if (prevUnit != null && prevUnit <= unit) throw IllegalArgumentException("Unexpected order of duration components")
prevUnit = unit
if ('.' in component) {
// TODO: parse fractional part separately
result += component.toDouble().toDuration(unit)
val dotIndex = component.indexOf('.')
if (dotIndex > 0) {
val whole = component.substring(0, dotIndex)
result += whole.toLong().toDuration(unit)
result += component.substring(dotIndex).toDouble().toDuration(unit)
if (index < length) throw IllegalArgumentException("Fractional component must be last")
} else {
result += component.toLong().toDuration(unit)
@@ -1143,6 +1144,18 @@ private fun parseDuration(value: String, strictIso: Boolean): Duration {
}
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
}
return value.toLong()
}
private inline fun String.substringWhile(startIndex: Int, predicate: (Char) -> Boolean): String =
substring(startIndex, skipWhile(startIndex, predicate))