[WASM] Implement std kotlin.time

This commit is contained in:
Igor Yakovlev
2022-01-25 13:03:23 +01:00
parent 1052ebb44d
commit ece0f83ab2
3 changed files with 95 additions and 26 deletions
@@ -1,9 +1,38 @@
/* /*
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors. * Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/ */
package kotlin.time package kotlin.time
import kotlin.math.*
internal actual inline val durationAssertionsEnabled: Boolean get() = true internal actual inline val durationAssertionsEnabled: Boolean get() = true
internal actual fun formatToExactDecimals(value: Double, decimals: Int): String = TODO("Wasm stdlib: Duration")
internal actual fun formatUpToDecimals(value: Double, decimals: Int): String = TODO("Wasm stdlib: Duration") @JsFun("(value, decimals) => value.toFixed(decimals)")
private external fun toFixed(value: Double, decimals: Int): String
@JsFun("(value, decimals) => value.toPrecision(decimals)")
private external fun toPrecision(value: Double, decimals: Int): String
internal actual fun formatToExactDecimals(value: Double, decimals: Int): String {
val rounded = if (decimals == 0) {
value
} else {
val pow = (10.0).pow(decimals)
round(abs(value) * pow) / pow * sign(value)
round(abs(value) * pow) / pow * sign(value)
}
return if (abs(rounded) < 1e21) {
// toFixed switches to scientific format after 1e21
toFixed(rounded, decimals)
} else {
// toPrecision outputs the specified number of digits, but only for positive numbers
val positive = abs(rounded)
val positiveString = toPrecision(positive, ceil(log10(positive)).toInt() + decimals)
if (rounded < 0) "-$positiveString" else positiveString
}
}
@JsFun("(value, decimals) => value.toLocaleString(\"en-us\", ({\"maximumFractionDigits\": decimals}))")
external internal actual fun formatUpToDecimals(value: Double, decimals: Int): String
@@ -1,55 +1,77 @@
/* /*
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors. * Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/ */
package kotlin.time package kotlin.time
/**
* The list of possible time measurement units, in which a duration can be expressed.
*
* The smallest time unit is [NANOSECONDS] and the largest is [DAYS], which corresponds to exactly 24 [HOURS].
*/
@SinceKotlin("1.6") @SinceKotlin("1.6")
@WasExperimental(ExperimentalTime::class) @WasExperimental(ExperimentalTime::class)
public actual enum class DurationUnit { public actual enum class DurationUnit(internal val scale: Double) {
/** /**
* Time unit representing one nanosecond, which is 1/1000 of a microsecond. * Time unit representing one nanosecond, which is 1/1000 of a microsecond.
*/ */
NANOSECONDS, NANOSECONDS(1e0),
/** /**
* Time unit representing one microsecond, which is 1/1000 of a millisecond. * Time unit representing one microsecond, which is 1/1000 of a millisecond.
*/ */
MICROSECONDS, MICROSECONDS(1e3),
/** /**
* Time unit representing one millisecond, which is 1/1000 of a second. * Time unit representing one millisecond, which is 1/1000 of a second.
*/ */
MILLISECONDS, MILLISECONDS(1e6),
/** /**
* Time unit representing one second. * Time unit representing one second.
*/ */
SECONDS, SECONDS(1e9),
/** /**
* Time unit representing one minute. * Time unit representing one minute.
*/ */
MINUTES, MINUTES(60e9),
/** /**
* Time unit representing one hour. * Time unit representing one hour.
*/ */
HOURS, HOURS(3600e9),
/** /**
* Time unit representing one day, which is always equal to 24 hours. * Time unit representing one day, which is always equal to 24 hours.
*/ */
DAYS; DAYS(86400e9);
} }
/** Converts the given time duration [value] expressed in the specified [sourceUnit] into the specified [targetUnit]. */
@SinceKotlin("1.3") @SinceKotlin("1.3")
internal actual fun convertDurationUnit(value: Double, sourceUnit: DurationUnit, targetUnit: DurationUnit): Double = TODO("Wasm stdlib: convertDurationUnit Double") internal actual fun convertDurationUnit(value: Double, sourceUnit: DurationUnit, targetUnit: DurationUnit): Double {
val sourceCompareTarget = sourceUnit.scale.compareTo(targetUnit.scale)
return when {
sourceCompareTarget > 0 -> value * (sourceUnit.scale / targetUnit.scale)
sourceCompareTarget < 0 -> value / (targetUnit.scale / sourceUnit.scale)
else -> value
}
}
@SinceKotlin("1.5") @SinceKotlin("1.5")
internal actual fun convertDurationUnitOverflow(value: Long, sourceUnit: DurationUnit, targetUnit: DurationUnit): Long = TODO("Wasm stdlib: convertDurationUnitOverflow Long") internal actual fun convertDurationUnitOverflow(value: Long, sourceUnit: DurationUnit, targetUnit: DurationUnit): Long {
val sourceCompareTarget = sourceUnit.scale.compareTo(targetUnit.scale)
return when {
sourceCompareTarget > 0 -> value * (sourceUnit.scale / targetUnit.scale).toLong()
sourceCompareTarget < 0 -> value / (targetUnit.scale / sourceUnit.scale).toLong()
else -> value
}
}
@SinceKotlin("1.5") @SinceKotlin("1.5")
internal actual fun convertDurationUnit(value: Long, sourceUnit: DurationUnit, targetUnit: DurationUnit): Long = TODO("Wasm stdlib: convertDurationUnit Long") internal actual fun convertDurationUnit(value: Long, sourceUnit: DurationUnit, targetUnit: DurationUnit): Long {
val sourceCompareTarget = sourceUnit.scale.compareTo(targetUnit.scale)
return when {
sourceCompareTarget > 0 -> {
val scale = (sourceUnit.scale / targetUnit.scale).toLong()
val result = value * scale
when {
result / scale == value -> result
value > 0 -> Long.MAX_VALUE
else -> Long.MIN_VALUE
}
}
sourceCompareTarget < 0 -> value / (targetUnit.scale / sourceUnit.scale).toLong()
else -> value
}
}
@@ -1,11 +1,29 @@
/* /*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors. * Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/ */
package kotlin.time package kotlin.time
import kotlin.wasm.internal.ExternalInterfaceType
@JsFun("() => typeof globalThis !== 'undefined' && typeof globalThis.performance !== 'undefined' ? globalThis.performance : null")
private external fun tryGetPerformance(): ExternalInterfaceType?
@JsFun("(performance) => performance.now()")
private external fun getPerformanceNow(performance: ExternalInterfaceType): Double
@JsFun("() => Date.now()")
private external fun dateNow(): Double
@SinceKotlin("1.3") @SinceKotlin("1.3")
@ExperimentalTime @ExperimentalTime
internal actual object MonotonicTimeSource : TimeSource { internal actual object MonotonicTimeSource : TimeSource, AbstractDoubleTimeSource(unit = DurationUnit.MILLISECONDS) {
override fun markNow(): TimeMark = TODO("Wasm stdlib: MonotonicTimeSource::markNow") private val performance: ExternalInterfaceType? = tryGetPerformance()
}
override fun read(): Double =
if (performance != null) getPerformanceNow(performance) else dateNow()
override fun toString(): String =
if (performance != null) "TimeSource(globalThis.performance.now())" else "TimeSource(Date.now())"
}