diff --git a/core/common/src/LocalTime.kt b/core/common/src/LocalTime.kt index bc42931a7..af3ed4a50 100644 --- a/core/common/src/LocalTime.kt +++ b/core/common/src/LocalTime.kt @@ -40,6 +40,20 @@ public expect class LocalTime : Comparable { */ public fun parse(isoString: String): LocalTime + /** + * Returns a LocalTime with the specified [secondOfDay]. The nanosecond field will be set to zero. + * + * @throws IllegalArgumentException if the boundaries of [secondOfDay] are exceeded. + */ + public fun fromSecondOfDay(secondOfDay: Int): LocalTime + + /** + * Returns a LocalTime with the specified [nanosecondOfDay]. + * + * @throws IllegalArgumentException if the boundaries of [nanosecondOfDay] are exceeded. + */ + public fun fromNanosecondOfDay(nanosecondOfDay: Long): LocalTime + internal val MIN: LocalTime internal val MAX: LocalTime } @@ -66,6 +80,12 @@ public expect class LocalTime : Comparable { /** Returns the nanosecond-of-second time component of this time value. */ public val nanosecond: Int + /** Returns the time as a second of a day, from 0 to 24 * 60 * 60 - 1. */ + public fun toSecondOfDay(): Int + + /** Returns the time as a nanosecond of a day, from 0 to 24 * 60 * 60 * 1_000_000_000 - 1. */ + public fun toNanosecondOfDay(): Long + /** * Compares `this` time value with the [other] time value. * Returns zero if this value is equal to the other, a negative number if this value occurs earlier @@ -110,4 +130,4 @@ public fun LocalTime.atDate(year: Int, month: Month, dayOfMonth: Int = 0): Local /** * Combines this time's components with the specified [LocalDate] components into a [LocalDateTime] value. */ -public fun LocalTime.atDate(date: LocalDate): LocalDateTime = LocalDateTime(date, this) \ No newline at end of file +public fun LocalTime.atDate(date: LocalDate): LocalDateTime = LocalDateTime(date, this) diff --git a/core/common/test/LocalTimeTest.kt b/core/common/test/LocalTimeTest.kt index 037e57455..52911ea2a 100644 --- a/core/common/test/LocalTimeTest.kt +++ b/core/common/test/LocalTimeTest.kt @@ -6,8 +6,6 @@ package kotlinx.datetime.test import kotlinx.datetime.* -import kotlinx.datetime.Clock -import kotlin.math.min import kotlin.test.* class LocalTimeTest { @@ -85,6 +83,58 @@ class LocalTimeTest { assertFailsWith { LocalTime(0, 0, 0, 1_000_000_000) } } + @Test + fun fromNanosecondOfDay() { + val data = mapOf( + 0L to LocalTime(0, 0), + 5000000001L to LocalTime(0, 0, 5, 1), + 44105123456789L to LocalTime(12, 15, 5, 123456789), + 86399999999999L to LocalTime(23, 59, 59, 999999999), + ) + + data.forEach { (nanosecondOfDay, localTime) -> + assertEquals(nanosecondOfDay, localTime.toNanosecondOfDay()) + assertEquals(localTime, LocalTime.fromNanosecondOfDay(nanosecondOfDay)) + } + } + + @Test + fun fromNanosecondOfDayInvalid() { + assertFailsWith { LocalTime.fromNanosecondOfDay(-1) } + assertFailsWith { LocalTime.fromNanosecondOfDay(86400000000000L) } + assertFailsWith { LocalTime.fromNanosecondOfDay(Long.MAX_VALUE) } + } + + @Test + fun fromSecondOfDay() { + val data = mapOf( + 0 to LocalTime(0, 0), + 5 to LocalTime(0, 0, 5), + 44105 to LocalTime(12, 15, 5), + 86399 to LocalTime(23, 59, 59), + ) + + data.forEach { (secondOfDay, localTime) -> + assertEquals(secondOfDay, localTime.toSecondOfDay()) + assertEquals(localTime, LocalTime.fromSecondOfDay(secondOfDay)) + } + } + + @Test + fun fromSecondOfDayInvalid() { + assertFailsWith { LocalTime.fromSecondOfDay(-1) } + assertFailsWith { LocalTime.fromSecondOfDay(86400) } + assertFailsWith { LocalTime.fromSecondOfDay(Int.MAX_VALUE) } + } + + @Test + fun fromSecondOfDayIgnoresNanosecond() { + assertEquals( + 0, + LocalTime(0, 0, 0, 100).toSecondOfDay(), + ) + } + @Test fun atDate() { val time = LocalTime(12, 1, 59) diff --git a/core/js/src/LocalTime.kt b/core/js/src/LocalTime.kt index 088beea33..fd3b669e3 100644 --- a/core/js/src/LocalTime.kt +++ b/core/js/src/LocalTime.kt @@ -26,6 +26,8 @@ public actual class LocalTime internal constructor(internal val value: jtLocalTi public actual val minute: Int get() = value.minute().toInt() public actual val second: Int get() = value.second().toInt() public actual val nanosecond: Int get() = value.nano().toInt() + public actual fun toSecondOfDay(): Int = value.toSecondOfDay().toInt() + public actual fun toNanosecondOfDay(): Long = value.toNanoOfDay().toLong() override fun equals(other: Any?): Boolean = (this === other) || (other is LocalTime && this.value == other.value) @@ -44,7 +46,19 @@ public actual class LocalTime internal constructor(internal val value: jtLocalTi throw e } + public actual fun fromSecondOfDay(secondOfDay: Int): LocalTime = try { + jtLocalTime.ofSecondOfDay(secondOfDay, 0).let(::LocalTime) + } catch (e: Throwable) { + throw IllegalArgumentException(e) + } + + public actual fun fromNanosecondOfDay(nanosecondOfDay: Long): LocalTime = try { + jtLocalTime.ofNanoOfDay(nanosecondOfDay).let(::LocalTime) + } catch (e: Throwable) { + throw IllegalArgumentException(e) + } + internal actual val MIN: LocalTime = LocalTime(jtLocalTime.MIN) internal actual val MAX: LocalTime = LocalTime(jtLocalTime.MAX) } -} \ No newline at end of file +} diff --git a/core/jvm/src/LocalTime.kt b/core/jvm/src/LocalTime.kt index 7bf22cd08..b377fbea2 100644 --- a/core/jvm/src/LocalTime.kt +++ b/core/jvm/src/LocalTime.kt @@ -29,6 +29,8 @@ public actual class LocalTime internal constructor(internal val value: jtLocalTi public actual val minute: Int get() = value.minute public actual val second: Int get() = value.second public actual val nanosecond: Int get() = value.nano + public actual fun toSecondOfDay(): Int = value.toSecondOfDay() + public actual fun toNanosecondOfDay(): Long = value.toNanoOfDay() override fun equals(other: Any?): Boolean = (this === other) || (other is LocalTime && this.value == other.value) @@ -46,7 +48,19 @@ public actual class LocalTime internal constructor(internal val value: jtLocalTi throw DateTimeFormatException(e) } + public actual fun fromSecondOfDay(secondOfDay: Int): LocalTime = try { + jtLocalTime.ofSecondOfDay(secondOfDay.toLong()).let(::LocalTime) + } catch (e: DateTimeException) { + throw IllegalArgumentException(e) + } + + public actual fun fromNanosecondOfDay(nanosecondOfDay: Long): LocalTime = try { + jtLocalTime.ofNanoOfDay(nanosecondOfDay).let(::LocalTime) + } catch (e: DateTimeException) { + throw IllegalArgumentException(e) + } + internal actual val MIN: LocalTime = LocalTime(jtLocalTime.MIN) internal actual val MAX: LocalTime = LocalTime(jtLocalTime.MAX) } -} \ No newline at end of file +} diff --git a/core/native/src/LocalTime.kt b/core/native/src/LocalTime.kt index 2713e53c1..73103248f 100644 --- a/core/native/src/LocalTime.kt +++ b/core/native/src/LocalTime.kt @@ -62,6 +62,12 @@ public actual class LocalTime actual constructor( public actual fun parse(isoString: String): LocalTime = localTimeParser.parse(isoString) + public actual fun fromSecondOfDay(secondOfDay: Int): LocalTime = + ofSecondOfDay(secondOfDay, 0) + + public actual fun fromNanosecondOfDay(nanosecondOfDay: Long): LocalTime = + ofNanoOfDay(nanosecondOfDay) + // org.threeten.bp.LocalTime#ofSecondOfDay(long, int) internal fun ofSecondOfDay(secondOfDay: Int, nanoOfSecond: Int): LocalTime { // Unidiomatic code due to https://github.com/Kotlin/kotlinx-datetime/issues/5 @@ -117,6 +123,16 @@ public actual class LocalTime actual constructor( return (nod xor (nod ushr 32)).toInt() } + // org.threeten.bp.LocalTime#toSecondOfDay + public actual fun toSecondOfDay(): Int { + var total: Int = hour * SECONDS_PER_HOUR + total += minute * SECONDS_PER_MINUTE + total += second + return total + } + + public actual fun toNanosecondOfDay(): Long = toNanoOfDay() + // org.threeten.bp.LocalTime#toNanoOfDay internal fun toNanoOfDay(): Long { var total: Long = hour.toLong() * NANOS_PER_ONE * SECONDS_PER_HOUR @@ -126,14 +142,6 @@ public actual class LocalTime actual constructor( return total } - // org.threeten.bp.LocalTime#toSecondOfDay - internal fun toSecondOfDay(): Int { - var total: Int = hour * SECONDS_PER_HOUR - total += minute * SECONDS_PER_MINUTE - total += second - return total - } - // org.threeten.bp.LocalTime#toString actual override fun toString(): String { val buf = StringBuilder(18)