[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.
*/
package kotlin.time
import kotlin.math.*
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.
*/
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")
@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.
*/
NANOSECONDS,
NANOSECONDS(1e0),
/**
* 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.
*/
MILLISECONDS,
MILLISECONDS(1e6),
/**
* Time unit representing one second.
*/
SECONDS,
SECONDS(1e9),
/**
* Time unit representing one minute.
*/
MINUTES,
MINUTES(60e9),
/**
* Time unit representing one hour.
*/
HOURS,
HOURS(3600e9),
/**
* 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")
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")
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")
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.
*/
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")
@ExperimentalTime
internal actual object MonotonicTimeSource : TimeSource {
override fun markNow(): TimeMark = TODO("Wasm stdlib: MonotonicTimeSource::markNow")
}
internal actual object MonotonicTimeSource : TimeSource, AbstractDoubleTimeSource(unit = DurationUnit.MILLISECONDS) {
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())"
}