Duration: parse and format negative values parenthesized
This commit is contained in:
@@ -721,8 +721,9 @@ public value class Duration internal constructor(private val rawValue: Long) : C
|
|||||||
INFINITE.rawValue -> "Infinity"
|
INFINITE.rawValue -> "Infinity"
|
||||||
NEG_INFINITE.rawValue -> "-Infinity"
|
NEG_INFINITE.rawValue -> "-Infinity"
|
||||||
else -> {
|
else -> {
|
||||||
|
val isNegative = isNegative()
|
||||||
buildString {
|
buildString {
|
||||||
if (isNegative()) append('-')
|
if (isNegative) append('-')
|
||||||
absoluteValue.run {
|
absoluteValue.run {
|
||||||
toComponents { _, hours, minutes, seconds, nanoseconds ->
|
toComponents { _, hours, minutes, seconds, nanoseconds ->
|
||||||
val days = inWholeDays
|
val days = inWholeDays
|
||||||
@@ -730,22 +731,21 @@ public value class Duration internal constructor(private val rawValue: Long) : C
|
|||||||
val hasHours = hours != 0
|
val hasHours = hours != 0
|
||||||
val hasMinutes = minutes != 0
|
val hasMinutes = minutes != 0
|
||||||
val hasSeconds = seconds != 0 || nanoseconds != 0
|
val hasSeconds = seconds != 0 || nanoseconds != 0
|
||||||
|
var components = 0
|
||||||
if (hasDays) {
|
if (hasDays) {
|
||||||
append(days)
|
append(days).append('d')
|
||||||
append('d')
|
components++
|
||||||
}
|
}
|
||||||
if (hasHours || (hasDays && (hasMinutes || hasSeconds))) {
|
if (hasHours || (hasDays && (hasMinutes || hasSeconds))) {
|
||||||
if (length > 1) append(' ')
|
if (components++ > 0) append(' ')
|
||||||
append(hours)
|
append(hours).append('h')
|
||||||
append('h')
|
|
||||||
}
|
}
|
||||||
if (hasMinutes || (hasSeconds && (hasHours || hasDays))) {
|
if (hasMinutes || (hasSeconds && (hasHours || hasDays))) {
|
||||||
if (length > 1) append(' ')
|
if (components++ > 0) append(' ')
|
||||||
append(minutes)
|
append(minutes).append('m')
|
||||||
append('m')
|
|
||||||
}
|
}
|
||||||
if (hasSeconds) {
|
if (hasSeconds) {
|
||||||
if (length > 1) append(' ')
|
if (components++ > 0) append(' ')
|
||||||
when {
|
when {
|
||||||
seconds != 0 || hasDays || hasHours || hasMinutes ->
|
seconds != 0 || hasDays || hasHours || hasMinutes ->
|
||||||
appendFractional(seconds, nanoseconds, "s")
|
appendFractional(seconds, nanoseconds, "s")
|
||||||
@@ -757,6 +757,7 @@ public value class Duration internal constructor(private val rawValue: Long) : C
|
|||||||
append(nanoseconds).append("ns")
|
append(nanoseconds).append("ns")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (isNegative && components > 1) insert(1, '(').append(')')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1065,7 +1066,7 @@ public inline operator fun Double.times(duration: Duration): Duration = duration
|
|||||||
|
|
||||||
@ExperimentalTime
|
@ExperimentalTime
|
||||||
private fun parseDuration(value: String, strictIso: Boolean): Duration {
|
private fun parseDuration(value: String, strictIso: Boolean): Duration {
|
||||||
val length = value.length
|
var length = value.length
|
||||||
if (length == 0) throw IllegalArgumentException("The string is empty")
|
if (length == 0) throw IllegalArgumentException("The string is empty")
|
||||||
var index = 0
|
var index = 0
|
||||||
var result = Duration.ZERO
|
var result = Duration.ZERO
|
||||||
@@ -1073,9 +1074,10 @@ private fun parseDuration(value: String, strictIso: Boolean): Duration {
|
|||||||
when (value[index]) {
|
when (value[index]) {
|
||||||
'+', '-' -> index++
|
'+', '-' -> index++
|
||||||
}
|
}
|
||||||
|
val isNegative = value.startsWith('-')
|
||||||
when {
|
when {
|
||||||
length <= index ->
|
length <= index ->
|
||||||
throw IllegalArgumentException()
|
throw IllegalArgumentException("No components")
|
||||||
value[index] == 'P' -> {
|
value[index] == 'P' -> {
|
||||||
if (++index == length) throw IllegalArgumentException()
|
if (++index == length) throw IllegalArgumentException()
|
||||||
val signedDigits = "+-0123456789."
|
val signedDigits = "+-0123456789."
|
||||||
@@ -1115,8 +1117,13 @@ private fun parseDuration(value: String, strictIso: Boolean): Duration {
|
|||||||
val digits = "0123456789."
|
val digits = "0123456789."
|
||||||
var prevUnit: DurationUnit? = null
|
var prevUnit: DurationUnit? = null
|
||||||
var afterFirst = false
|
var afterFirst = false
|
||||||
|
var allowSpaces = !isNegative
|
||||||
|
if (isNegative && value[index] == '(' && value.last() == ')') {
|
||||||
|
allowSpaces = true
|
||||||
|
if (++index == --length) throw IllegalArgumentException("No components")
|
||||||
|
}
|
||||||
while (index < length) {
|
while (index < length) {
|
||||||
if (afterFirst) {
|
if (afterFirst && allowSpaces) {
|
||||||
index = value.skipWhile(index) { it == ' ' }
|
index = value.skipWhile(index) { it == ' ' }
|
||||||
}
|
}
|
||||||
afterFirst = true
|
afterFirst = true
|
||||||
@@ -1140,7 +1147,7 @@ private fun parseDuration(value: String, strictIso: Boolean): Duration {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return if (value.startsWith('-')) -result else result
|
return if (isNegative) -result else result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -564,17 +564,30 @@ class DurationTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun parseAndFormatDefault() {
|
fun parseAndFormatDefault() {
|
||||||
|
fun testParsing(string: String, expectedDuration: Duration) {
|
||||||
|
assertEquals(expectedDuration, Duration.parse(string), string)
|
||||||
|
assertEquals(expectedDuration, Duration.parseOrNull(string), string)
|
||||||
|
}
|
||||||
|
|
||||||
fun test(duration: Duration, vararg expected: String) {
|
fun test(duration: Duration, vararg expected: String) {
|
||||||
val actual = duration.toString()
|
val actual = duration.toString()
|
||||||
assertEquals(expected.first(), actual)
|
assertEquals(expected.first(), actual)
|
||||||
|
|
||||||
if (duration > Duration.ZERO) {
|
if (duration.isPositive()) {
|
||||||
assertEquals("-$actual", (-duration).toString())
|
if (' ' in actual) {
|
||||||
|
assertEquals("-($actual)", (-duration).toString())
|
||||||
|
} else {
|
||||||
|
assertEquals("-$actual", (-duration).toString())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (string in expected) {
|
for (string in expected) {
|
||||||
assertEquals(duration, Duration.parse(string), string)
|
testParsing(string, duration)
|
||||||
assertEquals(duration, Duration.parseOrNull(string), string)
|
if (duration.isPositive() && duration.isFinite()) {
|
||||||
|
testParsing("-($string)", -duration)
|
||||||
|
if (' ' !in string)
|
||||||
|
testParsing("-$string", -duration)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -642,7 +655,8 @@ class DurationTest {
|
|||||||
// test(universeAge, "5.04e+12d")
|
// test(universeAge, "5.04e+12d")
|
||||||
// test(planckTime, "5.40e-44s")
|
// test(planckTime, "5.40e-44s")
|
||||||
// test(Duration.nanoseconds(Double.MAX_VALUE), "2.08e+294d")
|
// test(Duration.nanoseconds(Double.MAX_VALUE), "2.08e+294d")
|
||||||
test(Duration.INFINITE, "Infinity")
|
test(Duration.INFINITE, "Infinity", "53375995583d 20h", "+Infinity")
|
||||||
|
test(-Duration.INFINITE, "-Infinity", "-(53375995583d 20h)")
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -652,7 +666,7 @@ class DurationTest {
|
|||||||
"1234567890123456789012ns", "Inf", "-Infinity value",
|
"1234567890123456789012ns", "Inf", "-Infinity value",
|
||||||
"1s ", " 1s",
|
"1s ", " 1s",
|
||||||
"1d 1m 1h", "1s 2s",
|
"1d 1m 1h", "1s 2s",
|
||||||
"-12m -15s",
|
"-12m 15s", "-12m -15s", "(12m 30s)", "-()", "()", "-(12m 30s",
|
||||||
"12.5m 11.5s", ".2s", "0.1553.39m",
|
"12.5m 11.5s", ".2s", "0.1553.39m",
|
||||||
"P+12+34D", "P12-34D", "PT1234567890-1234567890S",
|
"P+12+34D", "P12-34D", "PT1234567890-1234567890S",
|
||||||
" P1D", "PT1S ",
|
" P1D", "PT1S ",
|
||||||
|
|||||||
Reference in New Issue
Block a user