Change Duration.toString format KT-42851

This commit is contained in:
Ilya Gorbunov
2021-06-27 03:09:53 +03:00
committed by Space
parent 804db3ce91
commit 42cd2e65e6
3 changed files with 89 additions and 62 deletions
@@ -25,12 +25,12 @@ class Durations {
@Sample @Sample
fun toStringDefault() { fun toStringDefault() {
assertPrints(Duration.days(45), "45.0d") assertPrints(Duration.days(45), "1080h")
assertPrints(Duration.days(1.5), "36.0h") assertPrints(Duration.days(1.5), "36h")
assertPrints(Duration.minutes(1230), "20.5h") assertPrints(Duration.minutes(1230), "20h 30m")
assertPrints(Duration.minutes(920), "920m") assertPrints(Duration.minutes(920), "15h 20m")
assertPrints(Duration.seconds(1.546), "1.55s") assertPrints(Duration.seconds(1.546), "1.546s")
assertPrints(Duration.milliseconds(25.12), "25.1ms") assertPrints(Duration.milliseconds(25.12), "0.025120s")
} }
@Sample @Sample
+50 -26
View File
@@ -639,15 +639,25 @@ public value class Duration internal constructor(private val rawValue: Long) : C
public fun toLongMilliseconds(): Long = inWholeMilliseconds public fun toLongMilliseconds(): Long = inWholeMilliseconds
/** /**
* Returns a string representation of this duration value expressed in the unit which yields the most compact and readable number value. * Returns a string representation of this duration value
* expressed as a combination of numeric components, each in its own unit.
*
* Each component is a number followed by the unit abbreviated name: `d`, `h`, `m`, `s`:
* `5h`, `1d 12h`, `1h 0m 30.3340s`.
* The last component, usually seconds, can be a number with a fractional part.
*
* If the duration is less than a second, it is represented as a single number
* with one of sub-second units: `ms` (milliseconds), `us` (microseconds), or `ns` (nanoseconds):
* `140.884ms`, `500us`, `24ns`.
*
* A negative duration is prefixed with `-` sign and, if it consists of multiple components, surrounded with parentheses:
* `-12m` and `-(1h 30m)`.
* *
* Special cases: * Special cases:
* - zero duration is formatted as `"0s"` * - an infinite duration is formatted as `"Infinity"` or `"-Infinity"` without a unit.
* - the infinite duration is formatted as `"Infinity"` without unit
* - very small durations (less than 1e-15 s) are expressed in seconds and formatted in scientific notation
* - very big durations (more than 1e+7 days) are expressed in days and formatted in scientific notation
* *
* @return the value of duration in the automatically determined unit followed by that unit abbreviated name: `d`, `h`, `m`, `s`, `ms`, `us`, or `ns`. * It's recommended to use [toIsoString] that uses more strict ISO-8601 format instead of this `toString`
* when you want to convert a duration to a string in cases of serialization, interchange, etc.
* *
* @sample samples.time.Durations.toStringDefault * @sample samples.time.Durations.toStringDefault
*/ */
@@ -656,27 +666,41 @@ 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 absNs = absoluteValue.toDouble(DurationUnit.NANOSECONDS) buildString {
var scientific = false if (isNegative()) append('-')
var maxDecimals = 0 absoluteValue.run {
val unit = when { toComponents { _, minutes, seconds, nanoseconds ->
absNs < 1e-6 -> DurationUnit.SECONDS.also { scientific = true } val hours = inWholeHours
absNs < 1 -> DurationUnit.NANOSECONDS.also { maxDecimals = 7 } val hasHours = hours != 0L
absNs < 1e3 -> DurationUnit.NANOSECONDS val hasSeconds = seconds != 0 || nanoseconds != 0
absNs < 1e6 -> DurationUnit.MICROSECONDS if (hasHours) {
absNs < 1e9 -> DurationUnit.MILLISECONDS append(hours)
absNs < 1000e9 -> DurationUnit.SECONDS append('h')
absNs < 60_000e9 -> DurationUnit.MINUTES }
absNs < 3600_000e9 -> DurationUnit.HOURS if (minutes != 0 || (hasSeconds && hasHours)) {
absNs < 86400e9 * 1e7 -> DurationUnit.DAYS if (length > 1) append(' ')
else -> DurationUnit.DAYS.also { scientific = true } append(minutes)
append('m')
}
if (hasSeconds) {
if (length > 1) append(' ')
append(seconds)
if (nanoseconds != 0) {
append('.')
when {
(nanoseconds % 1_000_000 == 0) ->
append((nanoseconds / 1_000_000).toString().padStart(3, '0'))
(nanoseconds % 1_000 == 0) ->
append((nanoseconds / 1_000).toString().padStart(6, '0'))
else ->
append(nanoseconds.toString().padStart(9, '0'))
}
}
append('s')
}
}
}
} }
val value = toDouble(unit)
when {
scientific -> formatScientific(value)
maxDecimals > 0 -> formatUpToDecimals(value, maxDecimals)
else -> formatToExactDecimals(value, precision(abs(value)))
} + unit.shortName()
} }
} }
+33 -30
View File
@@ -543,40 +543,43 @@ class DurationTest {
assertEquals("-$actual", (-duration).toString()) assertEquals("-$actual", (-duration).toString())
} }
test(Duration.days(101), "101d") test(Duration.days(101), "2424h")
test(Duration.days(45.3), "45.3d") test(Duration.days(45.3), "1087h 12m") // 1080h + 7.2h
test(Duration.days(45), "45.0d") test(Duration.days(45), "1080h")
test(Duration.days(40.5), "972h") test(Duration.days(40.5), "972h")
test(Duration.hours(40) + Duration.minutes(15), "40.3h", "40.2h") test(Duration.hours(40) + Duration.minutes(15), "40h 15m")
test(Duration.hours(40), "40.0h") test(Duration.hours(40), "40h")
test(Duration.hours(12.5), "750m") test(Duration.hours(12.5), "12h 30m")
test(Duration.minutes(30), "30.0m") test(Duration.hours(12) + Duration.seconds(15), "12h 0m 15s")
test(Duration.minutes(17.5), "17.5m") test(Duration.hours(12) + Duration.nanoseconds(1), "12h 0m 0.000000001s")
test(Duration.minutes(30), "30m")
test(Duration.minutes(17.5), "17m 30s")
test(Duration.minutes(16.5), "990s") test(Duration.minutes(16.5), "16m 30s")
test(Duration.seconds(90.36), "90.4s") test(Duration.seconds(1097.1), "18m 17.099999999s") // Double to Long ns truncation!
test(Duration.seconds(50), "50.0s") test(Duration.seconds(90.36), "1m 30.360s")
test(Duration.seconds(1.3), "1.30s") test(Duration.seconds(50), "50s")
test(Duration.seconds(1), "1.00s") test(Duration.seconds(1.3), "1.300s")
test(Duration.seconds(1), "1s")
test(Duration.seconds(0.5), "500ms") test(Duration.seconds(0.5), "0.500s")
test(Duration.milliseconds(40.2), "40.2ms") test(Duration.milliseconds(40.2), "0.040200s")
test(Duration.milliseconds(4.225), "4.23ms", "4.22ms") test(Duration.milliseconds(4.225), "0.004225s")
test(Duration.milliseconds(4.245), "4.25ms") test(Duration.milliseconds(4.245), "0.004245s")
test(Duration.milliseconds(1), "1.00ms") test(Duration.milliseconds(1), "0.001s")
test(Duration.milliseconds(0.75), "750us") test(Duration.milliseconds(0.75), "0.000750s")
test(Duration.microseconds(75.35), "75.4us", "75.3us") test(Duration.microseconds(75.35), "0.000075350s")
test(Duration.microseconds(7.25), "7.25us") test(Duration.microseconds(7.25), "0.000007250s")
test(Duration.microseconds(1.035), "1.04us", "1.03us") test(Duration.microseconds(1.035), "0.000001035s")
test(Duration.microseconds(1.005), "1.01us", "1.00us") test(Duration.microseconds(1.005), "0.000001004s") // Double to Long ns truncation!
test(Duration.nanoseconds(950.5), "951ns", "950ns") test(Duration.nanoseconds(950.5), "0.000000950s")
test(Duration.nanoseconds(85.23), "85.0ns") test(Duration.nanoseconds(85.23), "0.000000085s")
test(Duration.nanoseconds(8.235), "8.00ns") test(Duration.nanoseconds(8.235), "0.000000008s")
test(Duration.nanoseconds(1.3), "1.00ns") test(Duration.nanoseconds(1.3), "0.000000001s")
// equal to zero // equal to zero
// test(Duration.nanoseconds(0.75), "0.75ns") // test(Duration.nanoseconds(0.75), "0.75ns")
@@ -586,9 +589,9 @@ class DurationTest {
// test(Duration.nanoseconds(0.0000035), "0.0000035ns") // test(Duration.nanoseconds(0.0000035), "0.0000035ns")
test(Duration.ZERO, "0s") test(Duration.ZERO, "0s")
test(Duration.days(365) * 10000, "3650000d") test(Duration.days(365) * 10000, "87600000h")
test(Duration.days(300) * 100000, "3.00e+7d") test(Duration.days(300) * 100000, "720000000h")
test(Duration.days(365) * 100000, "3.65e+7d") test(Duration.days(365) * 100000, "876000000h")
// all infinite // all infinite
// val universeAge = Duration.days(365.25) * 13.799e9 // val universeAge = Duration.days(365.25) * 13.799e9