From 1da22b60a249f1f3ce1d941fd4e8d20b84681c8d Mon Sep 17 00:00:00 2001 From: t0m3x Date: Fri, 23 Aug 2024 12:15:42 +0200 Subject: [PATCH] Separated `Time` unit into `TimeDelta` and `TimeStamp` Addition and subtruction between `Timestamp`s is not allowed, but any other `Unit` operation/comparison is. `TimeDelta`s can be added/subtructed to/form `Timestamp`s. Deserialization of `Timestamp`: - `Number` -> interpreted as millis from Epoch - `Instant` (string representation) -> converted to Timestamp - `Duration` (string representation) -> interpreted as duration since Epoch (warn msg is logged) Deserialization of `TimeDelta` is the same as `Time` was before, with the diference that when an `Instant` is converted to an timedelta since Epoch a warning message is logged. --- .../org/opendc/common/units/DataRate.kt | 6 +- .../org/opendc/common/units/DataSize.kt | 6 +- .../kotlin/org/opendc/common/units/Energy.kt | 6 +- .../org/opendc/common/units/Frequency.kt | 6 +- .../org/opendc/common/units/Percentage.kt | 5 +- .../kotlin/org/opendc/common/units/Power.kt | 6 +- .../org/opendc/common/units/TimeDelta.kt | 167 ++++++++++++++++++ .../org/opendc/common/units/Timestamp.kt | 167 ++++++++++++++++++ .../kotlin/org/opendc/common/units/Unit.kt | 29 ++- .../org/opendc/common/units/UnitSerializer.kt | 5 +- 10 files changed, 375 insertions(+), 28 deletions(-) create mode 100644 opendc-common/src/main/kotlin/org/opendc/common/units/TimeDelta.kt create mode 100644 opendc-common/src/main/kotlin/org/opendc/common/units/Timestamp.kt diff --git a/opendc-common/src/main/kotlin/org/opendc/common/units/DataRate.kt b/opendc-common/src/main/kotlin/org/opendc/common/units/DataRate.kt index 2af45b7b5..8c2fa1c8f 100644 --- a/opendc-common/src/main/kotlin/org/opendc/common/units/DataRate.kt +++ b/opendc-common/src/main/kotlin/org/opendc/common/units/DataRate.kt @@ -26,7 +26,7 @@ package org.opendc.common.units import kotlinx.serialization.Serializable import org.opendc.common.annotations.InternalUse -import org.opendc.common.units.Time.Companion.toTime +import org.opendc.common.units.TimeDelta.Companion.toTimeDelta import org.opendc.common.utils.ifNeg0thenPos0 import java.time.Duration @@ -79,9 +79,9 @@ public value class DataRate private constructor( else -> "${String.format(fmt, toGbps())} Gbps" } - public operator fun times(time: Time): DataSize = DataSize.ofKiB(toKiBps() * time.toSec()) + public operator fun times(timeDelta: TimeDelta): DataSize = DataSize.ofKiB(toKiBps() * timeDelta.toSec()) - public operator fun times(duration: Duration): DataSize = this * duration.toTime() + public operator fun times(duration: Duration): DataSize = this * duration.toTimeDelta() public companion object { @JvmStatic public val ZERO: DataRate = DataRate(.0) diff --git a/opendc-common/src/main/kotlin/org/opendc/common/units/DataSize.kt b/opendc-common/src/main/kotlin/org/opendc/common/units/DataSize.kt index e32d9e88d..2bd3436b0 100644 --- a/opendc-common/src/main/kotlin/org/opendc/common/units/DataSize.kt +++ b/opendc-common/src/main/kotlin/org/opendc/common/units/DataSize.kt @@ -26,7 +26,7 @@ package org.opendc.common.units import kotlinx.serialization.Serializable import org.opendc.common.annotations.InternalUse -import org.opendc.common.units.Time.Companion.toTime +import org.opendc.common.units.TimeDelta.Companion.toTimeDelta import org.opendc.common.utils.fmt import java.time.Duration @@ -93,9 +93,9 @@ public value class DataSize private constructor( else -> "${toGiB().fmt(fmt)} GiB" } - public operator fun div(time: Time): DataRate = DataRate.ofKBps(this.toKiB() / time.toSec()) + public operator fun div(timeDelta: TimeDelta): DataRate = DataRate.ofKBps(this.toKiB() / timeDelta.toSec()) - public operator fun div(duration: Duration): DataRate = this / duration.toTime() + public operator fun div(duration: Duration): DataRate = this / duration.toTimeDelta() public companion object { @JvmStatic public val ZERO: DataSize = DataSize(.0) diff --git a/opendc-common/src/main/kotlin/org/opendc/common/units/Energy.kt b/opendc-common/src/main/kotlin/org/opendc/common/units/Energy.kt index 467192a01..d6b892cb9 100644 --- a/opendc-common/src/main/kotlin/org/opendc/common/units/Energy.kt +++ b/opendc-common/src/main/kotlin/org/opendc/common/units/Energy.kt @@ -26,7 +26,7 @@ package org.opendc.common.units import kotlinx.serialization.Serializable import org.opendc.common.annotations.InternalUse -import org.opendc.common.units.Time.Companion.toTime +import org.opendc.common.units.TimeDelta.Companion.toTimeDelta import org.opendc.common.utils.fmt import org.opendc.common.utils.ifNeg0thenPos0 import java.time.Duration @@ -61,9 +61,9 @@ public value class Energy private constructor( "${toKJoule().fmt(fmt)} KJoule" } - public operator fun div(time: Time): Power = Power.ofWatts(toWh() / time.toHours()) + public operator fun div(timeDelta: TimeDelta): Power = Power.ofWatts(toWh() / timeDelta.toHours()) - public operator fun div(duration: Duration): Power = this / duration.toTime() + public operator fun div(duration: Duration): Power = this / duration.toTimeDelta() public companion object { @JvmStatic diff --git a/opendc-common/src/main/kotlin/org/opendc/common/units/Frequency.kt b/opendc-common/src/main/kotlin/org/opendc/common/units/Frequency.kt index df1b49f65..2d1f2604f 100644 --- a/opendc-common/src/main/kotlin/org/opendc/common/units/Frequency.kt +++ b/opendc-common/src/main/kotlin/org/opendc/common/units/Frequency.kt @@ -26,7 +26,7 @@ package org.opendc.common.units import kotlinx.serialization.Serializable import org.opendc.common.annotations.InternalUse -import org.opendc.common.units.Time.Companion.toTime +import org.opendc.common.units.TimeDelta.Companion.toTimeDelta import org.opendc.common.utils.fmt import org.opendc.common.utils.ifNeg0thenPos0 import java.time.Duration @@ -62,9 +62,9 @@ public value class Frequency private constructor( else -> "${toGHz().fmt(fmt)} GHz" } - public operator fun times(time: Time): Double = toHz() * time.toSec() + public operator fun times(timeDelta: TimeDelta): Double = toHz() * timeDelta.toSec() - public operator fun times(duration: Duration): Double = toHz() * duration.toTime().toSec() + public operator fun times(duration: Duration): Double = toHz() * duration.toTimeDelta().toSec() public companion object { @JvmStatic public val ZERO: Frequency = Frequency(.0) diff --git a/opendc-common/src/main/kotlin/org/opendc/common/units/Percentage.kt b/opendc-common/src/main/kotlin/org/opendc/common/units/Percentage.kt index 377fdecc4..3b10b85a8 100644 --- a/opendc-common/src/main/kotlin/org/opendc/common/units/Percentage.kt +++ b/opendc-common/src/main/kotlin/org/opendc/common/units/Percentage.kt @@ -25,8 +25,8 @@ package org.opendc.common.units import kotlinx.serialization.Serializable -import mu.KotlinLogging import org.opendc.common.annotations.InternalUse +import org.opendc.common.logger.logger import org.opendc.common.utils.fmt import org.opendc.common.utils.ifNeg0thenPos0 import kotlin.text.RegexOption.IGNORE_CASE @@ -210,8 +210,7 @@ public value class BoundedPercentage } public companion object { - // TODO: replace with `by logger()` if pr #241 is approved - private val LOG = KotlinLogging.logger(name = this::class.java.enclosingClass.simpleName) + private val LOG by logger() } } diff --git a/opendc-common/src/main/kotlin/org/opendc/common/units/Power.kt b/opendc-common/src/main/kotlin/org/opendc/common/units/Power.kt index fc9f6bf43..9f25dbe53 100644 --- a/opendc-common/src/main/kotlin/org/opendc/common/units/Power.kt +++ b/opendc-common/src/main/kotlin/org/opendc/common/units/Power.kt @@ -26,7 +26,7 @@ package org.opendc.common.units import kotlinx.serialization.Serializable import org.opendc.common.annotations.InternalUse -import org.opendc.common.units.Time.Companion.toTime +import org.opendc.common.units.TimeDelta.Companion.toTimeDelta import org.opendc.common.utils.fmt import org.opendc.common.utils.ifNeg0thenPos0 import java.time.Duration @@ -58,9 +58,9 @@ public value class Power private constructor( "${toWatts().fmt(fmt)} Watts" } - public operator fun times(time: Time): Energy = Energy.ofWh(toWatts() * time.toHours()) + public operator fun times(timeDelta: TimeDelta): Energy = Energy.ofWh(toWatts() * timeDelta.toHours()) - public operator fun times(duration: Duration): Energy = this * duration.toTime() + public operator fun times(duration: Duration): Energy = this * duration.toTimeDelta() public companion object { @JvmStatic diff --git a/opendc-common/src/main/kotlin/org/opendc/common/units/TimeDelta.kt b/opendc-common/src/main/kotlin/org/opendc/common/units/TimeDelta.kt new file mode 100644 index 000000000..fae095af9 --- /dev/null +++ b/opendc-common/src/main/kotlin/org/opendc/common/units/TimeDelta.kt @@ -0,0 +1,167 @@ +/* + * Copyright (c) 2024 AtLarge Research + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +@file:OptIn(InternalUse::class) + +package org.opendc.common.units + +import kotlinx.serialization.Serializable +import org.opendc.common.annotations.InternalUse +import org.opendc.common.utils.ifNeg0thenPos0 +import java.time.Duration +import java.time.Instant +import kotlin.text.RegexOption.IGNORE_CASE + +/** + * Represents time interval values. + * @see[Unit] + */ +@JvmInline +@Serializable(with = TimeDelta.Companion.TimeDeltaSerializer::class) +public value class TimeDelta private constructor( + // In milliseconds. + public override val value: Double, +) : Unit { + @InternalUse + override fun new(value: Double): TimeDelta = TimeDelta(value.ifNeg0thenPos0()) + + public fun toNs(): Double = value * 1e6 + + public fun toMicros(): Double = value * 1e3 + + public fun toMs(): Double = value + + public fun toMsLong(): Long = value.toLong() + + public fun toSec(): Double = value / 1000.0 + + public fun toMin(): Double = toSec() / 60 + + public fun toHours(): Double = toMin() / 60 + + public fun toInstantFromEpoch(): Instant = Instant.ofEpochMilli(value.toLong()) + + override fun toString(): String = fmtValue() + + /** + * @return the [Duration] [toString] result of this time value. + */ + override fun fmtValue(fmt: String): String = Duration.ofMillis(value.toLong()).toString() + + public operator fun times(power: Power): Energy = Energy.ofWh(toHours() * power.toWatts()) + + public operator fun times(dataRate: DataRate): DataSize = DataSize.ofKB(toSec() * dataRate.toKBps()) + + public companion object { + @JvmStatic public val ZERO: TimeDelta = TimeDelta(.0) + + @JvmStatic + @JvmName("ofNanos") + public fun ofNanos(nanos: Number): TimeDelta = TimeDelta(nanos.toDouble() / 1e6) + + @JvmStatic + @JvmName("ofMicros") + public fun ofMicros(micros: Number): TimeDelta = TimeDelta(micros.toDouble() / 1e3) + + @JvmStatic + @JvmName("ofMillis") + public fun ofMillis(ms: Number): TimeDelta = TimeDelta(ms.toDouble()) + + @JvmStatic + @JvmName("ofSec") + public fun ofSec(sec: Number): TimeDelta = TimeDelta(sec.toDouble() * 1000.0) + + @JvmStatic + @JvmName("ofMin") + public fun ofMin(min: Number): TimeDelta = TimeDelta(min.toDouble() * 60 * 1000.0) + + @JvmStatic + @JvmName("ofHours") + public fun ofHours(hours: Number): TimeDelta = TimeDelta(hours.toDouble() * 60 * 60 * 1000.0) + + @JvmStatic + @JvmName("ofDuration") + public fun ofDuration(duration: Duration): TimeDelta = duration.toTimeDelta() + + /** + * Serializer for [TimeDelta] value class. It needs to be a compile + * time constant in order to be used as serializer automatically, + * hence `object :` instead of class instantiation. + * + * ```json + * // e.g. + * "timedelta": "10 hours" + * "timedelta": " 30 minutes " + * "timedelta": "1 ms" + * "timedelta": "PT13H" + * // etc. + * ``` + */ + internal object TimeDeltaSerializer : UnitSerializer( + ifNumber = { + LOG.warn( + "deserialization of number with no unit of measure, assuming it is in milliseconds." + + "Keep in mind that you can also specify the value as '$it ms'", + ) + ofMillis(it.toDouble()) + }, + serializerFun = { this.encodeString(it.toString()) }, + ifMatches("$NUM_GROUP$NANO$SEC(?:|s)\\s*", IGNORE_CASE) { ofNanos(json.decNumFromStr(groupValues[1])) }, + ifMatches("$NUM_GROUP$MICRO$SEC(?:|s)\\s*", IGNORE_CASE) { ofMicros(json.decNumFromStr(groupValues[1])) }, + ifMatches("$NUM_GROUP$MILLI$SEC(?:|s)\\s*", IGNORE_CASE) { ofMillis(json.decNumFromStr(groupValues[1])) }, + ifMatches("$NUM_GROUP$SEC(?:|s)\\s*", IGNORE_CASE) { ofSec(json.decNumFromStr(groupValues[1])) }, + ifMatches("$NUM_GROUP$MIN(?:|s)\\s*") { ofMin(json.decNumFromStr(groupValues[1])) }, + ifMatches("$NUM_GROUP$HOUR(?:|s)\\s*") { ofHours(json.decNumFromStr(groupValues[1])) }, + ifNoExc { ofDuration(Duration.parse(this)) }, + ifNoExc { + val instant = Instant.parse(this) + LOG.warn("`TimeDelta` value was expected but `Instant` string representation found. Converting to `TimeDelta` since Epoch") + + ofMillis(instant.toEpochMilli()) + }, + ) + + /** + * @return [this] converted to a [TimeDelta] value, with the highest possible accuracy. + * + * @throws RuntimeException if [this] cannot be represented as nanos, millis, seconds, minutes or hours with a [Long]. + */ + public fun Duration.toTimeDelta(): TimeDelta { + fun tryNoThrow(block: () -> TimeDelta?) = + try { + block() + } catch (_: Exception) { + null + } + + return tryNoThrow { ofNanos(this.toNanos()) } + ?: tryNoThrow { ofMillis(this.toMillis()) } + ?: tryNoThrow { ofSec(this.toSeconds()) } + ?: tryNoThrow { ofMin(this.toMinutes()) } + ?: tryNoThrow { ofHours(this.toHours()) } + ?: throw RuntimeException( + "duration $this cannot be converted to ${TimeDelta::class.simpleName}, " + + "duration value overflow Long representation of nanos, millis, seconds, minutes and hours", + ) + } + } +} diff --git a/opendc-common/src/main/kotlin/org/opendc/common/units/Timestamp.kt b/opendc-common/src/main/kotlin/org/opendc/common/units/Timestamp.kt new file mode 100644 index 000000000..162e8354c --- /dev/null +++ b/opendc-common/src/main/kotlin/org/opendc/common/units/Timestamp.kt @@ -0,0 +1,167 @@ +/* + * Copyright (c) 2024 AtLarge Research + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +@file:OptIn(InternalUse::class) + +package org.opendc.common.units + +import kotlinx.serialization.Serializable +import org.opendc.common.annotations.InternalUse +import org.opendc.common.units.TimeDelta.Companion.toTimeDelta +import org.opendc.common.utils.ifNeg0thenPos0 +import java.time.Duration +import java.time.Instant +import javax.naming.OperationNotSupportedException + +/** + * Represents timestamp values. + * @see[Unit] + */ +@JvmInline +@Serializable(with = Timestamp.Companion.TimeStampSerializer::class) +public value class Timestamp private constructor( + // In milliseconds since Epoch. + public override val value: Double, +) : Unit { + @InternalUse + override fun new(value: Double): Timestamp = Timestamp(value.ifNeg0thenPos0()) + + @Deprecated( + message = "operation not permitted on Timestamps, likely to be undesired operation", + level = DeprecationLevel.ERROR, + replaceWith = ReplaceWith("+ other.toIntervalFromEpoch()"), + ) + override fun plus(other: Timestamp): Timestamp = throw OperationNotSupportedException() + + public operator fun plus(other: TimeDelta): Timestamp = ofEpochMs(value + other.toMs()) + + @Deprecated( + message = "operation not permitted on Timestamps, likely to be undesired operation", + level = DeprecationLevel.ERROR, + replaceWith = ReplaceWith("- other.toIntervalFromEpoch()"), + ) + override fun minus(other: Timestamp): Timestamp = throw OperationNotSupportedException() + + public operator fun minus(other: TimeDelta): Timestamp = ofEpochMs(value - other.toMs()) + + /** + * @return the [TimeDelta] between *this* and [other]. Be aware that this is not the absolute value, it can be negative. + */ + public infix fun timeDelta(other: Timestamp): TimeDelta = this.toTimeDeltaFromEpoch() - other.toTimeDeltaFromEpoch() + + public fun toEpochNs(): Double = value * 1e6 + + public fun toEpochMicros(): Double = value * 1e3 + + public fun toEpochMs(): Double = value + + public fun toEpochSec(): Double = value / 1000.0 + + public fun toEpochMin(): Double = toEpochSec() / 60 + + public fun toEpochHours(): Double = toEpochMin() / 60 + + public fun toEpochDays(): Double = toEpochHours() / 24 + + public fun toInstant(): Instant = + if (toEpochMs() > Long.MAX_VALUE) { + Instant.ofEpochSecond(toEpochSec().toLong()) + } else { + Instant.ofEpochMilli(toEpochMs().toLong()) + } + + public fun toTimeDeltaFromEpoch(): TimeDelta = TimeDelta.ofMillis(toEpochMs()) + + override fun toString(): String = fmtValue() + + /** + * @return the [Instant] [toString] result of this [Timestamp] value. + * @param[fmt] no ops. + */ + override fun fmtValue(fmt: String): String = toInstant().toString() + + public companion object { + @JvmStatic public val ZERO: Timestamp = Timestamp(.0) + + @JvmStatic + @JvmName("ofEpochNs") + public fun ofEpochNs(nanos: Number): Timestamp = Timestamp(nanos.toDouble() / 1e6) + + @JvmStatic + @JvmName("ofEpochMicros") + public fun ofEpochMicros(micros: Number): Timestamp = Timestamp(micros.toDouble() / 1e3) + + @JvmStatic + @JvmName("ofEpochMs") + public fun ofEpochMs(ms: Number): Timestamp = Timestamp(ms.toDouble()) + + @JvmStatic + @JvmName("ofEpochSec") + public fun ofEpochSec(sec: Number): Timestamp = ofEpochMs(sec.toDouble() * 1000.0) + + @JvmStatic + @JvmName("ofEpochMin") + public fun ofEpochMin(sec: Number): Timestamp = ofEpochSec(sec.toDouble() * 60) + + @JvmStatic + @JvmName("ofEpochHours") + public fun ofEpochHours(sec: Number): Timestamp = ofEpochMin(sec.toDouble() * 60) + + @JvmStatic + @JvmName("ofInstant") + public fun ofInstant(instant: Instant): Timestamp = ofEpochMs(instant.toEpochMilli()) + + @JvmStatic + @JvmName("toTimestamp") + public fun Instant.toTimestamp(): Timestamp = ofEpochMs(toEpochMilli()) + + /** + * Serializer for [Timestamp] value class. It needs to be a compile + * time constant in order to be used as serializer automatically, + * hence `object :` instead of class instantiation. + * + * ```json + * // e.g. + * "timestamp": "10000" // 10,000 ms since Epoch + * "timestamp": "2001-09-09T01:48:19Z" + * // etc. + * ``` + */ + internal object TimeStampSerializer : UnitSerializer( + ifNumber = { + LOG.warn( + "deserialization of number with no unit of measure, assuming it is in milliseconds since Epoch." + + "Keep in mind that you can also specify the value with timestamp representation (e.g. '2001-09-09T01:48:19Z')", + ) + ofEpochMs(it.toDouble()) + }, + serializerFun = { this.encodeString(it.toString()) }, + ifNoExc { ofInstant(Instant.parse(this)) }, + ifNoExc { + val duration = Duration.parse(this) + LOG.warn("timestamp value was expected but duration string representation found. Assuming it is a duration since Epoch.") + + ofEpochMs(duration.toTimeDelta().toMs()) + }, + ) + } +} diff --git a/opendc-common/src/main/kotlin/org/opendc/common/units/Unit.kt b/opendc-common/src/main/kotlin/org/opendc/common/units/Unit.kt index 8bcbb1482..0a6d071d7 100644 --- a/opendc-common/src/main/kotlin/org/opendc/common/units/Unit.kt +++ b/opendc-common/src/main/kotlin/org/opendc/common/units/Unit.kt @@ -25,7 +25,7 @@ package org.opendc.common.units import org.opendc.common.annotations.InternalUse -import org.opendc.common.units.Time.Companion.toTime +import org.opendc.common.units.TimeDelta.Companion.toTimeDelta import org.opendc.common.utils.DFLT_MIN_EPS import org.opendc.common.utils.adaptiveEps import org.opendc.common.utils.approx @@ -65,7 +65,11 @@ import kotlin.experimental.ExperimentalTypeInference * * // e.g. operations between different unit of measures * val a: DataRate = DataRate.ofMBps(1) +<<<<<<< HEAD * val b: Time = Time.ofSec(3) +======= + * val b: TimeDelta = TimeDelta.ofSec(3) +>>>>>>> 799f5da3 (isk) * val c: DataSize = a * b * c.fmt() // "3MB" * ``` @@ -83,6 +87,7 @@ import kotlin.experimental.ExperimentalTypeInference * ```kotlin * // kotlin * @JvmStatic @JvmName("function") +<<<<<<< HEAD * fun function(time: Time) { } * ``` * ```java @@ -91,6 +96,16 @@ import kotlin.experimental.ExperimentalTypeInference * function(time) * // or * function(Time.ofHours(2)) +======= + * fun function(time: TimeDelta) { } + * ``` + * ```java + * // java + * double time = TimeDelta.ofHours(2); + * function(time) + * // or + * function(TimeDelta.ofHours(2)) +>>>>>>> 799f5da3 (isk) * ``` * * @param[T] the unit of measure that is represented (e.g. [DataRate]) @@ -306,13 +321,13 @@ public sealed interface Unit> : Comparable { // Operations whose 'this' is not a `Unit` are defined in their classes // and not as extension function so that they do not need to be imported - public operator fun Duration.times(dataRate: DataRate): DataSize = toTime() * dataRate + public operator fun Duration.times(dataRate: DataRate): DataSize = toTimeDelta() * dataRate - public operator fun Duration.times(power: Power): Energy = toTime() * power + public operator fun Duration.times(power: Power): Energy = toTimeDelta() * power - public operator fun Number.div(time: Time): Frequency = Frequency.ofHz(this.toDouble() / time.toSec()) + public operator fun Number.div(timeDelta: TimeDelta): Frequency = Frequency.ofHz(this.toDouble() / timeDelta.toSec()) - public operator fun Number.div(duration: Duration): Frequency = this / duration.toTime() + public operator fun Number.div(duration: Duration): Frequency = this / duration.toTimeDelta() // Defined here so that they can overload the same method name, instead of having a different name forEach unit. // You can not overload `sumOf` and using that name results in not being able to use the overloads for unit and for number in the same file. @@ -359,8 +374,8 @@ public sealed interface Unit> : Comparable { @OptIn(ExperimentalTypeInference::class) @OverloadResolutionByLambdaReturnType @JvmName("sumOfTime") - public inline fun Iterable.sumOfUnit(selector: (T) -> Time): Time { - var sum: Time = Time.ZERO + public inline fun Iterable.sumOfUnit(selector: (T) -> TimeDelta): TimeDelta { + var sum: TimeDelta = TimeDelta.ZERO forEach { sum += selector(it) } return sum } diff --git a/opendc-common/src/main/kotlin/org/opendc/common/units/UnitSerializer.kt b/opendc-common/src/main/kotlin/org/opendc/common/units/UnitSerializer.kt index aaf18498a..72519814f 100644 --- a/opendc-common/src/main/kotlin/org/opendc/common/units/UnitSerializer.kt +++ b/opendc-common/src/main/kotlin/org/opendc/common/units/UnitSerializer.kt @@ -32,7 +32,7 @@ import kotlinx.serialization.json.Json import kotlinx.serialization.json.JsonElement import kotlinx.serialization.json.JsonPrimitive import kotlinx.serialization.json.JsonTransformingSerializer -import mu.KotlinLogging +import org.opendc.common.logger.logger /** * Serializer for [T]. @@ -84,8 +84,7 @@ internal open class UnitSerializer>( }, ) { companion object { - // TODO: replace with `by logger()` if pr #241 is approved - val LOG = KotlinLogging.logger(name = this::class.java.enclosingClass.simpleName) + val LOG by logger() val json = Json