diff --git a/libraries/stdlib/src/kotlin/time/Duration.kt b/libraries/stdlib/src/kotlin/time/Duration.kt index 173d885aa1b..3812c4dd012 100644 --- a/libraries/stdlib/src/kotlin/time/Duration.kt +++ b/libraries/stdlib/src/kotlin/time/Duration.kt @@ -628,6 +628,17 @@ public value class Duration internal constructor(private val rawValue: Long) : C return this.toDouble(coarserUnit) / other.toDouble(coarserUnit) } + /** + * Returns a duration whose value is this duration value truncated to the specified duration [unit]. + */ + internal fun truncateTo(unit: DurationUnit): Duration { + val storageUnit = storageUnit + if (unit <= storageUnit || this.isInfinite()) return this + val scale = convertDurationUnit(1, unit, storageUnit) + val result = value - value % scale + return result.toDuration(storageUnit) + } + /** Returns true, if the duration value is less than zero. */ public fun isNegative(): Boolean = rawValue < 0 diff --git a/libraries/stdlib/test/time/DurationTest.kt b/libraries/stdlib/test/time/DurationTest.kt index e791e525ea1..10298b7e838 100644 --- a/libraries/stdlib/test/time/DurationTest.kt +++ b/libraries/stdlib/test/time/DurationTest.kt @@ -496,6 +496,50 @@ class DurationTest { assertTrue((Duration.ZERO / Duration.ZERO).isNaN()) } + @Test + fun truncation() { + fun expect(expected: Duration, value: Duration, unit: DurationUnit) { + assertEquals(expected, value.truncateTo(unit)) + assertEquals(-expected, (-value).truncateTo(unit)) + } + for (unit in units) { + expect(Duration.ZERO, Duration.ZERO, unit) + expect(Duration.INFINITE, Duration.INFINITE, unit) + expect(Duration.ZERO, 1.toDuration(unit) - 1.nanoseconds, unit) + repeat(100) { + val whole = Random.nextInt(100_000).toDuration(unit) + expect(whole, whole, unit) + if (unit > DurationUnit.NANOSECONDS) { + val part = Random.nextLong(1, 1.toDuration(unit).inWholeNanoseconds).nanoseconds + expect(Duration.ZERO, part, unit) + expect(whole, whole + part, unit) + } + } + } + repeat(10) { + val d = Random.nextLong().nanoseconds + expect(d, d, DurationUnit.NANOSECONDS) + } + expect(12.microseconds, 12998.nanoseconds, DurationUnit.MICROSECONDS) + expect(1503.milliseconds, 1503_889_404.nanoseconds, DurationUnit.MILLISECONDS) + expect(340.seconds, 340_990_567_444L.nanoseconds, DurationUnit.SECONDS) + expect(3.minutes, 200.seconds, DurationUnit.MINUTES) + expect(4.hours, 250.minutes, DurationUnit.HOURS) + expect(1.days, 30.hours, DurationUnit.DAYS) + + // big durations + run { + val d = (Long.MAX_VALUE / 4).milliseconds + for (unit in units) { + if (unit <= DurationUnit.MILLISECONDS) { + expect(d, d, unit) + } else { + expect(d.toLong(unit).toDuration(unit), d, unit) + } + } + } + } + @Test fun parseAndFormatIsoString() { fun test(duration: Duration, vararg isoStrings: String) {