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
fun toStringDefault() {
assertPrints(Duration.days(45), "45.0d")
assertPrints(Duration.days(1.5), "36.0h")
assertPrints(Duration.minutes(1230), "20.5h")
assertPrints(Duration.minutes(920), "920m")
assertPrints(Duration.seconds(1.546), "1.55s")
assertPrints(Duration.milliseconds(25.12), "25.1ms")
assertPrints(Duration.days(45), "1080h")
assertPrints(Duration.days(1.5), "36h")
assertPrints(Duration.minutes(1230), "20h 30m")
assertPrints(Duration.minutes(920), "15h 20m")
assertPrints(Duration.seconds(1.546), "1.546s")
assertPrints(Duration.milliseconds(25.12), "0.025120s")
}
@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
/**
* 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:
* - zero duration is formatted as `"0s"`
* - 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
* - an infinite duration is formatted as `"Infinity"` or `"-Infinity"` without a unit.
*
* @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
*/
@@ -656,27 +666,41 @@ public value class Duration internal constructor(private val rawValue: Long) : C
INFINITE.rawValue -> "Infinity"
NEG_INFINITE.rawValue -> "-Infinity"
else -> {
val absNs = absoluteValue.toDouble(DurationUnit.NANOSECONDS)
var scientific = false
var maxDecimals = 0
val unit = when {
absNs < 1e-6 -> DurationUnit.SECONDS.also { scientific = true }
absNs < 1 -> DurationUnit.NANOSECONDS.also { maxDecimals = 7 }
absNs < 1e3 -> DurationUnit.NANOSECONDS
absNs < 1e6 -> DurationUnit.MICROSECONDS
absNs < 1e9 -> DurationUnit.MILLISECONDS
absNs < 1000e9 -> DurationUnit.SECONDS
absNs < 60_000e9 -> DurationUnit.MINUTES
absNs < 3600_000e9 -> DurationUnit.HOURS
absNs < 86400e9 * 1e7 -> DurationUnit.DAYS
else -> DurationUnit.DAYS.also { scientific = true }
buildString {
if (isNegative()) append('-')
absoluteValue.run {
toComponents { _, minutes, seconds, nanoseconds ->
val hours = inWholeHours
val hasHours = hours != 0L
val hasSeconds = seconds != 0 || nanoseconds != 0
if (hasHours) {
append(hours)
append('h')
}
if (minutes != 0 || (hasSeconds && hasHours)) {
if (length > 1) append(' ')
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())
}
test(Duration.days(101), "101d")
test(Duration.days(45.3), "45.3d")
test(Duration.days(45), "45.0d")
test(Duration.days(101), "2424h")
test(Duration.days(45.3), "1087h 12m") // 1080h + 7.2h
test(Duration.days(45), "1080h")
test(Duration.days(40.5), "972h")
test(Duration.hours(40) + Duration.minutes(15), "40.3h", "40.2h")
test(Duration.hours(40), "40.0h")
test(Duration.hours(40) + Duration.minutes(15), "40h 15m")
test(Duration.hours(40), "40h")
test(Duration.hours(12.5), "750m")
test(Duration.minutes(30), "30.0m")
test(Duration.minutes(17.5), "17.5m")
test(Duration.hours(12.5), "12h 30m")
test(Duration.hours(12) + Duration.seconds(15), "12h 0m 15s")
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.seconds(90.36), "90.4s")
test(Duration.seconds(50), "50.0s")
test(Duration.seconds(1.3), "1.30s")
test(Duration.seconds(1), "1.00s")
test(Duration.minutes(16.5), "16m 30s")
test(Duration.seconds(1097.1), "18m 17.099999999s") // Double to Long ns truncation!
test(Duration.seconds(90.36), "1m 30.360s")
test(Duration.seconds(50), "50s")
test(Duration.seconds(1.3), "1.300s")
test(Duration.seconds(1), "1s")
test(Duration.seconds(0.5), "500ms")
test(Duration.milliseconds(40.2), "40.2ms")
test(Duration.milliseconds(4.225), "4.23ms", "4.22ms")
test(Duration.milliseconds(4.245), "4.25ms")
test(Duration.milliseconds(1), "1.00ms")
test(Duration.seconds(0.5), "0.500s")
test(Duration.milliseconds(40.2), "0.040200s")
test(Duration.milliseconds(4.225), "0.004225s")
test(Duration.milliseconds(4.245), "0.004245s")
test(Duration.milliseconds(1), "0.001s")
test(Duration.milliseconds(0.75), "750us")
test(Duration.microseconds(75.35), "75.4us", "75.3us")
test(Duration.microseconds(7.25), "7.25us")
test(Duration.microseconds(1.035), "1.04us", "1.03us")
test(Duration.microseconds(1.005), "1.01us", "1.00us")
test(Duration.milliseconds(0.75), "0.000750s")
test(Duration.microseconds(75.35), "0.000075350s")
test(Duration.microseconds(7.25), "0.000007250s")
test(Duration.microseconds(1.035), "0.000001035s")
test(Duration.microseconds(1.005), "0.000001004s") // Double to Long ns truncation!
test(Duration.nanoseconds(950.5), "951ns", "950ns")
test(Duration.nanoseconds(85.23), "85.0ns")
test(Duration.nanoseconds(8.235), "8.00ns")
test(Duration.nanoseconds(1.3), "1.00ns")
test(Duration.nanoseconds(950.5), "0.000000950s")
test(Duration.nanoseconds(85.23), "0.000000085s")
test(Duration.nanoseconds(8.235), "0.000000008s")
test(Duration.nanoseconds(1.3), "0.000000001s")
// equal to zero
// test(Duration.nanoseconds(0.75), "0.75ns")
@@ -586,9 +589,9 @@ class DurationTest {
// test(Duration.nanoseconds(0.0000035), "0.0000035ns")
test(Duration.ZERO, "0s")
test(Duration.days(365) * 10000, "3650000d")
test(Duration.days(300) * 100000, "3.00e+7d")
test(Duration.days(365) * 100000, "3.65e+7d")
test(Duration.days(365) * 10000, "87600000h")
test(Duration.days(300) * 100000, "720000000h")
test(Duration.days(365) * 100000, "876000000h")
// all infinite
// val universeAge = Duration.days(365.25) * 13.799e9