From dc7fe9e93811846e66b7f3f08d9c7b5412f7a92e Mon Sep 17 00:00:00 2001 From: Dmitry Khalanskiy Date: Thu, 28 Nov 2024 12:48:30 +0100 Subject: [PATCH 1/7] Copy Instant.kt to kotlinx.time --- fake-kotlinx-time/build.gradle.kts | 111 ++++ fake-kotlinx-time/common/src/Clock.kt | 128 ++++ fake-kotlinx-time/common/src/Instant.kt | 802 ++++++++++++++++++++++++ fake-kotlinx-time/js/src/Converters.kt | 21 + fake-kotlinx-time/jvm/src/Converters.kt | 95 +++ settings.gradle.kts | 2 + 6 files changed, 1159 insertions(+) create mode 100644 fake-kotlinx-time/build.gradle.kts create mode 100644 fake-kotlinx-time/common/src/Clock.kt create mode 100644 fake-kotlinx-time/common/src/Instant.kt create mode 100644 fake-kotlinx-time/js/src/Converters.kt create mode 100644 fake-kotlinx-time/jvm/src/Converters.kt diff --git a/fake-kotlinx-time/build.gradle.kts b/fake-kotlinx-time/build.gradle.kts new file mode 100644 index 000000000..750ec3cba --- /dev/null +++ b/fake-kotlinx-time/build.gradle.kts @@ -0,0 +1,111 @@ +import java.util.Locale + +plugins { + id("kotlin-multiplatform") + id("org.jetbrains.kotlinx.kover") +} + +val mainJavaToolchainVersion: String by project +val serializationVersion: String by project + +java { + toolchain { languageVersion.set(JavaLanguageVersion.of(mainJavaToolchainVersion)) } +} + +kotlin { + infra { + target("linuxX64") + target("linuxArm64") + target("linuxArm32Hfp") + target("mingwX64") + target("macosX64") + target("macosArm64") + target("iosX64") + target("iosArm64") + target("iosSimulatorArm64") + target("watchosArm32") + target("watchosArm64") + target("watchosX64") + target("watchosSimulatorArm64") + target("watchosDeviceArm64") + target("tvosArm64") + target("tvosX64") + target("tvosSimulatorArm64") + target("androidNativeArm32") + target("androidNativeArm64") + target("androidNativeX86") + target("androidNativeX64") + } + + jvm { + attributes { + attribute(TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE, 8) + } + } + + js { + nodejs { + } + compilations.all { + kotlinOptions { + sourceMap = true + moduleKind = "umd" + metaInfo = true + } + } + } + + + wasmJs { + nodejs { + } + } + + wasmWasi { + nodejs { + } + } + + sourceSets.all { + val suffixIndex = name.indexOfLast { it.isUpperCase() } + val targetName = name.substring(0, suffixIndex) + val suffix = name.substring(suffixIndex).toLowerCase(Locale.ROOT).takeIf { it != "main" } + kotlin.srcDir("$targetName/${suffix ?: "src"}") + resources.srcDir("$targetName/${suffix?.let { it + "Resources" } ?: "resources"}") + } + + targets.withType { + compilations["test"].kotlinOptions { + freeCompilerArgs += listOf("-trw") + } + } + + sourceSets { + commonMain { + dependencies { + } + } + + commonTest { + dependencies { + api("org.jetbrains.kotlin:kotlin-test") + api("org.jetbrains.kotlinx:kotlinx-serialization-json:$serializationVersion") + } + } + + val jvmMain by getting + val jvmTest by getting + + val jsMain by getting + val jsTest by getting + + val wasmJsMain by getting + val wasmJsTest by getting + + val wasmWasiMain by getting + val wasmWasiTest by getting + + val nativeMain by getting + val nativeTest by getting + } +} diff --git a/fake-kotlinx-time/common/src/Clock.kt b/fake-kotlinx-time/common/src/Clock.kt new file mode 100644 index 000000000..e9cd9137e --- /dev/null +++ b/fake-kotlinx-time/common/src/Clock.kt @@ -0,0 +1,128 @@ +/* + * Copyright 2019-2020 JetBrains s.r.o. + * Use of this source code is governed by the Apache 2.0 License that can be found in the LICENSE.txt file. + */ + +package kotlinx.datetime + +import kotlin.time.* + +/** + * A source of [Instant] values. + * + * See [Clock.System][Clock.System] for the clock instance that queries the operating system. + * + * It is not recommended to use [Clock.System] directly in the implementation. Instead, you can pass a + * [Clock] explicitly to the necessary functions or classes. + * This way, tests can be written deterministically by providing custom [Clock] implementations + * to the system under test. + */ +public interface Clock { + /** + * Returns the [Instant] corresponding to the current time, according to this clock. + * + * Calling [now] later is not guaranteed to return a larger [Instant]. + * In particular, for [Clock.System], the opposite is completely expected, + * and it must be taken into account. + * See the [System] documentation for details. + * + * Even though [Instant] is defined to be on the UTC-SLS time scale, which enforces a specific way of handling + * leap seconds, [now] is not guaranteed to handle leap seconds in any specific way. + */ + public fun now(): Instant + + /** + * The [Clock] instance that queries the platform-specific system clock as its source of time knowledge. + * + * Successive calls to [now] will not necessarily return increasing [Instant] values, and when they do, + * these increases will not necessarily correspond to the elapsed time. + * + * For example, when using [Clock.System], the following could happen: + * - [now] returns `2023-01-02T22:35:01Z`. + * - The system queries the Internet and recognizes that its clock needs adjusting. + * - [now] returns `2023-01-02T22:32:05Z`. + * + * When you need predictable intervals between successive measurements, consider using [TimeSource.Monotonic]. + * + * For improved testability, you should avoid using [Clock.System] directly in the implementation + * and pass a [Clock] explicitly instead. For example: + * + * @sample kotlinx.datetime.test.samples.ClockSamples.system + * @sample kotlinx.datetime.test.samples.ClockSamples.dependencyInjection + */ + public object System : Clock { + override fun now(): Instant = @Suppress("DEPRECATION_ERROR") Instant.now() + } + + /** A companion object used purely for namespacing. */ + public companion object { + + } +} + +/** + * Returns the current date at the given [time zone][timeZone], according to [this Clock][this]. + * + * The time zone is important because the current date is not the same in all time zones at the same instant. + * + * @sample kotlinx.datetime.test.samples.ClockSamples.todayIn + */ +public fun Clock.todayIn(timeZone: TimeZone): LocalDate = + now().toLocalDateTime(timeZone).date + +/** + * Returns a [TimeSource] that uses this [Clock] to mark a time instant and to find the amount of time elapsed since that mark. + * + * **Pitfall**: using this function with [Clock.System] is error-prone + * because [Clock.System] is not well suited for measuring time intervals. + * Please only use this conversion function on the [Clock] instances that are fully controlled programmatically. + */ +@ExperimentalTime +public fun Clock.asTimeSource(): TimeSource.WithComparableMarks = object : TimeSource.WithComparableMarks { + override fun markNow(): ComparableTimeMark = InstantTimeMark(now(), this@asTimeSource) +} + +@ExperimentalTime +private class InstantTimeMark(private val instant: Instant, private val clock: Clock) : ComparableTimeMark { + override fun elapsedNow(): Duration = saturatingDiff(clock.now(), instant) + + override fun plus(duration: Duration): ComparableTimeMark = InstantTimeMark(instant.saturatingAdd(duration), clock) + override fun minus(duration: Duration): ComparableTimeMark = InstantTimeMark(instant.saturatingAdd(-duration), clock) + + override fun minus(other: ComparableTimeMark): Duration { + if (other !is InstantTimeMark || other.clock != this.clock) { + throw IllegalArgumentException("Subtracting or comparing time marks from different time sources is not possible: $this and $other") + } + return saturatingDiff(this.instant, other.instant) + } + + override fun equals(other: Any?): Boolean { + return other is InstantTimeMark && this.clock == other.clock && this.instant == other.instant + } + + override fun hashCode(): Int = instant.hashCode() + + override fun toString(): String = "InstantTimeMark($instant, $clock)" + + private fun Instant.isSaturated() = this == Instant.MAX || this == Instant.MIN + private fun Instant.saturatingAdd(duration: Duration): Instant { + if (isSaturated()) { + if (duration.isInfinite() && duration.isPositive() != this.isDistantFuture) { + throw IllegalArgumentException("Summing infinities of different signs") + } + return this + } + return this + duration + } + private fun saturatingDiff(instant1: Instant, instant2: Instant): Duration = when { + instant1 == instant2 -> + Duration.ZERO + instant1.isSaturated() || instant2.isSaturated() -> + (instant1 - instant2) * Double.POSITIVE_INFINITY + else -> + instant1 - instant2 + } +} + +@Deprecated("Use Clock.todayIn instead", ReplaceWith("this.todayIn(timeZone)"), DeprecationLevel.WARNING) +public fun Clock.todayAt(timeZone: TimeZone): LocalDate = todayIn(timeZone) diff --git a/fake-kotlinx-time/common/src/Instant.kt b/fake-kotlinx-time/common/src/Instant.kt new file mode 100644 index 000000000..3deedd946 --- /dev/null +++ b/fake-kotlinx-time/common/src/Instant.kt @@ -0,0 +1,802 @@ +/* + * Copyright 2019-2020 JetBrains s.r.o. + * Use of this source code is governed by the Apache 2.0 License that can be found in the LICENSE.txt file. + */ + +package kotlinx.datetime + +import kotlinx.datetime.format.* +import kotlinx.datetime.internal.* +import kotlinx.datetime.serializers.InstantIso8601Serializer +import kotlinx.datetime.serializers.InstantComponentSerializer +import kotlinx.serialization.Serializable +import kotlin.time.* + +/** + * A moment in time. + * + * A point in time must be uniquely identified in a way that is independent of a time zone. + * For example, `1970-01-01, 00:00:00` does not represent a moment in time since this would happen at different times + * in different time zones: someone in Tokyo would think it is already `1970-01-01` several hours earlier than someone in + * Berlin would. To represent such entities, use [LocalDateTime]. + * In contrast, "the moment the clocks in London first showed 00:00 on Jan 1, 2000" is a specific moment + * in time, as is "1970-01-01, 00:00:00 UTC+0", so it can be represented as an [Instant]. + * + * `Instant` uses the UTC-SLS (smeared leap second) time scale. This time scale doesn't contain instants + * corresponding to leap seconds, but instead "smears" positive and negative leap seconds among the last 1000 seconds + * of the day when a leap second happens. + * + * ### Obtaining the current moment + * + * The [Clock] interface is the primary way to obtain the current moment: + * + * ``` + * val clock: Clock = Clock.System + * val instant = clock.now() + * ``` + * + * The [Clock.System] implementation uses the platform-specific system clock to obtain the current moment. + * Note that this clock is not guaranteed to be monotonic, and the user or the system may adjust it at any time, + * so it should not be used for measuring time intervals. + * For that, consider using [TimeSource.Monotonic] and [TimeMark] instead of [Clock.System] and [Instant]. + * + * ### Obtaining human-readable representations + * + * #### Date and time + * + * [Instant] is essentially the number of seconds and nanoseconds since a designated moment in time, + * stored as something like `1709898983.123456789`. + * [Instant] does not contain information about the day or time, as this depends on the time zone. + * To work with this information for a specific time zone, obtain a [LocalDateTime] using [Instant.toLocalDateTime]: + * + * ``` + * val instant = Instant.fromEpochSeconds(1709898983, 123456789) + * instant.toLocalDateTime(TimeZone.of("Europe/Berlin")) // 2024-03-08T12:56:23.123456789 + * instant.toLocalDateTime(TimeZone.UTC) // 2024-03-08T11:56:23.123456789 + * ``` + * + * For values very far in the past or the future, this conversion may fail. + * The specific range of values that can be converted to [LocalDateTime] is unspecified, but at least + * [DISTANT_PAST], [DISTANT_FUTURE], and all values between them are included in that range. + * + * #### Date or time separately + * + * To obtain a [LocalDate] or [LocalTime], first, obtain a [LocalDateTime], and then use its [LocalDateTime.date] + * and [LocalDateTime.time] properties: + * + * ``` + * val instant = Instant.fromEpochSeconds(1709898983, 123456789) + * instant.toLocalDateTime(TimeZone.of("Europe/Berlin")).date // 2024-03-08 + * ``` + * + * ### Arithmetic operations + * + * #### Elapsed-time-based + * + * The [plus] and [minus] operators can be used to add [Duration]s to and subtract them from an [Instant]: + * + * ``` + * Clock.System.now() + 5.seconds // 5 seconds from now + * ``` + * + * Durations can also be represented as multiples of some [time-based datetime unit][DateTimeUnit.TimeBased]: + * + * ``` + * Clock.System.now().plus(4, DateTimeUnit.HOUR) // 4 hours from now + * ``` + * + * Also, there is a [minus] operator that returns the [Duration] representing the difference between two instants: + * + * ``` + * val start = Clock.System.now() + * val concertStart = LocalDateTime(2023, 1, 1, 20, 0, 0).toInstant(TimeZone.of("Europe/Berlin")) + * val timeUntilConcert = concertStart - start + * ``` + * + * #### Calendar-based + * + * Since [Instant] represents a point in time, it is always well-defined what the result of arithmetic operations on it + * is, including the cases when a calendar is used. + * This is not the case for [LocalDateTime], where the result of arithmetic operations depends on the time zone. + * See the [LocalDateTime] documentation for more details. + * + * Adding and subtracting calendar-based units can be done using the [plus] and [minus] operators, + * requiring a [TimeZone]: + * + * ``` + * // One day from now in Berlin + * Clock.System.now().plus(1, DateTimeUnit.DAY, TimeZone.of("Europe/Berlin")) + * + * // A day and two hours short from two months later in Berlin + * Clock.System.now().plus(DateTimePeriod(months = 2, days = -1, hours = -2), TimeZone.of("Europe/Berlin")) + * ``` + * + * The difference between [Instant] values in terms of calendar-based units can be obtained using the [periodUntil] + * method: + * + * ``` + * val start = Clock.System.now() + * val concertStart = LocalDateTime(2023, 1, 1, 20, 0, 0).toInstant(TimeZone.of("Europe/Berlin")) + * val timeUntilConcert = start.periodUntil(concertStart, TimeZone.of("Europe/Berlin")) + * // Two months, three days, four hours, and five minutes until the concert + * ``` + * + * Or the [Instant.until] method, as well as [Instant.daysUntil], [Instant.monthsUntil], + * and [Instant.yearsUntil] extension functions: + * + * ``` + * val start = Clock.System.now() + * val concertStart = LocalDateTime(2023, 1, 1, 20, 0, 0).toInstant(TimeZone.of("Europe/Berlin")) + * val timeUntilConcert = start.until(concertStart, DateTimeUnit.DAY, TimeZone.of("Europe/Berlin")) + * // 63 days until the concert, rounded down + * ``` + * + * ### Platform specifics + * + * On the JVM, there are `Instant.toJavaInstant()` and `java.time.Instant.toKotlinInstant()` + * extension functions to convert between `kotlinx.datetime` and `java.time` objects used for the same purpose. + * Similarly, on the Darwin platforms, there are `Instant.toNSDate()` and `NSDate.toKotlinInstant()` + * extension functions. + * + * ### Construction, serialization, and deserialization + * + * [fromEpochSeconds] can be used to construct an instant from the number of seconds since + * `1970-01-01T00:00:00Z` (the Unix epoch). + * [epochSeconds] and [nanosecondsOfSecond] can be used to obtain the number of seconds and nanoseconds since the epoch. + * + * ``` + * val instant = Instant.fromEpochSeconds(1709898983, 123456789) + * instant.epochSeconds // 1709898983 + * instant.nanosecondsOfSecond // 123456789 + * ``` + * + * [fromEpochMilliseconds] allows constructing an instant from the number of milliseconds since the epoch. + * [toEpochMilliseconds] can be used to obtain the number of milliseconds since the epoch. + * Note that [Instant] supports nanosecond precision, so converting to milliseconds is a lossy operation. + * + * ``` + * val instant1 = Instant.fromEpochSeconds(1709898983, 123456789) + * instant1.nanosecondsOfSecond // 123456789 + * val milliseconds = instant1.toEpochMilliseconds() // 1709898983123 + * val instant2 = Instant.fromEpochMilliseconds(milliseconds) + * instant2.nanosecondsOfSecond // 123000000 + * ``` + * + * [parse] and [toString] methods can be used to obtain an [Instant] from and convert it to a string in the + * ISO 8601 extended format. + * + * ``` + * val instant = Instant.parse("2023-01-02T22:35:01+01:00") + * instant.toString() // 2023-01-02T21:35:01Z + * ``` + * + * During parsing, the UTC offset is not returned separately. + * If the UTC offset is important, use [DateTimeComponents] with [DateTimeComponents.Formats.ISO_DATE_TIME_OFFSET] to + * parse the string instead. + * + * [Instant.parse] and [Instant.format] also accept custom formats: + * + * ``` + * val customFormat = DateTimeComponents.Format { + * date(LocalDate.Formats.ISO) + * char(' ') + * time(LocalTime.Formats.ISO) + * char(' ') + * offset(UtcOffset.Formats.ISO) + * } + * val instant = Instant.parse("2023-01-02 22:35:01.14 +01:00", customFormat) + * instant.format(customFormat, offset = UtcOffset(hours = 2)) // 2023-01-02 23:35:01.14 +02:00 + * ``` + * + * Additionally, there are several `kotlinx-serialization` serializers for [Instant]: + * - [InstantIso8601Serializer] for the ISO 8601 extended format. + * - [InstantComponentSerializer] for an object with components. + * + * @see LocalDateTime for a user-visible representation of moments in time in an unspecified time zone. + */ +@Serializable(with = InstantIso8601Serializer::class) +public expect class Instant : Comparable { + + /** + * The number of seconds from the epoch instant `1970-01-01T00:00:00Z` rounded down to a [Long] number. + * + * The difference between the rounded number of seconds and the actual number of seconds + * is returned by [nanosecondsOfSecond] property expressed in nanoseconds. + * + * Note that this number doesn't include leap seconds added or removed since the epoch. + * + * @see fromEpochSeconds + * @sample kotlinx.datetime.test.samples.InstantSamples.epochSeconds + */ + public val epochSeconds: Long + + /** + * The number of nanoseconds by which this instant is later than [epochSeconds] from the epoch instant. + * + * The value is always non-negative and lies in the range `0..999_999_999`. + * + * @see fromEpochSeconds + * @sample kotlinx.datetime.test.samples.InstantSamples.nanosecondsOfSecond + */ + public val nanosecondsOfSecond: Int + + /** + * Returns the number of milliseconds from the epoch instant `1970-01-01T00:00:00Z`. + * + * Any fractional part of a millisecond is rounded toward zero to the whole number of milliseconds. + * + * If the result does not fit in [Long], + * returns [Long.MAX_VALUE] for a positive result or [Long.MIN_VALUE] for a negative result. + * + * @see fromEpochMilliseconds + * @sample kotlinx.datetime.test.samples.InstantSamples.toEpochMilliseconds + */ + public fun toEpochMilliseconds(): Long + + /** + * Returns an instant that is the result of adding the specified [duration] to this instant. + * + * If the [duration] is positive, the returned instant is later than this instant. + * If the [duration] is negative, the returned instant is earlier than this instant. + * + * The return value is clamped to the boundaries of [Instant] if the result exceeds them. + * + * **Pitfall**: [Duration.Companion.days] are multiples of 24 hours and are not calendar-based. + * Consider using the [plus] overload that accepts a multiple of a [DateTimeUnit] instead for calendar-based + * operations instead of using [Duration]. + * For an explanation of why some days are not 24 hours, see [DateTimeUnit.DayBased]. + * + * @sample kotlinx.datetime.test.samples.InstantSamples.plusDuration + */ + public operator fun plus(duration: Duration): Instant + + /** + * Returns an instant that is the result of subtracting the specified [duration] from this instant. + * + * If the [duration] is positive, the returned instant is earlier than this instant. + * If the [duration] is negative, the returned instant is later than this instant. + * + * The return value is clamped to the boundaries of [Instant] if the result exceeds them. + * + * **Pitfall**: [Duration.Companion.days] are multiples of 24 hours and are not calendar-based. + * Consider using the [minus] overload that accepts a multiple of a [DateTimeUnit] instead for calendar-based + * operations instead of using [Duration]. + * For an explanation of why some days are not 24 hours, see [DateTimeUnit.DayBased]. + * + * @sample kotlinx.datetime.test.samples.InstantSamples.minusDuration + */ + public operator fun minus(duration: Duration): Instant + + // questionable + /** + * Returns the [Duration] between two instants: [other] and `this`. + * + * The duration returned is positive if this instant is later than the other, + * and negative if this instant is earlier than the other. + * + * The result is never clamped, but note that for instants that are far apart, + * the value returned may represent the duration between them inexactly due to the loss of precision. + * + * Note that sources of [Instant] values (in particular, [Clock]) are not guaranteed to be in sync with each other + * or even monotonic, so the result of this operation may be negative even if the other instant was observed later + * than this one, or vice versa. + * For measuring time intervals, consider using [TimeSource.Monotonic]. + * + * @sample kotlinx.datetime.test.samples.InstantSamples.minusInstant + */ + public operator fun minus(other: Instant): Duration + + /** + * Compares `this` instant with the [other] instant. + * Returns zero if this instant represents the same moment as the other (meaning they are equal to one another), + * a negative number if this instant is earlier than the other, + * and a positive number if this instant is later than the other. + * + * @sample kotlinx.datetime.test.samples.InstantSamples.compareToSample + */ + public override operator fun compareTo(other: Instant): Int + + /** + * Converts this instant to the ISO 8601 string representation, for example, `2023-01-02T23:40:57.120Z`. + * + * The representation uses the UTC-SLS time scale instead of UTC. + * In practice, this means that leap second handling will not be readjusted to the UTC. + * Leap seconds will not be added or skipped, so it is impossible to acquire a string + * where the component for seconds is 60, and for any day, it's possible to observe 23:59:59. + * + * @see parse + * @see DateTimeComponents.Formats.ISO_DATE_TIME_OFFSET for a very similar format. The difference is that + * [DateTimeComponents.Formats.ISO_DATE_TIME_OFFSET] will not add trailing zeros for readability to the + * fractional part of the second. + * @sample kotlinx.datetime.test.samples.InstantSamples.toStringSample + */ + public override fun toString(): String + + + public companion object { + @Deprecated("Use Clock.System.now() instead", ReplaceWith("Clock.System.now()", "kotlinx.datetime.Clock"), level = DeprecationLevel.ERROR) + public fun now(): Instant + + /** + * Returns an [Instant] that is [epochMilliseconds] number of milliseconds from the epoch instant `1970-01-01T00:00:00Z`. + * + * Every value of [epochMilliseconds] is guaranteed to be representable as an [Instant]. + * + * Note that [Instant] also supports nanosecond precision via [fromEpochSeconds]. + * + * @see Instant.toEpochMilliseconds + * @sample kotlinx.datetime.test.samples.InstantSamples.fromEpochMilliseconds + */ + public fun fromEpochMilliseconds(epochMilliseconds: Long): Instant + + /** + * Returns an [Instant] that is the [epochSeconds] number of seconds from the epoch instant `1970-01-01T00:00:00Z` + * and the [nanosecondAdjustment] number of nanoseconds from the whole second. + * + * The return value is clamped to the boundaries of [Instant] if the result exceeds them. + * In any case, it is guaranteed that instants between [DISTANT_PAST] and [DISTANT_FUTURE] can be represented. + * + * [fromEpochMilliseconds] is a similar function for when input data only has millisecond precision. + * + * @see Instant.epochSeconds + * @see Instant.nanosecondsOfSecond + * @sample kotlinx.datetime.test.samples.InstantSamples.fromEpochSeconds + */ + public fun fromEpochSeconds(epochSeconds: Long, nanosecondAdjustment: Long = 0): Instant + + /** + * Returns an [Instant] that is the [epochSeconds] number of seconds from the epoch instant `1970-01-01T00:00:00Z` + * and the [nanosecondAdjustment] number of nanoseconds from the whole second. + * + * The return value is clamped to the boundaries of [Instant] if the result exceeds them. + * In any case, it is guaranteed that instants between [DISTANT_PAST] and [DISTANT_FUTURE] can be represented. + * + * [fromEpochMilliseconds] is a similar function for when input data only has millisecond precision. + * + * @see Instant.epochSeconds + * @see Instant.nanosecondsOfSecond + * @sample kotlinx.datetime.test.samples.InstantSamples.fromEpochSecondsIntNanos + */ + public fun fromEpochSeconds(epochSeconds: Long, nanosecondAdjustment: Int): Instant + + /** + * A shortcut for calling [DateTimeFormat.parse], followed by [DateTimeComponents.toInstantUsingOffset]. + * + * Parses a string that represents an instant, including date and time components and a mandatory + * time zone offset and returns the parsed [Instant] value. + * + * The string is considered to represent time on the UTC-SLS time scale instead of UTC. + * In practice, this means that, even if there is a leap second on the given day, it will not affect how the + * time is parsed, even if it's in the last 1000 seconds of the day. + * Instead, even if there is a negative leap second on the given day, 23:59:59 is still considered a valid time. + * 23:59:60 is invalid on UTC-SLS, so parsing it will fail. + * + * If the format is not specified, [DateTimeComponents.Formats.ISO_DATE_TIME_OFFSET] is used. + * `2023-01-02T23:40:57.120Z` is an example of a string in this format. + * + * @throws IllegalArgumentException if the text cannot be parsed or the boundaries of [Instant] are exceeded. + * + * @see Instant.toString for formatting using the default format. + * @see Instant.format for formatting using a custom format. + * @sample kotlinx.datetime.test.samples.InstantSamples.parsing + */ + public fun parse( + input: CharSequence, + format: DateTimeFormat = DateTimeComponents.Formats.ISO_DATE_TIME_OFFSET + ): Instant + + /** + * An instant value that is far in the past. + * + * All instants in the range `DISTANT_PAST..DISTANT_FUTURE` can be [converted][Instant.toLocalDateTime] to + * [LocalDateTime] without exceptions in every time zone. + * + * [isDistantPast] returns true for this value and all earlier ones. + */ + public val DISTANT_PAST: Instant // -100001-12-31T23:59:59.999999999Z + + /** + * An instant value that is far in the future. + * + * All instants in the range `DISTANT_PAST..DISTANT_FUTURE` can be [converted][Instant.toLocalDateTime] to + * [LocalDateTime] without exceptions in every time zone. + * + * [isDistantFuture] returns true for this value and all later ones. + */ + public val DISTANT_FUTURE: Instant // +100000-01-01T00:00:00Z + + internal val MIN: Instant + internal val MAX: Instant + } +} + +/** + * Returns true if the instant is [Instant.DISTANT_PAST] or earlier. + * + * @sample kotlinx.datetime.test.samples.InstantSamples.isDistantPast + */ +public val Instant.isDistantPast: Boolean + get() = this <= Instant.DISTANT_PAST + +/** + * Returns true if the instant is [Instant.DISTANT_FUTURE] or later. + * + * @sample kotlinx.datetime.test.samples.InstantSamples.isDistantFuture + */ +public val Instant.isDistantFuture: Boolean + get() = this >= Instant.DISTANT_FUTURE + +/** + * @suppress + */ +@Deprecated("Removed to support more idiomatic code. See https://github.com/Kotlin/kotlinx-datetime/issues/339", ReplaceWith("Instant.parse(this)"), DeprecationLevel.WARNING) +public fun String.toInstant(): Instant = Instant.parse(this) + +/** + * Returns an instant that is the result of adding components of [DateTimePeriod] to this instant. The components are + * added in the order from the largest units to the smallest, i.e., from years to nanoseconds. + * + * - If the [DateTimePeriod] only contains time-based components, please consider adding a [Duration] instead, + * as in `Clock.System.now() + 5.hours`. + * Then, it will not be necessary to pass the [timeZone]. + * - If the [DateTimePeriod] only has a single non-zero component (only the months or only the days), + * please consider using a multiple of [DateTimeUnit.DAY] or [DateTimeUnit.MONTH], like in + * `Clock.System.now().plus(5, DateTimeUnit.DAY, TimeZone.currentSystemDefault())`. + * + * @throws DateTimeArithmeticException if this value or the results of intermediate computations are too large to fit in + * [LocalDateTime]. + * @sample kotlinx.datetime.test.samples.InstantSamples.plusPeriod + */ +public expect fun Instant.plus(period: DateTimePeriod, timeZone: TimeZone): Instant + +/** + * Returns an instant that is the result of subtracting components of [DateTimePeriod] from this instant. The components + * are subtracted in the order from the largest units to the smallest, i.e., from years to nanoseconds. + * + * - If the [DateTimePeriod] only contains time-based components, please consider subtracting a [Duration] instead, + * as in `Clock.System.now() - 5.hours`. + * Then, it is not necessary to pass the [timeZone]. + * - If the [DateTimePeriod] only has a single non-zero component (only the months or only the days), + * please consider using a multiple of [DateTimeUnit.DAY] or [DateTimeUnit.MONTH], as in + * `Clock.System.now().minus(5, DateTimeUnit.DAY, TimeZone.currentSystemDefault())`. + * + * @throws DateTimeArithmeticException if this value or the results of intermediate computations are too large to fit in + * [LocalDateTime]. + * @sample kotlinx.datetime.test.samples.InstantSamples.minusPeriod + */ +public fun Instant.minus(period: DateTimePeriod, timeZone: TimeZone): Instant = + /* An overflow can happen for any component, but we are only worried about nanoseconds, as having an overflow in + any other component means that `plus` will throw due to the minimum value of the numeric type overflowing the + `Instant` limits. */ + if (period.totalNanoseconds != Long.MIN_VALUE) { + val negatedPeriod = with(period) { buildDateTimePeriod(-totalMonths, -days, -totalNanoseconds) } + plus(negatedPeriod, timeZone) + } else { + val negatedPeriod = with(period) { buildDateTimePeriod(-totalMonths, -days, -(totalNanoseconds+1)) } + plus(negatedPeriod, timeZone).plus(1, DateTimeUnit.NANOSECOND) + } + +/** + * Returns a [DateTimePeriod] representing the difference between `this` and [other] instants. + * + * The components of [DateTimePeriod] are calculated so that adding it to `this` instant results in the [other] instant. + * + * All components of the [DateTimePeriod] returned are: + * - Positive or zero if this instant is earlier than the other. + * - Negative or zero if this instant is later than the other. + * - Exactly zero if this instant is equal to the other. + * + * @throws DateTimeArithmeticException if `this` or [other] instant is too large to fit in [LocalDateTime]. + * @sample kotlinx.datetime.test.samples.InstantSamples.periodUntil + */ +public expect fun Instant.periodUntil(other: Instant, timeZone: TimeZone): DateTimePeriod + +/** + * Returns the whole number of the specified date or time [units][unit] between `this` and [other] instants + * in the specified [timeZone]. + * + * The value returned is: + * - Positive or zero if this instant is earlier than the other. + * - Negative or zero if this instant is later than the other. + * - Zero if this instant is equal to the other. + * + * If the result does not fit in [Long], returns [Long.MAX_VALUE] for a positive result or [Long.MIN_VALUE] for a negative result. + * + * @throws DateTimeArithmeticException if `this` or [other] instant is too large to fit in [LocalDateTime]. + * @sample kotlinx.datetime.test.samples.InstantSamples.untilAsDateTimeUnit + */ +public expect fun Instant.until(other: Instant, unit: DateTimeUnit, timeZone: TimeZone): Long + +/** + * Returns the whole number of the specified time [units][unit] between `this` and [other] instants. + * + * The value returned is: + * - Positive or zero if this instant is earlier than the other. + * - Negative or zero if this instant is later than the other. + * - Zero if this instant is equal to the other. + * + * If the result does not fit in [Long], returns [Long.MAX_VALUE] for a positive result or [Long.MIN_VALUE] for a negative result. + * + * @sample kotlinx.datetime.test.samples.InstantSamples.untilAsTimeBasedUnit + */ +public fun Instant.until(other: Instant, unit: DateTimeUnit.TimeBased): Long = + try { + multiplyAddAndDivide(other.epochSeconds - epochSeconds, + NANOS_PER_ONE.toLong(), + (other.nanosecondsOfSecond - nanosecondsOfSecond).toLong(), + unit.nanoseconds) + } catch (_: ArithmeticException) { + if (this < other) Long.MAX_VALUE else Long.MIN_VALUE + } + +/** + * Returns the number of whole days between two instants in the specified [timeZone]. + * + * If the result does not fit in [Int], returns [Int.MAX_VALUE] for a positive result or [Int.MIN_VALUE] for a negative result. + * + * @see Instant.until + * @throws DateTimeArithmeticException if `this` or [other] instant is too large to fit in [LocalDateTime]. + * @sample kotlinx.datetime.test.samples.InstantSamples.daysUntil + */ +public fun Instant.daysUntil(other: Instant, timeZone: TimeZone): Int = + until(other, DateTimeUnit.DAY, timeZone).clampToInt() + +/** + * Returns the number of whole months between two instants in the specified [timeZone]. + * + * If the result does not fit in [Int], returns [Int.MAX_VALUE] for a positive result or [Int.MIN_VALUE] for a negative result. + * + * @see Instant.until + * @throws DateTimeArithmeticException if `this` or [other] instant is too large to fit in [LocalDateTime]. + * @sample kotlinx.datetime.test.samples.InstantSamples.monthsUntil + */ +public fun Instant.monthsUntil(other: Instant, timeZone: TimeZone): Int = + until(other, DateTimeUnit.MONTH, timeZone).clampToInt() + +/** + * Returns the number of whole years between two instants in the specified [timeZone]. + * + * If the result does not fit in [Int], returns [Int.MAX_VALUE] for a positive result or [Int.MIN_VALUE] for a negative result. + * + * @see Instant.until + * @throws DateTimeArithmeticException if `this` or [other] instant is too large to fit in [LocalDateTime]. + * @sample kotlinx.datetime.test.samples.InstantSamples.yearsUntil + */ +public fun Instant.yearsUntil(other: Instant, timeZone: TimeZone): Int = + until(other, DateTimeUnit.YEAR, timeZone).clampToInt() + +/** + * Returns a [DateTimePeriod] representing the difference between [other] and `this` instants. + * + * The components of [DateTimePeriod] are calculated so that adding it back to the `other` instant results in this instant. + * + * All components of the [DateTimePeriod] returned are: + * - Negative or zero if this instant is earlier than the other. + * - Positive or zero if this instant is later than the other. + * - Exactly zero if this instant is equal to the other. + * + * @throws DateTimeArithmeticException if `this` or [other] instant is too large to fit in [LocalDateTime]. + * @see Instant.periodUntil + * @sample kotlinx.datetime.test.samples.InstantSamples.minusInstantInZone + */ +public fun Instant.minus(other: Instant, timeZone: TimeZone): DateTimePeriod = + other.periodUntil(this, timeZone) + + +/** + * Returns an instant that is the result of adding one [unit] to this instant + * in the specified [timeZone]. + * + * The returned instant is later than this instant. + * + * @throws DateTimeArithmeticException if this value or the result is too large to fit in [LocalDateTime]. + */ +@Deprecated("Use the plus overload with an explicit number of units", ReplaceWith("this.plus(1, unit, timeZone)")) +public expect fun Instant.plus(unit: DateTimeUnit, timeZone: TimeZone): Instant + +/** + * Returns an instant that is the result of subtracting one [unit] from this instant + * in the specified [timeZone]. + * + * The returned instant is earlier than this instant. + * + * @throws DateTimeArithmeticException if this value or the result is too large to fit in [LocalDateTime]. + */ +@Deprecated("Use the minus overload with an explicit number of units", ReplaceWith("this.minus(1, unit, timeZone)")) +public fun Instant.minus(unit: DateTimeUnit, timeZone: TimeZone): Instant = + plus(-1, unit, timeZone) + +/** + * Returns an instant that is the result of adding one [unit] to this instant. + * + * The returned instant is later than this instant. + * + * The return value is clamped to the boundaries of [Instant] if the result exceeds them. + */ +@Deprecated("Use the plus overload with an explicit number of units", ReplaceWith("this.plus(1, unit)")) +public fun Instant.plus(unit: DateTimeUnit.TimeBased): Instant = + plus(1L, unit) + +/** + * Returns an instant that is the result of subtracting one [unit] from this instant. + * + * The returned instant is earlier than this instant. + * + * The return value is clamped to the boundaries of [Instant] if the result exceeds them. + */ +@Deprecated("Use the minus overload with an explicit number of units", ReplaceWith("this.minus(1, unit)")) +public fun Instant.minus(unit: DateTimeUnit.TimeBased): Instant = + plus(-1L, unit) + +/** + * Returns an instant that is the result of adding the [value] number of the specified [unit] to this instant + * in the specified [timeZone]. + * + * If the [value] is positive, the returned instant is later than this instant. + * If the [value] is negative, the returned instant is earlier than this instant. + * + * Note that the time zone does not need to be passed when the [unit] is a time-based unit. + * It is also not needed when adding date-based units to a [LocalDate][LocalDate.plus]. + * + * @throws DateTimeArithmeticException if this value or the result is too large to fit in [LocalDateTime]. + * @sample kotlinx.datetime.test.samples.InstantSamples.plusDateTimeUnit + */ +public expect fun Instant.plus(value: Int, unit: DateTimeUnit, timeZone: TimeZone): Instant + +/** + * Returns an instant that is the result of subtracting the [value] number of the specified [unit] from this instant + * in the specified [timeZone]. + * + * If the [value] is positive, the returned instant is earlier than this instant. + * If the [value] is negative, the returned instant is later than this instant. + * + * Note that the time zone does not need to be passed when the [unit] is a time-based unit. + * It is also not needed when subtracting date-based units from a [LocalDate]. + * + * If the [value] is positive, the returned instant is earlier than this instant. + * If the [value] is negative, the returned instant is later than this instant. + * + * @throws DateTimeArithmeticException if this value or the result is too large to fit in [LocalDateTime]. + * @sample kotlinx.datetime.test.samples.InstantSamples.minusDateTimeUnit + */ +public expect fun Instant.minus(value: Int, unit: DateTimeUnit, timeZone: TimeZone): Instant + +/** + * Returns an instant that is the result of adding the [value] number of the specified [unit] to this instant. + * + * If the [value] is positive, the returned instant is later than this instant. + * If the [value] is negative, the returned instant is earlier than this instant. + * + * The return value is clamped to the boundaries of [Instant] if the result exceeds them. + * + * @sample kotlinx.datetime.test.samples.InstantSamples.plusTimeBasedUnit + */ +public fun Instant.plus(value: Int, unit: DateTimeUnit.TimeBased): Instant = + plus(value.toLong(), unit) + +/** + * Returns an instant that is the result of subtracting the [value] number of the specified [unit] from this instant. + * + * If the [value] is positive, the returned instant is earlier than this instant. + * If the [value] is negative, the returned instant is later than this instant. + * + * The return value is clamped to the boundaries of [Instant] if the result exceeds them. + * + * @sample kotlinx.datetime.test.samples.InstantSamples.minusTimeBasedUnit + */ +public fun Instant.minus(value: Int, unit: DateTimeUnit.TimeBased): Instant = + minus(value.toLong(), unit) + +/** + * Returns an instant that is the result of adding the [value] number of the specified [unit] to this instant + * in the specified [timeZone]. + * + * If the [value] is positive, the returned instant is later than this instant. + * If the [value] is negative, the returned instant is earlier than this instant. + * + * Note that the time zone does not need to be passed when the [unit] is a time-based unit. + * It is also not needed when adding date-based units to a [LocalDate]. + * + * @throws DateTimeArithmeticException if this value or the result is too large to fit in [LocalDateTime]. + * @sample kotlinx.datetime.test.samples.InstantSamples.plusDateTimeUnit + */ +public expect fun Instant.plus(value: Long, unit: DateTimeUnit, timeZone: TimeZone): Instant + +/** + * Returns an instant that is the result of subtracting the [value] number of the specified [unit] from this instant + * in the specified [timeZone]. + * + * If the [value] is positive, the returned instant is earlier than this instant. + * If the [value] is negative, the returned instant is later than this instant. + * + * Note that the time zone does not need to be passed when the [unit] is a time-based unit. + * It is also not needed when subtracting date-based units from a [LocalDate]. + * + * @throws DateTimeArithmeticException if this value or the result is too large to fit in [LocalDateTime]. + * @sample kotlinx.datetime.test.samples.InstantSamples.minusDateTimeUnit + */ +public fun Instant.minus(value: Long, unit: DateTimeUnit, timeZone: TimeZone): Instant = + if (value != Long.MIN_VALUE) { + plus(-value, unit, timeZone) + } else { + plus(-(value + 1), unit, timeZone).plus(1, unit, timeZone) + } + +/** + * Returns an instant that is the result of adding the [value] number of the specified [unit] to this instant. + * + * If the [value] is positive, the returned instant is later than this instant. + * If the [value] is negative, the returned instant is earlier than this instant. + * + * The return value is clamped to the boundaries of [Instant] if the result exceeds them. + * + * @sample kotlinx.datetime.test.samples.InstantSamples.plusTimeBasedUnit + */ +public expect fun Instant.plus(value: Long, unit: DateTimeUnit.TimeBased): Instant + +/** + * Returns an instant that is the result of subtracting the [value] number of the specified [unit] from this instant. + * + * If the [value] is positive, the returned instant is earlier than this instant. + * If the [value] is negative, the returned instant is later than this instant. + * + * The return value is clamped to the boundaries of [Instant] if the result exceeds them. + * + * @sample kotlinx.datetime.test.samples.InstantSamples.minusTimeBasedUnit + */ +public fun Instant.minus(value: Long, unit: DateTimeUnit.TimeBased): Instant = + if (value != Long.MIN_VALUE) { + plus(-value, unit) + } else { + plus(-(value + 1), unit).plus(1, unit) + } + +/** + * Returns the whole number of the specified date or time [units][unit] between [other] and `this` instants + * in the specified [timeZone]. + * + * The value returned is negative or zero if this instant is earlier than the other, + * and positive or zero if this instant is later than the other. + * + * If the result does not fit in [Long], returns [Long.MAX_VALUE] for a positive result or [Long.MIN_VALUE] for a negative result. + * + * @throws DateTimeArithmeticException if `this` or [other] instant is too large to fit in [LocalDateTime]. + * @see Instant.until for the same operation but with swapped arguments. + * @sample kotlinx.datetime.test.samples.InstantSamples.minusAsDateTimeUnit + */ +public fun Instant.minus(other: Instant, unit: DateTimeUnit, timeZone: TimeZone): Long = + other.until(this, unit, timeZone) + +/** + * Returns the whole number of the specified time [units][unit] between [other] and `this` instants. + * + * The value returned is negative or zero if this instant is earlier than the other, + * and positive or zero if this instant is later than the other. + * + * If the result does not fit in [Long], returns [Long.MAX_VALUE] for a positive result or [Long.MIN_VALUE] for a negative result. + * + * @see Instant.until for the same operation but with swapped arguments. + * @sample kotlinx.datetime.test.samples.InstantSamples.minusAsTimeBasedUnit + */ +public fun Instant.minus(other: Instant, unit: DateTimeUnit.TimeBased): Long = + other.until(this, unit) + +/** + * Formats this value using the given [format] using the given [offset]. + * + * Equivalent to calling [DateTimeFormat.format] on [format] and using [DateTimeComponents.setDateTimeOffset] in + * the lambda. + * + * [DateTimeComponents.Formats.ISO_DATE_TIME_OFFSET] is a format very similar to the one used by [toString]. + * The only difference is that [Instant.toString] adds trailing zeros to the fraction-of-second component so that the + * number of digits after a dot is a multiple of three. + * + * @sample kotlinx.datetime.test.samples.InstantSamples.formatting + */ +public fun Instant.format(format: DateTimeFormat, offset: UtcOffset = UtcOffset.ZERO): String { + val instant = this + return format.format { setDateTimeOffset(instant, offset) } +} + +internal const val DISTANT_PAST_SECONDS = -3217862419201 +internal const val DISTANT_FUTURE_SECONDS = 3093527980800 diff --git a/fake-kotlinx-time/js/src/Converters.kt b/fake-kotlinx-time/js/src/Converters.kt new file mode 100644 index 000000000..a4a8c9df9 --- /dev/null +++ b/fake-kotlinx-time/js/src/Converters.kt @@ -0,0 +1,21 @@ +/* + * Copyright 2019-2022 JetBrains s.r.o. and contributors. + * Use of this source code is governed by the Apache 2.0 License that can be found in the LICENSE.txt file. + */ + +package kotlinx.datetime + +import kotlin.js.* + +/** + * Converts the [Instant] to an instance of JS [Date]. + * + * The conversion is lossy: JS uses millisecond precision to represent dates, and [Instant] allows for nanosecond + * resolution. + */ +public fun Instant.toJSDate(): Date = Date(milliseconds = toEpochMilliseconds().toDouble()) + +/** + * Converts the JS [Date] to the corresponding [Instant]. + */ +public fun Date.toKotlinInstant(): Instant = Instant.fromEpochMilliseconds(getTime().toLong()) diff --git a/fake-kotlinx-time/jvm/src/Converters.kt b/fake-kotlinx-time/jvm/src/Converters.kt new file mode 100644 index 000000000..6db8339d3 --- /dev/null +++ b/fake-kotlinx-time/jvm/src/Converters.kt @@ -0,0 +1,95 @@ +/* + * Copyright 2019-2022 JetBrains s.r.o. and contributors. + * Use of this source code is governed by the Apache 2.0 License that can be found in the LICENSE.txt file. + */ + +package kotlinx.datetime + +/** + * Converts this [kotlinx.datetime.Instant][Instant] value to a [java.time.Instant][java.time.Instant] value. + */ +public fun Instant.toJavaInstant(): java.time.Instant = this.value + +/** + * Converts this [java.time.Instant][java.time.Instant] value to a [kotlinx.datetime.Instant][Instant] value. + */ +public fun java.time.Instant.toKotlinInstant(): Instant = Instant(this) + + +/** + * Converts this [kotlinx.datetime.LocalDateTime][LocalDateTime] value to a [java.time.LocalDateTime][java.time.LocalDateTime] value. + */ +public fun LocalDateTime.toJavaLocalDateTime(): java.time.LocalDateTime = this.value + +/** + * Converts this [java.time.LocalDateTime][java.time.LocalDateTime] value to a [kotlinx.datetime.LocalDateTime][LocalDateTime] value. + */ +public fun java.time.LocalDateTime.toKotlinLocalDateTime(): LocalDateTime = LocalDateTime(this) + +/** + * Converts this [kotlinx.datetime.LocalDateTime][LocalTime] value to a [java.time.LocalTime][java.time.LocalTime] value. + */ +public fun LocalTime.toJavaLocalTime(): java.time.LocalTime = this.value + +/** + * Converts this [java.time.LocalTime][java.time.LocalTime] value to a [kotlinx.datetime.LocalTime][LocalTime] value. + */ +public fun java.time.LocalTime.toKotlinLocalTime(): LocalTime = LocalTime(this) + + +/** + * Converts this [kotlinx.datetime.LocalDate][LocalDate] value to a [java.time.LocalDate][java.time.LocalDate] value. + */ +public fun LocalDate.toJavaLocalDate(): java.time.LocalDate = this.value + +/** + * Converts this [java.time.LocalDate][java.time.LocalDate] value to a [kotlinx.datetime.LocalDate][LocalDate] value. + */ +public fun java.time.LocalDate.toKotlinLocalDate(): LocalDate = LocalDate(this) + + +/** + * Converts this [kotlinx.datetime.DatePeriod][DatePeriod] value to a [java.time.Period][java.time.Period] value. + */ +public fun DatePeriod.toJavaPeriod(): java.time.Period = java.time.Period.of(this.years, this.months, this.days) + +/** + * Converts this [java.time.Period][java.time.Period] value to a [kotlinx.datetime.DatePeriod][DatePeriod] value. + */ +public fun java.time.Period.toKotlinDatePeriod(): DatePeriod = DatePeriod(this.years, this.months, this.days) + + +/** + * Converts this [kotlinx.datetime.TimeZone][TimeZone] value to a [java.time.ZoneId][java.time.ZoneId] value. + */ +public fun TimeZone.toJavaZoneId(): java.time.ZoneId = this.zoneId + +/** + * Converts this [java.time.ZoneId][java.time.ZoneId] value to a [kotlinx.datetime.TimeZone][TimeZone] value. + */ +public fun java.time.ZoneId.toKotlinTimeZone(): TimeZone = TimeZone.ofZone(this) + + +/** + * Converts this [kotlinx.datetime.FixedOffsetTimeZone][FixedOffsetTimeZone] value to a [java.time.ZoneOffset][java.time.ZoneOffset] value. + */ +public fun FixedOffsetTimeZone.toJavaZoneOffset(): java.time.ZoneOffset = this.offset.zoneOffset + +/** + * Converts this [java.time.ZoneOffset][java.time.ZoneOffset] value to a [kotlinx.datetime.FixedOffsetTimeZone][FixedOffsetTimeZone] value. + */ +public fun java.time.ZoneOffset.toKotlinFixedOffsetTimeZone(): FixedOffsetTimeZone = FixedOffsetTimeZone(UtcOffset(this)) + +@Deprecated("Use toKotlinFixedOffsetTimeZone() instead.", ReplaceWith("this.toKotlinFixedOffsetTimeZone()")) +public fun java.time.ZoneOffset.toKotlinZoneOffset(): FixedOffsetTimeZone = toKotlinFixedOffsetTimeZone() + +/** + * Converts this [kotlinx.datetime.UtcOffset][UtcOffset] value to a [java.time.ZoneOffset][java.time.ZoneOffset] value. + */ +public fun UtcOffset.toJavaZoneOffset(): java.time.ZoneOffset = this.zoneOffset + +/** + * Converts this [java.time.ZoneOffset][java.time.ZoneOffset] value to a [kotlinx.datetime.UtcOffset][UtcOffset] value. + */ +public fun java.time.ZoneOffset.toKotlinUtcOffset(): UtcOffset = UtcOffset(this) + diff --git a/settings.gradle.kts b/settings.gradle.kts index a342b81fd..84e43653b 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -23,3 +23,5 @@ project(":serialization").name = "kotlinx-datetime-serialization" include(":js-without-timezones") project(":js-without-timezones").name = "kotlinx-datetime-js-test-without-timezones" include(":benchmarks") + +include(":fake-kotlinx-time") From 75ba1859ea0ab79186439df3254fe468b5e8281b Mon Sep 17 00:00:00 2001 From: Dmitry Khalanskiy Date: Thu, 28 Nov 2024 13:01:10 +0100 Subject: [PATCH 2/7] Adapt the kotlinx.time classes API for the stdlib --- fake-kotlinx-time/common/src/Clock.kt | 69 +-- fake-kotlinx-time/common/src/Instant.kt | 546 ++---------------------- fake-kotlinx-time/js/src/Converters.kt | 2 +- fake-kotlinx-time/jvm/src/Converters.kt | 90 +--- 4 files changed, 39 insertions(+), 668 deletions(-) diff --git a/fake-kotlinx-time/common/src/Clock.kt b/fake-kotlinx-time/common/src/Clock.kt index e9cd9137e..c5e2feec7 100644 --- a/fake-kotlinx-time/common/src/Clock.kt +++ b/fake-kotlinx-time/common/src/Clock.kt @@ -3,7 +3,7 @@ * Use of this source code is governed by the Apache 2.0 License that can be found in the LICENSE.txt file. */ -package kotlinx.datetime +package kotlinx.time import kotlin.time.* @@ -59,70 +59,3 @@ public interface Clock { } } - -/** - * Returns the current date at the given [time zone][timeZone], according to [this Clock][this]. - * - * The time zone is important because the current date is not the same in all time zones at the same instant. - * - * @sample kotlinx.datetime.test.samples.ClockSamples.todayIn - */ -public fun Clock.todayIn(timeZone: TimeZone): LocalDate = - now().toLocalDateTime(timeZone).date - -/** - * Returns a [TimeSource] that uses this [Clock] to mark a time instant and to find the amount of time elapsed since that mark. - * - * **Pitfall**: using this function with [Clock.System] is error-prone - * because [Clock.System] is not well suited for measuring time intervals. - * Please only use this conversion function on the [Clock] instances that are fully controlled programmatically. - */ -@ExperimentalTime -public fun Clock.asTimeSource(): TimeSource.WithComparableMarks = object : TimeSource.WithComparableMarks { - override fun markNow(): ComparableTimeMark = InstantTimeMark(now(), this@asTimeSource) -} - -@ExperimentalTime -private class InstantTimeMark(private val instant: Instant, private val clock: Clock) : ComparableTimeMark { - override fun elapsedNow(): Duration = saturatingDiff(clock.now(), instant) - - override fun plus(duration: Duration): ComparableTimeMark = InstantTimeMark(instant.saturatingAdd(duration), clock) - override fun minus(duration: Duration): ComparableTimeMark = InstantTimeMark(instant.saturatingAdd(-duration), clock) - - override fun minus(other: ComparableTimeMark): Duration { - if (other !is InstantTimeMark || other.clock != this.clock) { - throw IllegalArgumentException("Subtracting or comparing time marks from different time sources is not possible: $this and $other") - } - return saturatingDiff(this.instant, other.instant) - } - - override fun equals(other: Any?): Boolean { - return other is InstantTimeMark && this.clock == other.clock && this.instant == other.instant - } - - override fun hashCode(): Int = instant.hashCode() - - override fun toString(): String = "InstantTimeMark($instant, $clock)" - - private fun Instant.isSaturated() = this == Instant.MAX || this == Instant.MIN - private fun Instant.saturatingAdd(duration: Duration): Instant { - if (isSaturated()) { - if (duration.isInfinite() && duration.isPositive() != this.isDistantFuture) { - throw IllegalArgumentException("Summing infinities of different signs") - } - return this - } - return this + duration - } - private fun saturatingDiff(instant1: Instant, instant2: Instant): Duration = when { - instant1 == instant2 -> - Duration.ZERO - instant1.isSaturated() || instant2.isSaturated() -> - (instant1 - instant2) * Double.POSITIVE_INFINITY - else -> - instant1 - instant2 - } -} - -@Deprecated("Use Clock.todayIn instead", ReplaceWith("this.todayIn(timeZone)"), DeprecationLevel.WARNING) -public fun Clock.todayAt(timeZone: TimeZone): LocalDate = todayIn(timeZone) diff --git a/fake-kotlinx-time/common/src/Instant.kt b/fake-kotlinx-time/common/src/Instant.kt index 3deedd946..fcd4f7ffe 100644 --- a/fake-kotlinx-time/common/src/Instant.kt +++ b/fake-kotlinx-time/common/src/Instant.kt @@ -3,13 +3,8 @@ * Use of this source code is governed by the Apache 2.0 License that can be found in the LICENSE.txt file. */ -package kotlinx.datetime +package kotlinx.time -import kotlinx.datetime.format.* -import kotlinx.datetime.internal.* -import kotlinx.datetime.serializers.InstantIso8601Serializer -import kotlinx.datetime.serializers.InstantComponentSerializer -import kotlinx.serialization.Serializable import kotlin.time.* /** @@ -18,7 +13,7 @@ import kotlin.time.* * A point in time must be uniquely identified in a way that is independent of a time zone. * For example, `1970-01-01, 00:00:00` does not represent a moment in time since this would happen at different times * in different time zones: someone in Tokyo would think it is already `1970-01-01` several hours earlier than someone in - * Berlin would. To represent such entities, use [LocalDateTime]. + * Berlin would. To represent such entities, use the `LocalDateTime` from `kotlinx-datetime`. * In contrast, "the moment the clocks in London first showed 00:00 on Jan 1, 2000" is a specific moment * in time, as is "1970-01-01, 00:00:00 UTC+0", so it can be represented as an [Instant]. * @@ -40,103 +35,30 @@ import kotlin.time.* * so it should not be used for measuring time intervals. * For that, consider using [TimeSource.Monotonic] and [TimeMark] instead of [Clock.System] and [Instant]. * - * ### Obtaining human-readable representations - * - * #### Date and time - * - * [Instant] is essentially the number of seconds and nanoseconds since a designated moment in time, - * stored as something like `1709898983.123456789`. - * [Instant] does not contain information about the day or time, as this depends on the time zone. - * To work with this information for a specific time zone, obtain a [LocalDateTime] using [Instant.toLocalDateTime]: - * - * ``` - * val instant = Instant.fromEpochSeconds(1709898983, 123456789) - * instant.toLocalDateTime(TimeZone.of("Europe/Berlin")) // 2024-03-08T12:56:23.123456789 - * instant.toLocalDateTime(TimeZone.UTC) // 2024-03-08T11:56:23.123456789 - * ``` - * - * For values very far in the past or the future, this conversion may fail. - * The specific range of values that can be converted to [LocalDateTime] is unspecified, but at least - * [DISTANT_PAST], [DISTANT_FUTURE], and all values between them are included in that range. - * - * #### Date or time separately - * - * To obtain a [LocalDate] or [LocalTime], first, obtain a [LocalDateTime], and then use its [LocalDateTime.date] - * and [LocalDateTime.time] properties: - * - * ``` - * val instant = Instant.fromEpochSeconds(1709898983, 123456789) - * instant.toLocalDateTime(TimeZone.of("Europe/Berlin")).date // 2024-03-08 - * ``` - * * ### Arithmetic operations * - * #### Elapsed-time-based - * * The [plus] and [minus] operators can be used to add [Duration]s to and subtract them from an [Instant]: * * ``` * Clock.System.now() + 5.seconds // 5 seconds from now * ``` * - * Durations can also be represented as multiples of some [time-based datetime unit][DateTimeUnit.TimeBased]: - * - * ``` - * Clock.System.now().plus(4, DateTimeUnit.HOUR) // 4 hours from now - * ``` - * * Also, there is a [minus] operator that returns the [Duration] representing the difference between two instants: * * ``` - * val start = Clock.System.now() - * val concertStart = LocalDateTime(2023, 1, 1, 20, 0, 0).toInstant(TimeZone.of("Europe/Berlin")) - * val timeUntilConcert = concertStart - start - * ``` - * - * #### Calendar-based - * - * Since [Instant] represents a point in time, it is always well-defined what the result of arithmetic operations on it - * is, including the cases when a calendar is used. - * This is not the case for [LocalDateTime], where the result of arithmetic operations depends on the time zone. - * See the [LocalDateTime] documentation for more details. - * - * Adding and subtracting calendar-based units can be done using the [plus] and [minus] operators, - * requiring a [TimeZone]: - * - * ``` - * // One day from now in Berlin - * Clock.System.now().plus(1, DateTimeUnit.DAY, TimeZone.of("Europe/Berlin")) - * - * // A day and two hours short from two months later in Berlin - * Clock.System.now().plus(DateTimePeriod(months = 2, days = -1, hours = -2), TimeZone.of("Europe/Berlin")) - * ``` - * - * The difference between [Instant] values in terms of calendar-based units can be obtained using the [periodUntil] - * method: - * - * ``` - * val start = Clock.System.now() - * val concertStart = LocalDateTime(2023, 1, 1, 20, 0, 0).toInstant(TimeZone.of("Europe/Berlin")) - * val timeUntilConcert = start.periodUntil(concertStart, TimeZone.of("Europe/Berlin")) - * // Two months, three days, four hours, and five minutes until the concert - * ``` - * - * Or the [Instant.until] method, as well as [Instant.daysUntil], [Instant.monthsUntil], - * and [Instant.yearsUntil] extension functions: - * - * ``` - * val start = Clock.System.now() - * val concertStart = LocalDateTime(2023, 1, 1, 20, 0, 0).toInstant(TimeZone.of("Europe/Berlin")) - * val timeUntilConcert = start.until(concertStart, DateTimeUnit.DAY, TimeZone.of("Europe/Berlin")) - * // 63 days until the concert, rounded down + * val kotlinRelease = Instant.parse("2016-02-15T02:00T12:00:00+03:00") + * val kotlinStableDuration = Clock.System.now() - kotlinRelease * ``` * * ### Platform specifics * * On the JVM, there are `Instant.toJavaInstant()` and `java.time.Instant.toKotlinInstant()` - * extension functions to convert between `kotlinx.datetime` and `java.time` objects used for the same purpose. - * Similarly, on the Darwin platforms, there are `Instant.toNSDate()` and `NSDate.toKotlinInstant()` - * extension functions. + * extension functions to convert between `kotlin.time` and `java.time` objects used for the same purpose. + * Likewise, on JS, there are `Instant.toJSDate()` and `Date.toKotlinInstant()` extension functions. + * + * For technical reasons, converting [Instant] to and from Foundation's `NSDate` is provided in + * `kotlinx-datetime` via `Instant.toNSDate()` and `NSDate.toKotlinInstant()` extension functions. + * These functions will be made available in `kotlin.time` in the future. * * ### Construction, serialization, and deserialization * @@ -169,32 +91,7 @@ import kotlin.time.* * val instant = Instant.parse("2023-01-02T22:35:01+01:00") * instant.toString() // 2023-01-02T21:35:01Z * ``` - * - * During parsing, the UTC offset is not returned separately. - * If the UTC offset is important, use [DateTimeComponents] with [DateTimeComponents.Formats.ISO_DATE_TIME_OFFSET] to - * parse the string instead. - * - * [Instant.parse] and [Instant.format] also accept custom formats: - * - * ``` - * val customFormat = DateTimeComponents.Format { - * date(LocalDate.Formats.ISO) - * char(' ') - * time(LocalTime.Formats.ISO) - * char(' ') - * offset(UtcOffset.Formats.ISO) - * } - * val instant = Instant.parse("2023-01-02 22:35:01.14 +01:00", customFormat) - * instant.format(customFormat, offset = UtcOffset(hours = 2)) // 2023-01-02 23:35:01.14 +02:00 - * ``` - * - * Additionally, there are several `kotlinx-serialization` serializers for [Instant]: - * - [InstantIso8601Serializer] for the ISO 8601 extended format. - * - [InstantComponentSerializer] for an object with components. - * - * @see LocalDateTime for a user-visible representation of moments in time in an unspecified time zone. */ -@Serializable(with = InstantIso8601Serializer::class) public expect class Instant : Comparable { /** @@ -241,10 +138,9 @@ public expect class Instant : Comparable { * * The return value is clamped to the boundaries of [Instant] if the result exceeds them. * - * **Pitfall**: [Duration.Companion.days] are multiples of 24 hours and are not calendar-based. - * Consider using the [plus] overload that accepts a multiple of a [DateTimeUnit] instead for calendar-based - * operations instead of using [Duration]. - * For an explanation of why some days are not 24 hours, see [DateTimeUnit.DayBased]. + * **Pitfall**: [Duration.Companion.days] are multiples of 24 hours, but in some time zones, + * some days can be shorter or longer because clocks are shifted. + * Consider using `kotlinx-datetime` for arithmetic operations that take time zone transitions into account. * * @sample kotlinx.datetime.test.samples.InstantSamples.plusDuration */ @@ -258,10 +154,9 @@ public expect class Instant : Comparable { * * The return value is clamped to the boundaries of [Instant] if the result exceeds them. * - * **Pitfall**: [Duration.Companion.days] are multiples of 24 hours and are not calendar-based. - * Consider using the [minus] overload that accepts a multiple of a [DateTimeUnit] instead for calendar-based - * operations instead of using [Duration]. - * For an explanation of why some days are not 24 hours, see [DateTimeUnit.DayBased]. + * **Pitfall**: [Duration.Companion.days] are multiples of 24 hours, but in some time zones, + * some days can be shorter or longer because clocks are shifted. + * Consider using `kotlinx-datetime` for arithmetic operations that take time zone transitions into account. * * @sample kotlinx.datetime.test.samples.InstantSamples.minusDuration */ @@ -305,10 +200,6 @@ public expect class Instant : Comparable { * where the component for seconds is 60, and for any day, it's possible to observe 23:59:59. * * @see parse - * @see DateTimeComponents.Formats.ISO_DATE_TIME_OFFSET for a very similar format. The difference is that - * [DateTimeComponents.Formats.ISO_DATE_TIME_OFFSET] will not add trailing zeros for readability to the - * fractional part of the second. - * @sample kotlinx.datetime.test.samples.InstantSamples.toStringSample */ public override fun toString(): String @@ -360,10 +251,20 @@ public expect class Instant : Comparable { public fun fromEpochSeconds(epochSeconds: Long, nanosecondAdjustment: Int): Instant /** - * A shortcut for calling [DateTimeFormat.parse], followed by [DateTimeComponents.toInstantUsingOffset]. + * Parses an ISO 8601 string that represents an instant (for example, `2020-08-30T18:43:00Z`). * - * Parses a string that represents an instant, including date and time components and a mandatory - * time zone offset and returns the parsed [Instant] value. + * Guaranteed to parse all strings that [Instant.toString] produces. + * + * Examples of instants in the ISO 8601 format: + * - `2020-08-30T18:43:00Z` + * - `2020-08-30T18:43:00.50Z` + * - `2020-08-30T18:43:00.123456789Z` + * - `2020-08-30T18:40:00+03:00` + * - `2020-08-30T18:40:00+03:30:20` + * * `2020-01-01T23:59:59.123456789+01` + * * `+12020-01-31T23:59:59Z` + * + * See ISO-8601-1:2019, 5.4.2.1b), excluding the format without the offset. * * The string is considered to represent time on the UTC-SLS time scale instead of UTC. * In practice, this means that, even if there is a leap second on the given day, it will not affect how the @@ -371,26 +272,16 @@ public expect class Instant : Comparable { * Instead, even if there is a negative leap second on the given day, 23:59:59 is still considered a valid time. * 23:59:60 is invalid on UTC-SLS, so parsing it will fail. * - * If the format is not specified, [DateTimeComponents.Formats.ISO_DATE_TIME_OFFSET] is used. - * `2023-01-02T23:40:57.120Z` is an example of a string in this format. - * * @throws IllegalArgumentException if the text cannot be parsed or the boundaries of [Instant] are exceeded. * - * @see Instant.toString for formatting using the default format. - * @see Instant.format for formatting using a custom format. + * @see Instant.toString for formatting. * @sample kotlinx.datetime.test.samples.InstantSamples.parsing */ - public fun parse( - input: CharSequence, - format: DateTimeFormat = DateTimeComponents.Formats.ISO_DATE_TIME_OFFSET - ): Instant + public fun parse(input: CharSequence): Instant /** * An instant value that is far in the past. * - * All instants in the range `DISTANT_PAST..DISTANT_FUTURE` can be [converted][Instant.toLocalDateTime] to - * [LocalDateTime] without exceptions in every time zone. - * * [isDistantPast] returns true for this value and all earlier ones. */ public val DISTANT_PAST: Instant // -100001-12-31T23:59:59.999999999Z @@ -398,9 +289,6 @@ public expect class Instant : Comparable { /** * An instant value that is far in the future. * - * All instants in the range `DISTANT_PAST..DISTANT_FUTURE` can be [converted][Instant.toLocalDateTime] to - * [LocalDateTime] without exceptions in every time zone. - * * [isDistantFuture] returns true for this value and all later ones. */ public val DISTANT_FUTURE: Instant // +100000-01-01T00:00:00Z @@ -426,377 +314,5 @@ public val Instant.isDistantPast: Boolean public val Instant.isDistantFuture: Boolean get() = this >= Instant.DISTANT_FUTURE -/** - * @suppress - */ -@Deprecated("Removed to support more idiomatic code. See https://github.com/Kotlin/kotlinx-datetime/issues/339", ReplaceWith("Instant.parse(this)"), DeprecationLevel.WARNING) -public fun String.toInstant(): Instant = Instant.parse(this) - -/** - * Returns an instant that is the result of adding components of [DateTimePeriod] to this instant. The components are - * added in the order from the largest units to the smallest, i.e., from years to nanoseconds. - * - * - If the [DateTimePeriod] only contains time-based components, please consider adding a [Duration] instead, - * as in `Clock.System.now() + 5.hours`. - * Then, it will not be necessary to pass the [timeZone]. - * - If the [DateTimePeriod] only has a single non-zero component (only the months or only the days), - * please consider using a multiple of [DateTimeUnit.DAY] or [DateTimeUnit.MONTH], like in - * `Clock.System.now().plus(5, DateTimeUnit.DAY, TimeZone.currentSystemDefault())`. - * - * @throws DateTimeArithmeticException if this value or the results of intermediate computations are too large to fit in - * [LocalDateTime]. - * @sample kotlinx.datetime.test.samples.InstantSamples.plusPeriod - */ -public expect fun Instant.plus(period: DateTimePeriod, timeZone: TimeZone): Instant - -/** - * Returns an instant that is the result of subtracting components of [DateTimePeriod] from this instant. The components - * are subtracted in the order from the largest units to the smallest, i.e., from years to nanoseconds. - * - * - If the [DateTimePeriod] only contains time-based components, please consider subtracting a [Duration] instead, - * as in `Clock.System.now() - 5.hours`. - * Then, it is not necessary to pass the [timeZone]. - * - If the [DateTimePeriod] only has a single non-zero component (only the months or only the days), - * please consider using a multiple of [DateTimeUnit.DAY] or [DateTimeUnit.MONTH], as in - * `Clock.System.now().minus(5, DateTimeUnit.DAY, TimeZone.currentSystemDefault())`. - * - * @throws DateTimeArithmeticException if this value or the results of intermediate computations are too large to fit in - * [LocalDateTime]. - * @sample kotlinx.datetime.test.samples.InstantSamples.minusPeriod - */ -public fun Instant.minus(period: DateTimePeriod, timeZone: TimeZone): Instant = - /* An overflow can happen for any component, but we are only worried about nanoseconds, as having an overflow in - any other component means that `plus` will throw due to the minimum value of the numeric type overflowing the - `Instant` limits. */ - if (period.totalNanoseconds != Long.MIN_VALUE) { - val negatedPeriod = with(period) { buildDateTimePeriod(-totalMonths, -days, -totalNanoseconds) } - plus(negatedPeriod, timeZone) - } else { - val negatedPeriod = with(period) { buildDateTimePeriod(-totalMonths, -days, -(totalNanoseconds+1)) } - plus(negatedPeriod, timeZone).plus(1, DateTimeUnit.NANOSECOND) - } - -/** - * Returns a [DateTimePeriod] representing the difference between `this` and [other] instants. - * - * The components of [DateTimePeriod] are calculated so that adding it to `this` instant results in the [other] instant. - * - * All components of the [DateTimePeriod] returned are: - * - Positive or zero if this instant is earlier than the other. - * - Negative or zero if this instant is later than the other. - * - Exactly zero if this instant is equal to the other. - * - * @throws DateTimeArithmeticException if `this` or [other] instant is too large to fit in [LocalDateTime]. - * @sample kotlinx.datetime.test.samples.InstantSamples.periodUntil - */ -public expect fun Instant.periodUntil(other: Instant, timeZone: TimeZone): DateTimePeriod - -/** - * Returns the whole number of the specified date or time [units][unit] between `this` and [other] instants - * in the specified [timeZone]. - * - * The value returned is: - * - Positive or zero if this instant is earlier than the other. - * - Negative or zero if this instant is later than the other. - * - Zero if this instant is equal to the other. - * - * If the result does not fit in [Long], returns [Long.MAX_VALUE] for a positive result or [Long.MIN_VALUE] for a negative result. - * - * @throws DateTimeArithmeticException if `this` or [other] instant is too large to fit in [LocalDateTime]. - * @sample kotlinx.datetime.test.samples.InstantSamples.untilAsDateTimeUnit - */ -public expect fun Instant.until(other: Instant, unit: DateTimeUnit, timeZone: TimeZone): Long - -/** - * Returns the whole number of the specified time [units][unit] between `this` and [other] instants. - * - * The value returned is: - * - Positive or zero if this instant is earlier than the other. - * - Negative or zero if this instant is later than the other. - * - Zero if this instant is equal to the other. - * - * If the result does not fit in [Long], returns [Long.MAX_VALUE] for a positive result or [Long.MIN_VALUE] for a negative result. - * - * @sample kotlinx.datetime.test.samples.InstantSamples.untilAsTimeBasedUnit - */ -public fun Instant.until(other: Instant, unit: DateTimeUnit.TimeBased): Long = - try { - multiplyAddAndDivide(other.epochSeconds - epochSeconds, - NANOS_PER_ONE.toLong(), - (other.nanosecondsOfSecond - nanosecondsOfSecond).toLong(), - unit.nanoseconds) - } catch (_: ArithmeticException) { - if (this < other) Long.MAX_VALUE else Long.MIN_VALUE - } - -/** - * Returns the number of whole days between two instants in the specified [timeZone]. - * - * If the result does not fit in [Int], returns [Int.MAX_VALUE] for a positive result or [Int.MIN_VALUE] for a negative result. - * - * @see Instant.until - * @throws DateTimeArithmeticException if `this` or [other] instant is too large to fit in [LocalDateTime]. - * @sample kotlinx.datetime.test.samples.InstantSamples.daysUntil - */ -public fun Instant.daysUntil(other: Instant, timeZone: TimeZone): Int = - until(other, DateTimeUnit.DAY, timeZone).clampToInt() - -/** - * Returns the number of whole months between two instants in the specified [timeZone]. - * - * If the result does not fit in [Int], returns [Int.MAX_VALUE] for a positive result or [Int.MIN_VALUE] for a negative result. - * - * @see Instant.until - * @throws DateTimeArithmeticException if `this` or [other] instant is too large to fit in [LocalDateTime]. - * @sample kotlinx.datetime.test.samples.InstantSamples.monthsUntil - */ -public fun Instant.monthsUntil(other: Instant, timeZone: TimeZone): Int = - until(other, DateTimeUnit.MONTH, timeZone).clampToInt() - -/** - * Returns the number of whole years between two instants in the specified [timeZone]. - * - * If the result does not fit in [Int], returns [Int.MAX_VALUE] for a positive result or [Int.MIN_VALUE] for a negative result. - * - * @see Instant.until - * @throws DateTimeArithmeticException if `this` or [other] instant is too large to fit in [LocalDateTime]. - * @sample kotlinx.datetime.test.samples.InstantSamples.yearsUntil - */ -public fun Instant.yearsUntil(other: Instant, timeZone: TimeZone): Int = - until(other, DateTimeUnit.YEAR, timeZone).clampToInt() - -/** - * Returns a [DateTimePeriod] representing the difference between [other] and `this` instants. - * - * The components of [DateTimePeriod] are calculated so that adding it back to the `other` instant results in this instant. - * - * All components of the [DateTimePeriod] returned are: - * - Negative or zero if this instant is earlier than the other. - * - Positive or zero if this instant is later than the other. - * - Exactly zero if this instant is equal to the other. - * - * @throws DateTimeArithmeticException if `this` or [other] instant is too large to fit in [LocalDateTime]. - * @see Instant.periodUntil - * @sample kotlinx.datetime.test.samples.InstantSamples.minusInstantInZone - */ -public fun Instant.minus(other: Instant, timeZone: TimeZone): DateTimePeriod = - other.periodUntil(this, timeZone) - - -/** - * Returns an instant that is the result of adding one [unit] to this instant - * in the specified [timeZone]. - * - * The returned instant is later than this instant. - * - * @throws DateTimeArithmeticException if this value or the result is too large to fit in [LocalDateTime]. - */ -@Deprecated("Use the plus overload with an explicit number of units", ReplaceWith("this.plus(1, unit, timeZone)")) -public expect fun Instant.plus(unit: DateTimeUnit, timeZone: TimeZone): Instant - -/** - * Returns an instant that is the result of subtracting one [unit] from this instant - * in the specified [timeZone]. - * - * The returned instant is earlier than this instant. - * - * @throws DateTimeArithmeticException if this value or the result is too large to fit in [LocalDateTime]. - */ -@Deprecated("Use the minus overload with an explicit number of units", ReplaceWith("this.minus(1, unit, timeZone)")) -public fun Instant.minus(unit: DateTimeUnit, timeZone: TimeZone): Instant = - plus(-1, unit, timeZone) - -/** - * Returns an instant that is the result of adding one [unit] to this instant. - * - * The returned instant is later than this instant. - * - * The return value is clamped to the boundaries of [Instant] if the result exceeds them. - */ -@Deprecated("Use the plus overload with an explicit number of units", ReplaceWith("this.plus(1, unit)")) -public fun Instant.plus(unit: DateTimeUnit.TimeBased): Instant = - plus(1L, unit) - -/** - * Returns an instant that is the result of subtracting one [unit] from this instant. - * - * The returned instant is earlier than this instant. - * - * The return value is clamped to the boundaries of [Instant] if the result exceeds them. - */ -@Deprecated("Use the minus overload with an explicit number of units", ReplaceWith("this.minus(1, unit)")) -public fun Instant.minus(unit: DateTimeUnit.TimeBased): Instant = - plus(-1L, unit) - -/** - * Returns an instant that is the result of adding the [value] number of the specified [unit] to this instant - * in the specified [timeZone]. - * - * If the [value] is positive, the returned instant is later than this instant. - * If the [value] is negative, the returned instant is earlier than this instant. - * - * Note that the time zone does not need to be passed when the [unit] is a time-based unit. - * It is also not needed when adding date-based units to a [LocalDate][LocalDate.plus]. - * - * @throws DateTimeArithmeticException if this value or the result is too large to fit in [LocalDateTime]. - * @sample kotlinx.datetime.test.samples.InstantSamples.plusDateTimeUnit - */ -public expect fun Instant.plus(value: Int, unit: DateTimeUnit, timeZone: TimeZone): Instant - -/** - * Returns an instant that is the result of subtracting the [value] number of the specified [unit] from this instant - * in the specified [timeZone]. - * - * If the [value] is positive, the returned instant is earlier than this instant. - * If the [value] is negative, the returned instant is later than this instant. - * - * Note that the time zone does not need to be passed when the [unit] is a time-based unit. - * It is also not needed when subtracting date-based units from a [LocalDate]. - * - * If the [value] is positive, the returned instant is earlier than this instant. - * If the [value] is negative, the returned instant is later than this instant. - * - * @throws DateTimeArithmeticException if this value or the result is too large to fit in [LocalDateTime]. - * @sample kotlinx.datetime.test.samples.InstantSamples.minusDateTimeUnit - */ -public expect fun Instant.minus(value: Int, unit: DateTimeUnit, timeZone: TimeZone): Instant - -/** - * Returns an instant that is the result of adding the [value] number of the specified [unit] to this instant. - * - * If the [value] is positive, the returned instant is later than this instant. - * If the [value] is negative, the returned instant is earlier than this instant. - * - * The return value is clamped to the boundaries of [Instant] if the result exceeds them. - * - * @sample kotlinx.datetime.test.samples.InstantSamples.plusTimeBasedUnit - */ -public fun Instant.plus(value: Int, unit: DateTimeUnit.TimeBased): Instant = - plus(value.toLong(), unit) - -/** - * Returns an instant that is the result of subtracting the [value] number of the specified [unit] from this instant. - * - * If the [value] is positive, the returned instant is earlier than this instant. - * If the [value] is negative, the returned instant is later than this instant. - * - * The return value is clamped to the boundaries of [Instant] if the result exceeds them. - * - * @sample kotlinx.datetime.test.samples.InstantSamples.minusTimeBasedUnit - */ -public fun Instant.minus(value: Int, unit: DateTimeUnit.TimeBased): Instant = - minus(value.toLong(), unit) - -/** - * Returns an instant that is the result of adding the [value] number of the specified [unit] to this instant - * in the specified [timeZone]. - * - * If the [value] is positive, the returned instant is later than this instant. - * If the [value] is negative, the returned instant is earlier than this instant. - * - * Note that the time zone does not need to be passed when the [unit] is a time-based unit. - * It is also not needed when adding date-based units to a [LocalDate]. - * - * @throws DateTimeArithmeticException if this value or the result is too large to fit in [LocalDateTime]. - * @sample kotlinx.datetime.test.samples.InstantSamples.plusDateTimeUnit - */ -public expect fun Instant.plus(value: Long, unit: DateTimeUnit, timeZone: TimeZone): Instant - -/** - * Returns an instant that is the result of subtracting the [value] number of the specified [unit] from this instant - * in the specified [timeZone]. - * - * If the [value] is positive, the returned instant is earlier than this instant. - * If the [value] is negative, the returned instant is later than this instant. - * - * Note that the time zone does not need to be passed when the [unit] is a time-based unit. - * It is also not needed when subtracting date-based units from a [LocalDate]. - * - * @throws DateTimeArithmeticException if this value or the result is too large to fit in [LocalDateTime]. - * @sample kotlinx.datetime.test.samples.InstantSamples.minusDateTimeUnit - */ -public fun Instant.minus(value: Long, unit: DateTimeUnit, timeZone: TimeZone): Instant = - if (value != Long.MIN_VALUE) { - plus(-value, unit, timeZone) - } else { - plus(-(value + 1), unit, timeZone).plus(1, unit, timeZone) - } - -/** - * Returns an instant that is the result of adding the [value] number of the specified [unit] to this instant. - * - * If the [value] is positive, the returned instant is later than this instant. - * If the [value] is negative, the returned instant is earlier than this instant. - * - * The return value is clamped to the boundaries of [Instant] if the result exceeds them. - * - * @sample kotlinx.datetime.test.samples.InstantSamples.plusTimeBasedUnit - */ -public expect fun Instant.plus(value: Long, unit: DateTimeUnit.TimeBased): Instant - -/** - * Returns an instant that is the result of subtracting the [value] number of the specified [unit] from this instant. - * - * If the [value] is positive, the returned instant is earlier than this instant. - * If the [value] is negative, the returned instant is later than this instant. - * - * The return value is clamped to the boundaries of [Instant] if the result exceeds them. - * - * @sample kotlinx.datetime.test.samples.InstantSamples.minusTimeBasedUnit - */ -public fun Instant.minus(value: Long, unit: DateTimeUnit.TimeBased): Instant = - if (value != Long.MIN_VALUE) { - plus(-value, unit) - } else { - plus(-(value + 1), unit).plus(1, unit) - } - -/** - * Returns the whole number of the specified date or time [units][unit] between [other] and `this` instants - * in the specified [timeZone]. - * - * The value returned is negative or zero if this instant is earlier than the other, - * and positive or zero if this instant is later than the other. - * - * If the result does not fit in [Long], returns [Long.MAX_VALUE] for a positive result or [Long.MIN_VALUE] for a negative result. - * - * @throws DateTimeArithmeticException if `this` or [other] instant is too large to fit in [LocalDateTime]. - * @see Instant.until for the same operation but with swapped arguments. - * @sample kotlinx.datetime.test.samples.InstantSamples.minusAsDateTimeUnit - */ -public fun Instant.minus(other: Instant, unit: DateTimeUnit, timeZone: TimeZone): Long = - other.until(this, unit, timeZone) - -/** - * Returns the whole number of the specified time [units][unit] between [other] and `this` instants. - * - * The value returned is negative or zero if this instant is earlier than the other, - * and positive or zero if this instant is later than the other. - * - * If the result does not fit in [Long], returns [Long.MAX_VALUE] for a positive result or [Long.MIN_VALUE] for a negative result. - * - * @see Instant.until for the same operation but with swapped arguments. - * @sample kotlinx.datetime.test.samples.InstantSamples.minusAsTimeBasedUnit - */ -public fun Instant.minus(other: Instant, unit: DateTimeUnit.TimeBased): Long = - other.until(this, unit) - -/** - * Formats this value using the given [format] using the given [offset]. - * - * Equivalent to calling [DateTimeFormat.format] on [format] and using [DateTimeComponents.setDateTimeOffset] in - * the lambda. - * - * [DateTimeComponents.Formats.ISO_DATE_TIME_OFFSET] is a format very similar to the one used by [toString]. - * The only difference is that [Instant.toString] adds trailing zeros to the fraction-of-second component so that the - * number of digits after a dot is a multiple of three. - * - * @sample kotlinx.datetime.test.samples.InstantSamples.formatting - */ -public fun Instant.format(format: DateTimeFormat, offset: UtcOffset = UtcOffset.ZERO): String { - val instant = this - return format.format { setDateTimeOffset(instant, offset) } -} - internal const val DISTANT_PAST_SECONDS = -3217862419201 internal const val DISTANT_FUTURE_SECONDS = 3093527980800 diff --git a/fake-kotlinx-time/js/src/Converters.kt b/fake-kotlinx-time/js/src/Converters.kt index a4a8c9df9..5e74ab188 100644 --- a/fake-kotlinx-time/js/src/Converters.kt +++ b/fake-kotlinx-time/js/src/Converters.kt @@ -3,7 +3,7 @@ * Use of this source code is governed by the Apache 2.0 License that can be found in the LICENSE.txt file. */ -package kotlinx.datetime +package kotlinx.time import kotlin.js.* diff --git a/fake-kotlinx-time/jvm/src/Converters.kt b/fake-kotlinx-time/jvm/src/Converters.kt index 6db8339d3..3774d4b37 100644 --- a/fake-kotlinx-time/jvm/src/Converters.kt +++ b/fake-kotlinx-time/jvm/src/Converters.kt @@ -3,93 +3,15 @@ * Use of this source code is governed by the Apache 2.0 License that can be found in the LICENSE.txt file. */ -package kotlinx.datetime +package kotlinx.time /** - * Converts this [kotlinx.datetime.Instant][Instant] value to a [java.time.Instant][java.time.Instant] value. + * Converts this [kotlin.time.Instant][Instant] value to a [java.time.Instant][java.time.Instant] value. */ -public fun Instant.toJavaInstant(): java.time.Instant = this.value +public fun Instant.toJavaInstant(): java.time.Instant = + java.time.Instant.ofEpochSecond(epochSeconds, nanosecondsOfSecond.toLong()) /** - * Converts this [java.time.Instant][java.time.Instant] value to a [kotlinx.datetime.Instant][Instant] value. + * Converts this [java.time.Instant][java.time.Instant] value to a [kotlin.time.Instant][Instant] value. */ -public fun java.time.Instant.toKotlinInstant(): Instant = Instant(this) - - -/** - * Converts this [kotlinx.datetime.LocalDateTime][LocalDateTime] value to a [java.time.LocalDateTime][java.time.LocalDateTime] value. - */ -public fun LocalDateTime.toJavaLocalDateTime(): java.time.LocalDateTime = this.value - -/** - * Converts this [java.time.LocalDateTime][java.time.LocalDateTime] value to a [kotlinx.datetime.LocalDateTime][LocalDateTime] value. - */ -public fun java.time.LocalDateTime.toKotlinLocalDateTime(): LocalDateTime = LocalDateTime(this) - -/** - * Converts this [kotlinx.datetime.LocalDateTime][LocalTime] value to a [java.time.LocalTime][java.time.LocalTime] value. - */ -public fun LocalTime.toJavaLocalTime(): java.time.LocalTime = this.value - -/** - * Converts this [java.time.LocalTime][java.time.LocalTime] value to a [kotlinx.datetime.LocalTime][LocalTime] value. - */ -public fun java.time.LocalTime.toKotlinLocalTime(): LocalTime = LocalTime(this) - - -/** - * Converts this [kotlinx.datetime.LocalDate][LocalDate] value to a [java.time.LocalDate][java.time.LocalDate] value. - */ -public fun LocalDate.toJavaLocalDate(): java.time.LocalDate = this.value - -/** - * Converts this [java.time.LocalDate][java.time.LocalDate] value to a [kotlinx.datetime.LocalDate][LocalDate] value. - */ -public fun java.time.LocalDate.toKotlinLocalDate(): LocalDate = LocalDate(this) - - -/** - * Converts this [kotlinx.datetime.DatePeriod][DatePeriod] value to a [java.time.Period][java.time.Period] value. - */ -public fun DatePeriod.toJavaPeriod(): java.time.Period = java.time.Period.of(this.years, this.months, this.days) - -/** - * Converts this [java.time.Period][java.time.Period] value to a [kotlinx.datetime.DatePeriod][DatePeriod] value. - */ -public fun java.time.Period.toKotlinDatePeriod(): DatePeriod = DatePeriod(this.years, this.months, this.days) - - -/** - * Converts this [kotlinx.datetime.TimeZone][TimeZone] value to a [java.time.ZoneId][java.time.ZoneId] value. - */ -public fun TimeZone.toJavaZoneId(): java.time.ZoneId = this.zoneId - -/** - * Converts this [java.time.ZoneId][java.time.ZoneId] value to a [kotlinx.datetime.TimeZone][TimeZone] value. - */ -public fun java.time.ZoneId.toKotlinTimeZone(): TimeZone = TimeZone.ofZone(this) - - -/** - * Converts this [kotlinx.datetime.FixedOffsetTimeZone][FixedOffsetTimeZone] value to a [java.time.ZoneOffset][java.time.ZoneOffset] value. - */ -public fun FixedOffsetTimeZone.toJavaZoneOffset(): java.time.ZoneOffset = this.offset.zoneOffset - -/** - * Converts this [java.time.ZoneOffset][java.time.ZoneOffset] value to a [kotlinx.datetime.FixedOffsetTimeZone][FixedOffsetTimeZone] value. - */ -public fun java.time.ZoneOffset.toKotlinFixedOffsetTimeZone(): FixedOffsetTimeZone = FixedOffsetTimeZone(UtcOffset(this)) - -@Deprecated("Use toKotlinFixedOffsetTimeZone() instead.", ReplaceWith("this.toKotlinFixedOffsetTimeZone()")) -public fun java.time.ZoneOffset.toKotlinZoneOffset(): FixedOffsetTimeZone = toKotlinFixedOffsetTimeZone() - -/** - * Converts this [kotlinx.datetime.UtcOffset][UtcOffset] value to a [java.time.ZoneOffset][java.time.ZoneOffset] value. - */ -public fun UtcOffset.toJavaZoneOffset(): java.time.ZoneOffset = this.zoneOffset - -/** - * Converts this [java.time.ZoneOffset][java.time.ZoneOffset] value to a [kotlinx.datetime.UtcOffset][UtcOffset] value. - */ -public fun java.time.ZoneOffset.toKotlinUtcOffset(): UtcOffset = UtcOffset(this) - +public fun java.time.Instant.toKotlinInstant(): Instant = Instant.fromEpochSeconds(epochSecond, nano) From d3238e99d48b08c8edca700df56bf83908d55a50 Mon Sep 17 00:00:00 2001 From: Dmitry Khalanskiy Date: Thu, 28 Nov 2024 13:55:38 +0100 Subject: [PATCH 3/7] Copy the commonKotlin Instant implementation to kotlinx.time --- fake-kotlinx-time/build.gradle.kts | 1 + fake-kotlinx-time/common/src/Instant.kt | 492 ++++++++++++++++++++- fake-kotlinx-time/js/src/Platform.kt | 10 + fake-kotlinx-time/jvm/src/Platform.kt | 8 + fake-kotlinx-time/native/src/Platform.kt | 21 + fake-kotlinx-time/wasmJs/src/Platform.kt | 12 + fake-kotlinx-time/wasmWasi/src/Platform.kt | 38 ++ 7 files changed, 564 insertions(+), 18 deletions(-) create mode 100644 fake-kotlinx-time/js/src/Platform.kt create mode 100644 fake-kotlinx-time/jvm/src/Platform.kt create mode 100644 fake-kotlinx-time/native/src/Platform.kt create mode 100644 fake-kotlinx-time/wasmJs/src/Platform.kt create mode 100644 fake-kotlinx-time/wasmWasi/src/Platform.kt diff --git a/fake-kotlinx-time/build.gradle.kts b/fake-kotlinx-time/build.gradle.kts index 750ec3cba..2cfd7a452 100644 --- a/fake-kotlinx-time/build.gradle.kts +++ b/fake-kotlinx-time/build.gradle.kts @@ -13,6 +13,7 @@ java { } kotlin { + explicitApi() infra { target("linuxX64") target("linuxArm64") diff --git a/fake-kotlinx-time/common/src/Instant.kt b/fake-kotlinx-time/common/src/Instant.kt index fcd4f7ffe..a9abcd514 100644 --- a/fake-kotlinx-time/common/src/Instant.kt +++ b/fake-kotlinx-time/common/src/Instant.kt @@ -5,7 +5,10 @@ package kotlinx.time +import kotlin.math.absoluteValue import kotlin.time.* +import kotlin.time.Duration.Companion.nanoseconds +import kotlin.time.Duration.Companion.seconds /** * A moment in time. @@ -92,8 +95,7 @@ import kotlin.time.* * instant.toString() // 2023-01-02T21:35:01Z * ``` */ -public expect class Instant : Comparable { - +public class Instant internal constructor( /** * The number of seconds from the epoch instant `1970-01-01T00:00:00Z` rounded down to a [Long] number. * @@ -105,8 +107,7 @@ public expect class Instant : Comparable { * @see fromEpochSeconds * @sample kotlinx.datetime.test.samples.InstantSamples.epochSeconds */ - public val epochSeconds: Long - + public val epochSeconds: Long, /** * The number of nanoseconds by which this instant is later than [epochSeconds] from the epoch instant. * @@ -116,6 +117,11 @@ public expect class Instant : Comparable { * @sample kotlinx.datetime.test.samples.InstantSamples.nanosecondsOfSecond */ public val nanosecondsOfSecond: Int +) : Comparable { + + init { + require(epochSeconds in MIN_SECOND..MAX_SECOND) { "Instant exceeds minimum or maximum instant" } + } /** * Returns the number of milliseconds from the epoch instant `1970-01-01T00:00:00Z`. @@ -128,7 +134,39 @@ public expect class Instant : Comparable { * @see fromEpochMilliseconds * @sample kotlinx.datetime.test.samples.InstantSamples.toEpochMilliseconds */ - public fun toEpochMilliseconds(): Long + // org.threeten.bp.Instant#toEpochMilli + public fun toEpochMilliseconds(): Long = try { + if (epochSeconds >= 0) { + val millis = safeMultiply(epochSeconds, MILLIS_PER_ONE.toLong()) + safeAdd(millis, (nanosecondsOfSecond / NANOS_PER_MILLI).toLong()) + } else { + // prevent an overflow in seconds * 1000 + // instead of going form the second farther away from 0 + // going toward 0 + // we go from the second closer to 0 away from 0 + // that way we always stay in the valid long range + // seconds + 1 can not overflow because it is negative + val millis = safeMultiply(epochSeconds + 1, MILLIS_PER_ONE.toLong()) + safeAdd(millis, (nanosecondsOfSecond / NANOS_PER_MILLI - MILLIS_PER_ONE).toLong()) + } + } catch (_: ArithmeticException) { + if (epochSeconds > 0) Long.MAX_VALUE else Long.MIN_VALUE + } + + // org.threeten.bp.Instant#plus(long, long) + /** + * @throws ArithmeticException if arithmetic overflow occurs + * @throws IllegalArgumentException if the boundaries of Instant are overflown + */ + internal fun plus(secondsToAdd: Long, nanosToAdd: Long): Instant { + if ((secondsToAdd or nanosToAdd) == 0L) { + return this + } + val newEpochSeconds: Long = safeAdd(safeAdd(epochSeconds, secondsToAdd), (nanosToAdd / NANOS_PER_ONE)) + val newNanosToAdd = nanosToAdd % NANOS_PER_ONE + val nanoAdjustment = (nanosecondsOfSecond + newNanosToAdd) // safe int+NANOS_PER_ONE + return fromEpochSecondsThrowing(newEpochSeconds, nanoAdjustment) + } /** * Returns an instant that is the result of adding the specified [duration] to this instant. @@ -144,7 +182,15 @@ public expect class Instant : Comparable { * * @sample kotlinx.datetime.test.samples.InstantSamples.plusDuration */ - public operator fun plus(duration: Duration): Instant + public operator fun plus(duration: Duration): Instant = duration.toComponents { secondsToAdd, nanosecondsToAdd -> + try { + plus(secondsToAdd, nanosecondsToAdd.toLong()) + } catch (_: IllegalArgumentException) { + if (duration.isPositive()) MAX else MIN + } catch (_: ArithmeticException) { + if (duration.isPositive()) MAX else MIN + } + } /** * Returns an instant that is the result of subtracting the specified [duration] from this instant. @@ -160,7 +206,7 @@ public expect class Instant : Comparable { * * @sample kotlinx.datetime.test.samples.InstantSamples.minusDuration */ - public operator fun minus(duration: Duration): Instant + public operator fun minus(duration: Duration): Instant = plus(-duration) // questionable /** @@ -179,7 +225,9 @@ public expect class Instant : Comparable { * * @sample kotlinx.datetime.test.samples.InstantSamples.minusInstant */ - public operator fun minus(other: Instant): Duration + public operator fun minus(other: Instant): Duration = + (this.epochSeconds - other.epochSeconds).seconds + // won't overflow given the instant bounds + (this.nanosecondsOfSecond - other.nanosecondsOfSecond).nanoseconds /** * Compares `this` instant with the [other] instant. @@ -189,7 +237,21 @@ public expect class Instant : Comparable { * * @sample kotlinx.datetime.test.samples.InstantSamples.compareToSample */ - public override operator fun compareTo(other: Instant): Int + public override operator fun compareTo(other: Instant): Int { + val s = epochSeconds.compareTo(other.epochSeconds) + if (s != 0) { + return s + } + return nanosecondsOfSecond.compareTo(other.nanosecondsOfSecond) + } + + override fun equals(other: Any?): Boolean = + this === other || other is Instant && this.epochSeconds == other.epochSeconds + && this.nanosecondsOfSecond == other.nanosecondsOfSecond + + // org.threeten.bp.Instant#hashCode + override fun hashCode(): Int = + (epochSeconds xor (epochSeconds ushr 32)).toInt() + 51 * nanosecondsOfSecond /** * Converts this instant to the ISO 8601 string representation, for example, `2023-01-02T23:40:57.120Z`. @@ -201,12 +263,11 @@ public expect class Instant : Comparable { * * @see parse */ - public override fun toString(): String - + public override fun toString(): String = formatIso(this) public companion object { @Deprecated("Use Clock.System.now() instead", ReplaceWith("Clock.System.now()", "kotlinx.datetime.Clock"), level = DeprecationLevel.ERROR) - public fun now(): Instant + public fun now(): Instant = currentTime() /** * Returns an [Instant] that is [epochMilliseconds] number of milliseconds from the epoch instant `1970-01-01T00:00:00Z`. @@ -218,7 +279,16 @@ public expect class Instant : Comparable { * @see Instant.toEpochMilliseconds * @sample kotlinx.datetime.test.samples.InstantSamples.fromEpochMilliseconds */ - public fun fromEpochMilliseconds(epochMilliseconds: Long): Instant + // org.threeten.bp.Instant#ofEpochMilli + public fun fromEpochMilliseconds(epochMilliseconds: Long): Instant { + val epochSeconds = epochMilliseconds.floorDiv(MILLIS_PER_ONE.toLong()) + val nanosecondsOfSecond = (epochMilliseconds.mod(MILLIS_PER_ONE.toLong()) * NANOS_PER_MILLI).toInt() + return when { + epochSeconds < MIN_SECOND -> MIN + epochSeconds > MAX_SECOND -> MAX + else -> fromEpochSeconds(epochSeconds, nanosecondsOfSecond) + } + } /** * Returns an [Instant] that is the [epochSeconds] number of seconds from the epoch instant `1970-01-01T00:00:00Z` @@ -233,7 +303,15 @@ public expect class Instant : Comparable { * @see Instant.nanosecondsOfSecond * @sample kotlinx.datetime.test.samples.InstantSamples.fromEpochSeconds */ - public fun fromEpochSeconds(epochSeconds: Long, nanosecondAdjustment: Long = 0): Instant + // org.threeten.bp.Instant#ofEpochSecond(long, long) + public fun fromEpochSeconds(epochSeconds: Long, nanosecondAdjustment: Long = 0): Instant = + try { + fromEpochSecondsThrowing(epochSeconds, nanosecondAdjustment) + } catch (_: ArithmeticException) { + if (epochSeconds > 0) MAX else MIN + } catch (_: IllegalArgumentException) { + if (epochSeconds > 0) MAX else MIN + } /** * Returns an [Instant] that is the [epochSeconds] number of seconds from the epoch instant `1970-01-01T00:00:00Z` @@ -248,7 +326,8 @@ public expect class Instant : Comparable { * @see Instant.nanosecondsOfSecond * @sample kotlinx.datetime.test.samples.InstantSamples.fromEpochSecondsIntNanos */ - public fun fromEpochSeconds(epochSeconds: Long, nanosecondAdjustment: Int): Instant + public fun fromEpochSeconds(epochSeconds: Long, nanosecondAdjustment: Int): Instant = + fromEpochSeconds(epochSeconds, nanosecondAdjustment.toLong()) /** * Parses an ISO 8601 string that represents an instant (for example, `2020-08-30T18:43:00Z`). @@ -277,7 +356,8 @@ public expect class Instant : Comparable { * @see Instant.toString for formatting. * @sample kotlinx.datetime.test.samples.InstantSamples.parsing */ - public fun parse(input: CharSequence): Instant + public fun parse(input: CharSequence): Instant = parseIso(input) + /** * An instant value that is far in the past. @@ -285,6 +365,7 @@ public expect class Instant : Comparable { * [isDistantPast] returns true for this value and all earlier ones. */ public val DISTANT_PAST: Instant // -100001-12-31T23:59:59.999999999Z + get() = fromEpochSeconds(DISTANT_PAST_SECONDS, 999_999_999) /** * An instant value that is far in the future. @@ -292,9 +373,20 @@ public expect class Instant : Comparable { * [isDistantFuture] returns true for this value and all later ones. */ public val DISTANT_FUTURE: Instant // +100000-01-01T00:00:00Z + get() = fromEpochSeconds(DISTANT_FUTURE_SECONDS, 0) + + /** + * @throws ArithmeticException if arithmetic overflow occurs + * @throws IllegalArgumentException if the boundaries of Instant are overflown + */ + private fun fromEpochSecondsThrowing(epochSeconds: Long, nanosecondAdjustment: Long): Instant { + val secs = safeAdd(epochSeconds, nanosecondAdjustment.floorDiv(NANOS_PER_ONE.toLong())) + val nos = nanosecondAdjustment.mod(NANOS_PER_ONE.toLong()).toInt() + return Instant(secs, nos) + } - internal val MIN: Instant - internal val MAX: Instant + internal val MIN = Instant(MIN_SECOND, 0) + internal val MAX = Instant(MAX_SECOND, 999_999_999) } } @@ -316,3 +408,367 @@ public val Instant.isDistantFuture: Boolean internal const val DISTANT_PAST_SECONDS = -3217862419201 internal const val DISTANT_FUTURE_SECONDS = 3093527980800 + +internal expect fun currentTime(): Instant + +/** + * The minimum supported epoch second. + */ +private const val MIN_SECOND = -31557014167219200L // -1000000000-01-01T00:00:00Z + +/** + * The maximum supported epoch second. + */ +private const val MAX_SECOND = 31556889864403199L // +1000000000-12-31T23:59:59 + +private class UnboundedLocalDateTime( + val year: Int, + val month: Int, + val day: Int, + val hour: Int, + val minute: Int, + val second: Int, + val nanosecond: Int, +) { + fun toInstant(offsetSeconds: Int): Instant { + val epochSeconds = run { + // org.threeten.bp.LocalDate#toEpochDay + val epochDays = run { + val y = year.toLong() + var total = 365 * y + if (y >= 0) { + total += (y + 3) / 4 - (y + 99) / 100 + (y + 399) / 400 + } else { + total -= y / -4 - y / -100 + y / -400 + } + total += ((367 * month - 362) / 12) + total += day - 1 + if (month > 2) { + total-- + if (!isLeapYear(year)) { + total-- + } + } + total - DAYS_0000_TO_1970 + } + // org.threeten.bp.LocalTime#toSecondOfDay + val daySeconds = hour * SECONDS_PER_HOUR + minute * SECONDS_PER_MINUTE + second + // org.threeten.bp.chrono.ChronoLocalDateTime#toEpochSecond + epochDays * 86400L + daySeconds - offsetSeconds + } + if (epochSeconds < Instant.MIN.epochSeconds || epochSeconds > Instant.MAX.epochSeconds) + throw IllegalArgumentException( + "The parsed date is outside the range representable by Instant (Unix epoch second $epochSeconds)" + ) + return Instant.fromEpochSeconds(epochSeconds, nanosecond) + } + + override fun toString(): String = "UnboundedLocalDateTime($year-$month-$day $hour:$minute:$second.$nanosecond)" + + companion object { + fun fromInstant(instant: Instant, offsetSeconds: Int): UnboundedLocalDateTime { + val localSecond: Long = instant.epochSeconds + offsetSeconds + val epochDays = localSecond.floorDiv(SECONDS_PER_DAY.toLong()) + val secsOfDay = localSecond.mod(SECONDS_PER_DAY.toLong()).toInt() + val year: Int + val month: Int + val day: Int + // org.threeten.bp.LocalDate#toEpochDay + run { + var zeroDay = epochDays + DAYS_0000_TO_1970 + // find the march-based year + zeroDay -= 60 // adjust to 0000-03-01 so leap day is at end of four year cycle + + var adjust = 0L + if (zeroDay < 0) { // adjust negative years to positive for calculation + val adjustCycles = (zeroDay + 1) / DAYS_PER_CYCLE - 1 + adjust = adjustCycles * 400 + zeroDay += -adjustCycles * DAYS_PER_CYCLE + } + var yearEst = ((400 * zeroDay + 591) / DAYS_PER_CYCLE) + var doyEst = zeroDay - (365 * yearEst + yearEst / 4 - yearEst / 100 + yearEst / 400) + if (doyEst < 0) { // fix estimate + yearEst-- + doyEst = zeroDay - (365 * yearEst + yearEst / 4 - yearEst / 100 + yearEst / 400) + } + yearEst += adjust // reset any negative year + + val marchDoy0 = doyEst.toInt() + + // convert march-based values back to january-based + val marchMonth0 = (marchDoy0 * 5 + 2) / 153 + month = (marchMonth0 + 2) % 12 + 1 + day = marchDoy0 - (marchMonth0 * 306 + 5) / 10 + 1 + year = (yearEst + marchMonth0 / 10).toInt() + } + val hours = (secsOfDay / SECONDS_PER_HOUR) + val secondWithoutHours = secsOfDay - hours * SECONDS_PER_HOUR + val minutes = (secondWithoutHours / SECONDS_PER_MINUTE) + val second = secondWithoutHours - minutes * SECONDS_PER_MINUTE + return UnboundedLocalDateTime(year, month, day, hours, minutes, second, instant.nanosecondsOfSecond) + } + } +} + +private fun parseIso(isoString: CharSequence): Instant { + fun parseFailure(error: String): Nothing { + throw IllegalArgumentException("$error when parsing an Instant from $isoString") + } + fun expect(what: String, where: Int, predicate: (Char) -> Boolean) { + val c = isoString[where] + if (!predicate(c)) { + parseFailure("Expected $what, but got $c at position $where") + } + } + val s = isoString + var i = 0 + require(s.isNotEmpty()) { "An empty string is not a valid Instant" } + val yearSign = when (val c = s[i]) { + '+', '-' -> { ++i; c } + else -> ' ' + } + val yearStart = i + var absYear = 0 + while (i < s.length && s[i] in '0'..'9') { + absYear = absYear * 10 + (s[i] - '0') + ++i + } + val year = when { + i > yearStart + 10 -> { + parseFailure("Expected at most 10 digits for the year number, got ${i - yearStart}") + } + i == yearStart + 10 && s[yearStart] >= '2' -> { + parseFailure("Expected at most 9 digits for the year number or year 1000000000, got ${i - yearStart}") + } + i - yearStart < 4 -> { + parseFailure("The year number must be padded to 4 digits, got ${i - yearStart} digits") + } + else -> { + if (yearSign == '+' && i - yearStart == 4) { + parseFailure("The '+' sign at the start is only valid for year numbers longer than 4 digits") + } + if (yearSign == ' ' && i - yearStart != 4) { + parseFailure("A '+' or '-' sign is required for year numbers longer than 4 digits") + } + if (yearSign == '-') -absYear else absYear + } + } + // reading at least -MM-DDTHH:MM:SSZ + // 0123456789012345 16 chars + if (s.length < i + 16) { + parseFailure("The input string is too short") + } + expect("'-'", i) { it == '-' } + expect("'-'", i + 3) { it == '-' } + expect("'T' or 't'", i + 6) { it == 'T' || it == 't' } + expect("':'", i + 9) { it == ':' } + expect("':'", i + 12) { it == ':' } + for (j in listOf(1, 2, 4, 5, 7, 8, 10, 11, 13, 14)) { + expect("an ASCII digit", i + j) { it in '0'..'9' } + } + fun twoDigitNumber(index: Int) = s[index].code * 10 + s[index + 1].code - '0'.code * 11 + val month = twoDigitNumber(i + 1) + val day = twoDigitNumber(i + 4) + val hour = twoDigitNumber(i + 7) + val minute = twoDigitNumber(i + 10) + val second = twoDigitNumber(i + 13) + val nanosecond = if (s[i + 15] == '.') { + val fractionStart = i + 16 + i = fractionStart + var fraction = 0 + while (i < s.length && s[i] in '0'..'9') { + fraction = fraction * 10 + (s[i] - '0') + ++i + } + if (i - fractionStart in 1..9) { + fraction * POWERS_OF_TEN[fractionStart + 9 - i] + } else { + parseFailure("1..9 digits are supported for the fraction of the second, got {i - fractionStart}") + } + } else { + i += 15 + 0 + } + val offsetSeconds = when (val sign = s.getOrNull(i)) { + null -> { + parseFailure("The UTC offset at the end of the string is missing") + } + 'z', 'Z' -> if (s.length == i + 1) { + 0 + } else { + parseFailure("Extra text after the instant at position ${i + 1}") + } + '-', '+' -> { + val offsetStrLength = s.length - i + if (offsetStrLength % 3 != 0) { parseFailure("Invalid UTC offset string '${s.substring(i)}'") } + if (offsetStrLength > 9) { parseFailure("The UTC offset string '${s.substring(i)}' is too long") } + for (j in listOf(3, 6)) { + if ((s.getOrNull(i + j) ?: break) != ':') + parseFailure("Expected ':' at index ${i + j}, got '${s[i + j]}'") + } + for (j in listOf(1, 2, 4, 5, 7, 8)) { + if ((s.getOrNull(i + j) ?: break) !in '0'..'9') + parseFailure("Expected a digit at index ${i + j}, got '${s[i + j]}'") + } + val offsetHour = twoDigitNumber(i + 1) + val offsetMinute = if (offsetStrLength > 3) { twoDigitNumber(i + 4) } else { 0 } + val offsetSecond = if (offsetStrLength > 6) { twoDigitNumber(i + 7) } else { 0 } + if (offsetMinute > 59) { parseFailure("Expected offset-minute-of-hour in 0..59, got $offsetMinute") } + if (offsetSecond > 59) { parseFailure("Expected offset-second-of-minute in 0..59, got $offsetSecond") } + if (offsetHour > 17 && !(offsetHour == 18 && offsetMinute == 0 && offsetSecond == 0)) { + parseFailure("Expected an offset in -18:00..+18:00, got $sign$offsetHour:$offsetMinute:$offsetSecond") + } + (offsetHour * 3600 + offsetMinute * 60 + offsetSecond) * if (sign == '-') -1 else 1 + } + else -> { + parseFailure("Expected the UTC offset at position $i, got '$sign'") + } + } + if (month !in 1..12) { parseFailure("Expected a month number in 1..12, got $month") } + if (day !in 1..month.monthLength(isLeapYear(year))) { + parseFailure("Expected a valid day-of-month for $year-$month, got $day") + } + if (hour > 23) { parseFailure("Expected hour in 0..23, got $hour") } + if (minute > 59) { parseFailure("Expected minute-of-hour in 0..59, got $minute") } + if (second > 59) { parseFailure("Expected second-of-minute in 0..59, got $second") } + return UnboundedLocalDateTime(year, month, day, hour, minute, second, nanosecond).toInstant(offsetSeconds) +} + +private fun formatIso(instant: Instant): String = buildString { + val ldt = UnboundedLocalDateTime.fromInstant(instant, 0) + fun Appendable.appendTwoDigits(number: Int) { + if (number < 10) append('0') + append(number) + } + run { + val number = ldt.year + when { + number.absoluteValue < 1_000 -> { + val innerBuilder = StringBuilder() + if (number >= 0) { + innerBuilder.append((number + 10_000)).deleteAt(0) + } else { + innerBuilder.append((number - 10_000)).deleteAt(1) + } + append(innerBuilder) + } + else -> { + if (number >= 10_000) append('+') + append(number) + } + } + } + append('-') + appendTwoDigits(ldt.month) + append('-') + appendTwoDigits(ldt.day) + append('T') + appendTwoDigits(ldt.hour) + append(':') + appendTwoDigits(ldt.minute) + append(':') + appendTwoDigits(ldt.second) + if (ldt.nanosecond != 0) { + append('.') + var zerosToStrip = 0 + while (ldt.nanosecond % POWERS_OF_TEN[zerosToStrip + 1] == 0) { + ++zerosToStrip + } + zerosToStrip -= (zerosToStrip.mod(3)) // rounding down to a multiple of 3 + val numberToOutput = ldt.nanosecond / POWERS_OF_TEN[zerosToStrip] + append((numberToOutput + POWERS_OF_TEN[9 - zerosToStrip]).toString().substring(1)) + } + append('Z') +} + +/** + * All code below was taken from various places of https://github.com/ThreeTen/threetenbp with few changes + */ + +/** + * The number of days in a 400 year cycle. + */ +private const val DAYS_PER_CYCLE = 146097 + +/** + * The number of days from year zero to year 1970. + * There are five 400 year cycles from year zero to 2000. + * There are 7 leap years from 1970 to 2000. + */ +private const val DAYS_0000_TO_1970 = DAYS_PER_CYCLE * 5 - (30 * 365 + 7) + +/** + * Safely adds two long values. + * throws [ArithmeticException] if the result overflows a long + */ +private fun safeAdd(a: Long, b: Long): Long { + val sum = a + b + // check for a change of sign in the result when the inputs have the same sign + if ((a xor sum) < 0 && (a xor b) >= 0) { + throw ArithmeticException("Addition overflows a long: $a + $b") + } + return sum +} + +/** + * Safely multiply a long by a long. + * + * @param a the first value + * @param b the second value + * @return the new total + * @throws ArithmeticException if the result overflows a long + */ +private fun safeMultiply(a: Long, b: Long): Long { + if (b == 1L) { + return a + } + if (a == 1L) { + return b + } + if (a == 0L || b == 0L) { + return 0 + } + val total = a * b + if (total / b != a || a == Long.MIN_VALUE && b == -1L || b == Long.MIN_VALUE && a == -1L) { + throw ArithmeticException("Multiplication overflows a long: $a * $b") + } + return total +} + +private const val SECONDS_PER_HOUR = 60 * 60 + +private const val SECONDS_PER_MINUTE = 60 + +private const val HOURS_PER_DAY = 24 + +private const val SECONDS_PER_DAY: Int = SECONDS_PER_HOUR * HOURS_PER_DAY + +internal const val NANOS_PER_ONE = 1_000_000_000 +private const val NANOS_PER_MILLI = 1_000_000 +private const val MILLIS_PER_ONE = 1_000 + +// org.threeten.bp.chrono.IsoChronology#isLeapYear +private fun isLeapYear(year: Int): Boolean { + val prolepticYear: Long = year.toLong() + return prolepticYear and 3 == 0L && (prolepticYear % 100 != 0L || prolepticYear % 400 == 0L) +} + +private fun Int.monthLength(isLeapYear: Boolean): Int = + when (this) { + 2 -> if (isLeapYear) 29 else 28 + 4, 6, 9, 11 -> 30 + else -> 31 + } + +private val POWERS_OF_TEN = intArrayOf( + 1, + 10, + 100, + 1000, + 10000, + 100000, + 1000000, + 10000000, + 100000000, + 1000000000 +) diff --git a/fake-kotlinx-time/js/src/Platform.kt b/fake-kotlinx-time/js/src/Platform.kt new file mode 100644 index 000000000..52a3cbd31 --- /dev/null +++ b/fake-kotlinx-time/js/src/Platform.kt @@ -0,0 +1,10 @@ +/* + * Copyright 2019-2024 JetBrains s.r.o. and contributors. + * Use of this source code is governed by the Apache 2.0 License that can be found in the LICENSE.txt file. + */ + +package kotlinx.time + +import kotlin.js.Date + +internal actual fun currentTime(): Instant = Instant.fromEpochMilliseconds(Date().getTime().toLong()) diff --git a/fake-kotlinx-time/jvm/src/Platform.kt b/fake-kotlinx-time/jvm/src/Platform.kt new file mode 100644 index 000000000..71a7516b7 --- /dev/null +++ b/fake-kotlinx-time/jvm/src/Platform.kt @@ -0,0 +1,8 @@ +/* + * Copyright 2019-2024 JetBrains s.r.o. and contributors. + * Use of this source code is governed by the Apache 2.0 License that can be found in the LICENSE.txt file. + */ + +package kotlinx.time + +internal actual fun currentTime(): Instant = java.time.Instant.now().toKotlinInstant() diff --git a/fake-kotlinx-time/native/src/Platform.kt b/fake-kotlinx-time/native/src/Platform.kt new file mode 100644 index 000000000..8e036c159 --- /dev/null +++ b/fake-kotlinx-time/native/src/Platform.kt @@ -0,0 +1,21 @@ +/* + * Copyright 2019-2024 JetBrains s.r.o. and contributors. + * Use of this source code is governed by the Apache 2.0 License that can be found in the LICENSE.txt file. + */ + +package kotlinx.time +import kotlinx.cinterop.* +import platform.posix.* + +@OptIn(ExperimentalForeignApi::class, UnsafeNumber::class) +internal actual fun currentTime(): Instant = memScoped { + val tm = alloc() + val error = clock_gettime(CLOCK_REALTIME.convert(), tm.ptr) + check(error == 0) { "Error when reading the system clock: ${strerror(errno)?.toKString() ?: "Unknown error"}" } + try { + require(tm.tv_nsec in 0 until NANOS_PER_ONE) + Instant(tm.tv_sec.convert(), tm.tv_nsec.convert()) + } catch (e: IllegalArgumentException) { + throw IllegalStateException("The readings from the system clock (${tm.tv_sec} seconds, ${tm.tv_nsec} nanoseconds) are not representable as an Instant") + } +} diff --git a/fake-kotlinx-time/wasmJs/src/Platform.kt b/fake-kotlinx-time/wasmJs/src/Platform.kt new file mode 100644 index 000000000..f8c26688b --- /dev/null +++ b/fake-kotlinx-time/wasmJs/src/Platform.kt @@ -0,0 +1,12 @@ +/* + * Copyright 2019-2024 JetBrains s.r.o. and contributors. + * Use of this source code is governed by the Apache 2.0 License that can be found in the LICENSE.txt file. + */ + +package kotlinx.time + +internal actual fun currentTime(): Instant = Instant.fromEpochMilliseconds(Date().getTime().toLong()) + +private external class Date { + fun getTime(): Double +} diff --git a/fake-kotlinx-time/wasmWasi/src/Platform.kt b/fake-kotlinx-time/wasmWasi/src/Platform.kt new file mode 100644 index 000000000..012e08c18 --- /dev/null +++ b/fake-kotlinx-time/wasmWasi/src/Platform.kt @@ -0,0 +1,38 @@ +/* + * Copyright 2019-2024 JetBrains s.r.o. and contributors. + * Use of this source code is governed by the Apache 2.0 License that can be found in the LICENSE.txt file. + */ + +package kotlinx.time + +import kotlin.wasm.WasmImport +import kotlin.wasm.unsafe.UnsafeWasmMemoryApi +import kotlin.wasm.unsafe.withScopedMemoryAllocator + +/** + * Return the time value of a clock. Note: This is similar to `clock_gettime` in POSIX. + */ +@WasmImport("wasi_snapshot_preview1", "clock_time_get") +private external fun wasiRawClockTimeGet(clockId: Int, precision: Long, resultPtr: Int): Int + +private const val CLOCKID_REALTIME = 0 + +@OptIn(UnsafeWasmMemoryApi::class) +private fun clockTimeGet(): Long = withScopedMemoryAllocator { allocator -> + val rp0 = allocator.allocate(8) + val ret = wasiRawClockTimeGet( + clockId = CLOCKID_REALTIME, + precision = 1, + resultPtr = rp0.address.toInt() + ) + if (ret == 0) { + rp0.loadLong() + } else { + error("WASI call failed with $ret") + } +} + +internal actual fun currentTime(): Instant = clockTimeGet().let { time -> + // Instant.MAX and Instant.MIN are never going to be exceeded using just the Long number of nanoseconds + Instant(time.floorDiv(NANOS_PER_ONE.toLong()), time.mod(NANOS_PER_ONE.toLong()).toInt()) +} From 81611f1405c3c5c3ba19d4e3be9dd20ebb5dde50 Mon Sep 17 00:00:00 2001 From: Dmitry Khalanskiy Date: Fri, 13 Dec 2024 10:14:23 +0100 Subject: [PATCH 4/7] Support Android devices with API level < 24 --- fake-kotlinx-time/jvm/src/Platform.kt | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/fake-kotlinx-time/jvm/src/Platform.kt b/fake-kotlinx-time/jvm/src/Platform.kt index 71a7516b7..96031eab0 100644 --- a/fake-kotlinx-time/jvm/src/Platform.kt +++ b/fake-kotlinx-time/jvm/src/Platform.kt @@ -5,4 +5,22 @@ package kotlinx.time -internal actual fun currentTime(): Instant = java.time.Instant.now().toKotlinInstant() +internal actual fun currentTime(): Instant = if (javaTimeAvailable) { + java.time.Instant.now().toKotlinInstant() +} else { + /* After experimenting in Android Studio, it seems like on Android with API < 24, only millisecond precision + is available in `Instant.now()` with core library desugaring enabled. Because of that, `currentTimeMillis` + is good enough + suggesting that our users enable core library desugaring isn't going to bring any benefits, + so the KDoc for [Clock] does not mention any of this. */ + Instant.fromEpochMilliseconds(System.currentTimeMillis()) +} + +/** + * `false` for Android devices with API level < 24, where java.time is not available. + */ +private val javaTimeAvailable = try { + java.time.Instant.now() + true +} catch (e: NoClassDefFoundError) { + false +} From 6b623bf783d9f26f31809d047321e1fd70766f2c Mon Sep 17 00:00:00 2001 From: Dmitry Khalanskiy Date: Thu, 28 Nov 2024 14:34:55 +0100 Subject: [PATCH 5/7] Add tests and samples to kotlinx.time --- fake-kotlinx-time/common/src/Instant.kt | 6 +- fake-kotlinx-time/common/test/InstantTest.kt | 608 ++++++++++++++++++ .../common/test/ThreeTenBpInstantTest.kt | 96 +++ .../common/test/ThreeTenBpUtilTest.kt | 49 ++ fake-kotlinx-time/common/test/assertions.kt | 20 + .../common/test/samples/ClockSamples.kt | 36 ++ .../common/test/samples/InstantSamples.kt | 122 ++++ fake-kotlinx-time/js/test/ConvertersTest.kt | 29 + fake-kotlinx-time/jvm/test/ConvertersTest.kt | 34 + 9 files changed, 997 insertions(+), 3 deletions(-) create mode 100644 fake-kotlinx-time/common/test/InstantTest.kt create mode 100644 fake-kotlinx-time/common/test/ThreeTenBpInstantTest.kt create mode 100644 fake-kotlinx-time/common/test/ThreeTenBpUtilTest.kt create mode 100644 fake-kotlinx-time/common/test/assertions.kt create mode 100644 fake-kotlinx-time/common/test/samples/ClockSamples.kt create mode 100644 fake-kotlinx-time/common/test/samples/InstantSamples.kt create mode 100644 fake-kotlinx-time/js/test/ConvertersTest.kt create mode 100644 fake-kotlinx-time/jvm/test/ConvertersTest.kt diff --git a/fake-kotlinx-time/common/src/Instant.kt b/fake-kotlinx-time/common/src/Instant.kt index a9abcd514..24ebc7d13 100644 --- a/fake-kotlinx-time/common/src/Instant.kt +++ b/fake-kotlinx-time/common/src/Instant.kt @@ -234,8 +234,6 @@ public class Instant internal constructor( * Returns zero if this instant represents the same moment as the other (meaning they are equal to one another), * a negative number if this instant is earlier than the other, * and a positive number if this instant is later than the other. - * - * @sample kotlinx.datetime.test.samples.InstantSamples.compareToSample */ public override operator fun compareTo(other: Instant): Int { val s = epochSeconds.compareTo(other.epochSeconds) @@ -262,6 +260,8 @@ public class Instant internal constructor( * where the component for seconds is 60, and for any day, it's possible to observe 23:59:59. * * @see parse + * + * @sample kotlinx.datetime.test.samples.InstantSamples.toStringSample */ public override fun toString(): String = formatIso(this) @@ -748,7 +748,7 @@ private const val NANOS_PER_MILLI = 1_000_000 private const val MILLIS_PER_ONE = 1_000 // org.threeten.bp.chrono.IsoChronology#isLeapYear -private fun isLeapYear(year: Int): Boolean { +internal fun isLeapYear(year: Int): Boolean { val prolepticYear: Long = year.toLong() return prolepticYear and 3 == 0L && (prolepticYear % 100 != 0L || prolepticYear % 400 == 0L) } diff --git a/fake-kotlinx-time/common/test/InstantTest.kt b/fake-kotlinx-time/common/test/InstantTest.kt new file mode 100644 index 000000000..c42fa9e2a --- /dev/null +++ b/fake-kotlinx-time/common/test/InstantTest.kt @@ -0,0 +1,608 @@ +/* + * Copyright 2019-2020 JetBrains s.r.o. + * Use of this source code is governed by the Apache 2.0 License that can be found in the LICENSE.txt file. + */ + +package kotlinx.time.test + +import kotlinx.time.* +import kotlin.math.absoluteValue +import kotlin.random.* +import kotlin.test.* +import kotlin.time.* +import kotlin.time.Duration.Companion.milliseconds +import kotlin.time.Duration.Companion.nanoseconds +import kotlin.time.Duration.Companion.seconds + +class InstantTest { + + @Test + fun testNow() { + val instant = Clock.System.now() + val millis = instant.toEpochMilliseconds() + + assertTrue(millis > 1_500_000_000_000L) + + println(instant) + println(instant.toEpochMilliseconds()) + + val millisInstant = Instant.fromEpochMilliseconds(millis) + + assertEquals(millis, millisInstant.toEpochMilliseconds()) + + val notEqualInstant = Instant.fromEpochMilliseconds(millis + 1) + assertNotEquals(notEqualInstant, instant) + } + + @Test + fun instantArithmetic() { + val instant = Clock.System.now().toEpochMilliseconds().let { Instant.fromEpochMilliseconds(it) } // round to millis + val diffMillis = Random.nextLong(1000, 1_000_000_000) + val diff = diffMillis.milliseconds + + val nextInstant = (instant.toEpochMilliseconds() + diffMillis).let { Instant.fromEpochMilliseconds(it) } + + assertEquals(diff, nextInstant - instant) + assertEquals(nextInstant, instant + diff) + assertEquals(instant, nextInstant - diff) + + println("this: $instant, next: $nextInstant, diff: ${diff.toIsoString()}") + } + + @Test + fun addingMultiplesOf2_32() { + val pow2_32 = 1L shl 32 + val instant1 = Instant.fromEpochSeconds(0) + val instant2 = instant1.plus(pow2_32.nanoseconds) + assertEquals(pow2_32 / NANOS_PER_ONE, instant2.epochSeconds) + assertEquals(pow2_32 % NANOS_PER_ONE, instant2.nanosecondsOfSecond.toLong()) + + val instant3 = instant1.plus(pow2_32.seconds) + assertEquals(pow2_32, instant3.epochSeconds) + } + + /* Based on the ThreeTenBp project. + * Copyright (c) 2007-present, Stephen Colebourne & Michael Nascimento Santos + */ + @Test + fun nanosecondAdjustment() { + for (i in -2..2L) { + for (j in 0..9) { + val t: Instant = Instant.fromEpochSeconds(i, j) + val t2: Instant = Instant.fromEpochSeconds(i, j.toLong()) + assertEquals(i, t.epochSeconds) + assertEquals(j, t.nanosecondsOfSecond) + assertEquals(t, t2) + } + for (j in -10..-1) { + val t: Instant = Instant.fromEpochSeconds(i, j) + val t2: Instant = Instant.fromEpochSeconds(i, j.toLong()) + assertEquals(i - 1, t.epochSeconds) + assertEquals(j + 1000000000, t.nanosecondsOfSecond) + assertEquals(t, t2) + } + for (j in 999_999_990..999_999_999) { + val t: Instant = Instant.fromEpochSeconds(i, j) + val t2: Instant = Instant.fromEpochSeconds(i, j.toLong()) + assertEquals(i, t.epochSeconds) + assertEquals(j, t.nanosecondsOfSecond) + assertEquals(t, t2) + } + } + val t = Instant.fromEpochSeconds(0, Int.MAX_VALUE) + assertEquals((Int.MAX_VALUE / 1_000_000_000).toLong(), t.epochSeconds) + assertEquals(Int.MAX_VALUE % 1_000_000_000, t.nanosecondsOfSecond) + val t2 = Instant.fromEpochSeconds(0, Long.MAX_VALUE) + assertEquals(Long.MAX_VALUE / 1_000_000_000, t2.epochSeconds) + assertEquals((Long.MAX_VALUE % 1_000_000_000).toInt(), t2.nanosecondsOfSecond) + } + + @Test + fun distantPastAndFuture() { + val distantFutureString = "+100000-01-01T00:00:00Z" + val distantPastString = "-100001-12-31T23:59:59.999999999Z" + assertEquals(distantFutureString, Instant.DISTANT_FUTURE.toString()) + assertEquals(Instant.DISTANT_FUTURE, Instant.parse(distantFutureString)) + assertEquals(distantPastString, Instant.DISTANT_PAST.toString()) + assertEquals(Instant.DISTANT_PAST, Instant.parse(distantPastString)) + assertTrue(Instant.DISTANT_PAST.isDistantPast) + assertTrue(Instant.DISTANT_FUTURE.isDistantFuture) + assertFalse(Instant.DISTANT_PAST.isDistantFuture) + assertFalse(Instant.DISTANT_FUTURE.isDistantPast) + assertFalse((Instant.DISTANT_PAST + 1.nanoseconds).isDistantPast) + assertFalse((Instant.DISTANT_FUTURE - 1.nanoseconds).isDistantFuture) + assertTrue((Instant.DISTANT_PAST - 1.nanoseconds).isDistantPast) + assertTrue((Instant.DISTANT_FUTURE + 1.nanoseconds).isDistantFuture) + assertTrue(Instant.MAX.isDistantFuture) + assertFalse(Instant.MAX.isDistantPast) + assertTrue(Instant.MIN.isDistantPast) + assertFalse(Instant.MIN.isDistantFuture) + } + +} + +class InstantRangeTest { + private val largePositiveLongs = listOf(Long.MAX_VALUE, Long.MAX_VALUE - 1, Long.MAX_VALUE - 50) + private val largeNegativeLongs = listOf(Long.MIN_VALUE, Long.MIN_VALUE + 1, Long.MIN_VALUE + 50) + + private val largePositiveInstants = listOf(Instant.MAX, Instant.MAX - 1.seconds, Instant.MAX - 50.seconds) + private val largeNegativeInstants = listOf(Instant.MIN, Instant.MIN + 1.seconds, Instant.MIN + 50.seconds) + + private val smallInstants = listOf( + Instant.fromEpochMilliseconds(0), + Instant.fromEpochMilliseconds(1003), + Instant.fromEpochMilliseconds(253112) + ) + + + @Test + fun epochMillisecondsClamping() { + /* Any number of milliseconds in Long is representable as an Instant */ + for (instant in largePositiveInstants) { + assertEquals(Long.MAX_VALUE, instant.toEpochMilliseconds(), "$instant") + } + for (instant in largeNegativeInstants) { + assertEquals(Long.MIN_VALUE, instant.toEpochMilliseconds(), "$instant") + } + for (milliseconds in largePositiveLongs + largeNegativeLongs) { + assertEquals(milliseconds, Instant.fromEpochMilliseconds(milliseconds).toEpochMilliseconds(), + "$milliseconds") + } + } + + @Test + fun epochSecondsClamping() { + // fromEpochSeconds + // On all platforms Long.MAX_VALUE of seconds is not a valid instant. + for (seconds in largePositiveLongs) { + assertEquals(Instant.MAX, Instant.fromEpochSeconds(seconds, 35)) + } + for (seconds in largeNegativeLongs) { + assertEquals(Instant.MIN, Instant.fromEpochSeconds(seconds, 35)) + } + for (instant in largePositiveInstants + smallInstants + largeNegativeInstants) { + assertEquals(instant, Instant.fromEpochSeconds(instant.epochSeconds, instant.nanosecondsOfSecond.toLong())) + } + } + + @Test + fun durationArithmeticClamping() { + val longDurations = listOf(Duration.INFINITE) + + for (duration in longDurations) { + for (instant in smallInstants + largeNegativeInstants + largePositiveInstants) { + assertEquals(Instant.MAX, instant + duration) + } + for (instant in smallInstants + largeNegativeInstants + largePositiveInstants) { + assertEquals(Instant.MIN, instant - duration) + } + } + assertEquals(Instant.MAX, (Instant.MAX - 4.seconds) + 5.seconds) + assertEquals(Instant.MIN, (Instant.MIN + 10.seconds) - 12.seconds) + } + + @Test + fun timeBasedUnitArithmeticOutOfRange() { + // Instant.plus(Long, DateTimeUnit.TimeBased) + // Arithmetic overflow + for (instant in smallInstants + largeNegativeInstants + largePositiveInstants) { + assertEquals(Instant.MAX, instant.plus(Long.MAX_VALUE.seconds)) + assertEquals(Instant.MIN, instant.plus(Long.MIN_VALUE.seconds)) + } + // Overflow of Instant boundaries + for (instant in smallInstants + largeNegativeInstants + largePositiveInstants) { + assertEquals(Instant.MAX, instant.plus((Instant.MAX.epochSeconds - instant.epochSeconds + 1).seconds)) + assertEquals(Instant.MIN, instant.plus((Instant.MIN.epochSeconds - instant.epochSeconds - 1).seconds)) + } + } + + // https://github.com/Kotlin/kotlinx-datetime/issues/263 + @Test + fun addSmallDurationsToLargeInstants() { + for (smallDuration in listOf(1.nanoseconds, 999_999.nanoseconds, 1.seconds - 1.nanoseconds)) { + assertEquals(expected = Instant.MAX, actual = Instant.MAX + smallDuration) + assertEquals(expected = Instant.MIN, actual = Instant.MIN - smallDuration) + } + } + + @Test + fun subtractInstants() { + val max = Instant.fromEpochSeconds(31494816403199L) + val min = Instant.fromEpochSeconds(-31619119219200L) + assertEquals(max.epochSeconds - min.epochSeconds, (max - min).inWholeSeconds) + } +} + +class InstantIsoStringsTest { + + @Test + fun parseDates() { + fun Int.zeroPadded(digits: Int): String = when { + this >= 0 -> toString().padStart(digits, '0') + else -> "-${absoluteValue.toString().padStart(digits, '0')}" + } + + fun localDateToString(year: Int, month: Int, day: Int) = + "${year.zeroPadded(4)}-${month.zeroPadded(2)}-${day.zeroPadded(2)}" + + // only works for 1-4-digit years + fun assertMonthBoundariesAreCorrect(year: Int, month: Int, lastDayOfMonth: Int) { + val validString = "${localDateToString(year, month, lastDayOfMonth)}T23:59:59Z" + val invalidString = "${localDateToString(year, month, lastDayOfMonth + 1)}T23:59:59Z" + Instant.parse(validString) // shouldn't throw + assertInvalidFormat(invalidString) { Instant.parse(invalidString) } + } + + val nonLeapYears = listOf( + 1970, 1971, 1973, 1974, 1975, 2021, 2022, 2023, 2100, 1100, 1, 2, 3, 5, -1, -2, -1971, 100, -100 + ) + val leapYears = listOf( + 0, 1972, 1976, 1980, 2000, 1200, 400, -400, -4, -8, + ) + for ((month, lastDayOfMonth) in arrayOf( + 1 to 31, 3 to 31, 4 to 30, 5 to 31, 6 to 30, + 7 to 31, 8 to 31, 9 to 30, 10 to 31, 11 to 30, 12 to 31, + )) { + for (year in nonLeapYears + leapYears) { + assertMonthBoundariesAreCorrect(year, month, lastDayOfMonth) + } + } + for (leapYear in leapYears) { + assertMonthBoundariesAreCorrect(leapYear, 2, 29) + } + for (nonLeapYear in nonLeapYears) { + assertMonthBoundariesAreCorrect(nonLeapYear, 2, 28) + } + } + + @Test + fun parseIsoString() { + for ((str, seconds, nanos) in arrayOf>( + // all components are taken into account + Triple("1970-01-01T00:00:00Z", 0, 0), + Triple("1970-01-01T00:00:00.000000001Z", 0, 1), + Triple("1970-01-01T00:00:00.100Z", 0, 100000000), + Triple("1970-01-01T00:00:01Z", 1, 0), + Triple("1970-01-01T00:01:00Z", 60, 0), + Triple("1970-01-01T00:01:01Z", 61, 0), + Triple("1970-01-01T00:01:01.000000001Z", 61, 1), + Triple("1970-01-01T01:00:00Z", 3600, 0), + Triple("1970-01-01T01:01:01.000000001Z", 3661, 1), + Triple("1970-01-02T01:01:01.100Z", 90061, 100000000), + Triple("1970-02-02T01:01:01.100Z", 31 * 86400 + 90061, 100000000), + Triple("1971-02-02T01:01:01.100Z", (365 + 31) * 86400 + 90061, 100000000), + // how many digits get output for various precision of the sub-second portion + Triple("1970-01-01T00:00:00.100Z", 0, 100_000_000), + Triple("1970-01-01T00:00:00.010Z", 0, 10_000_000), + Triple("1970-01-01T00:00:00.001Z", 0, 1_000_000), + Triple("1970-01-01T00:00:00.000100Z", 0, 100_000), + Triple("1970-01-01T00:00:00.000010Z", 0, 10_000), + Triple("1970-01-01T00:00:00.000001Z", 0, 1_000), + Triple("1970-01-01T00:00:00.000000100Z", 0, 100), + Triple("1970-01-01T00:00:00.000000010Z", 0, 10), + Triple("1970-01-01T00:00:00.000000001Z", 0, 1), + // random data queried from java.time + Triple("+51861-09-21T11:07:43.782719883Z", 1574430692863, 782719883), + Triple("+395069-04-30T01:28:37.454777349Z", 12405016603717, 454777349), + Triple("-551259-03-05T08:01:36.195722269Z", -17458215523104, 195722269), + Triple("+498403-02-11T17:47:05.156642423Z", 15665915958425, 156642423), + Triple("+283686-10-14T23:00:25.666521845Z", 8890123158025, 666521845), + Triple("-910329-04-04T09:27:54.456784744Z", -28789367639526, 456784744), + Triple("-37222-03-21T18:04:37.006055123Z", -1236773166923, 6055123), + Triple("-189377-03-30T01:37:14.288808090Z", -6038320515766, 288808090), + Triple("-67394-03-24T03:19:41.794404047Z", -2188909341619, 794404047), + Triple("-870649-05-27T13:47:39.925150102Z", -27537183223941, 925150102), + Triple("+94020-04-10T14:51:21.569206089Z", 2904826114281, 569206089), + Triple("-945485-07-11T23:28:58.240153828Z", -29898775384262, 240153828), + Triple("-73722-02-22T11:19:54.364548772Z", -2388604250406, 364548772), + Triple("-645899-05-17T16:44:21.522135477Z", -20444759104539, 522135477), + Triple("-702594-10-20T10:13:53.212104714Z", -22233867083167, 212104714), + Triple("-442579-11-22T01:35:44.591216727Z", -14028583357456, 591216727), + Triple("-849915-06-25T01:28:27.625015449Z", -26882878833093, 625015449), + Triple("-481897-08-13T05:44:47.077814711Z", -15269348340913, 77814711), + Triple("+295919-02-07T15:47:37.850981753Z", 9276137682457, 850981753), + Triple("+967334-01-15T15:08:10.235167075Z", 30463946694490, 235167075), + Triple("+774237-04-30T16:00:32.810606451Z", 24370403011232, 810606451), + Triple("+792959-05-03T08:18:31.616194572Z", 24961212490711, 616194572), + Triple("-261823-02-16T03:17:35.085815500Z", -8324498983345, 85815500), + Triple("+931062-03-22T17:04:54.135075640Z", 29319318637494, 135075640), + Triple("+623320-01-26T03:08:05.121769356Z", 19607914264085, 121769356), + Triple("+322804-03-06T11:31:24.788006817Z", 10124548774284, 788006817), + Triple("-784322-04-03T21:25:19.666588404Z", -24812970806081, 666588404), + Triple("+403293-01-07T05:59:41.601460200Z", 12664531288781, 601460200), + Triple("-835821-06-01T00:52:15.782852248Z", -26438117296065, 782852248), + Triple("+222483-07-15T08:29:55.019931345Z", 6958735086595, 19931345), + Triple("-663595-09-05T04:36:24.110433196Z", -21003181356216, 110433196), + Triple("+166626-02-15T22:16:34.070665743Z", 5196045449794, 70665743), + Triple("-517158-01-02T22:52:24.155574933Z", -16382097162456, 155574933), + Triple("+850155-01-02T10:25:31.349473798Z", 26766133467931, 349473798), + Triple("-967697-04-25T20:43:33.328060156Z", -30599725115787, 328060156), + Triple("+437131-04-26T07:32:58.134219875Z", 13732364705578, 134219875), + Triple("+372920-11-25T13:38:22.852562723Z", 11706079786702, 852562723), + Triple("+169255-09-07T11:28:18.481625778Z", 5279026303698, 481625778), + Triple("-980786-08-18T17:05:22.581779094Z", -31012764044078, 581779094), + Triple("+182945-05-25T20:39:24.545585221Z", 5711031952764, 545585221), + Triple("+300811-12-15T02:53:38.676752671Z", 9430541175218, 676752671), + Triple("-807816-01-18T18:04:26.291749218Z", -25554376389334, 291749218), + Triple("-53033-12-30T22:02:01.398533618Z", -1735695568679, 398533618), + Triple("-354903-06-14T10:08:46.111648055Z", -11261809864274, 111648055), + Triple("+842009-03-11T23:58:06.537554993Z", 26509076495886, 537554993), + Triple("-391976-11-09T04:16:17.862484469Z", -12431707962223, 862484469), + Triple("-733019-10-28T17:07:13.450343935Z", -23193986539967, 450343935), + Triple("+595280-03-05T23:36:27.765851400Z", 18723060833787, 765851400), + Triple("-930296-07-17T03:33:33.094509320Z", -29419456335987, 94509320), + Triple("+609508-02-29T10:58:02.703241053Z", 19172052557882, 703241053), + Triple("+996233-06-25T06:01:55.647461964Z", 31375924927315, 647461964), + Triple("-93200-12-06T21:29:56.140938343Z", -3003245692204, 140938343), + Triple("+794143-07-02T09:49:35.585085194Z", 24998581100975, 585085194), + Triple("-783550-12-31T17:10:16.577723428Z", -24788585371784, 577723428), + Triple("-240168-11-03T17:22:09.108424624Z", -7641110702271, 108424624), + Triple("+613419-02-15T12:00:07.012460989Z", 19295470641607, 12460989), + Triple("-521405-03-25T02:03:46.552711998Z", -16516112536574, 552711998), + Triple("-938829-01-22T16:48:43.582709371Z", -29688747030677, 582709371), + Triple("+916785-05-16T21:54:45.983221956Z", 28868784818085, 983221956), + Triple("+482425-06-09T04:24:32.683186155Z", 15161709183872, 683186155), + Triple("+622585-08-20T05:45:52.555088343Z", 19584737819152, 555088343), + Triple("-451048-11-02T01:49:29.076392891Z", -14295840847831, 76392891), + Triple("+721083-09-17T00:31:34.648020241Z", 22693036811494, 648020241), + Triple("+235979-10-28T12:07:33.706273641Z", 7384636728453, 706273641), + Triple("+285234-04-12T18:30:25.215363003Z", 8938957285825, 215363003), + Triple("-917176-03-10T10:03:25.943265324Z", -29005440213395, 943265324), + Triple("-381932-09-05T02:47:17.004960541Z", -12114755529163, 4960541), + Triple("-52158-11-11T09:38:45.489915403Z", -1708087530075, 489915403), + Triple("-584290-11-15T20:15:24.377620606Z", -18500551127076, 377620606), + Triple("-645616-05-05T17:36:59.941608628Z", -20435829488581, 941608628), + Triple("+794405-06-22T21:08:20.853641989Z", 25006848239300, 853641989), + Triple("+986590-08-01T05:15:25.827177433Z", 31071624470125, 827177433), + Triple("+527158-02-06T12:34:35.088546391Z", 16573335654875, 88546391), + Triple("-513116-05-01T07:28:44.448204123Z", -16254533665876, 448204123), + Triple("+397065-10-19T21:59:05.831855226Z", 12468019211945, 831855226), + Triple("+312769-04-26T11:33:07.802217284Z", 9807879123187, 802217284), + Triple("+682473-04-14T01:00:38.067076018Z", 21474609498038, 67076018), + Triple("+731560-02-15T02:15:06.599802467Z", 23023640456106, 599802467), + Triple("-877354-10-27T22:55:02.723751549Z", -27748759338298, 723751549), + Triple("-746193-01-02T07:19:56.258497483Z", -23609743807204, 258497483), + Triple("-822112-07-28T08:55:19.319285417Z", -26005498038281, 319285417), + Triple("-400365-04-30T00:05:51.210582736Z", -12696455980449, 210582736), + Triple("+436254-07-11T18:08:06.937065549Z", 13704695921286, 937065549), + Triple("-340854-01-07T03:17:32.367173472Z", -10818479997748, 367173472), + Triple("-985221-04-25T22:57:01.511559459Z", -31152729085379, 511559459), + Triple("+859861-09-01T02:21:20.289341591Z", 27072446149280, 289341591), + Triple("-0131-07-16T10:47:54.756333457Z", -66284140326, 756333457), + Triple("-327041-11-18T22:55:21.885337272Z", -10382556503079, 885337272), + Triple("-268616-05-06T10:27:54.420166505Z", -8538858480726, 420166505), + Triple("-228012-05-16T15:26:54.680432991Z", -7257519160386, 680432991), + Triple("+857168-09-12T13:29:36.945689251Z", 26987464272576, 945689251), + Triple("-974181-04-12T08:47:35.627678735Z", -30804341526745, 627678735), + Triple("-435700-10-20T22:33:13.897477229Z", -13811505874007, 897477229), + Triple("-507467-01-19T23:06:05.156792267Z", -16076277276835, 156792267), + Triple("-382257-11-19T08:00:10.407963305Z", -12125005142390, 407963305), + Triple("+83082-01-04T20:18:56.409867424Z", 2559647852336, 409867424), + Triple("-916839-09-12T22:45:39.091941363Z", -28994789466861, 91941363), + Triple("-147771-05-07T08:31:34.950238979Z", -4725358615706, 950238979), + // random enormous values queried from java.time + Triple("+639727757-10-17T17:26:30.359003681Z", 20187795978609990, 359003681), + Triple("-375448814-11-11T03:04:48.637504595Z", -11848082341899312, 637504595), + Triple("+99162559-10-16T11:21:05.057717803Z", 3129205972303265, 57717803), + Triple("-826813174-05-22T21:35:39.018693830Z", -26091765799784661, 18693830), + Triple("+254125623-04-28T11:42:22.659957596Z", 8019367929949342, 659957596), + Triple("+540978343-01-03T20:31:36.945404442Z", 17071565436102696, 945404442), + Triple("+988277529-06-06T13:25:41.942771350Z", 31186964391657941, 942771350), + Triple("+566487909-09-04T08:09:46.007490076Z", 17876569606963786, 7490076), + Triple("+442225124-02-16T18:23:29.975096773Z", 13955214848034209, 975096773), + Triple("-399250586-02-19T22:58:29.585918917Z", -12599193741267691, 585918917), + Triple("-190217791-04-28T00:49:29.270751921Z", -6002755857213031, 270751921), + Triple("-716173704-05-24T22:05:33.639928802Z", -22600321355469267, 639928802), + Triple("+910504788-10-26T05:23:43.517192887Z", 28732693749312223, 517192887), + Triple("+515896807-08-17T06:36:25.012343642Z", 16280068627982185, 12343642), + Triple("-623794742-03-20T17:09:07.396143995Z", -19685122891483853, 396143995), + Triple("-416781718-10-06T01:41:32.866307162Z", -13152422812544308, 866307162), + Triple("+287346593-09-30T23:30:55.109337183Z", 9067720499134255, 109337183), + Triple("-819839065-09-06T07:25:58.953784983Z", -25871684167672442, 953784983), + Triple("+673467211-06-05T02:15:40.712392732Z", 21252510297310540, 712392732), + Triple("+982441727-04-13T12:12:06.776817565Z", 31002804263391126, 776817565), + )) { + val instant = Instant.parse(str) + assertEquals( + Instant.fromEpochSeconds(seconds, nanos), instant, + "Parsed $instant from $str, with Unix time = `$seconds + 10^-9 * $nanos`" + ) + assertEquals(str, instant.toString()) + } + // non-canonical strings are parsed as well, but formatted differently + for ((str, seconds, nanos) in arrayOf( + // upper, lower case, trailing zeros + Triple("2024-07-15T14:06:29.461245000z", 1721052389, 461245000), + Triple("2024-07-15t14:06:29.4612450z", 1721052389, 461245000), + // current time + Triple("2024-07-15T16:06:29.461245691+02:00", 1721052389, 461245691), + )) { + val instant = Instant.parse(str) + assertEquals( + seconds.toLong() * 1000 + nanos / 1000000, instant.toEpochMilliseconds(), + "Parsed $instant from $str, with Unix time = `$seconds + 10^-9 * $nanos`" + ) + } + } + + @Test + fun nonParseableInstantStrings() { + for (nonIsoString in listOf( + // empty string + "", + // a non-empty but clearly unsuitable string + "x", + // something other than a sign at the beginning + " 1970-01-01T00:00:00Z", + // too many digits for the year + "+1234567890-01-01T00:00:00Z", + "-1234567890-01-01T00:00:00Z", + // not enough padding for the year + "003-01-01T00:00:00Z", + "-003-01-01T00:00:00Z", + // a plus sign even though there is only 4 digits + "+1970-01-01T00:00:00Z", + // too many digits without padding + "11970-01-01T00:00:00Z", + // incorrect separators between the components + "1970/01-01T00:00:00Z", + "1970-01/01T00:00:00Z", + "1970-01-01 00:00:00Z", + "1970-01-01T00-00:00Z", + "1970-01-01T00:00-00Z", + // non-digits where digits are expected + "1970-X1-01T00:00:00Z", + "1970-1X-01T00:00:00Z", + "1970-11-X1T00:00:00Z", + "1970-11-1XT00:00:00Z", + "1970-11-10TX0:00:00Z", + "1970-11-10T0X:00:00Z", + "1970-11-10T00:X0:00Z", + "1970-11-10T00:0X:00Z", + "1970-11-10T00:00:X0Z", + "1970-11-10T00:00:0XZ", + // a non-ascii digit + "1970-11-10T00:00:0٩Z", + // not enough components + "1970-11-10T00:00Z", + // not enough components, even if the length is sufficient + "1970-11-10T00:00+01:15", + // a dot without any fraction of the second following it + "1970-11-10T00:00:00.Z", + // too many digits in the fraction of the second + "1970-11-10T00:00:00.1234567890Z", + // out-of-range values + "1970-00-10T00:00:00Z", + "1970-13-10T00:00:00Z", + "1970-01-32T00:00:00Z", + "1970-02-29T00:00:00Z", + "1972-02-30T00:00:00Z", + "2000-02-30T00:00:00Z", + "2100-02-29T00:00:00Z", + "2004-02-30T00:00:00Z", + "2005-02-29T00:00:00Z", + "2005-04-31T00:00:00Z", + "2005-04-01T24:00:00Z", + "2005-04-01T00:60:00Z", + "2005-04-01T00:00:60Z", + // leap second + "1970-01-01T23:59:60Z", + // lack of padding + "1970-1-10T00:00:00+05:00", + "1970-10-1T00:00:00+05:00", + "1970-10-10T0:00:00+05:00", + "1970-10-10T00:0:00+05:00", + "1970-10-10T00:00:0+05:00", + // no offset + "1970-02-03T04:05:06.123456789", + // some invalid single-character offsets + "1970-02-03T04:05:06.123456789A", + "1970-02-03T04:05:06.123456789+", + "1970-02-03T04:05:06.123456789-", + // too many components in the offset + "1970-02-03T04:05:06.123456789+03:02:01:00", + "1970-02-03T04:05:06.123456789+03:02:01.02", + // single-digit offset + "1970-02-03T04:05:06.123456789+3", + // incorrect sign in the offset + "1970-02-03T04:05:06.123456789 03", + // non-digits in the offset + "1970-02-03T04:05:06.123456789+X3", + "1970-02-03T04:05:06.123456789+1X", + "1970-02-03T04:05:06.123456789+X3:12", + "1970-02-03T04:05:06.123456789+1X:12", + "1970-02-03T04:05:06.123456789+X3:12", + "1970-02-03T04:05:06.123456789+13:X2", + "1970-02-03T04:05:06.123456789+13:1X", + "1970-02-03T04:05:06.123456789+X3:12:59", + "1970-02-03T04:05:06.123456789+1X:12:59", + "1970-02-03T04:05:06.123456789+X3:12:59", + "1970-02-03T04:05:06.123456789+13:X2:59", + "1970-02-03T04:05:06.123456789+13:1X:59", + "1970-02-03T04:05:06.123456789+13:12:X9", + "1970-02-03T04:05:06.123456789+13:12:5X", + // incorrect separators in the offset + "1970-02-03T04:05:06.123456789+13/12", + "1970-02-03T04:05:06.123456789+13/12:59", + "1970-02-03T04:05:06.123456789+13:12/59", + "1970-02-03T04:05:06.123456789+0130", + "1970-02-03T04:05:06.123456789-0130", + // incorrect field length + "1970-02-03T04:05:06.123456789-18:001", + // out-of-range offsets + "1970-02-03T04:05:06.123456789+18:12:59", + "1970-02-03T04:05:06.123456789-18:12:59", + "1970-02-03T04:05:06.123456789+18:00:01", + "1970-02-03T04:05:06.123456789-18:00:01", + "1970-02-03T04:05:06.123456789+18:01", + "1970-02-03T04:05:06.123456789-18:01", + "1970-02-03T04:05:06.123456789+19", + "1970-02-03T04:05:06.123456789-19", + // out-of-range fields of the offset + "1970-02-03T04:05:06.123456789+01:12:60", + "1970-02-03T04:05:06.123456789-01:12:60", + "1970-02-03T04:05:06.123456789+01:60", + "1970-02-03T04:05:06.123456789-01:60", + // lack of padding in the offset + "1970-02-03T04:05:06.123456789+1:12:50", + "1970-02-03T04:05:06.123456789+01:2:60", + "1970-02-03T04:05:06.123456789+01:12:6", + )) { + assertInvalidFormat(nonIsoString) { Instant.parse(nonIsoString) } + } + // this string represents an Instant that is currently larger than Instant.MAX any of the implementations: + assertInvalidFormat { Instant.parse("+1000000001-12-31T23:59:59.000000000Z") } + } + + @Test + fun parseStringsWithOffsets() { + val strings = arrayOf( + Pair("2020-01-01T00:01:01.02+18:00", "2019-12-31T06:01:01.020Z"), + Pair("2020-01-01T00:01:01.123456789-17:59:59", "2020-01-01T18:01:00.123456789Z"), + Pair("2020-01-01T00:01:01.010203040+17:59:59", "2019-12-31T06:01:02.010203040Z"), + Pair("2020-01-01T00:01:01.010203040+17:59", "2019-12-31T06:02:01.010203040Z"), + Pair("2020-01-01T00:01:01+00", "2020-01-01T00:01:01Z"), + ) + strings.forEach { (str, strInZ) -> + val instant = Instant.parse(str) + assertEquals(Instant.parse(strInZ), instant, str) + assertEquals(strInZ, instant.toString(), str) + } + assertInvalidFormat { Instant.parse("2020-01-01T00:01:01+18:01") } + assertInvalidFormat { Instant.parse("2020-01-01T00:01:01+1801") } + assertInvalidFormat { Instant.parse("2020-01-01T00:01:01+0") } + assertInvalidFormat { Instant.parse("2020-01-01T00:01:01+") } + assertInvalidFormat { Instant.parse("2020-01-01T00:01:01") } + assertInvalidFormat { Instant.parse("2020-01-01T00:01:01+000000") } + + val instants = listOf( + Instant.DISTANT_FUTURE, + Instant.DISTANT_PAST, + Instant.fromEpochSeconds(0, 0), + Instant.parse("2020-01-02T03:04:05.6789Z"), + Instant.MAX, + Instant.MIN, + ) + + val offsets = listOf( + 0 to "Z", + 3 * 3600 + 12 * 60 + 14 to "+03:12:14", + - 3 * 3600 - 12 * 60 - 14 to "-03:12:14", + 2 * 3600 + 35 * 60 to "+02:35", + - 2 * 3600 - 35 * 60 to "-02:35", + 4 * 3600 to "+04", + - 4 * 3600 to "-04", + ) + + for (instant in instants) { + for ((offsetSeconds, offsetString) in offsets) { + if (instant == Instant.MAX && offsetSeconds < 0 || + instant == Instant.MIN && offsetSeconds > 0 + ) continue + val newInstant = Instant.parse("${instant.toString().dropLast(1)}$offsetString") + assertEquals(newInstant, instant.minus(offsetSeconds.seconds)) + } + } + } + +} diff --git a/fake-kotlinx-time/common/test/ThreeTenBpInstantTest.kt b/fake-kotlinx-time/common/test/ThreeTenBpInstantTest.kt new file mode 100644 index 000000000..b56e18152 --- /dev/null +++ b/fake-kotlinx-time/common/test/ThreeTenBpInstantTest.kt @@ -0,0 +1,96 @@ +/* + * Copyright 2019-2020 JetBrains s.r.o. + * Use of this source code is governed by the Apache 2.0 License that can be found in the LICENSE.txt file. + */ +/* Based on the ThreeTenBp project. + * Copyright (c) 2007-present, Stephen Colebourne & Michael Nascimento Santos + */ + +package kotlinx.time.test + +import kotlinx.time.* +import kotlin.test.* + +class ThreeTenBpInstantTest { + + @Test + fun instantComparisons() { + val instants = arrayOf( + Instant.fromEpochSeconds(-2L, 0), + Instant.fromEpochSeconds(-2L, 999999998), + Instant.fromEpochSeconds(-2L, 999999999), + Instant.fromEpochSeconds(-1L, 0), + Instant.fromEpochSeconds(-1L, 1), + Instant.fromEpochSeconds(-1L, 999999998), + Instant.fromEpochSeconds(-1L, 999999999), + Instant.fromEpochSeconds(0L, 0), + Instant.fromEpochSeconds(0L, 1), + Instant.fromEpochSeconds(0L, 2), + Instant.fromEpochSeconds(0L, 999999999), + Instant.fromEpochSeconds(1L, 0), + Instant.fromEpochSeconds(2L, 0) + ) + for (i in instants.indices) { + val a = instants[i] + for (j in instants.indices) { + val b = instants[j] + when { + i < j -> { + assertTrue(a < b, "$a <=> $b") + assertNotEquals(a, b, "$a <=> $b") + } + i > j -> { + assertTrue(a > b, "$a <=> $b") + assertNotEquals(a, b, "$a <=> $b") + } + else -> { + assertEquals(0, a.compareTo(b), "$a <=> $b") + assertEquals(a, b, "$a <=> $b") + } + } + } + } + } + + @Test + fun instantEquals() { + val test5a: Instant = Instant.fromEpochSeconds(5L, 20) + val test5b: Instant = Instant.fromEpochSeconds(5L, 20) + val test5n: Instant = Instant.fromEpochSeconds(5L, 30) + val test6: Instant = Instant.fromEpochSeconds(6L, 20) + assertEquals(true, test5a == test5a) + assertEquals(true, test5a == test5b) + assertEquals(false, test5a == test5n) + assertEquals(false, test5a == test6) + assertEquals(true, test5b == test5a) + assertEquals(true, test5b == test5b) + assertEquals(false, test5b == test5n) + assertEquals(false, test5b == test6) + assertEquals(false, test5n == test5a) + assertEquals(false, test5n == test5b) + assertEquals(true, test5n == test5n) + assertEquals(false, test5n == test6) + assertEquals(false, test6 == test5a) + assertEquals(false, test6 == test5b) + assertEquals(false, test6 == test5n) + assertEquals(true, test6 == test6) + } + + @Test + fun toEpochMilliseconds() { + assertEquals(Instant.fromEpochSeconds(1L, 1000000).toEpochMilliseconds(), 1001L) + assertEquals(Instant.fromEpochSeconds(1L, 2000000).toEpochMilliseconds(), 1002L) + assertEquals(Instant.fromEpochSeconds(1L, 567).toEpochMilliseconds(), 1000L) + assertEquals(Instant.fromEpochSeconds(Long.MAX_VALUE / 1_000_000).toEpochMilliseconds(), Long.MAX_VALUE / 1_000_000 * 1000) + assertEquals(Instant.fromEpochSeconds(Long.MIN_VALUE / 1_000_000).toEpochMilliseconds(), Long.MIN_VALUE / 1_000_000 * 1000) + assertEquals(Instant.fromEpochSeconds(0L, -1000000).toEpochMilliseconds(), -1L) + assertEquals(Instant.fromEpochSeconds(0L, 1000000).toEpochMilliseconds(), 1) + assertEquals(Instant.fromEpochSeconds(0L, 999999).toEpochMilliseconds(), 0) + assertEquals(Instant.fromEpochSeconds(0L, 1).toEpochMilliseconds(), 0) + assertEquals(Instant.fromEpochSeconds(0L, 0).toEpochMilliseconds(), 0) + assertEquals(Instant.fromEpochSeconds(0L, -1).toEpochMilliseconds(), -1L) + assertEquals(Instant.fromEpochSeconds(0L, -999999).toEpochMilliseconds(), -1L) + assertEquals(Instant.fromEpochSeconds(0L, -1000000).toEpochMilliseconds(), -1L) + assertEquals(Instant.fromEpochSeconds(0L, -1000001).toEpochMilliseconds(), -2L) + } +} diff --git a/fake-kotlinx-time/common/test/ThreeTenBpUtilTest.kt b/fake-kotlinx-time/common/test/ThreeTenBpUtilTest.kt new file mode 100644 index 000000000..a698f88df --- /dev/null +++ b/fake-kotlinx-time/common/test/ThreeTenBpUtilTest.kt @@ -0,0 +1,49 @@ +/* + * Copyright 2019-2020 JetBrains s.r.o. + * Use of this source code is governed by the Apache 2.0 License that can be found in the LICENSE.txt file. + */ +/* Based on the ThreeTenBp project. + * Copyright (c) 2007-present, Stephen Colebourne & Michael Nascimento Santos + */ + +package kotlinx.time.test + +import kotlinx.time.* +import kotlin.test.* + +class ThreeTenBpUtilTest { + @Test + fun isLeap() { + assertEquals(false, isLeapYear(1999)) + assertEquals(true, isLeapYear(2000)) + assertEquals(false, isLeapYear(2001)) + assertEquals(false, isLeapYear(2007)) + assertEquals(true, isLeapYear(2008)) + assertEquals(false, isLeapYear(2009)) + assertEquals(false, isLeapYear(2010)) + assertEquals(false, isLeapYear(2011)) + assertEquals(true, isLeapYear(2012)) + assertEquals(false, isLeapYear(2095)) + assertEquals(true, isLeapYear(2096)) + assertEquals(false, isLeapYear(2097)) + assertEquals(false, isLeapYear(2098)) + assertEquals(false, isLeapYear(2099)) + assertEquals(false, isLeapYear(2100)) + assertEquals(false, isLeapYear(2101)) + assertEquals(false, isLeapYear(2102)) + assertEquals(false, isLeapYear(2103)) + assertEquals(true, isLeapYear(2104)) + assertEquals(false, isLeapYear(2105)) + assertEquals(false, isLeapYear(-500)) + assertEquals(true, isLeapYear(-400)) + assertEquals(false, isLeapYear(-300)) + assertEquals(false, isLeapYear(-200)) + assertEquals(false, isLeapYear(-100)) + assertEquals(true, isLeapYear(0)) + assertEquals(false, isLeapYear(100)) + assertEquals(false, isLeapYear(200)) + assertEquals(false, isLeapYear(300)) + assertEquals(true, isLeapYear(400)) + assertEquals(false, isLeapYear(500)) + } +} diff --git a/fake-kotlinx-time/common/test/assertions.kt b/fake-kotlinx-time/common/test/assertions.kt new file mode 100644 index 000000000..368f4c6b4 --- /dev/null +++ b/fake-kotlinx-time/common/test/assertions.kt @@ -0,0 +1,20 @@ +/* + * Copyright 2019-2021 JetBrains s.r.o. + * Use of this source code is governed by the Apache 2.0 License that can be found in the LICENSE.txt file. + */ +package kotlinx.time.test + +import kotlin.test.assertFailsWith +import kotlin.test.fail + +inline fun assertInvalidFormat(message: String? = null, f: () -> T) { + assertFailsWith(message) { + val result = f() + fail(result.toString()) + } +} + +/** + * The number of iterations to perform in nondeterministic tests. + */ +const val STRESS_TEST_ITERATIONS = 1000 diff --git a/fake-kotlinx-time/common/test/samples/ClockSamples.kt b/fake-kotlinx-time/common/test/samples/ClockSamples.kt new file mode 100644 index 000000000..d799e6bf1 --- /dev/null +++ b/fake-kotlinx-time/common/test/samples/ClockSamples.kt @@ -0,0 +1,36 @@ +/* + * Copyright 2019-2024 JetBrains s.r.o. and contributors. + * Use of this source code is governed by the Apache 2.0 License that can be found in the LICENSE.txt file. + */ + +package kotlinx.datetime.test.samples + +import kotlinx.time.* +import kotlin.test.* + +class ClockSamples { + @Test + fun system() { + // Getting the current date and time + val currentInstant = Clock.System.now() + currentInstant.toEpochMilliseconds() // the number of milliseconds since the Unix epoch + } + + @Test + fun dependencyInjection() { + fun formatCurrentTime(clock: Clock): String = + clock.now().toString() + + // In the production code: + val currentTimeInProduction = formatCurrentTime(Clock.System) + // Testing this value is tricky because it changes all the time. + + // In the test code: + val testClock = object: Clock { + override fun now(): Instant = Instant.parse("2023-01-02T22:35:01Z") + } + // Then, one can write a completely deterministic test: + val currentTimeForTests = formatCurrentTime(testClock) + check(currentTimeForTests == "2023-01-02T22:35:01Z") + } +} diff --git a/fake-kotlinx-time/common/test/samples/InstantSamples.kt b/fake-kotlinx-time/common/test/samples/InstantSamples.kt new file mode 100644 index 000000000..af141866e --- /dev/null +++ b/fake-kotlinx-time/common/test/samples/InstantSamples.kt @@ -0,0 +1,122 @@ +/* + * Copyright 2019-2024 JetBrains s.r.o. and contributors. + * Use of this source code is governed by the Apache 2.0 License that can be found in the LICENSE.txt file. + */ + +package kotlinx.datetime.test.samples + +import kotlinx.time.* +import kotlin.random.* +import kotlin.test.* +import kotlin.time.Duration.Companion.hours + +class InstantSamples { + + @Test + fun epochSeconds() { + // Getting the number of whole seconds that passed since the Unix epoch + val instant1 = Instant.fromEpochSeconds(999_999, nanosecondAdjustment = 123_456_789) + check(instant1.epochSeconds == 999_999L) + val instant2 = Instant.fromEpochSeconds(1_000_000, nanosecondAdjustment = 100_123_456_789) + check(instant2.epochSeconds == 1_000_000 + 100L) + val instant3 = Instant.fromEpochSeconds(1_000_000, nanosecondAdjustment = -100_876_543_211) + check(instant3.epochSeconds == 1_000_000 - 101L) + } + + @Test + fun nanosecondsOfSecond() { + // Getting the number of nanoseconds that passed since the start of the second + val instant1 = Instant.fromEpochSeconds(999_999, nanosecondAdjustment = 123_456_789) + check(instant1.nanosecondsOfSecond == 123_456_789) + val instant2 = Instant.fromEpochSeconds(1_000_000, nanosecondAdjustment = 100_123_456_789) + check(instant2.nanosecondsOfSecond == 123_456_789) + val instant3 = Instant.fromEpochSeconds(1_000_000, nanosecondAdjustment = -100_876_543_211) + check(instant3.nanosecondsOfSecond == 123_456_789) + } + + @Test + fun toEpochMilliseconds() { + // Converting an Instant to the number of milliseconds since the Unix epoch + check(Instant.fromEpochMilliseconds(0).toEpochMilliseconds() == 0L) + check(Instant.fromEpochMilliseconds(1_000_000_000_123).toEpochMilliseconds() == 1_000_000_000_123L) + check(Instant.fromEpochSeconds(1_000_000_000, nanosecondAdjustment = 123_999_999) + .toEpochMilliseconds() == 1_000_000_000_123L) + } + + @Test + fun plusDuration() { + // Finding a moment that's later than the starting point by the given amount of real time + val instant = Instant.fromEpochSeconds(7 * 60 * 60, nanosecondAdjustment = 123_456_789) + val fiveHoursLater = instant + 5.hours + check(fiveHoursLater.epochSeconds == 12 * 60 * 60L) + check(fiveHoursLater.nanosecondsOfSecond == 123_456_789) + } + + @Test + fun minusDuration() { + // Finding a moment that's earlier than the starting point by the given amount of real time + val instant = Instant.fromEpochSeconds(7 * 60 * 60, nanosecondAdjustment = 123_456_789) + val fiveHoursEarlier = instant - 5.hours + check(fiveHoursEarlier.epochSeconds == 2 * 60 * 60L) + check(fiveHoursEarlier.nanosecondsOfSecond == 123_456_789) + } + + @Test + fun minusInstant() { + // Finding the difference between two instants in terms of elapsed time + check(Instant.fromEpochSeconds(0) - Instant.fromEpochSeconds(epochSeconds = 7 * 60 * 60) == (-7).hours) + } + + @Test + fun toStringSample() { + // Converting an Instant to a string + check(Instant.fromEpochSeconds(0).toString() == "1970-01-01T00:00:00Z") + } + + @Test + fun fromEpochMilliseconds() { + // Constructing an Instant from the number of milliseconds since the Unix epoch + check(Instant.fromEpochMilliseconds(epochMilliseconds = 0) == Instant.parse("1970-01-01T00:00:00Z")) + check(Instant.fromEpochMilliseconds(epochMilliseconds = 1_000_000_000_123) + == Instant.parse("2001-09-09T01:46:40.123Z")) + } + + @Test + fun fromEpochSeconds() { + // Constructing an Instant from the number of seconds and nanoseconds since the Unix epoch + check(Instant.fromEpochSeconds(epochSeconds = 0) == Instant.parse("1970-01-01T00:00:00Z")) + check(Instant.fromEpochSeconds(epochSeconds = 1_000_001_234, nanosecondAdjustment = -1_234_000_000_001) + == Instant.parse("2001-09-09T01:46:39.999999999Z")) + } + + @Test + fun fromEpochSecondsIntNanos() { + // Constructing an Instant from the number of seconds and nanoseconds since the Unix epoch + check(Instant.fromEpochSeconds(epochSeconds = 0) == Instant.parse("1970-01-01T00:00:00Z")) + check(Instant.fromEpochSeconds(epochSeconds = 1_000_000_000, nanosecondAdjustment = -1) == Instant.parse("2001-09-09T01:46:39.999999999Z")) + } + + @Test + fun parsing() { + // Parsing an Instant from a string + check(Instant.parse("1970-01-01T00:00:00Z") == Instant.fromEpochSeconds(0)) + } + + @Test + fun isDistantPast() { + // Checking if an instant is so far in the past that it's probably irrelevant + val currentInstant = Clock.System.now() + val tenThousandYearsAgo = currentInstant.minus(24.hours * 365 * 1_000) + check(!tenThousandYearsAgo.isDistantPast) + check(Instant.DISTANT_PAST.isDistantPast) + } + + @Test + fun isDistantFuture() { + // Checking if an instant is so far in the future that it's probably irrelevant + val currentInstant = Clock.System.now() + val tenThousandYearsLater = currentInstant.plus(24.hours * 365 * 10_000) + check(!tenThousandYearsLater.isDistantFuture) + check(Instant.DISTANT_FUTURE.isDistantFuture) + } +} diff --git a/fake-kotlinx-time/js/test/ConvertersTest.kt b/fake-kotlinx-time/js/test/ConvertersTest.kt new file mode 100644 index 000000000..2d067b258 --- /dev/null +++ b/fake-kotlinx-time/js/test/ConvertersTest.kt @@ -0,0 +1,29 @@ +/* + * Copyright 2019-2022 JetBrains s.r.o. and contributors. + * Use of this source code is governed by the Apache 2.0 License that can be found in the LICENSE.txt file. + */ + +package kotlinx.time.test + +import kotlinx.time.* +import kotlin.js.Date +import kotlin.test.* + +class ConvertersTest { + @Test + fun toJSDateTest() { + val releaseInstant = Instant.parse("2016-02-15T00:00:00Z") + val releaseDate = releaseInstant.toJSDate() + assertEquals(2016, releaseDate.getUTCFullYear()) + assertEquals(1, releaseDate.getUTCMonth()) + assertEquals(15, releaseDate.getUTCDate()) + } + + @Test + fun toInstantTest() { + val kotlinReleaseEpochMilliseconds = 1455494400000 + val releaseDate = Date(milliseconds = kotlinReleaseEpochMilliseconds) + val releaseInstant = Instant.parse("2016-02-15T00:00:00Z") + assertEquals(releaseInstant, releaseDate.toKotlinInstant()) + } +} diff --git a/fake-kotlinx-time/jvm/test/ConvertersTest.kt b/fake-kotlinx-time/jvm/test/ConvertersTest.kt new file mode 100644 index 000000000..33b3018b7 --- /dev/null +++ b/fake-kotlinx-time/jvm/test/ConvertersTest.kt @@ -0,0 +1,34 @@ +/* + * Copyright 2019-2022 JetBrains s.r.o. and contributors. + * Use of this source code is governed by the Apache 2.0 License that can be found in the LICENSE.txt file. + */ +package kotlinx.time.test + +import kotlinx.time.* +import kotlin.test.* +import kotlin.random.* +import java.time.Instant as JTInstant + +class ConvertersTest { + + @Test + fun instant() { + fun test(seconds: Long, nanosecond: Int) { + val ktInstant = Instant.fromEpochSeconds(seconds, nanosecond.toLong()) + val jtInstant = JTInstant.ofEpochSecond(seconds, nanosecond.toLong()) + + assertEquals(ktInstant, jtInstant.toKotlinInstant()) + assertEquals(jtInstant, ktInstant.toJavaInstant()) + + assertEquals(ktInstant, jtInstant.toString().let(Instant::parse)) + assertEquals(jtInstant, ktInstant.toString().let(JTInstant::parse)) + } + + repeat(STRESS_TEST_ITERATIONS) { + val seconds = Random.nextLong(1_000_000_000_000) + val nanos = Random.nextInt() + test(seconds, nanos) + } + } + +} From 9e2d69e178c62343b6c6783916ab55f1d6a64064 Mon Sep 17 00:00:00 2001 From: Dmitry Khalanskiy Date: Fri, 29 Nov 2024 14:32:39 +0100 Subject: [PATCH 6/7] Enable binary compatibility validation --- benchmarks/api/benchmarks.api | 0 build.gradle.kts | 8 + core/api/kotlinx-datetime.api | 881 +++++++++++++++++ core/api/kotlinx-datetime.klib.api | 935 ++++++++++++++++++ fake-kotlinx-time/api/fake-kotlinx-time.api | 49 + .../api/fake-kotlinx-time.klib.api | 57 ++ ...inx-datetime-js-without-timezones.klib.api | 0 .../api/kotlinx-datetime-serialization.api | 0 .../kotlinx-datetime-serialization.klib.api | 0 .../api/kotlinx-datetime-zoneinfo.klib.api | 8 + 10 files changed, 1938 insertions(+) create mode 100644 benchmarks/api/benchmarks.api create mode 100644 core/api/kotlinx-datetime.api create mode 100644 core/api/kotlinx-datetime.klib.api create mode 100644 fake-kotlinx-time/api/fake-kotlinx-time.api create mode 100644 fake-kotlinx-time/api/fake-kotlinx-time.klib.api create mode 100644 js-without-timezones/api/kotlinx-datetime-js-without-timezones.klib.api create mode 100644 serialization/api/kotlinx-datetime-serialization.api create mode 100644 serialization/api/kotlinx-datetime-serialization.klib.api create mode 100644 timezones/full/api/kotlinx-datetime-zoneinfo.klib.api diff --git a/benchmarks/api/benchmarks.api b/benchmarks/api/benchmarks.api new file mode 100644 index 000000000..e69de29bb diff --git a/build.gradle.kts b/build.gradle.kts index 0c16b804b..47dcd5581 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -2,6 +2,7 @@ plugins { id("kotlinx.team.infra") version "0.4.0-dev-81" kotlin("multiplatform") apply false id("org.jetbrains.kotlinx.kover") version "0.8.0-Beta2" + id("org.jetbrains.kotlinx.binary-compatibility-validator") version "0.16.3" } infra { @@ -57,3 +58,10 @@ dependencies { kover(project(":kotlinx-datetime")) kover(project(":kotlinx-datetime-serialization")) } + +apiValidation { + @OptIn(kotlinx.validation.ExperimentalBCVApi::class) + klib { + enabled = true + } +} diff --git a/core/api/kotlinx-datetime.api b/core/api/kotlinx-datetime.api new file mode 100644 index 000000000..829ee261a --- /dev/null +++ b/core/api/kotlinx-datetime.api @@ -0,0 +1,881 @@ +public abstract interface class kotlinx/datetime/Clock { + public static final field Companion Lkotlinx/datetime/Clock$Companion; + public abstract fun now ()Lkotlinx/datetime/Instant; +} + +public final class kotlinx/datetime/Clock$Companion { +} + +public final class kotlinx/datetime/Clock$System : kotlinx/datetime/Clock { + public static final field INSTANCE Lkotlinx/datetime/Clock$System; + public fun now ()Lkotlinx/datetime/Instant; +} + +public final class kotlinx/datetime/ClockKt { + public static final fun asTimeSource (Lkotlinx/datetime/Clock;)Lkotlin/time/TimeSource$WithComparableMarks; + public static final fun todayAt (Lkotlinx/datetime/Clock;Lkotlinx/datetime/TimeZone;)Lkotlinx/datetime/LocalDate; + public static final fun todayIn (Lkotlinx/datetime/Clock;Lkotlinx/datetime/TimeZone;)Lkotlinx/datetime/LocalDate; +} + +public final class kotlinx/datetime/ConvertersKt { + public static final fun toJavaInstant (Lkotlinx/datetime/Instant;)Ljava/time/Instant; + public static final fun toJavaLocalDate (Lkotlinx/datetime/LocalDate;)Ljava/time/LocalDate; + public static final fun toJavaLocalDateTime (Lkotlinx/datetime/LocalDateTime;)Ljava/time/LocalDateTime; + public static final fun toJavaLocalTime (Lkotlinx/datetime/LocalTime;)Ljava/time/LocalTime; + public static final fun toJavaPeriod (Lkotlinx/datetime/DatePeriod;)Ljava/time/Period; + public static final fun toJavaZoneId (Lkotlinx/datetime/TimeZone;)Ljava/time/ZoneId; + public static final fun toJavaZoneOffset (Lkotlinx/datetime/FixedOffsetTimeZone;)Ljava/time/ZoneOffset; + public static final fun toJavaZoneOffset (Lkotlinx/datetime/UtcOffset;)Ljava/time/ZoneOffset; + public static final fun toKotlinDatePeriod (Ljava/time/Period;)Lkotlinx/datetime/DatePeriod; + public static final fun toKotlinFixedOffsetTimeZone (Ljava/time/ZoneOffset;)Lkotlinx/datetime/FixedOffsetTimeZone; + public static final fun toKotlinInstant (Ljava/time/Instant;)Lkotlinx/datetime/Instant; + public static final fun toKotlinLocalDate (Ljava/time/LocalDate;)Lkotlinx/datetime/LocalDate; + public static final fun toKotlinLocalDateTime (Ljava/time/LocalDateTime;)Lkotlinx/datetime/LocalDateTime; + public static final fun toKotlinLocalTime (Ljava/time/LocalTime;)Lkotlinx/datetime/LocalTime; + public static final fun toKotlinTimeZone (Ljava/time/ZoneId;)Lkotlinx/datetime/TimeZone; + public static final fun toKotlinUtcOffset (Ljava/time/ZoneOffset;)Lkotlinx/datetime/UtcOffset; + public static final fun toKotlinZoneOffset (Ljava/time/ZoneOffset;)Lkotlinx/datetime/FixedOffsetTimeZone; +} + +public final class kotlinx/datetime/DatePeriod : kotlinx/datetime/DateTimePeriod { + public static final field Companion Lkotlinx/datetime/DatePeriod$Companion; + public fun (III)V + public synthetic fun (IIIILkotlin/jvm/internal/DefaultConstructorMarker;)V + public fun getDays ()I + public fun getHours ()I + public fun getMinutes ()I + public fun getNanoseconds ()I + public fun getSeconds ()I +} + +public final class kotlinx/datetime/DatePeriod$Companion { + public final fun parse (Ljava/lang/String;)Lkotlinx/datetime/DatePeriod; + public final fun serializer ()Lkotlinx/serialization/KSerializer; +} + +public final class kotlinx/datetime/DateTimeArithmeticException : java/lang/RuntimeException { + public fun ()V + public fun (Ljava/lang/String;)V + public fun (Ljava/lang/String;Ljava/lang/Throwable;)V + public fun (Ljava/lang/Throwable;)V +} + +public abstract class kotlinx/datetime/DateTimePeriod { + public static final field Companion Lkotlinx/datetime/DateTimePeriod$Companion; + public fun equals (Ljava/lang/Object;)Z + public abstract fun getDays ()I + public fun getHours ()I + public fun getMinutes ()I + public final fun getMonths ()I + public fun getNanoseconds ()I + public fun getSeconds ()I + public final fun getYears ()I + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + +public final class kotlinx/datetime/DateTimePeriod$Companion { + public final fun parse (Ljava/lang/String;)Lkotlinx/datetime/DateTimePeriod; + public final fun serializer ()Lkotlinx/serialization/KSerializer; +} + +public final class kotlinx/datetime/DateTimePeriodKt { + public static final fun DateTimePeriod (IIIIIIJ)Lkotlinx/datetime/DateTimePeriod; + public static synthetic fun DateTimePeriod$default (IIIIIIJILjava/lang/Object;)Lkotlinx/datetime/DateTimePeriod; + public static final fun plus (Lkotlinx/datetime/DatePeriod;Lkotlinx/datetime/DatePeriod;)Lkotlinx/datetime/DatePeriod; + public static final fun plus (Lkotlinx/datetime/DateTimePeriod;Lkotlinx/datetime/DateTimePeriod;)Lkotlinx/datetime/DateTimePeriod; + public static final fun toDatePeriod (Ljava/lang/String;)Lkotlinx/datetime/DatePeriod; + public static final fun toDateTimePeriod (Ljava/lang/String;)Lkotlinx/datetime/DateTimePeriod; + public static final fun toDateTimePeriod-LRDsOJo (J)Lkotlinx/datetime/DateTimePeriod; +} + +public abstract class kotlinx/datetime/DateTimeUnit { + public static final field Companion Lkotlinx/datetime/DateTimeUnit$Companion; + protected final fun formatToString (ILjava/lang/String;)Ljava/lang/String; + protected final fun formatToString (JLjava/lang/String;)Ljava/lang/String; + public abstract fun times (I)Lkotlinx/datetime/DateTimeUnit; +} + +public final class kotlinx/datetime/DateTimeUnit$Companion { + public final fun getCENTURY ()Lkotlinx/datetime/DateTimeUnit$MonthBased; + public final fun getDAY ()Lkotlinx/datetime/DateTimeUnit$DayBased; + public final fun getHOUR ()Lkotlinx/datetime/DateTimeUnit$TimeBased; + public final fun getMICROSECOND ()Lkotlinx/datetime/DateTimeUnit$TimeBased; + public final fun getMILLISECOND ()Lkotlinx/datetime/DateTimeUnit$TimeBased; + public final fun getMINUTE ()Lkotlinx/datetime/DateTimeUnit$TimeBased; + public final fun getMONTH ()Lkotlinx/datetime/DateTimeUnit$MonthBased; + public final fun getNANOSECOND ()Lkotlinx/datetime/DateTimeUnit$TimeBased; + public final fun getQUARTER ()Lkotlinx/datetime/DateTimeUnit$MonthBased; + public final fun getSECOND ()Lkotlinx/datetime/DateTimeUnit$TimeBased; + public final fun getWEEK ()Lkotlinx/datetime/DateTimeUnit$DayBased; + public final fun getYEAR ()Lkotlinx/datetime/DateTimeUnit$MonthBased; + public final fun serializer ()Lkotlinx/serialization/KSerializer; +} + +public abstract class kotlinx/datetime/DateTimeUnit$DateBased : kotlinx/datetime/DateTimeUnit { + public static final field Companion Lkotlinx/datetime/DateTimeUnit$DateBased$Companion; +} + +public final class kotlinx/datetime/DateTimeUnit$DateBased$Companion { + public final fun serializer ()Lkotlinx/serialization/KSerializer; +} + +public final class kotlinx/datetime/DateTimeUnit$DayBased : kotlinx/datetime/DateTimeUnit$DateBased { + public static final field Companion Lkotlinx/datetime/DateTimeUnit$DayBased$Companion; + public fun (I)V + public fun equals (Ljava/lang/Object;)Z + public final fun getDays ()I + public fun hashCode ()I + public fun times (I)Lkotlinx/datetime/DateTimeUnit$DayBased; + public synthetic fun times (I)Lkotlinx/datetime/DateTimeUnit; + public fun toString ()Ljava/lang/String; +} + +public final class kotlinx/datetime/DateTimeUnit$DayBased$Companion { + public final fun serializer ()Lkotlinx/serialization/KSerializer; +} + +public final class kotlinx/datetime/DateTimeUnit$MonthBased : kotlinx/datetime/DateTimeUnit$DateBased { + public static final field Companion Lkotlinx/datetime/DateTimeUnit$MonthBased$Companion; + public fun (I)V + public fun equals (Ljava/lang/Object;)Z + public final fun getMonths ()I + public fun hashCode ()I + public fun times (I)Lkotlinx/datetime/DateTimeUnit$MonthBased; + public synthetic fun times (I)Lkotlinx/datetime/DateTimeUnit; + public fun toString ()Ljava/lang/String; +} + +public final class kotlinx/datetime/DateTimeUnit$MonthBased$Companion { + public final fun serializer ()Lkotlinx/serialization/KSerializer; +} + +public final class kotlinx/datetime/DateTimeUnit$TimeBased : kotlinx/datetime/DateTimeUnit { + public static final field Companion Lkotlinx/datetime/DateTimeUnit$TimeBased$Companion; + public fun (J)V + public fun equals (Ljava/lang/Object;)Z + public final fun getDuration-UwyO8pc ()J + public final fun getNanoseconds ()J + public fun hashCode ()I + public fun times (I)Lkotlinx/datetime/DateTimeUnit$TimeBased; + public synthetic fun times (I)Lkotlinx/datetime/DateTimeUnit; + public fun toString ()Ljava/lang/String; +} + +public final class kotlinx/datetime/DateTimeUnit$TimeBased$Companion { + public final fun serializer ()Lkotlinx/serialization/KSerializer; +} + +public final class kotlinx/datetime/DayOfWeekKt { + public static final fun DayOfWeek (I)Ljava/time/DayOfWeek; + public static final fun getIsoDayNumber (Ljava/time/DayOfWeek;)I +} + +public final class kotlinx/datetime/FixedOffsetTimeZone : kotlinx/datetime/TimeZone { + public static final field Companion Lkotlinx/datetime/FixedOffsetTimeZone$Companion; + public fun (Lkotlinx/datetime/UtcOffset;)V + public final fun getOffset ()Lkotlinx/datetime/UtcOffset; + public final fun getTotalSeconds ()I +} + +public final class kotlinx/datetime/FixedOffsetTimeZone$Companion { + public final fun serializer ()Lkotlinx/serialization/KSerializer; +} + +public final class kotlinx/datetime/IllegalTimeZoneException : java/lang/IllegalArgumentException { + public fun ()V + public fun (Ljava/lang/String;)V + public fun (Ljava/lang/String;Ljava/lang/Throwable;)V + public fun (Ljava/lang/Throwable;)V +} + +public final class kotlinx/datetime/Instant : java/lang/Comparable { + public static final field Companion Lkotlinx/datetime/Instant$Companion; + public synthetic fun compareTo (Ljava/lang/Object;)I + public fun compareTo (Lkotlinx/datetime/Instant;)I + public fun equals (Ljava/lang/Object;)Z + public final fun getEpochSeconds ()J + public final fun getNanosecondsOfSecond ()I + public fun hashCode ()I + public final fun minus-5sfh64U (Lkotlinx/datetime/Instant;)J + public final fun minus-LRDsOJo (J)Lkotlinx/datetime/Instant; + public final fun plus-LRDsOJo (J)Lkotlinx/datetime/Instant; + public final fun toEpochMilliseconds ()J + public fun toString ()Ljava/lang/String; +} + +public final class kotlinx/datetime/Instant$Companion { + public final fun fromEpochMilliseconds (J)Lkotlinx/datetime/Instant; + public final fun fromEpochSeconds (JI)Lkotlinx/datetime/Instant; + public final fun fromEpochSeconds (JJ)Lkotlinx/datetime/Instant; + public static synthetic fun fromEpochSeconds$default (Lkotlinx/datetime/Instant$Companion;JJILjava/lang/Object;)Lkotlinx/datetime/Instant; + public final fun getDISTANT_FUTURE ()Lkotlinx/datetime/Instant; + public final fun getDISTANT_PAST ()Lkotlinx/datetime/Instant; + public final fun now ()Lkotlinx/datetime/Instant; + public final fun parse (Ljava/lang/CharSequence;Lkotlinx/datetime/format/DateTimeFormat;)Lkotlinx/datetime/Instant; + public final synthetic fun parse (Ljava/lang/String;)Lkotlinx/datetime/Instant; + public static synthetic fun parse$default (Lkotlinx/datetime/Instant$Companion;Ljava/lang/CharSequence;Lkotlinx/datetime/format/DateTimeFormat;ILjava/lang/Object;)Lkotlinx/datetime/Instant; + public final fun serializer ()Lkotlinx/serialization/KSerializer; +} + +public final class kotlinx/datetime/InstantJvmKt { + public static final fun minus (Lkotlinx/datetime/Instant;ILkotlinx/datetime/DateTimeUnit;Lkotlinx/datetime/TimeZone;)Lkotlinx/datetime/Instant; + public static final fun periodUntil (Lkotlinx/datetime/Instant;Lkotlinx/datetime/Instant;Lkotlinx/datetime/TimeZone;)Lkotlinx/datetime/DateTimePeriod; + public static final fun plus (Lkotlinx/datetime/Instant;ILkotlinx/datetime/DateTimeUnit;Lkotlinx/datetime/TimeZone;)Lkotlinx/datetime/Instant; + public static final fun plus (Lkotlinx/datetime/Instant;JLkotlinx/datetime/DateTimeUnit$TimeBased;)Lkotlinx/datetime/Instant; + public static final fun plus (Lkotlinx/datetime/Instant;JLkotlinx/datetime/DateTimeUnit;Lkotlinx/datetime/TimeZone;)Lkotlinx/datetime/Instant; + public static final fun plus (Lkotlinx/datetime/Instant;Lkotlinx/datetime/DateTimePeriod;Lkotlinx/datetime/TimeZone;)Lkotlinx/datetime/Instant; + public static final fun plus (Lkotlinx/datetime/Instant;Lkotlinx/datetime/DateTimeUnit;Lkotlinx/datetime/TimeZone;)Lkotlinx/datetime/Instant; + public static final fun until (Lkotlinx/datetime/Instant;Lkotlinx/datetime/Instant;Lkotlinx/datetime/DateTimeUnit;Lkotlinx/datetime/TimeZone;)J +} + +public final class kotlinx/datetime/InstantKt { + public static final fun daysUntil (Lkotlinx/datetime/Instant;Lkotlinx/datetime/Instant;Lkotlinx/datetime/TimeZone;)I + public static final fun format (Lkotlinx/datetime/Instant;Lkotlinx/datetime/format/DateTimeFormat;Lkotlinx/datetime/UtcOffset;)Ljava/lang/String; + public static synthetic fun format$default (Lkotlinx/datetime/Instant;Lkotlinx/datetime/format/DateTimeFormat;Lkotlinx/datetime/UtcOffset;ILjava/lang/Object;)Ljava/lang/String; + public static final fun isDistantFuture (Lkotlinx/datetime/Instant;)Z + public static final fun isDistantPast (Lkotlinx/datetime/Instant;)Z + public static final fun minus (Lkotlinx/datetime/Instant;ILkotlinx/datetime/DateTimeUnit$TimeBased;)Lkotlinx/datetime/Instant; + public static final fun minus (Lkotlinx/datetime/Instant;JLkotlinx/datetime/DateTimeUnit$TimeBased;)Lkotlinx/datetime/Instant; + public static final fun minus (Lkotlinx/datetime/Instant;JLkotlinx/datetime/DateTimeUnit;Lkotlinx/datetime/TimeZone;)Lkotlinx/datetime/Instant; + public static final fun minus (Lkotlinx/datetime/Instant;Lkotlinx/datetime/DateTimePeriod;Lkotlinx/datetime/TimeZone;)Lkotlinx/datetime/Instant; + public static final fun minus (Lkotlinx/datetime/Instant;Lkotlinx/datetime/DateTimeUnit$TimeBased;)Lkotlinx/datetime/Instant; + public static final fun minus (Lkotlinx/datetime/Instant;Lkotlinx/datetime/DateTimeUnit;Lkotlinx/datetime/TimeZone;)Lkotlinx/datetime/Instant; + public static final fun minus (Lkotlinx/datetime/Instant;Lkotlinx/datetime/Instant;Lkotlinx/datetime/DateTimeUnit$TimeBased;)J + public static final fun minus (Lkotlinx/datetime/Instant;Lkotlinx/datetime/Instant;Lkotlinx/datetime/DateTimeUnit;Lkotlinx/datetime/TimeZone;)J + public static final fun minus (Lkotlinx/datetime/Instant;Lkotlinx/datetime/Instant;Lkotlinx/datetime/TimeZone;)Lkotlinx/datetime/DateTimePeriod; + public static final fun monthsUntil (Lkotlinx/datetime/Instant;Lkotlinx/datetime/Instant;Lkotlinx/datetime/TimeZone;)I + public static final fun plus (Lkotlinx/datetime/Instant;ILkotlinx/datetime/DateTimeUnit$TimeBased;)Lkotlinx/datetime/Instant; + public static final fun plus (Lkotlinx/datetime/Instant;Lkotlinx/datetime/DateTimeUnit$TimeBased;)Lkotlinx/datetime/Instant; + public static final fun toInstant (Ljava/lang/String;)Lkotlinx/datetime/Instant; + public static final fun until (Lkotlinx/datetime/Instant;Lkotlinx/datetime/Instant;Lkotlinx/datetime/DateTimeUnit$TimeBased;)J + public static final fun yearsUntil (Lkotlinx/datetime/Instant;Lkotlinx/datetime/Instant;Lkotlinx/datetime/TimeZone;)I +} + +public final class kotlinx/datetime/LocalDate : java/lang/Comparable { + public static final field Companion Lkotlinx/datetime/LocalDate$Companion; + public fun (III)V + public fun (ILjava/time/Month;I)V + public synthetic fun compareTo (Ljava/lang/Object;)I + public fun compareTo (Lkotlinx/datetime/LocalDate;)I + public fun equals (Ljava/lang/Object;)Z + public final fun getDayOfMonth ()I + public final fun getDayOfWeek ()Ljava/time/DayOfWeek; + public final fun getDayOfYear ()I + public final fun getMonth ()Ljava/time/Month; + public final fun getMonthNumber ()I + public final fun getYear ()I + public fun hashCode ()I + public final fun toEpochDays ()I + public final fun toEpochDays ()J + public fun toString ()Ljava/lang/String; +} + +public final class kotlinx/datetime/LocalDate$Companion { + public final fun Format (Lkotlin/jvm/functions/Function1;)Lkotlinx/datetime/format/DateTimeFormat; + public final fun fromEpochDays (I)Lkotlinx/datetime/LocalDate; + public final fun fromEpochDays (J)Lkotlinx/datetime/LocalDate; + public final fun parse (Ljava/lang/CharSequence;Lkotlinx/datetime/format/DateTimeFormat;)Lkotlinx/datetime/LocalDate; + public final synthetic fun parse (Ljava/lang/String;)Lkotlinx/datetime/LocalDate; + public static synthetic fun parse$default (Lkotlinx/datetime/LocalDate$Companion;Ljava/lang/CharSequence;Lkotlinx/datetime/format/DateTimeFormat;ILjava/lang/Object;)Lkotlinx/datetime/LocalDate; + public final fun serializer ()Lkotlinx/serialization/KSerializer; +} + +public final class kotlinx/datetime/LocalDate$Formats { + public static final field INSTANCE Lkotlinx/datetime/LocalDate$Formats; + public final fun getISO ()Lkotlinx/datetime/format/DateTimeFormat; + public final fun getISO_BASIC ()Lkotlinx/datetime/format/DateTimeFormat; +} + +public final class kotlinx/datetime/LocalDateJvmKt { + public static final fun daysUntil (Lkotlinx/datetime/LocalDate;Lkotlinx/datetime/LocalDate;)I + public static final fun minus (Lkotlinx/datetime/LocalDate;ILkotlinx/datetime/DateTimeUnit$DateBased;)Lkotlinx/datetime/LocalDate; + public static final fun monthsUntil (Lkotlinx/datetime/LocalDate;Lkotlinx/datetime/LocalDate;)I + public static final fun periodUntil (Lkotlinx/datetime/LocalDate;Lkotlinx/datetime/LocalDate;)Lkotlinx/datetime/DatePeriod; + public static final fun plus (Lkotlinx/datetime/LocalDate;ILkotlinx/datetime/DateTimeUnit$DateBased;)Lkotlinx/datetime/LocalDate; + public static final fun plus (Lkotlinx/datetime/LocalDate;JLkotlinx/datetime/DateTimeUnit$DateBased;)Lkotlinx/datetime/LocalDate; + public static final fun plus (Lkotlinx/datetime/LocalDate;Lkotlinx/datetime/DatePeriod;)Lkotlinx/datetime/LocalDate; + public static final fun plus (Lkotlinx/datetime/LocalDate;Lkotlinx/datetime/DateTimeUnit$DateBased;)Lkotlinx/datetime/LocalDate; + public static final fun until (Lkotlinx/datetime/LocalDate;Lkotlinx/datetime/LocalDate;Lkotlinx/datetime/DateTimeUnit$DateBased;)I + public static final fun until (Lkotlinx/datetime/LocalDate;Lkotlinx/datetime/LocalDate;Lkotlinx/datetime/DateTimeUnit$DateBased;)J + public static final fun yearsUntil (Lkotlinx/datetime/LocalDate;Lkotlinx/datetime/LocalDate;)I +} + +public final class kotlinx/datetime/LocalDateKt { + public static final fun atTime (Lkotlinx/datetime/LocalDate;IIII)Lkotlinx/datetime/LocalDateTime; + public static final fun atTime (Lkotlinx/datetime/LocalDate;Lkotlinx/datetime/LocalTime;)Lkotlinx/datetime/LocalDateTime; + public static synthetic fun atTime$default (Lkotlinx/datetime/LocalDate;IIIIILjava/lang/Object;)Lkotlinx/datetime/LocalDateTime; + public static final fun format (Lkotlinx/datetime/LocalDate;Lkotlinx/datetime/format/DateTimeFormat;)Ljava/lang/String; + public static final fun minus (Lkotlinx/datetime/LocalDate;ILkotlinx/datetime/DateTimeUnit$DateBased;)Lkotlinx/datetime/LocalDate; + public static final fun minus (Lkotlinx/datetime/LocalDate;JLkotlinx/datetime/DateTimeUnit$DateBased;)Lkotlinx/datetime/LocalDate; + public static final fun minus (Lkotlinx/datetime/LocalDate;Lkotlinx/datetime/DatePeriod;)Lkotlinx/datetime/LocalDate; + public static final fun minus (Lkotlinx/datetime/LocalDate;Lkotlinx/datetime/DateTimeUnit$DateBased;)Lkotlinx/datetime/LocalDate; + public static final fun minus (Lkotlinx/datetime/LocalDate;Lkotlinx/datetime/LocalDate;)Lkotlinx/datetime/DatePeriod; + public static final fun plus (Lkotlinx/datetime/LocalDate;ILkotlinx/datetime/DateTimeUnit$DateBased;)Lkotlinx/datetime/LocalDate; + public static final fun toLocalDate (Ljava/lang/String;)Lkotlinx/datetime/LocalDate; +} + +public final class kotlinx/datetime/LocalDateTime : java/lang/Comparable { + public static final field Companion Lkotlinx/datetime/LocalDateTime$Companion; + public fun (IIIIIII)V + public synthetic fun (IIIIIIIILkotlin/jvm/internal/DefaultConstructorMarker;)V + public fun (ILjava/time/Month;IIIII)V + public synthetic fun (ILjava/time/Month;IIIIIILkotlin/jvm/internal/DefaultConstructorMarker;)V + public fun (Lkotlinx/datetime/LocalDate;Lkotlinx/datetime/LocalTime;)V + public synthetic fun compareTo (Ljava/lang/Object;)I + public fun compareTo (Lkotlinx/datetime/LocalDateTime;)I + public fun equals (Ljava/lang/Object;)Z + public final fun getDate ()Lkotlinx/datetime/LocalDate; + public final fun getDayOfMonth ()I + public final fun getDayOfWeek ()Ljava/time/DayOfWeek; + public final fun getDayOfYear ()I + public final fun getHour ()I + public final fun getMinute ()I + public final fun getMonth ()Ljava/time/Month; + public final fun getMonthNumber ()I + public final fun getNanosecond ()I + public final fun getSecond ()I + public final fun getTime ()Lkotlinx/datetime/LocalTime; + public final fun getYear ()I + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + +public final class kotlinx/datetime/LocalDateTime$Companion { + public final fun Format (Lkotlin/jvm/functions/Function1;)Lkotlinx/datetime/format/DateTimeFormat; + public final fun parse (Ljava/lang/CharSequence;Lkotlinx/datetime/format/DateTimeFormat;)Lkotlinx/datetime/LocalDateTime; + public final synthetic fun parse (Ljava/lang/String;)Lkotlinx/datetime/LocalDateTime; + public static synthetic fun parse$default (Lkotlinx/datetime/LocalDateTime$Companion;Ljava/lang/CharSequence;Lkotlinx/datetime/format/DateTimeFormat;ILjava/lang/Object;)Lkotlinx/datetime/LocalDateTime; + public final fun serializer ()Lkotlinx/serialization/KSerializer; +} + +public final class kotlinx/datetime/LocalDateTime$Formats { + public static final field INSTANCE Lkotlinx/datetime/LocalDateTime$Formats; + public final fun getISO ()Lkotlinx/datetime/format/DateTimeFormat; +} + +public final class kotlinx/datetime/LocalDateTimeKt { + public static final fun format (Lkotlinx/datetime/LocalDateTime;Lkotlinx/datetime/format/DateTimeFormat;)Ljava/lang/String; + public static final fun toLocalDateTime (Ljava/lang/String;)Lkotlinx/datetime/LocalDateTime; +} + +public final class kotlinx/datetime/LocalTime : java/lang/Comparable { + public static final field Companion Lkotlinx/datetime/LocalTime$Companion; + public fun (IIII)V + public synthetic fun (IIIIILkotlin/jvm/internal/DefaultConstructorMarker;)V + public synthetic fun compareTo (Ljava/lang/Object;)I + public fun compareTo (Lkotlinx/datetime/LocalTime;)I + public fun equals (Ljava/lang/Object;)Z + public final fun getHour ()I + public final fun getMinute ()I + public final fun getNanosecond ()I + public final fun getSecond ()I + public fun hashCode ()I + public final fun toMillisecondOfDay ()I + public final fun toNanosecondOfDay ()J + public final fun toSecondOfDay ()I + public fun toString ()Ljava/lang/String; +} + +public final class kotlinx/datetime/LocalTime$Companion { + public final fun Format (Lkotlin/jvm/functions/Function1;)Lkotlinx/datetime/format/DateTimeFormat; + public final fun fromMillisecondOfDay (I)Lkotlinx/datetime/LocalTime; + public final fun fromNanosecondOfDay (J)Lkotlinx/datetime/LocalTime; + public final fun fromSecondOfDay (I)Lkotlinx/datetime/LocalTime; + public final fun parse (Ljava/lang/CharSequence;Lkotlinx/datetime/format/DateTimeFormat;)Lkotlinx/datetime/LocalTime; + public final synthetic fun parse (Ljava/lang/String;)Lkotlinx/datetime/LocalTime; + public static synthetic fun parse$default (Lkotlinx/datetime/LocalTime$Companion;Ljava/lang/CharSequence;Lkotlinx/datetime/format/DateTimeFormat;ILjava/lang/Object;)Lkotlinx/datetime/LocalTime; + public final fun serializer ()Lkotlinx/serialization/KSerializer; +} + +public final class kotlinx/datetime/LocalTime$Formats { + public static final field INSTANCE Lkotlinx/datetime/LocalTime$Formats; + public final fun getISO ()Lkotlinx/datetime/format/DateTimeFormat; +} + +public final class kotlinx/datetime/LocalTimeKt { + public static final fun atDate (Lkotlinx/datetime/LocalTime;III)Lkotlinx/datetime/LocalDateTime; + public static final fun atDate (Lkotlinx/datetime/LocalTime;ILjava/time/Month;I)Lkotlinx/datetime/LocalDateTime; + public static final fun atDate (Lkotlinx/datetime/LocalTime;Lkotlinx/datetime/LocalDate;)Lkotlinx/datetime/LocalDateTime; + public static synthetic fun atDate$default (Lkotlinx/datetime/LocalTime;IIIILjava/lang/Object;)Lkotlinx/datetime/LocalDateTime; + public static synthetic fun atDate$default (Lkotlinx/datetime/LocalTime;ILjava/time/Month;IILjava/lang/Object;)Lkotlinx/datetime/LocalDateTime; + public static final fun format (Lkotlinx/datetime/LocalTime;Lkotlinx/datetime/format/DateTimeFormat;)Ljava/lang/String; + public static final fun toLocalTime (Ljava/lang/String;)Lkotlinx/datetime/LocalTime; +} + +public final class kotlinx/datetime/MonthKt { + public static final fun Month (I)Ljava/time/Month; + public static final fun getNumber (Ljava/time/Month;)I +} + +public class kotlinx/datetime/TimeZone { + public static final field Companion Lkotlinx/datetime/TimeZone$Companion; + public fun equals (Ljava/lang/Object;)Z + public final fun getId ()Ljava/lang/String; + public fun hashCode ()I + public final fun toInstant (Lkotlinx/datetime/LocalDateTime;)Lkotlinx/datetime/Instant; + public final fun toLocalDateTime (Lkotlinx/datetime/Instant;)Lkotlinx/datetime/LocalDateTime; + public fun toString ()Ljava/lang/String; +} + +public final class kotlinx/datetime/TimeZone$Companion { + public final fun currentSystemDefault ()Lkotlinx/datetime/TimeZone; + public final fun getAvailableZoneIds ()Ljava/util/Set; + public final fun getUTC ()Lkotlinx/datetime/FixedOffsetTimeZone; + public final fun of (Ljava/lang/String;)Lkotlinx/datetime/TimeZone; + public final fun serializer ()Lkotlinx/serialization/KSerializer; +} + +public final class kotlinx/datetime/TimeZoneKt { + public static final fun atStartOfDayIn (Lkotlinx/datetime/LocalDate;Lkotlinx/datetime/TimeZone;)Lkotlinx/datetime/Instant; + public static final fun offsetAt (Lkotlinx/datetime/TimeZone;Lkotlinx/datetime/Instant;)Lkotlinx/datetime/UtcOffset; + public static final fun offsetIn (Lkotlinx/datetime/Instant;Lkotlinx/datetime/TimeZone;)Lkotlinx/datetime/UtcOffset; + public static final fun toInstant (Lkotlinx/datetime/LocalDateTime;Lkotlinx/datetime/TimeZone;)Lkotlinx/datetime/Instant; + public static final fun toInstant (Lkotlinx/datetime/LocalDateTime;Lkotlinx/datetime/UtcOffset;)Lkotlinx/datetime/Instant; + public static final fun toLocalDateTime (Lkotlinx/datetime/Instant;Lkotlinx/datetime/TimeZone;)Lkotlinx/datetime/LocalDateTime; +} + +public final class kotlinx/datetime/UtcOffset { + public static final field Companion Lkotlinx/datetime/UtcOffset$Companion; + public fun (Ljava/time/ZoneOffset;)V + public fun equals (Ljava/lang/Object;)Z + public final fun getTotalSeconds ()I + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + +public final class kotlinx/datetime/UtcOffset$Companion { + public final fun Format (Lkotlin/jvm/functions/Function1;)Lkotlinx/datetime/format/DateTimeFormat; + public final fun getZERO ()Lkotlinx/datetime/UtcOffset; + public final fun parse (Ljava/lang/CharSequence;Lkotlinx/datetime/format/DateTimeFormat;)Lkotlinx/datetime/UtcOffset; + public final synthetic fun parse (Ljava/lang/String;)Lkotlinx/datetime/UtcOffset; + public static synthetic fun parse$default (Lkotlinx/datetime/UtcOffset$Companion;Ljava/lang/CharSequence;Lkotlinx/datetime/format/DateTimeFormat;ILjava/lang/Object;)Lkotlinx/datetime/UtcOffset; + public final fun serializer ()Lkotlinx/serialization/KSerializer; +} + +public final class kotlinx/datetime/UtcOffset$Formats { + public static final field INSTANCE Lkotlinx/datetime/UtcOffset$Formats; + public final fun getFOUR_DIGITS ()Lkotlinx/datetime/format/DateTimeFormat; + public final fun getISO ()Lkotlinx/datetime/format/DateTimeFormat; + public final fun getISO_BASIC ()Lkotlinx/datetime/format/DateTimeFormat; +} + +public final class kotlinx/datetime/UtcOffsetJvmKt { + public static final fun UtcOffset (Ljava/lang/Integer;Ljava/lang/Integer;Ljava/lang/Integer;)Lkotlinx/datetime/UtcOffset; + public static synthetic fun UtcOffset$default (Ljava/lang/Integer;Ljava/lang/Integer;Ljava/lang/Integer;ILjava/lang/Object;)Lkotlinx/datetime/UtcOffset; +} + +public final class kotlinx/datetime/UtcOffsetKt { + public static final fun UtcOffset ()Lkotlinx/datetime/UtcOffset; + public static final fun asTimeZone (Lkotlinx/datetime/UtcOffset;)Lkotlinx/datetime/FixedOffsetTimeZone; + public static final fun format (Lkotlinx/datetime/UtcOffset;Lkotlinx/datetime/format/DateTimeFormat;)Ljava/lang/String; +} + +public final class kotlinx/datetime/format/AmPmMarker : java/lang/Enum { + public static final field AM Lkotlinx/datetime/format/AmPmMarker; + public static final field PM Lkotlinx/datetime/format/AmPmMarker; + public static fun getEntries ()Lkotlin/enums/EnumEntries; + public static fun valueOf (Ljava/lang/String;)Lkotlinx/datetime/format/AmPmMarker; + public static fun values ()[Lkotlinx/datetime/format/AmPmMarker; +} + +public final class kotlinx/datetime/format/DateTimeComponents { + public static final field Companion Lkotlinx/datetime/format/DateTimeComponents$Companion; + public fun ()V + public final fun getAmPm ()Lkotlinx/datetime/format/AmPmMarker; + public final fun getDayOfMonth ()Ljava/lang/Integer; + public final fun getDayOfWeek ()Ljava/time/DayOfWeek; + public final fun getDayOfYear ()Ljava/lang/Integer; + public final fun getHour ()Ljava/lang/Integer; + public final fun getHourOfAmPm ()Ljava/lang/Integer; + public final fun getMinute ()Ljava/lang/Integer; + public final fun getMonth ()Ljava/time/Month; + public final fun getMonthNumber ()Ljava/lang/Integer; + public final fun getNanosecond ()Ljava/lang/Integer; + public final fun getOffsetHours ()Ljava/lang/Integer; + public final fun getOffsetIsNegative ()Ljava/lang/Boolean; + public final fun getOffsetMinutesOfHour ()Ljava/lang/Integer; + public final fun getOffsetSecondsOfMinute ()Ljava/lang/Integer; + public final fun getSecond ()Ljava/lang/Integer; + public final fun getTimeZoneId ()Ljava/lang/String; + public final fun getYear ()Ljava/lang/Integer; + public final fun setAmPm (Lkotlinx/datetime/format/AmPmMarker;)V + public final fun setDate (Lkotlinx/datetime/LocalDate;)V + public final fun setDateTime (Lkotlinx/datetime/LocalDateTime;)V + public final fun setDateTimeOffset (Lkotlinx/datetime/Instant;Lkotlinx/datetime/UtcOffset;)V + public final fun setDateTimeOffset (Lkotlinx/datetime/LocalDateTime;Lkotlinx/datetime/UtcOffset;)V + public final fun setDayOfMonth (Ljava/lang/Integer;)V + public final fun setDayOfWeek (Ljava/time/DayOfWeek;)V + public final fun setDayOfYear (Ljava/lang/Integer;)V + public final fun setHour (Ljava/lang/Integer;)V + public final fun setHourOfAmPm (Ljava/lang/Integer;)V + public final fun setMinute (Ljava/lang/Integer;)V + public final fun setMonth (Ljava/time/Month;)V + public final fun setMonthNumber (Ljava/lang/Integer;)V + public final fun setNanosecond (Ljava/lang/Integer;)V + public final fun setOffset (Lkotlinx/datetime/UtcOffset;)V + public final fun setOffsetHours (Ljava/lang/Integer;)V + public final fun setOffsetIsNegative (Ljava/lang/Boolean;)V + public final fun setOffsetMinutesOfHour (Ljava/lang/Integer;)V + public final fun setOffsetSecondsOfMinute (Ljava/lang/Integer;)V + public final fun setSecond (Ljava/lang/Integer;)V + public final fun setTime (Lkotlinx/datetime/LocalTime;)V + public final fun setTimeZoneId (Ljava/lang/String;)V + public final fun setYear (Ljava/lang/Integer;)V + public final fun toInstantUsingOffset ()Lkotlinx/datetime/Instant; + public final fun toLocalDate ()Lkotlinx/datetime/LocalDate; + public final fun toLocalDateTime ()Lkotlinx/datetime/LocalDateTime; + public final fun toLocalTime ()Lkotlinx/datetime/LocalTime; + public final fun toUtcOffset ()Lkotlinx/datetime/UtcOffset; +} + +public final class kotlinx/datetime/format/DateTimeComponents$Companion { + public final fun Format (Lkotlin/jvm/functions/Function1;)Lkotlinx/datetime/format/DateTimeFormat; +} + +public final class kotlinx/datetime/format/DateTimeComponents$Formats { + public static final field INSTANCE Lkotlinx/datetime/format/DateTimeComponents$Formats; + public final fun getISO_DATE_TIME_OFFSET ()Lkotlinx/datetime/format/DateTimeFormat; + public final fun getRFC_1123 ()Lkotlinx/datetime/format/DateTimeFormat; +} + +public final class kotlinx/datetime/format/DateTimeComponentsKt { + public static final fun format (Lkotlinx/datetime/format/DateTimeFormat;Lkotlin/jvm/functions/Function1;)Ljava/lang/String; + public static final fun parse (Lkotlinx/datetime/format/DateTimeComponents$Companion;Ljava/lang/CharSequence;Lkotlinx/datetime/format/DateTimeFormat;)Lkotlinx/datetime/format/DateTimeComponents; +} + +public abstract interface class kotlinx/datetime/format/DateTimeFormat { + public static final field Companion Lkotlinx/datetime/format/DateTimeFormat$Companion; + public abstract fun format (Ljava/lang/Object;)Ljava/lang/String; + public abstract fun formatTo (Ljava/lang/Appendable;Ljava/lang/Object;)Ljava/lang/Appendable; + public abstract fun parse (Ljava/lang/CharSequence;)Ljava/lang/Object; + public abstract fun parseOrNull (Ljava/lang/CharSequence;)Ljava/lang/Object; +} + +public final class kotlinx/datetime/format/DateTimeFormat$Companion { + public final fun formatAsKotlinBuilderDsl (Lkotlinx/datetime/format/DateTimeFormat;)Ljava/lang/String; +} + +public abstract interface class kotlinx/datetime/format/DateTimeFormatBuilder { + public abstract fun chars (Ljava/lang/String;)V +} + +public abstract interface class kotlinx/datetime/format/DateTimeFormatBuilder$WithDate : kotlinx/datetime/format/DateTimeFormatBuilder { + public abstract fun date (Lkotlinx/datetime/format/DateTimeFormat;)V + public abstract fun dayOfMonth (Lkotlinx/datetime/format/Padding;)V + public abstract fun dayOfWeek (Lkotlinx/datetime/format/DayOfWeekNames;)V + public abstract fun dayOfYear (Lkotlinx/datetime/format/Padding;)V + public abstract fun monthName (Lkotlinx/datetime/format/MonthNames;)V + public abstract fun monthNumber (Lkotlinx/datetime/format/Padding;)V + public abstract fun year (Lkotlinx/datetime/format/Padding;)V + public abstract fun yearTwoDigits (I)V +} + +public final class kotlinx/datetime/format/DateTimeFormatBuilder$WithDate$DefaultImpls { + public static synthetic fun dayOfMonth$default (Lkotlinx/datetime/format/DateTimeFormatBuilder$WithDate;Lkotlinx/datetime/format/Padding;ILjava/lang/Object;)V + public static synthetic fun dayOfYear$default (Lkotlinx/datetime/format/DateTimeFormatBuilder$WithDate;Lkotlinx/datetime/format/Padding;ILjava/lang/Object;)V + public static synthetic fun monthNumber$default (Lkotlinx/datetime/format/DateTimeFormatBuilder$WithDate;Lkotlinx/datetime/format/Padding;ILjava/lang/Object;)V + public static synthetic fun year$default (Lkotlinx/datetime/format/DateTimeFormatBuilder$WithDate;Lkotlinx/datetime/format/Padding;ILjava/lang/Object;)V +} + +public abstract interface class kotlinx/datetime/format/DateTimeFormatBuilder$WithDateTime : kotlinx/datetime/format/DateTimeFormatBuilder$WithDate, kotlinx/datetime/format/DateTimeFormatBuilder$WithTime { + public abstract fun dateTime (Lkotlinx/datetime/format/DateTimeFormat;)V +} + +public final class kotlinx/datetime/format/DateTimeFormatBuilder$WithDateTime$DefaultImpls { + public static fun secondFraction (Lkotlinx/datetime/format/DateTimeFormatBuilder$WithDateTime;I)V +} + +public abstract interface class kotlinx/datetime/format/DateTimeFormatBuilder$WithDateTimeComponents : kotlinx/datetime/format/DateTimeFormatBuilder$WithDateTime, kotlinx/datetime/format/DateTimeFormatBuilder$WithUtcOffset { + public abstract fun dateTimeComponents (Lkotlinx/datetime/format/DateTimeFormat;)V + public abstract fun timeZoneId ()V +} + +public final class kotlinx/datetime/format/DateTimeFormatBuilder$WithDateTimeComponents$DefaultImpls { + public static fun secondFraction (Lkotlinx/datetime/format/DateTimeFormatBuilder$WithDateTimeComponents;I)V +} + +public abstract interface class kotlinx/datetime/format/DateTimeFormatBuilder$WithTime : kotlinx/datetime/format/DateTimeFormatBuilder { + public abstract fun amPmHour (Lkotlinx/datetime/format/Padding;)V + public abstract fun amPmMarker (Ljava/lang/String;Ljava/lang/String;)V + public abstract fun hour (Lkotlinx/datetime/format/Padding;)V + public abstract fun minute (Lkotlinx/datetime/format/Padding;)V + public abstract fun second (Lkotlinx/datetime/format/Padding;)V + public abstract fun secondFraction (I)V + public abstract fun secondFraction (II)V + public abstract fun time (Lkotlinx/datetime/format/DateTimeFormat;)V +} + +public final class kotlinx/datetime/format/DateTimeFormatBuilder$WithTime$DefaultImpls { + public static synthetic fun amPmHour$default (Lkotlinx/datetime/format/DateTimeFormatBuilder$WithTime;Lkotlinx/datetime/format/Padding;ILjava/lang/Object;)V + public static synthetic fun hour$default (Lkotlinx/datetime/format/DateTimeFormatBuilder$WithTime;Lkotlinx/datetime/format/Padding;ILjava/lang/Object;)V + public static synthetic fun minute$default (Lkotlinx/datetime/format/DateTimeFormatBuilder$WithTime;Lkotlinx/datetime/format/Padding;ILjava/lang/Object;)V + public static synthetic fun second$default (Lkotlinx/datetime/format/DateTimeFormatBuilder$WithTime;Lkotlinx/datetime/format/Padding;ILjava/lang/Object;)V + public static fun secondFraction (Lkotlinx/datetime/format/DateTimeFormatBuilder$WithTime;I)V + public static synthetic fun secondFraction$default (Lkotlinx/datetime/format/DateTimeFormatBuilder$WithTime;IIILjava/lang/Object;)V +} + +public abstract interface class kotlinx/datetime/format/DateTimeFormatBuilder$WithUtcOffset : kotlinx/datetime/format/DateTimeFormatBuilder { + public abstract fun offset (Lkotlinx/datetime/format/DateTimeFormat;)V + public abstract fun offsetHours (Lkotlinx/datetime/format/Padding;)V + public abstract fun offsetMinutesOfHour (Lkotlinx/datetime/format/Padding;)V + public abstract fun offsetSecondsOfMinute (Lkotlinx/datetime/format/Padding;)V +} + +public final class kotlinx/datetime/format/DateTimeFormatBuilder$WithUtcOffset$DefaultImpls { + public static synthetic fun offsetHours$default (Lkotlinx/datetime/format/DateTimeFormatBuilder$WithUtcOffset;Lkotlinx/datetime/format/Padding;ILjava/lang/Object;)V + public static synthetic fun offsetMinutesOfHour$default (Lkotlinx/datetime/format/DateTimeFormatBuilder$WithUtcOffset;Lkotlinx/datetime/format/Padding;ILjava/lang/Object;)V + public static synthetic fun offsetSecondsOfMinute$default (Lkotlinx/datetime/format/DateTimeFormatBuilder$WithUtcOffset;Lkotlinx/datetime/format/Padding;ILjava/lang/Object;)V +} + +public final class kotlinx/datetime/format/DateTimeFormatBuilderKt { + public static final fun alternativeParsing (Lkotlinx/datetime/format/DateTimeFormatBuilder;[Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;)V + public static final fun char (Lkotlinx/datetime/format/DateTimeFormatBuilder;C)V + public static final fun optional (Lkotlinx/datetime/format/DateTimeFormatBuilder;Ljava/lang/String;Lkotlin/jvm/functions/Function1;)V + public static synthetic fun optional$default (Lkotlinx/datetime/format/DateTimeFormatBuilder;Ljava/lang/String;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)V +} + +public final class kotlinx/datetime/format/DayOfWeekNames { + public static final field Companion Lkotlinx/datetime/format/DayOfWeekNames$Companion; + public fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V + public fun (Ljava/util/List;)V + public fun equals (Ljava/lang/Object;)Z + public final fun getNames ()Ljava/util/List; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + +public final class kotlinx/datetime/format/DayOfWeekNames$Companion { + public final fun getENGLISH_ABBREVIATED ()Lkotlinx/datetime/format/DayOfWeekNames; + public final fun getENGLISH_FULL ()Lkotlinx/datetime/format/DayOfWeekNames; +} + +public abstract interface annotation class kotlinx/datetime/format/FormatStringsInDatetimeFormats : java/lang/annotation/Annotation { +} + +public final class kotlinx/datetime/format/MonthNames { + public static final field Companion Lkotlinx/datetime/format/MonthNames$Companion; + public fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V + public fun (Ljava/util/List;)V + public fun equals (Ljava/lang/Object;)Z + public final fun getNames ()Ljava/util/List; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + +public final class kotlinx/datetime/format/MonthNames$Companion { + public final fun getENGLISH_ABBREVIATED ()Lkotlinx/datetime/format/MonthNames; + public final fun getENGLISH_FULL ()Lkotlinx/datetime/format/MonthNames; +} + +public final class kotlinx/datetime/format/Padding : java/lang/Enum { + public static final field NONE Lkotlinx/datetime/format/Padding; + public static final field SPACE Lkotlinx/datetime/format/Padding; + public static final field ZERO Lkotlinx/datetime/format/Padding; + public static fun getEntries ()Lkotlin/enums/EnumEntries; + public static fun valueOf (Ljava/lang/String;)Lkotlinx/datetime/format/Padding; + public static fun values ()[Lkotlinx/datetime/format/Padding; +} + +public final class kotlinx/datetime/format/UnicodeKt { + public static final fun byUnicodePattern (Lkotlinx/datetime/format/DateTimeFormatBuilder;Ljava/lang/String;)V +} + +public final class kotlinx/datetime/serializers/DateBasedDateTimeUnitSerializer : kotlinx/serialization/internal/AbstractPolymorphicSerializer { + public static final field INSTANCE Lkotlinx/datetime/serializers/DateBasedDateTimeUnitSerializer; + public fun findPolymorphicSerializerOrNull (Lkotlinx/serialization/encoding/CompositeDecoder;Ljava/lang/String;)Lkotlinx/serialization/DeserializationStrategy; + public synthetic fun findPolymorphicSerializerOrNull (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)Lkotlinx/serialization/SerializationStrategy; + public fun findPolymorphicSerializerOrNull (Lkotlinx/serialization/encoding/Encoder;Lkotlinx/datetime/DateTimeUnit$DateBased;)Lkotlinx/serialization/SerializationStrategy; + public fun getBaseClass ()Lkotlin/reflect/KClass; + public fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor; +} + +public final class kotlinx/datetime/serializers/DatePeriodComponentSerializer : kotlinx/serialization/KSerializer { + public static final field INSTANCE Lkotlinx/datetime/serializers/DatePeriodComponentSerializer; + public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object; + public fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lkotlinx/datetime/DatePeriod; + public fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor; + public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V + public fun serialize (Lkotlinx/serialization/encoding/Encoder;Lkotlinx/datetime/DatePeriod;)V +} + +public final class kotlinx/datetime/serializers/DatePeriodIso8601Serializer : kotlinx/serialization/KSerializer { + public static final field INSTANCE Lkotlinx/datetime/serializers/DatePeriodIso8601Serializer; + public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object; + public fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lkotlinx/datetime/DatePeriod; + public fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor; + public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V + public fun serialize (Lkotlinx/serialization/encoding/Encoder;Lkotlinx/datetime/DatePeriod;)V +} + +public final class kotlinx/datetime/serializers/DateTimePeriodComponentSerializer : kotlinx/serialization/KSerializer { + public static final field INSTANCE Lkotlinx/datetime/serializers/DateTimePeriodComponentSerializer; + public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object; + public fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lkotlinx/datetime/DateTimePeriod; + public fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor; + public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V + public fun serialize (Lkotlinx/serialization/encoding/Encoder;Lkotlinx/datetime/DateTimePeriod;)V +} + +public final class kotlinx/datetime/serializers/DateTimePeriodIso8601Serializer : kotlinx/serialization/KSerializer { + public static final field INSTANCE Lkotlinx/datetime/serializers/DateTimePeriodIso8601Serializer; + public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object; + public fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lkotlinx/datetime/DateTimePeriod; + public fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor; + public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V + public fun serialize (Lkotlinx/serialization/encoding/Encoder;Lkotlinx/datetime/DateTimePeriod;)V +} + +public final class kotlinx/datetime/serializers/DateTimeUnitSerializer : kotlinx/serialization/internal/AbstractPolymorphicSerializer { + public static final field INSTANCE Lkotlinx/datetime/serializers/DateTimeUnitSerializer; + public fun findPolymorphicSerializerOrNull (Lkotlinx/serialization/encoding/CompositeDecoder;Ljava/lang/String;)Lkotlinx/serialization/DeserializationStrategy; + public synthetic fun findPolymorphicSerializerOrNull (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)Lkotlinx/serialization/SerializationStrategy; + public fun findPolymorphicSerializerOrNull (Lkotlinx/serialization/encoding/Encoder;Lkotlinx/datetime/DateTimeUnit;)Lkotlinx/serialization/SerializationStrategy; + public fun getBaseClass ()Lkotlin/reflect/KClass; + public fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor; +} + +public final class kotlinx/datetime/serializers/DayBasedDateTimeUnitSerializer : kotlinx/serialization/KSerializer { + public static final field INSTANCE Lkotlinx/datetime/serializers/DayBasedDateTimeUnitSerializer; + public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object; + public fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lkotlinx/datetime/DateTimeUnit$DayBased; + public fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor; + public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V + public fun serialize (Lkotlinx/serialization/encoding/Encoder;Lkotlinx/datetime/DateTimeUnit$DayBased;)V +} + +public final class kotlinx/datetime/serializers/DayOfWeekSerializer : kotlinx/serialization/KSerializer { + public static final field INSTANCE Lkotlinx/datetime/serializers/DayOfWeekSerializer; + public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object; + public fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/time/DayOfWeek; + public fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor; + public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V + public fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/time/DayOfWeek;)V +} + +public final class kotlinx/datetime/serializers/FixedOffsetTimeZoneSerializer : kotlinx/serialization/KSerializer { + public static final field INSTANCE Lkotlinx/datetime/serializers/FixedOffsetTimeZoneSerializer; + public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object; + public fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lkotlinx/datetime/FixedOffsetTimeZone; + public fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor; + public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V + public fun serialize (Lkotlinx/serialization/encoding/Encoder;Lkotlinx/datetime/FixedOffsetTimeZone;)V +} + +public final class kotlinx/datetime/serializers/InstantComponentSerializer : kotlinx/serialization/KSerializer { + public static final field INSTANCE Lkotlinx/datetime/serializers/InstantComponentSerializer; + public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object; + public fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lkotlinx/datetime/Instant; + public fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor; + public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V + public fun serialize (Lkotlinx/serialization/encoding/Encoder;Lkotlinx/datetime/Instant;)V +} + +public final class kotlinx/datetime/serializers/InstantIso8601Serializer : kotlinx/serialization/KSerializer { + public static final field INSTANCE Lkotlinx/datetime/serializers/InstantIso8601Serializer; + public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object; + public fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lkotlinx/datetime/Instant; + public fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor; + public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V + public fun serialize (Lkotlinx/serialization/encoding/Encoder;Lkotlinx/datetime/Instant;)V +} + +public final class kotlinx/datetime/serializers/LocalDateComponentSerializer : kotlinx/serialization/KSerializer { + public static final field INSTANCE Lkotlinx/datetime/serializers/LocalDateComponentSerializer; + public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object; + public fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lkotlinx/datetime/LocalDate; + public fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor; + public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V + public fun serialize (Lkotlinx/serialization/encoding/Encoder;Lkotlinx/datetime/LocalDate;)V +} + +public final class kotlinx/datetime/serializers/LocalDateIso8601Serializer : kotlinx/serialization/KSerializer { + public static final field INSTANCE Lkotlinx/datetime/serializers/LocalDateIso8601Serializer; + public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object; + public fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lkotlinx/datetime/LocalDate; + public fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor; + public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V + public fun serialize (Lkotlinx/serialization/encoding/Encoder;Lkotlinx/datetime/LocalDate;)V +} + +public final class kotlinx/datetime/serializers/LocalDateTimeComponentSerializer : kotlinx/serialization/KSerializer { + public static final field INSTANCE Lkotlinx/datetime/serializers/LocalDateTimeComponentSerializer; + public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object; + public fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lkotlinx/datetime/LocalDateTime; + public fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor; + public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V + public fun serialize (Lkotlinx/serialization/encoding/Encoder;Lkotlinx/datetime/LocalDateTime;)V +} + +public final class kotlinx/datetime/serializers/LocalDateTimeIso8601Serializer : kotlinx/serialization/KSerializer { + public static final field INSTANCE Lkotlinx/datetime/serializers/LocalDateTimeIso8601Serializer; + public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object; + public fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lkotlinx/datetime/LocalDateTime; + public fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor; + public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V + public fun serialize (Lkotlinx/serialization/encoding/Encoder;Lkotlinx/datetime/LocalDateTime;)V +} + +public final class kotlinx/datetime/serializers/LocalTimeComponentSerializer : kotlinx/serialization/KSerializer { + public static final field INSTANCE Lkotlinx/datetime/serializers/LocalTimeComponentSerializer; + public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object; + public fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lkotlinx/datetime/LocalTime; + public fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor; + public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V + public fun serialize (Lkotlinx/serialization/encoding/Encoder;Lkotlinx/datetime/LocalTime;)V +} + +public final class kotlinx/datetime/serializers/LocalTimeIso8601Serializer : kotlinx/serialization/KSerializer { + public static final field INSTANCE Lkotlinx/datetime/serializers/LocalTimeIso8601Serializer; + public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object; + public fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lkotlinx/datetime/LocalTime; + public fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor; + public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V + public fun serialize (Lkotlinx/serialization/encoding/Encoder;Lkotlinx/datetime/LocalTime;)V +} + +public final class kotlinx/datetime/serializers/MonthBasedDateTimeUnitSerializer : kotlinx/serialization/KSerializer { + public static final field INSTANCE Lkotlinx/datetime/serializers/MonthBasedDateTimeUnitSerializer; + public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object; + public fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lkotlinx/datetime/DateTimeUnit$MonthBased; + public fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor; + public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V + public fun serialize (Lkotlinx/serialization/encoding/Encoder;Lkotlinx/datetime/DateTimeUnit$MonthBased;)V +} + +public final class kotlinx/datetime/serializers/MonthSerializer : kotlinx/serialization/KSerializer { + public static final field INSTANCE Lkotlinx/datetime/serializers/MonthSerializer; + public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object; + public fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/time/Month; + public fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor; + public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V + public fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/time/Month;)V +} + +public final class kotlinx/datetime/serializers/TimeBasedDateTimeUnitSerializer : kotlinx/serialization/KSerializer { + public static final field INSTANCE Lkotlinx/datetime/serializers/TimeBasedDateTimeUnitSerializer; + public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object; + public fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lkotlinx/datetime/DateTimeUnit$TimeBased; + public fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor; + public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V + public fun serialize (Lkotlinx/serialization/encoding/Encoder;Lkotlinx/datetime/DateTimeUnit$TimeBased;)V +} + +public final class kotlinx/datetime/serializers/TimeZoneSerializer : kotlinx/serialization/KSerializer { + public static final field INSTANCE Lkotlinx/datetime/serializers/TimeZoneSerializer; + public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object; + public fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lkotlinx/datetime/TimeZone; + public fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor; + public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V + public fun serialize (Lkotlinx/serialization/encoding/Encoder;Lkotlinx/datetime/TimeZone;)V +} + +public final class kotlinx/datetime/serializers/UtcOffsetSerializer : kotlinx/serialization/KSerializer { + public static final field INSTANCE Lkotlinx/datetime/serializers/UtcOffsetSerializer; + public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object; + public fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lkotlinx/datetime/UtcOffset; + public fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor; + public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V + public fun serialize (Lkotlinx/serialization/encoding/Encoder;Lkotlinx/datetime/UtcOffset;)V +} + diff --git a/core/api/kotlinx-datetime.klib.api b/core/api/kotlinx-datetime.klib.api new file mode 100644 index 000000000..8c66835f1 --- /dev/null +++ b/core/api/kotlinx-datetime.klib.api @@ -0,0 +1,935 @@ +// Klib ABI Dump +// Targets: [androidNativeArm32, androidNativeArm64, androidNativeX64, androidNativeX86, iosArm64, iosSimulatorArm64, iosX64, js, linuxArm32Hfp, linuxArm64, linuxX64, macosArm64, macosX64, mingwX64, tvosArm64, tvosSimulatorArm64, tvosX64, wasmJs, wasmWasi, watchosArm32, watchosArm64, watchosDeviceArm64, watchosSimulatorArm64, watchosX64] +// Alias: apple => [iosArm64, iosSimulatorArm64, iosX64, macosArm64, macosX64, tvosArm64, tvosSimulatorArm64, tvosX64, watchosArm32, watchosArm64, watchosDeviceArm64, watchosSimulatorArm64, watchosX64] +// Rendering settings: +// - Signature version: 2 +// - Show manifest properties: true +// - Show declarations: true + +// Library unique name: +open annotation class kotlinx.datetime.format/FormatStringsInDatetimeFormats : kotlin/Annotation { // kotlinx.datetime.format/FormatStringsInDatetimeFormats|null[0] + constructor () // kotlinx.datetime.format/FormatStringsInDatetimeFormats.|(){}[0] +} + +final enum class kotlinx.datetime.format/AmPmMarker : kotlin/Enum { // kotlinx.datetime.format/AmPmMarker|null[0] + enum entry AM // kotlinx.datetime.format/AmPmMarker.AM|null[0] + enum entry PM // kotlinx.datetime.format/AmPmMarker.PM|null[0] + + final val entries // kotlinx.datetime.format/AmPmMarker.entries|#static{}entries[0] + final fun (): kotlin.enums/EnumEntries // kotlinx.datetime.format/AmPmMarker.entries.|#static(){}[0] + + final fun valueOf(kotlin/String): kotlinx.datetime.format/AmPmMarker // kotlinx.datetime.format/AmPmMarker.valueOf|valueOf#static(kotlin.String){}[0] + final fun values(): kotlin/Array // kotlinx.datetime.format/AmPmMarker.values|values#static(){}[0] +} + +final enum class kotlinx.datetime.format/Padding : kotlin/Enum { // kotlinx.datetime.format/Padding|null[0] + enum entry NONE // kotlinx.datetime.format/Padding.NONE|null[0] + enum entry SPACE // kotlinx.datetime.format/Padding.SPACE|null[0] + enum entry ZERO // kotlinx.datetime.format/Padding.ZERO|null[0] + + final val entries // kotlinx.datetime.format/Padding.entries|#static{}entries[0] + final fun (): kotlin.enums/EnumEntries // kotlinx.datetime.format/Padding.entries.|#static(){}[0] + + final fun valueOf(kotlin/String): kotlinx.datetime.format/Padding // kotlinx.datetime.format/Padding.valueOf|valueOf#static(kotlin.String){}[0] + final fun values(): kotlin/Array // kotlinx.datetime.format/Padding.values|values#static(){}[0] +} + +final enum class kotlinx.datetime/DayOfWeek : kotlin/Enum { // kotlinx.datetime/DayOfWeek|null[0] + enum entry FRIDAY // kotlinx.datetime/DayOfWeek.FRIDAY|null[0] + enum entry MONDAY // kotlinx.datetime/DayOfWeek.MONDAY|null[0] + enum entry SATURDAY // kotlinx.datetime/DayOfWeek.SATURDAY|null[0] + enum entry SUNDAY // kotlinx.datetime/DayOfWeek.SUNDAY|null[0] + enum entry THURSDAY // kotlinx.datetime/DayOfWeek.THURSDAY|null[0] + enum entry TUESDAY // kotlinx.datetime/DayOfWeek.TUESDAY|null[0] + enum entry WEDNESDAY // kotlinx.datetime/DayOfWeek.WEDNESDAY|null[0] + + final val entries // kotlinx.datetime/DayOfWeek.entries|#static{}entries[0] + final fun (): kotlin.enums/EnumEntries // kotlinx.datetime/DayOfWeek.entries.|#static(){}[0] + + final fun valueOf(kotlin/String): kotlinx.datetime/DayOfWeek // kotlinx.datetime/DayOfWeek.valueOf|valueOf#static(kotlin.String){}[0] + final fun values(): kotlin/Array // kotlinx.datetime/DayOfWeek.values|values#static(){}[0] +} + +final enum class kotlinx.datetime/Month : kotlin/Enum { // kotlinx.datetime/Month|null[0] + enum entry APRIL // kotlinx.datetime/Month.APRIL|null[0] + enum entry AUGUST // kotlinx.datetime/Month.AUGUST|null[0] + enum entry DECEMBER // kotlinx.datetime/Month.DECEMBER|null[0] + enum entry FEBRUARY // kotlinx.datetime/Month.FEBRUARY|null[0] + enum entry JANUARY // kotlinx.datetime/Month.JANUARY|null[0] + enum entry JULY // kotlinx.datetime/Month.JULY|null[0] + enum entry JUNE // kotlinx.datetime/Month.JUNE|null[0] + enum entry MARCH // kotlinx.datetime/Month.MARCH|null[0] + enum entry MAY // kotlinx.datetime/Month.MAY|null[0] + enum entry NOVEMBER // kotlinx.datetime/Month.NOVEMBER|null[0] + enum entry OCTOBER // kotlinx.datetime/Month.OCTOBER|null[0] + enum entry SEPTEMBER // kotlinx.datetime/Month.SEPTEMBER|null[0] + + final val entries // kotlinx.datetime/Month.entries|#static{}entries[0] + final fun (): kotlin.enums/EnumEntries // kotlinx.datetime/Month.entries.|#static(){}[0] + + final fun valueOf(kotlin/String): kotlinx.datetime/Month // kotlinx.datetime/Month.valueOf|valueOf#static(kotlin.String){}[0] + final fun values(): kotlin/Array // kotlinx.datetime/Month.values|values#static(){}[0] +} + +abstract interface kotlinx.datetime/Clock { // kotlinx.datetime/Clock|null[0] + abstract fun now(): kotlinx.datetime/Instant // kotlinx.datetime/Clock.now|now(){}[0] + + final object Companion // kotlinx.datetime/Clock.Companion|null[0] + + final object System : kotlinx.datetime/Clock { // kotlinx.datetime/Clock.System|null[0] + final fun now(): kotlinx.datetime/Instant // kotlinx.datetime/Clock.System.now|now(){}[0] + } +} + +sealed interface <#A: kotlin/Any?> kotlinx.datetime.format/DateTimeFormat { // kotlinx.datetime.format/DateTimeFormat|null[0] + abstract fun <#A1: kotlin.text/Appendable> formatTo(#A1, #A): #A1 // kotlinx.datetime.format/DateTimeFormat.formatTo|formatTo(0:0;1:0){0§}[0] + abstract fun format(#A): kotlin/String // kotlinx.datetime.format/DateTimeFormat.format|format(1:0){}[0] + abstract fun parse(kotlin/CharSequence): #A // kotlinx.datetime.format/DateTimeFormat.parse|parse(kotlin.CharSequence){}[0] + abstract fun parseOrNull(kotlin/CharSequence): #A? // kotlinx.datetime.format/DateTimeFormat.parseOrNull|parseOrNull(kotlin.CharSequence){}[0] + + final object Companion { // kotlinx.datetime.format/DateTimeFormat.Companion|null[0] + final fun formatAsKotlinBuilderDsl(kotlinx.datetime.format/DateTimeFormat<*>): kotlin/String // kotlinx.datetime.format/DateTimeFormat.Companion.formatAsKotlinBuilderDsl|formatAsKotlinBuilderDsl(kotlinx.datetime.format.DateTimeFormat<*>){}[0] + } +} + +sealed interface kotlinx.datetime.format/DateTimeFormatBuilder { // kotlinx.datetime.format/DateTimeFormatBuilder|null[0] + abstract fun chars(kotlin/String) // kotlinx.datetime.format/DateTimeFormatBuilder.chars|chars(kotlin.String){}[0] + + sealed interface WithDate : kotlinx.datetime.format/DateTimeFormatBuilder { // kotlinx.datetime.format/DateTimeFormatBuilder.WithDate|null[0] + abstract fun date(kotlinx.datetime.format/DateTimeFormat) // kotlinx.datetime.format/DateTimeFormatBuilder.WithDate.date|date(kotlinx.datetime.format.DateTimeFormat){}[0] + abstract fun dayOfMonth(kotlinx.datetime.format/Padding =...) // kotlinx.datetime.format/DateTimeFormatBuilder.WithDate.dayOfMonth|dayOfMonth(kotlinx.datetime.format.Padding){}[0] + abstract fun dayOfWeek(kotlinx.datetime.format/DayOfWeekNames) // kotlinx.datetime.format/DateTimeFormatBuilder.WithDate.dayOfWeek|dayOfWeek(kotlinx.datetime.format.DayOfWeekNames){}[0] + abstract fun dayOfYear(kotlinx.datetime.format/Padding =...) // kotlinx.datetime.format/DateTimeFormatBuilder.WithDate.dayOfYear|dayOfYear(kotlinx.datetime.format.Padding){}[0] + abstract fun monthName(kotlinx.datetime.format/MonthNames) // kotlinx.datetime.format/DateTimeFormatBuilder.WithDate.monthName|monthName(kotlinx.datetime.format.MonthNames){}[0] + abstract fun monthNumber(kotlinx.datetime.format/Padding =...) // kotlinx.datetime.format/DateTimeFormatBuilder.WithDate.monthNumber|monthNumber(kotlinx.datetime.format.Padding){}[0] + abstract fun year(kotlinx.datetime.format/Padding =...) // kotlinx.datetime.format/DateTimeFormatBuilder.WithDate.year|year(kotlinx.datetime.format.Padding){}[0] + abstract fun yearTwoDigits(kotlin/Int) // kotlinx.datetime.format/DateTimeFormatBuilder.WithDate.yearTwoDigits|yearTwoDigits(kotlin.Int){}[0] + } + + sealed interface WithDateTime : kotlinx.datetime.format/DateTimeFormatBuilder.WithDate, kotlinx.datetime.format/DateTimeFormatBuilder.WithTime { // kotlinx.datetime.format/DateTimeFormatBuilder.WithDateTime|null[0] + abstract fun dateTime(kotlinx.datetime.format/DateTimeFormat) // kotlinx.datetime.format/DateTimeFormatBuilder.WithDateTime.dateTime|dateTime(kotlinx.datetime.format.DateTimeFormat){}[0] + } + + sealed interface WithDateTimeComponents : kotlinx.datetime.format/DateTimeFormatBuilder.WithDateTime, kotlinx.datetime.format/DateTimeFormatBuilder.WithUtcOffset { // kotlinx.datetime.format/DateTimeFormatBuilder.WithDateTimeComponents|null[0] + abstract fun dateTimeComponents(kotlinx.datetime.format/DateTimeFormat) // kotlinx.datetime.format/DateTimeFormatBuilder.WithDateTimeComponents.dateTimeComponents|dateTimeComponents(kotlinx.datetime.format.DateTimeFormat){}[0] + abstract fun timeZoneId() // kotlinx.datetime.format/DateTimeFormatBuilder.WithDateTimeComponents.timeZoneId|timeZoneId(){}[0] + } + + sealed interface WithTime : kotlinx.datetime.format/DateTimeFormatBuilder { // kotlinx.datetime.format/DateTimeFormatBuilder.WithTime|null[0] + abstract fun amPmHour(kotlinx.datetime.format/Padding =...) // kotlinx.datetime.format/DateTimeFormatBuilder.WithTime.amPmHour|amPmHour(kotlinx.datetime.format.Padding){}[0] + abstract fun amPmMarker(kotlin/String, kotlin/String) // kotlinx.datetime.format/DateTimeFormatBuilder.WithTime.amPmMarker|amPmMarker(kotlin.String;kotlin.String){}[0] + abstract fun hour(kotlinx.datetime.format/Padding =...) // kotlinx.datetime.format/DateTimeFormatBuilder.WithTime.hour|hour(kotlinx.datetime.format.Padding){}[0] + abstract fun minute(kotlinx.datetime.format/Padding =...) // kotlinx.datetime.format/DateTimeFormatBuilder.WithTime.minute|minute(kotlinx.datetime.format.Padding){}[0] + abstract fun second(kotlinx.datetime.format/Padding =...) // kotlinx.datetime.format/DateTimeFormatBuilder.WithTime.second|second(kotlinx.datetime.format.Padding){}[0] + abstract fun secondFraction(kotlin/Int =..., kotlin/Int =...) // kotlinx.datetime.format/DateTimeFormatBuilder.WithTime.secondFraction|secondFraction(kotlin.Int;kotlin.Int){}[0] + abstract fun time(kotlinx.datetime.format/DateTimeFormat) // kotlinx.datetime.format/DateTimeFormatBuilder.WithTime.time|time(kotlinx.datetime.format.DateTimeFormat){}[0] + open fun secondFraction(kotlin/Int) // kotlinx.datetime.format/DateTimeFormatBuilder.WithTime.secondFraction|secondFraction(kotlin.Int){}[0] + } + + sealed interface WithUtcOffset : kotlinx.datetime.format/DateTimeFormatBuilder { // kotlinx.datetime.format/DateTimeFormatBuilder.WithUtcOffset|null[0] + abstract fun offset(kotlinx.datetime.format/DateTimeFormat) // kotlinx.datetime.format/DateTimeFormatBuilder.WithUtcOffset.offset|offset(kotlinx.datetime.format.DateTimeFormat){}[0] + abstract fun offsetHours(kotlinx.datetime.format/Padding =...) // kotlinx.datetime.format/DateTimeFormatBuilder.WithUtcOffset.offsetHours|offsetHours(kotlinx.datetime.format.Padding){}[0] + abstract fun offsetMinutesOfHour(kotlinx.datetime.format/Padding =...) // kotlinx.datetime.format/DateTimeFormatBuilder.WithUtcOffset.offsetMinutesOfHour|offsetMinutesOfHour(kotlinx.datetime.format.Padding){}[0] + abstract fun offsetSecondsOfMinute(kotlinx.datetime.format/Padding =...) // kotlinx.datetime.format/DateTimeFormatBuilder.WithUtcOffset.offsetSecondsOfMinute|offsetSecondsOfMinute(kotlinx.datetime.format.Padding){}[0] + } +} + +final class kotlinx.datetime.format/DateTimeComponents { // kotlinx.datetime.format/DateTimeComponents|null[0] + final var amPm // kotlinx.datetime.format/DateTimeComponents.amPm|{}amPm[0] + final fun (): kotlinx.datetime.format/AmPmMarker? // kotlinx.datetime.format/DateTimeComponents.amPm.|(){}[0] + final fun (kotlinx.datetime.format/AmPmMarker?) // kotlinx.datetime.format/DateTimeComponents.amPm.|(kotlinx.datetime.format.AmPmMarker?){}[0] + final var dayOfMonth // kotlinx.datetime.format/DateTimeComponents.dayOfMonth|{}dayOfMonth[0] + final fun (): kotlin/Int? // kotlinx.datetime.format/DateTimeComponents.dayOfMonth.|(){}[0] + final fun (kotlin/Int?) // kotlinx.datetime.format/DateTimeComponents.dayOfMonth.|(kotlin.Int?){}[0] + final var dayOfWeek // kotlinx.datetime.format/DateTimeComponents.dayOfWeek|{}dayOfWeek[0] + final fun (): kotlinx.datetime/DayOfWeek? // kotlinx.datetime.format/DateTimeComponents.dayOfWeek.|(){}[0] + final fun (kotlinx.datetime/DayOfWeek?) // kotlinx.datetime.format/DateTimeComponents.dayOfWeek.|(kotlinx.datetime.DayOfWeek?){}[0] + final var dayOfYear // kotlinx.datetime.format/DateTimeComponents.dayOfYear|{}dayOfYear[0] + final fun (): kotlin/Int? // kotlinx.datetime.format/DateTimeComponents.dayOfYear.|(){}[0] + final fun (kotlin/Int?) // kotlinx.datetime.format/DateTimeComponents.dayOfYear.|(kotlin.Int?){}[0] + final var hour // kotlinx.datetime.format/DateTimeComponents.hour|{}hour[0] + final fun (): kotlin/Int? // kotlinx.datetime.format/DateTimeComponents.hour.|(){}[0] + final fun (kotlin/Int?) // kotlinx.datetime.format/DateTimeComponents.hour.|(kotlin.Int?){}[0] + final var hourOfAmPm // kotlinx.datetime.format/DateTimeComponents.hourOfAmPm|{}hourOfAmPm[0] + final fun (): kotlin/Int? // kotlinx.datetime.format/DateTimeComponents.hourOfAmPm.|(){}[0] + final fun (kotlin/Int?) // kotlinx.datetime.format/DateTimeComponents.hourOfAmPm.|(kotlin.Int?){}[0] + final var minute // kotlinx.datetime.format/DateTimeComponents.minute|{}minute[0] + final fun (): kotlin/Int? // kotlinx.datetime.format/DateTimeComponents.minute.|(){}[0] + final fun (kotlin/Int?) // kotlinx.datetime.format/DateTimeComponents.minute.|(kotlin.Int?){}[0] + final var month // kotlinx.datetime.format/DateTimeComponents.month|{}month[0] + final fun (): kotlinx.datetime/Month? // kotlinx.datetime.format/DateTimeComponents.month.|(){}[0] + final fun (kotlinx.datetime/Month?) // kotlinx.datetime.format/DateTimeComponents.month.|(kotlinx.datetime.Month?){}[0] + final var monthNumber // kotlinx.datetime.format/DateTimeComponents.monthNumber|{}monthNumber[0] + final fun (): kotlin/Int? // kotlinx.datetime.format/DateTimeComponents.monthNumber.|(){}[0] + final fun (kotlin/Int?) // kotlinx.datetime.format/DateTimeComponents.monthNumber.|(kotlin.Int?){}[0] + final var nanosecond // kotlinx.datetime.format/DateTimeComponents.nanosecond|{}nanosecond[0] + final fun (): kotlin/Int? // kotlinx.datetime.format/DateTimeComponents.nanosecond.|(){}[0] + final fun (kotlin/Int?) // kotlinx.datetime.format/DateTimeComponents.nanosecond.|(kotlin.Int?){}[0] + final var offsetHours // kotlinx.datetime.format/DateTimeComponents.offsetHours|{}offsetHours[0] + final fun (): kotlin/Int? // kotlinx.datetime.format/DateTimeComponents.offsetHours.|(){}[0] + final fun (kotlin/Int?) // kotlinx.datetime.format/DateTimeComponents.offsetHours.|(kotlin.Int?){}[0] + final var offsetIsNegative // kotlinx.datetime.format/DateTimeComponents.offsetIsNegative|{}offsetIsNegative[0] + final fun (): kotlin/Boolean? // kotlinx.datetime.format/DateTimeComponents.offsetIsNegative.|(){}[0] + final fun (kotlin/Boolean?) // kotlinx.datetime.format/DateTimeComponents.offsetIsNegative.|(kotlin.Boolean?){}[0] + final var offsetMinutesOfHour // kotlinx.datetime.format/DateTimeComponents.offsetMinutesOfHour|{}offsetMinutesOfHour[0] + final fun (): kotlin/Int? // kotlinx.datetime.format/DateTimeComponents.offsetMinutesOfHour.|(){}[0] + final fun (kotlin/Int?) // kotlinx.datetime.format/DateTimeComponents.offsetMinutesOfHour.|(kotlin.Int?){}[0] + final var offsetSecondsOfMinute // kotlinx.datetime.format/DateTimeComponents.offsetSecondsOfMinute|{}offsetSecondsOfMinute[0] + final fun (): kotlin/Int? // kotlinx.datetime.format/DateTimeComponents.offsetSecondsOfMinute.|(){}[0] + final fun (kotlin/Int?) // kotlinx.datetime.format/DateTimeComponents.offsetSecondsOfMinute.|(kotlin.Int?){}[0] + final var second // kotlinx.datetime.format/DateTimeComponents.second|{}second[0] + final fun (): kotlin/Int? // kotlinx.datetime.format/DateTimeComponents.second.|(){}[0] + final fun (kotlin/Int?) // kotlinx.datetime.format/DateTimeComponents.second.|(kotlin.Int?){}[0] + final var timeZoneId // kotlinx.datetime.format/DateTimeComponents.timeZoneId|{}timeZoneId[0] + final fun (): kotlin/String? // kotlinx.datetime.format/DateTimeComponents.timeZoneId.|(){}[0] + final fun (kotlin/String?) // kotlinx.datetime.format/DateTimeComponents.timeZoneId.|(kotlin.String?){}[0] + final var year // kotlinx.datetime.format/DateTimeComponents.year|(kotlin.Int?){}[0] + final fun (): kotlin/Int? // kotlinx.datetime.format/DateTimeComponents.year.|(){}[0] + final fun (kotlin/Int?) // kotlinx.datetime.format/DateTimeComponents.year.|(kotlin.Int?){}[0] + + final fun setDate(kotlinx.datetime/LocalDate) // kotlinx.datetime.format/DateTimeComponents.setDate|setDate(kotlinx.datetime.LocalDate){}[0] + final fun setDateTime(kotlinx.datetime/LocalDateTime) // kotlinx.datetime.format/DateTimeComponents.setDateTime|setDateTime(kotlinx.datetime.LocalDateTime){}[0] + final fun setDateTimeOffset(kotlinx.datetime/Instant, kotlinx.datetime/UtcOffset) // kotlinx.datetime.format/DateTimeComponents.setDateTimeOffset|setDateTimeOffset(kotlinx.datetime.Instant;kotlinx.datetime.UtcOffset){}[0] + final fun setDateTimeOffset(kotlinx.datetime/LocalDateTime, kotlinx.datetime/UtcOffset) // kotlinx.datetime.format/DateTimeComponents.setDateTimeOffset|setDateTimeOffset(kotlinx.datetime.LocalDateTime;kotlinx.datetime.UtcOffset){}[0] + final fun setOffset(kotlinx.datetime/UtcOffset) // kotlinx.datetime.format/DateTimeComponents.setOffset|setOffset(kotlinx.datetime.UtcOffset){}[0] + final fun setTime(kotlinx.datetime/LocalTime) // kotlinx.datetime.format/DateTimeComponents.setTime|setTime(kotlinx.datetime.LocalTime){}[0] + final fun toInstantUsingOffset(): kotlinx.datetime/Instant // kotlinx.datetime.format/DateTimeComponents.toInstantUsingOffset|toInstantUsingOffset(){}[0] + final fun toLocalDate(): kotlinx.datetime/LocalDate // kotlinx.datetime.format/DateTimeComponents.toLocalDate|toLocalDate(){}[0] + final fun toLocalDateTime(): kotlinx.datetime/LocalDateTime // kotlinx.datetime.format/DateTimeComponents.toLocalDateTime|toLocalDateTime(){}[0] + final fun toLocalTime(): kotlinx.datetime/LocalTime // kotlinx.datetime.format/DateTimeComponents.toLocalTime|toLocalTime(){}[0] + final fun toUtcOffset(): kotlinx.datetime/UtcOffset // kotlinx.datetime.format/DateTimeComponents.toUtcOffset|toUtcOffset(){}[0] + + final object Companion { // kotlinx.datetime.format/DateTimeComponents.Companion|null[0] + final fun Format(kotlin/Function1): kotlinx.datetime.format/DateTimeFormat // kotlinx.datetime.format/DateTimeComponents.Companion.Format|Format(kotlin.Function1){}[0] + } + + final object Formats { // kotlinx.datetime.format/DateTimeComponents.Formats|null[0] + final val ISO_DATE_TIME_OFFSET // kotlinx.datetime.format/DateTimeComponents.Formats.ISO_DATE_TIME_OFFSET|{}ISO_DATE_TIME_OFFSET[0] + final fun (): kotlinx.datetime.format/DateTimeFormat // kotlinx.datetime.format/DateTimeComponents.Formats.ISO_DATE_TIME_OFFSET.|(){}[0] + final val RFC_1123 // kotlinx.datetime.format/DateTimeComponents.Formats.RFC_1123|{}RFC_1123[0] + final fun (): kotlinx.datetime.format/DateTimeFormat // kotlinx.datetime.format/DateTimeComponents.Formats.RFC_1123.|(){}[0] + } +} + +final class kotlinx.datetime.format/DayOfWeekNames { // kotlinx.datetime.format/DayOfWeekNames|null[0] + constructor (kotlin.collections/List) // kotlinx.datetime.format/DayOfWeekNames.|(kotlin.collections.List){}[0] + constructor (kotlin/String, kotlin/String, kotlin/String, kotlin/String, kotlin/String, kotlin/String, kotlin/String) // kotlinx.datetime.format/DayOfWeekNames.|(kotlin.String;kotlin.String;kotlin.String;kotlin.String;kotlin.String;kotlin.String;kotlin.String){}[0] + + final val names // kotlinx.datetime.format/DayOfWeekNames.names|{}names[0] + final fun (): kotlin.collections/List // kotlinx.datetime.format/DayOfWeekNames.names.|(){}[0] + + final fun equals(kotlin/Any?): kotlin/Boolean // kotlinx.datetime.format/DayOfWeekNames.equals|equals(kotlin.Any?){}[0] + final fun hashCode(): kotlin/Int // kotlinx.datetime.format/DayOfWeekNames.hashCode|hashCode(){}[0] + final fun toString(): kotlin/String // kotlinx.datetime.format/DayOfWeekNames.toString|toString(){}[0] + + final object Companion { // kotlinx.datetime.format/DayOfWeekNames.Companion|null[0] + final val ENGLISH_ABBREVIATED // kotlinx.datetime.format/DayOfWeekNames.Companion.ENGLISH_ABBREVIATED|{}ENGLISH_ABBREVIATED[0] + final fun (): kotlinx.datetime.format/DayOfWeekNames // kotlinx.datetime.format/DayOfWeekNames.Companion.ENGLISH_ABBREVIATED.|(){}[0] + final val ENGLISH_FULL // kotlinx.datetime.format/DayOfWeekNames.Companion.ENGLISH_FULL|{}ENGLISH_FULL[0] + final fun (): kotlinx.datetime.format/DayOfWeekNames // kotlinx.datetime.format/DayOfWeekNames.Companion.ENGLISH_FULL.|(){}[0] + } +} + +final class kotlinx.datetime.format/MonthNames { // kotlinx.datetime.format/MonthNames|null[0] + constructor (kotlin.collections/List) // kotlinx.datetime.format/MonthNames.|(kotlin.collections.List){}[0] + constructor (kotlin/String, kotlin/String, kotlin/String, kotlin/String, kotlin/String, kotlin/String, kotlin/String, kotlin/String, kotlin/String, kotlin/String, kotlin/String, kotlin/String) // kotlinx.datetime.format/MonthNames.|(kotlin.String;kotlin.String;kotlin.String;kotlin.String;kotlin.String;kotlin.String;kotlin.String;kotlin.String;kotlin.String;kotlin.String;kotlin.String;kotlin.String){}[0] + + final val names // kotlinx.datetime.format/MonthNames.names|{}names[0] + final fun (): kotlin.collections/List // kotlinx.datetime.format/MonthNames.names.|(){}[0] + + final fun equals(kotlin/Any?): kotlin/Boolean // kotlinx.datetime.format/MonthNames.equals|equals(kotlin.Any?){}[0] + final fun hashCode(): kotlin/Int // kotlinx.datetime.format/MonthNames.hashCode|hashCode(){}[0] + final fun toString(): kotlin/String // kotlinx.datetime.format/MonthNames.toString|toString(){}[0] + + final object Companion { // kotlinx.datetime.format/MonthNames.Companion|null[0] + final val ENGLISH_ABBREVIATED // kotlinx.datetime.format/MonthNames.Companion.ENGLISH_ABBREVIATED|{}ENGLISH_ABBREVIATED[0] + final fun (): kotlinx.datetime.format/MonthNames // kotlinx.datetime.format/MonthNames.Companion.ENGLISH_ABBREVIATED.|(){}[0] + final val ENGLISH_FULL // kotlinx.datetime.format/MonthNames.Companion.ENGLISH_FULL|{}ENGLISH_FULL[0] + final fun (): kotlinx.datetime.format/MonthNames // kotlinx.datetime.format/MonthNames.Companion.ENGLISH_FULL.|(){}[0] + } +} + +final class kotlinx.datetime/DatePeriod : kotlinx.datetime/DateTimePeriod { // kotlinx.datetime/DatePeriod|null[0] + constructor (kotlin/Int =..., kotlin/Int =..., kotlin/Int =...) // kotlinx.datetime/DatePeriod.|(kotlin.Int;kotlin.Int;kotlin.Int){}[0] + + final val days // kotlinx.datetime/DatePeriod.days|{}days[0] + final fun (): kotlin/Int // kotlinx.datetime/DatePeriod.days.|(){}[0] + final val hours // kotlinx.datetime/DatePeriod.hours|{}hours[0] + final fun (): kotlin/Int // kotlinx.datetime/DatePeriod.hours.|(){}[0] + final val minutes // kotlinx.datetime/DatePeriod.minutes|{}minutes[0] + final fun (): kotlin/Int // kotlinx.datetime/DatePeriod.minutes.|(){}[0] + final val nanoseconds // kotlinx.datetime/DatePeriod.nanoseconds|{}nanoseconds[0] + final fun (): kotlin/Int // kotlinx.datetime/DatePeriod.nanoseconds.|(){}[0] + final val seconds // kotlinx.datetime/DatePeriod.seconds|{}seconds[0] + final fun (): kotlin/Int // kotlinx.datetime/DatePeriod.seconds.|(){}[0] + + final object Companion { // kotlinx.datetime/DatePeriod.Companion|null[0] + final fun parse(kotlin/String): kotlinx.datetime/DatePeriod // kotlinx.datetime/DatePeriod.Companion.parse|parse(kotlin.String){}[0] + final fun serializer(): kotlinx.serialization/KSerializer // kotlinx.datetime/DatePeriod.Companion.serializer|serializer(){}[0] + } +} + +final class kotlinx.datetime/DateTimeArithmeticException : kotlin/RuntimeException { // kotlinx.datetime/DateTimeArithmeticException|null[0] + constructor () // kotlinx.datetime/DateTimeArithmeticException.|(){}[0] + constructor (kotlin/String) // kotlinx.datetime/DateTimeArithmeticException.|(kotlin.String){}[0] + constructor (kotlin/String, kotlin/Throwable) // kotlinx.datetime/DateTimeArithmeticException.|(kotlin.String;kotlin.Throwable){}[0] + constructor (kotlin/Throwable) // kotlinx.datetime/DateTimeArithmeticException.|(kotlin.Throwable){}[0] +} + +final class kotlinx.datetime/FixedOffsetTimeZone : kotlinx.datetime/TimeZone { // kotlinx.datetime/FixedOffsetTimeZone|null[0] + constructor (kotlinx.datetime/UtcOffset) // kotlinx.datetime/FixedOffsetTimeZone.|(kotlinx.datetime.UtcOffset){}[0] + + final val id // kotlinx.datetime/FixedOffsetTimeZone.id|{}id[0] + final fun (): kotlin/String // kotlinx.datetime/FixedOffsetTimeZone.id.|(){}[0] + final val offset // kotlinx.datetime/FixedOffsetTimeZone.offset|{}offset[0] + final fun (): kotlinx.datetime/UtcOffset // kotlinx.datetime/FixedOffsetTimeZone.offset.|(){}[0] + final val totalSeconds // kotlinx.datetime/FixedOffsetTimeZone.totalSeconds|{}totalSeconds[0] + final fun (): kotlin/Int // kotlinx.datetime/FixedOffsetTimeZone.totalSeconds.|(){}[0] + + final object Companion { // kotlinx.datetime/FixedOffsetTimeZone.Companion|null[0] + final fun serializer(): kotlinx.serialization/KSerializer // kotlinx.datetime/FixedOffsetTimeZone.Companion.serializer|serializer(){}[0] + } +} + +final class kotlinx.datetime/IllegalTimeZoneException : kotlin/IllegalArgumentException { // kotlinx.datetime/IllegalTimeZoneException|null[0] + constructor () // kotlinx.datetime/IllegalTimeZoneException.|(){}[0] + constructor (kotlin/String) // kotlinx.datetime/IllegalTimeZoneException.|(kotlin.String){}[0] + constructor (kotlin/String, kotlin/Throwable) // kotlinx.datetime/IllegalTimeZoneException.|(kotlin.String;kotlin.Throwable){}[0] + constructor (kotlin/Throwable) // kotlinx.datetime/IllegalTimeZoneException.|(kotlin.Throwable){}[0] +} + +final class kotlinx.datetime/Instant : kotlin/Comparable { // kotlinx.datetime/Instant|null[0] + final val epochSeconds // kotlinx.datetime/Instant.epochSeconds|{}epochSeconds[0] + final fun (): kotlin/Long // kotlinx.datetime/Instant.epochSeconds.|(){}[0] + final val nanosecondsOfSecond // kotlinx.datetime/Instant.nanosecondsOfSecond|{}nanosecondsOfSecond[0] + final fun (): kotlin/Int // kotlinx.datetime/Instant.nanosecondsOfSecond.|(){}[0] + + final fun compareTo(kotlinx.datetime/Instant): kotlin/Int // kotlinx.datetime/Instant.compareTo|compareTo(kotlinx.datetime.Instant){}[0] + final fun equals(kotlin/Any?): kotlin/Boolean // kotlinx.datetime/Instant.equals|equals(kotlin.Any?){}[0] + final fun hashCode(): kotlin/Int // kotlinx.datetime/Instant.hashCode|hashCode(){}[0] + final fun minus(kotlin.time/Duration): kotlinx.datetime/Instant // kotlinx.datetime/Instant.minus|minus(kotlin.time.Duration){}[0] + final fun minus(kotlinx.datetime/Instant): kotlin.time/Duration // kotlinx.datetime/Instant.minus|minus(kotlinx.datetime.Instant){}[0] + final fun plus(kotlin.time/Duration): kotlinx.datetime/Instant // kotlinx.datetime/Instant.plus|plus(kotlin.time.Duration){}[0] + final fun toEpochMilliseconds(): kotlin/Long // kotlinx.datetime/Instant.toEpochMilliseconds|toEpochMilliseconds(){}[0] + final fun toString(): kotlin/String // kotlinx.datetime/Instant.toString|toString(){}[0] + + final object Companion { // kotlinx.datetime/Instant.Companion|null[0] + final val DISTANT_FUTURE // kotlinx.datetime/Instant.Companion.DISTANT_FUTURE|{}DISTANT_FUTURE[0] + final fun (): kotlinx.datetime/Instant // kotlinx.datetime/Instant.Companion.DISTANT_FUTURE.|(){}[0] + final val DISTANT_PAST // kotlinx.datetime/Instant.Companion.DISTANT_PAST|{}DISTANT_PAST[0] + final fun (): kotlinx.datetime/Instant // kotlinx.datetime/Instant.Companion.DISTANT_PAST.|(){}[0] + + final fun fromEpochMilliseconds(kotlin/Long): kotlinx.datetime/Instant // kotlinx.datetime/Instant.Companion.fromEpochMilliseconds|fromEpochMilliseconds(kotlin.Long){}[0] + final fun fromEpochSeconds(kotlin/Long, kotlin/Int): kotlinx.datetime/Instant // kotlinx.datetime/Instant.Companion.fromEpochSeconds|fromEpochSeconds(kotlin.Long;kotlin.Int){}[0] + final fun fromEpochSeconds(kotlin/Long, kotlin/Long =...): kotlinx.datetime/Instant // kotlinx.datetime/Instant.Companion.fromEpochSeconds|fromEpochSeconds(kotlin.Long;kotlin.Long){}[0] + final fun now(): kotlinx.datetime/Instant // kotlinx.datetime/Instant.Companion.now|now(){}[0] + final fun parse(kotlin/CharSequence, kotlinx.datetime.format/DateTimeFormat =...): kotlinx.datetime/Instant // kotlinx.datetime/Instant.Companion.parse|parse(kotlin.CharSequence;kotlinx.datetime.format.DateTimeFormat){}[0] + final fun parse(kotlin/String): kotlinx.datetime/Instant // kotlinx.datetime/Instant.Companion.parse|parse(kotlin.String){}[0] + final fun serializer(): kotlinx.serialization/KSerializer // kotlinx.datetime/Instant.Companion.serializer|serializer(){}[0] + } +} + +final class kotlinx.datetime/LocalDate : kotlin/Comparable { // kotlinx.datetime/LocalDate|null[0] + constructor (kotlin/Int, kotlin/Int, kotlin/Int) // kotlinx.datetime/LocalDate.|(kotlin.Int;kotlin.Int;kotlin.Int){}[0] + constructor (kotlin/Int, kotlinx.datetime/Month, kotlin/Int) // kotlinx.datetime/LocalDate.|(kotlin.Int;kotlinx.datetime.Month;kotlin.Int){}[0] + + final val dayOfMonth // kotlinx.datetime/LocalDate.dayOfMonth|{}dayOfMonth[0] + final fun (): kotlin/Int // kotlinx.datetime/LocalDate.dayOfMonth.|(){}[0] + final val dayOfWeek // kotlinx.datetime/LocalDate.dayOfWeek|{}dayOfWeek[0] + final fun (): kotlinx.datetime/DayOfWeek // kotlinx.datetime/LocalDate.dayOfWeek.|(){}[0] + final val dayOfYear // kotlinx.datetime/LocalDate.dayOfYear|{}dayOfYear[0] + final fun (): kotlin/Int // kotlinx.datetime/LocalDate.dayOfYear.|(){}[0] + final val month // kotlinx.datetime/LocalDate.month|(){}[0] + final fun (): kotlinx.datetime/Month // kotlinx.datetime/LocalDate.month.|(){}[0] + final val monthNumber // kotlinx.datetime/LocalDate.monthNumber|{}monthNumber[0] + final fun (): kotlin/Int // kotlinx.datetime/LocalDate.monthNumber.|(){}[0] + final val year // kotlinx.datetime/LocalDate.year|{}year[0] + final fun (): kotlin/Int // kotlinx.datetime/LocalDate.year.|(){}[0] + + final fun compareTo(kotlinx.datetime/LocalDate): kotlin/Int // kotlinx.datetime/LocalDate.compareTo|compareTo(kotlinx.datetime.LocalDate){}[0] + final fun equals(kotlin/Any?): kotlin/Boolean // kotlinx.datetime/LocalDate.equals|equals(kotlin.Any?){}[0] + final fun hashCode(): kotlin/Int // kotlinx.datetime/LocalDate.hashCode|hashCode(){}[0] + final fun toEpochDays(): kotlin/Long // kotlinx.datetime/LocalDate.toEpochDays|toEpochDays(){}[0] + final fun toString(): kotlin/String // kotlinx.datetime/LocalDate.toString|toString(){}[0] + + final object Companion { // kotlinx.datetime/LocalDate.Companion|null[0] + final fun Format(kotlin/Function1): kotlinx.datetime.format/DateTimeFormat // kotlinx.datetime/LocalDate.Companion.Format|Format(kotlin.Function1){}[0] + final fun fromEpochDays(kotlin/Int): kotlinx.datetime/LocalDate // kotlinx.datetime/LocalDate.Companion.fromEpochDays|fromEpochDays(kotlin.Int){}[0] + final fun fromEpochDays(kotlin/Long): kotlinx.datetime/LocalDate // kotlinx.datetime/LocalDate.Companion.fromEpochDays|fromEpochDays(kotlin.Long){}[0] + final fun parse(kotlin/CharSequence, kotlinx.datetime.format/DateTimeFormat =...): kotlinx.datetime/LocalDate // kotlinx.datetime/LocalDate.Companion.parse|parse(kotlin.CharSequence;kotlinx.datetime.format.DateTimeFormat){}[0] + final fun parse(kotlin/String): kotlinx.datetime/LocalDate // kotlinx.datetime/LocalDate.Companion.parse|parse(kotlin.String){}[0] + final fun serializer(): kotlinx.serialization/KSerializer // kotlinx.datetime/LocalDate.Companion.serializer|serializer(){}[0] + } + + final object Formats { // kotlinx.datetime/LocalDate.Formats|null[0] + final val ISO // kotlinx.datetime/LocalDate.Formats.ISO|{}ISO[0] + final fun (): kotlinx.datetime.format/DateTimeFormat // kotlinx.datetime/LocalDate.Formats.ISO.|(){}[0] + final val ISO_BASIC // kotlinx.datetime/LocalDate.Formats.ISO_BASIC|{}ISO_BASIC[0] + final fun (): kotlinx.datetime.format/DateTimeFormat // kotlinx.datetime/LocalDate.Formats.ISO_BASIC.|(){}[0] + } +} + +final class kotlinx.datetime/LocalDateTime : kotlin/Comparable { // kotlinx.datetime/LocalDateTime|null[0] + constructor (kotlin/Int, kotlin/Int, kotlin/Int, kotlin/Int, kotlin/Int, kotlin/Int =..., kotlin/Int =...) // kotlinx.datetime/LocalDateTime.|(kotlin.Int;kotlin.Int;kotlin.Int;kotlin.Int;kotlin.Int;kotlin.Int;kotlin.Int){}[0] + constructor (kotlin/Int, kotlinx.datetime/Month, kotlin/Int, kotlin/Int, kotlin/Int, kotlin/Int =..., kotlin/Int =...) // kotlinx.datetime/LocalDateTime.|(kotlin.Int;kotlinx.datetime.Month;kotlin.Int;kotlin.Int;kotlin.Int;kotlin.Int;kotlin.Int){}[0] + constructor (kotlinx.datetime/LocalDate, kotlinx.datetime/LocalTime) // kotlinx.datetime/LocalDateTime.|(kotlinx.datetime.LocalDate;kotlinx.datetime.LocalTime){}[0] + + final val date // kotlinx.datetime/LocalDateTime.date|{}date[0] + final fun (): kotlinx.datetime/LocalDate // kotlinx.datetime/LocalDateTime.date.|(){}[0] + final val dayOfMonth // kotlinx.datetime/LocalDateTime.dayOfMonth|{}dayOfMonth[0] + final fun (): kotlin/Int // kotlinx.datetime/LocalDateTime.dayOfMonth.|(){}[0] + final val dayOfWeek // kotlinx.datetime/LocalDateTime.dayOfWeek|{}dayOfWeek[0] + final fun (): kotlinx.datetime/DayOfWeek // kotlinx.datetime/LocalDateTime.dayOfWeek.|(){}[0] + final val dayOfYear // kotlinx.datetime/LocalDateTime.dayOfYear|{}dayOfYear[0] + final fun (): kotlin/Int // kotlinx.datetime/LocalDateTime.dayOfYear.|(){}[0] + final val hour // kotlinx.datetime/LocalDateTime.hour|{}hour[0] + final fun (): kotlin/Int // kotlinx.datetime/LocalDateTime.hour.|(){}[0] + final val minute // kotlinx.datetime/LocalDateTime.minute|{}minute[0] + final fun (): kotlin/Int // kotlinx.datetime/LocalDateTime.minute.|(){}[0] + final val month // kotlinx.datetime/LocalDateTime.month|{}month[0] + final fun (): kotlinx.datetime/Month // kotlinx.datetime/LocalDateTime.month.|(){}[0] + final val monthNumber // kotlinx.datetime/LocalDateTime.monthNumber|{}monthNumber[0] + final fun (): kotlin/Int // kotlinx.datetime/LocalDateTime.monthNumber.|(){}[0] + final val nanosecond // kotlinx.datetime/LocalDateTime.nanosecond|{}nanosecond[0] + final fun (): kotlin/Int // kotlinx.datetime/LocalDateTime.nanosecond.|(){}[0] + final val second // kotlinx.datetime/LocalDateTime.second|{}second[0] + final fun (): kotlin/Int // kotlinx.datetime/LocalDateTime.second.|(){}[0] + final val time // kotlinx.datetime/LocalDateTime.time|{}time[0] + final fun (): kotlinx.datetime/LocalTime // kotlinx.datetime/LocalDateTime.time.|(){}[0] + final val year // kotlinx.datetime/LocalDateTime.year|{}year[0] + final fun (): kotlin/Int // kotlinx.datetime/LocalDateTime.year.|(){}[0] + + final fun compareTo(kotlinx.datetime/LocalDateTime): kotlin/Int // kotlinx.datetime/LocalDateTime.compareTo|compareTo(kotlinx.datetime.LocalDateTime){}[0] + final fun equals(kotlin/Any?): kotlin/Boolean // kotlinx.datetime/LocalDateTime.equals|equals(kotlin.Any?){}[0] + final fun hashCode(): kotlin/Int // kotlinx.datetime/LocalDateTime.hashCode|hashCode(){}[0] + final fun toString(): kotlin/String // kotlinx.datetime/LocalDateTime.toString|toString(){}[0] + + final object Companion { // kotlinx.datetime/LocalDateTime.Companion|null[0] + final fun Format(kotlin/Function1): kotlinx.datetime.format/DateTimeFormat // kotlinx.datetime/LocalDateTime.Companion.Format|Format(kotlin.Function1){}[0] + final fun parse(kotlin/CharSequence, kotlinx.datetime.format/DateTimeFormat =...): kotlinx.datetime/LocalDateTime // kotlinx.datetime/LocalDateTime.Companion.parse|parse(kotlin.CharSequence;kotlinx.datetime.format.DateTimeFormat){}[0] + final fun parse(kotlin/String): kotlinx.datetime/LocalDateTime // kotlinx.datetime/LocalDateTime.Companion.parse|parse(kotlin.String){}[0] + final fun serializer(): kotlinx.serialization/KSerializer // kotlinx.datetime/LocalDateTime.Companion.serializer|serializer(){}[0] + } + + final object Formats { // kotlinx.datetime/LocalDateTime.Formats|null[0] + final val ISO // kotlinx.datetime/LocalDateTime.Formats.ISO|{}ISO[0] + final fun (): kotlinx.datetime.format/DateTimeFormat // kotlinx.datetime/LocalDateTime.Formats.ISO.|(){}[0] + } +} + +final class kotlinx.datetime/LocalTime : kotlin/Comparable { // kotlinx.datetime/LocalTime|null[0] + constructor (kotlin/Int, kotlin/Int, kotlin/Int =..., kotlin/Int =...) // kotlinx.datetime/LocalTime.|(kotlin.Int;kotlin.Int;kotlin.Int;kotlin.Int){}[0] + + final val hour // kotlinx.datetime/LocalTime.hour|{}hour[0] + final fun (): kotlin/Int // kotlinx.datetime/LocalTime.hour.|(){}[0] + final val minute // kotlinx.datetime/LocalTime.minute|{}minute[0] + final fun (): kotlin/Int // kotlinx.datetime/LocalTime.minute.|(){}[0] + final val nanosecond // kotlinx.datetime/LocalTime.nanosecond|{}nanosecond[0] + final fun (): kotlin/Int // kotlinx.datetime/LocalTime.nanosecond.|(){}[0] + final val second // kotlinx.datetime/LocalTime.second|{}second[0] + final fun (): kotlin/Int // kotlinx.datetime/LocalTime.second.|(){}[0] + + final fun compareTo(kotlinx.datetime/LocalTime): kotlin/Int // kotlinx.datetime/LocalTime.compareTo|compareTo(kotlinx.datetime.LocalTime){}[0] + final fun equals(kotlin/Any?): kotlin/Boolean // kotlinx.datetime/LocalTime.equals|equals(kotlin.Any?){}[0] + final fun hashCode(): kotlin/Int // kotlinx.datetime/LocalTime.hashCode|hashCode(){}[0] + final fun toMillisecondOfDay(): kotlin/Int // kotlinx.datetime/LocalTime.toMillisecondOfDay|toMillisecondOfDay(){}[0] + final fun toNanosecondOfDay(): kotlin/Long // kotlinx.datetime/LocalTime.toNanosecondOfDay|toNanosecondOfDay(){}[0] + final fun toSecondOfDay(): kotlin/Int // kotlinx.datetime/LocalTime.toSecondOfDay|toSecondOfDay(){}[0] + final fun toString(): kotlin/String // kotlinx.datetime/LocalTime.toString|toString(){}[0] + + final object Companion { // kotlinx.datetime/LocalTime.Companion|null[0] + final fun Format(kotlin/Function1): kotlinx.datetime.format/DateTimeFormat // kotlinx.datetime/LocalTime.Companion.Format|Format(kotlin.Function1){}[0] + final fun fromMillisecondOfDay(kotlin/Int): kotlinx.datetime/LocalTime // kotlinx.datetime/LocalTime.Companion.fromMillisecondOfDay|fromMillisecondOfDay(kotlin.Int){}[0] + final fun fromNanosecondOfDay(kotlin/Long): kotlinx.datetime/LocalTime // kotlinx.datetime/LocalTime.Companion.fromNanosecondOfDay|fromNanosecondOfDay(kotlin.Long){}[0] + final fun fromSecondOfDay(kotlin/Int): kotlinx.datetime/LocalTime // kotlinx.datetime/LocalTime.Companion.fromSecondOfDay|fromSecondOfDay(kotlin.Int){}[0] + final fun parse(kotlin/CharSequence, kotlinx.datetime.format/DateTimeFormat =...): kotlinx.datetime/LocalTime // kotlinx.datetime/LocalTime.Companion.parse|parse(kotlin.CharSequence;kotlinx.datetime.format.DateTimeFormat){}[0] + final fun parse(kotlin/String): kotlinx.datetime/LocalTime // kotlinx.datetime/LocalTime.Companion.parse|parse(kotlin.String){}[0] + final fun serializer(): kotlinx.serialization/KSerializer // kotlinx.datetime/LocalTime.Companion.serializer|serializer(){}[0] + } + + final object Formats { // kotlinx.datetime/LocalTime.Formats|null[0] + final val ISO // kotlinx.datetime/LocalTime.Formats.ISO|{}ISO[0] + final fun (): kotlinx.datetime.format/DateTimeFormat // kotlinx.datetime/LocalTime.Formats.ISO.|(){}[0] + } +} + +final class kotlinx.datetime/UtcOffset { // kotlinx.datetime/UtcOffset|null[0] + final val totalSeconds // kotlinx.datetime/UtcOffset.totalSeconds|{}totalSeconds[0] + final fun (): kotlin/Int // kotlinx.datetime/UtcOffset.totalSeconds.|(){}[0] + + final fun equals(kotlin/Any?): kotlin/Boolean // kotlinx.datetime/UtcOffset.equals|equals(kotlin.Any?){}[0] + final fun hashCode(): kotlin/Int // kotlinx.datetime/UtcOffset.hashCode|hashCode(){}[0] + final fun toString(): kotlin/String // kotlinx.datetime/UtcOffset.toString|toString(){}[0] + + final object Companion { // kotlinx.datetime/UtcOffset.Companion|null[0] + final val ZERO // kotlinx.datetime/UtcOffset.Companion.ZERO|{}ZERO[0] + final fun (): kotlinx.datetime/UtcOffset // kotlinx.datetime/UtcOffset.Companion.ZERO.|(){}[0] + + final fun Format(kotlin/Function1): kotlinx.datetime.format/DateTimeFormat // kotlinx.datetime/UtcOffset.Companion.Format|Format(kotlin.Function1){}[0] + final fun parse(kotlin/CharSequence, kotlinx.datetime.format/DateTimeFormat =...): kotlinx.datetime/UtcOffset // kotlinx.datetime/UtcOffset.Companion.parse|parse(kotlin.CharSequence;kotlinx.datetime.format.DateTimeFormat){}[0] + final fun parse(kotlin/String): kotlinx.datetime/UtcOffset // kotlinx.datetime/UtcOffset.Companion.parse|parse(kotlin.String){}[0] + final fun serializer(): kotlinx.serialization/KSerializer // kotlinx.datetime/UtcOffset.Companion.serializer|serializer(){}[0] + } + + final object Formats { // kotlinx.datetime/UtcOffset.Formats|null[0] + final val FOUR_DIGITS // kotlinx.datetime/UtcOffset.Formats.FOUR_DIGITS|{}FOUR_DIGITS[0] + final fun (): kotlinx.datetime.format/DateTimeFormat // kotlinx.datetime/UtcOffset.Formats.FOUR_DIGITS.|(){}[0] + final val ISO // kotlinx.datetime/UtcOffset.Formats.ISO|(){}[0] + final fun (): kotlinx.datetime.format/DateTimeFormat // kotlinx.datetime/UtcOffset.Formats.ISO.|(){}[0] + final val ISO_BASIC // kotlinx.datetime/UtcOffset.Formats.ISO_BASIC|{}ISO_BASIC[0] + final fun (): kotlinx.datetime.format/DateTimeFormat // kotlinx.datetime/UtcOffset.Formats.ISO_BASIC.|(){}[0] + } +} + +open class kotlinx.datetime/TimeZone { // kotlinx.datetime/TimeZone|null[0] + open val id // kotlinx.datetime/TimeZone.id|{}id[0] + open fun (): kotlin/String // kotlinx.datetime/TimeZone.id.|(){}[0] + + final fun (kotlinx.datetime/Instant).toLocalDateTime(): kotlinx.datetime/LocalDateTime // kotlinx.datetime/TimeZone.toLocalDateTime|toLocalDateTime@kotlinx.datetime.Instant(){}[0] + final fun (kotlinx.datetime/LocalDateTime).toInstant(): kotlinx.datetime/Instant // kotlinx.datetime/TimeZone.toInstant|toInstant@kotlinx.datetime.LocalDateTime(){}[0] + open fun equals(kotlin/Any?): kotlin/Boolean // kotlinx.datetime/TimeZone.equals|equals(kotlin.Any?){}[0] + open fun hashCode(): kotlin/Int // kotlinx.datetime/TimeZone.hashCode|hashCode(){}[0] + open fun toString(): kotlin/String // kotlinx.datetime/TimeZone.toString|toString(){}[0] + + final object Companion { // kotlinx.datetime/TimeZone.Companion|null[0] + final val UTC // kotlinx.datetime/TimeZone.Companion.UTC|{}UTC[0] + final fun (): kotlinx.datetime/FixedOffsetTimeZone // kotlinx.datetime/TimeZone.Companion.UTC.|(){}[0] + final val availableZoneIds // kotlinx.datetime/TimeZone.Companion.availableZoneIds|{}availableZoneIds[0] + final fun (): kotlin.collections/Set // kotlinx.datetime/TimeZone.Companion.availableZoneIds.|(){}[0] + + final fun currentSystemDefault(): kotlinx.datetime/TimeZone // kotlinx.datetime/TimeZone.Companion.currentSystemDefault|currentSystemDefault(){}[0] + final fun of(kotlin/String): kotlinx.datetime/TimeZone // kotlinx.datetime/TimeZone.Companion.of|of(kotlin.String){}[0] + final fun serializer(): kotlinx.serialization/KSerializer // kotlinx.datetime/TimeZone.Companion.serializer|serializer(){}[0] + } +} + +sealed class kotlinx.datetime/DateTimePeriod { // kotlinx.datetime/DateTimePeriod|null[0] + constructor () // kotlinx.datetime/DateTimePeriod.|(){}[0] + + abstract val days // kotlinx.datetime/DateTimePeriod.days|{}days[0] + abstract fun (): kotlin/Int // kotlinx.datetime/DateTimePeriod.days.|(){}[0] + final val months // kotlinx.datetime/DateTimePeriod.months|{}months[0] + final fun (): kotlin/Int // kotlinx.datetime/DateTimePeriod.months.|(){}[0] + final val years // kotlinx.datetime/DateTimePeriod.years|{}years[0] + final fun (): kotlin/Int // kotlinx.datetime/DateTimePeriod.years.|(){}[0] + open val hours // kotlinx.datetime/DateTimePeriod.hours|{}hours[0] + open fun (): kotlin/Int // kotlinx.datetime/DateTimePeriod.hours.|(){}[0] + open val minutes // kotlinx.datetime/DateTimePeriod.minutes|{}minutes[0] + open fun (): kotlin/Int // kotlinx.datetime/DateTimePeriod.minutes.|(){}[0] + open val nanoseconds // kotlinx.datetime/DateTimePeriod.nanoseconds|{}nanoseconds[0] + open fun (): kotlin/Int // kotlinx.datetime/DateTimePeriod.nanoseconds.|(){}[0] + open val seconds // kotlinx.datetime/DateTimePeriod.seconds|{}seconds[0] + open fun (): kotlin/Int // kotlinx.datetime/DateTimePeriod.seconds.|(){}[0] + + open fun equals(kotlin/Any?): kotlin/Boolean // kotlinx.datetime/DateTimePeriod.equals|equals(kotlin.Any?){}[0] + open fun hashCode(): kotlin/Int // kotlinx.datetime/DateTimePeriod.hashCode|hashCode(){}[0] + open fun toString(): kotlin/String // kotlinx.datetime/DateTimePeriod.toString|toString(){}[0] + + final object Companion { // kotlinx.datetime/DateTimePeriod.Companion|null[0] + final fun parse(kotlin/String): kotlinx.datetime/DateTimePeriod // kotlinx.datetime/DateTimePeriod.Companion.parse|parse(kotlin.String){}[0] + final fun serializer(): kotlinx.serialization/KSerializer // kotlinx.datetime/DateTimePeriod.Companion.serializer|serializer(){}[0] + } +} + +sealed class kotlinx.datetime/DateTimeUnit { // kotlinx.datetime/DateTimeUnit|null[0] + constructor () // kotlinx.datetime/DateTimeUnit.|(){}[0] + + abstract fun times(kotlin/Int): kotlinx.datetime/DateTimeUnit // kotlinx.datetime/DateTimeUnit.times|times(kotlin.Int){}[0] + final fun formatToString(kotlin/Int, kotlin/String): kotlin/String // kotlinx.datetime/DateTimeUnit.formatToString|formatToString(kotlin.Int;kotlin.String){}[0] + final fun formatToString(kotlin/Long, kotlin/String): kotlin/String // kotlinx.datetime/DateTimeUnit.formatToString|formatToString(kotlin.Long;kotlin.String){}[0] + + final class DayBased : kotlinx.datetime/DateTimeUnit.DateBased { // kotlinx.datetime/DateTimeUnit.DayBased|null[0] + constructor (kotlin/Int) // kotlinx.datetime/DateTimeUnit.DayBased.|(kotlin.Int){}[0] + + final val days // kotlinx.datetime/DateTimeUnit.DayBased.days|{}days[0] + final fun (): kotlin/Int // kotlinx.datetime/DateTimeUnit.DayBased.days.|(){}[0] + + final fun equals(kotlin/Any?): kotlin/Boolean // kotlinx.datetime/DateTimeUnit.DayBased.equals|equals(kotlin.Any?){}[0] + final fun hashCode(): kotlin/Int // kotlinx.datetime/DateTimeUnit.DayBased.hashCode|hashCode(){}[0] + final fun times(kotlin/Int): kotlinx.datetime/DateTimeUnit.DayBased // kotlinx.datetime/DateTimeUnit.DayBased.times|times(kotlin.Int){}[0] + final fun toString(): kotlin/String // kotlinx.datetime/DateTimeUnit.DayBased.toString|toString(){}[0] + + final object Companion { // kotlinx.datetime/DateTimeUnit.DayBased.Companion|null[0] + final fun serializer(): kotlinx.serialization/KSerializer // kotlinx.datetime/DateTimeUnit.DayBased.Companion.serializer|serializer(){}[0] + } + } + + final class MonthBased : kotlinx.datetime/DateTimeUnit.DateBased { // kotlinx.datetime/DateTimeUnit.MonthBased|null[0] + constructor (kotlin/Int) // kotlinx.datetime/DateTimeUnit.MonthBased.|(kotlin.Int){}[0] + + final val months // kotlinx.datetime/DateTimeUnit.MonthBased.months|{}months[0] + final fun (): kotlin/Int // kotlinx.datetime/DateTimeUnit.MonthBased.months.|(){}[0] + + final fun equals(kotlin/Any?): kotlin/Boolean // kotlinx.datetime/DateTimeUnit.MonthBased.equals|equals(kotlin.Any?){}[0] + final fun hashCode(): kotlin/Int // kotlinx.datetime/DateTimeUnit.MonthBased.hashCode|hashCode(){}[0] + final fun times(kotlin/Int): kotlinx.datetime/DateTimeUnit.MonthBased // kotlinx.datetime/DateTimeUnit.MonthBased.times|times(kotlin.Int){}[0] + final fun toString(): kotlin/String // kotlinx.datetime/DateTimeUnit.MonthBased.toString|toString(){}[0] + + final object Companion { // kotlinx.datetime/DateTimeUnit.MonthBased.Companion|null[0] + final fun serializer(): kotlinx.serialization/KSerializer // kotlinx.datetime/DateTimeUnit.MonthBased.Companion.serializer|serializer(){}[0] + } + } + + final class TimeBased : kotlinx.datetime/DateTimeUnit { // kotlinx.datetime/DateTimeUnit.TimeBased|null[0] + constructor (kotlin/Long) // kotlinx.datetime/DateTimeUnit.TimeBased.|(kotlin.Long){}[0] + + final val duration // kotlinx.datetime/DateTimeUnit.TimeBased.duration|{}duration[0] + final fun (): kotlin.time/Duration // kotlinx.datetime/DateTimeUnit.TimeBased.duration.|(){}[0] + final val nanoseconds // kotlinx.datetime/DateTimeUnit.TimeBased.nanoseconds|{}nanoseconds[0] + final fun (): kotlin/Long // kotlinx.datetime/DateTimeUnit.TimeBased.nanoseconds.|(){}[0] + + final fun equals(kotlin/Any?): kotlin/Boolean // kotlinx.datetime/DateTimeUnit.TimeBased.equals|equals(kotlin.Any?){}[0] + final fun hashCode(): kotlin/Int // kotlinx.datetime/DateTimeUnit.TimeBased.hashCode|hashCode(){}[0] + final fun times(kotlin/Int): kotlinx.datetime/DateTimeUnit.TimeBased // kotlinx.datetime/DateTimeUnit.TimeBased.times|times(kotlin.Int){}[0] + final fun toString(): kotlin/String // kotlinx.datetime/DateTimeUnit.TimeBased.toString|toString(){}[0] + + final object Companion { // kotlinx.datetime/DateTimeUnit.TimeBased.Companion|null[0] + final fun serializer(): kotlinx.serialization/KSerializer // kotlinx.datetime/DateTimeUnit.TimeBased.Companion.serializer|serializer(){}[0] + } + } + + sealed class DateBased : kotlinx.datetime/DateTimeUnit { // kotlinx.datetime/DateTimeUnit.DateBased|null[0] + constructor () // kotlinx.datetime/DateTimeUnit.DateBased.|(){}[0] + + final object Companion { // kotlinx.datetime/DateTimeUnit.DateBased.Companion|null[0] + final fun serializer(): kotlinx.serialization/KSerializer // kotlinx.datetime/DateTimeUnit.DateBased.Companion.serializer|serializer(){}[0] + } + } + + final object Companion { // kotlinx.datetime/DateTimeUnit.Companion|null[0] + final val CENTURY // kotlinx.datetime/DateTimeUnit.Companion.CENTURY|{}CENTURY[0] + final fun (): kotlinx.datetime/DateTimeUnit.MonthBased // kotlinx.datetime/DateTimeUnit.Companion.CENTURY.|(){}[0] + final val DAY // kotlinx.datetime/DateTimeUnit.Companion.DAY|{}DAY[0] + final fun (): kotlinx.datetime/DateTimeUnit.DayBased // kotlinx.datetime/DateTimeUnit.Companion.DAY.|(){}[0] + final val HOUR // kotlinx.datetime/DateTimeUnit.Companion.HOUR|{}HOUR[0] + final fun (): kotlinx.datetime/DateTimeUnit.TimeBased // kotlinx.datetime/DateTimeUnit.Companion.HOUR.|(){}[0] + final val MICROSECOND // kotlinx.datetime/DateTimeUnit.Companion.MICROSECOND|{}MICROSECOND[0] + final fun (): kotlinx.datetime/DateTimeUnit.TimeBased // kotlinx.datetime/DateTimeUnit.Companion.MICROSECOND.|(){}[0] + final val MILLISECOND // kotlinx.datetime/DateTimeUnit.Companion.MILLISECOND|{}MILLISECOND[0] + final fun (): kotlinx.datetime/DateTimeUnit.TimeBased // kotlinx.datetime/DateTimeUnit.Companion.MILLISECOND.|(){}[0] + final val MINUTE // kotlinx.datetime/DateTimeUnit.Companion.MINUTE|{}MINUTE[0] + final fun (): kotlinx.datetime/DateTimeUnit.TimeBased // kotlinx.datetime/DateTimeUnit.Companion.MINUTE.|(){}[0] + final val MONTH // kotlinx.datetime/DateTimeUnit.Companion.MONTH|{}MONTH[0] + final fun (): kotlinx.datetime/DateTimeUnit.MonthBased // kotlinx.datetime/DateTimeUnit.Companion.MONTH.|(){}[0] + final val NANOSECOND // kotlinx.datetime/DateTimeUnit.Companion.NANOSECOND|{}NANOSECOND[0] + final fun (): kotlinx.datetime/DateTimeUnit.TimeBased // kotlinx.datetime/DateTimeUnit.Companion.NANOSECOND.|(){}[0] + final val QUARTER // kotlinx.datetime/DateTimeUnit.Companion.QUARTER|{}QUARTER[0] + final fun (): kotlinx.datetime/DateTimeUnit.MonthBased // kotlinx.datetime/DateTimeUnit.Companion.QUARTER.|(){}[0] + final val SECOND // kotlinx.datetime/DateTimeUnit.Companion.SECOND|{}SECOND[0] + final fun (): kotlinx.datetime/DateTimeUnit.TimeBased // kotlinx.datetime/DateTimeUnit.Companion.SECOND.|(){}[0] + final val WEEK // kotlinx.datetime/DateTimeUnit.Companion.WEEK|{}WEEK[0] + final fun (): kotlinx.datetime/DateTimeUnit.DayBased // kotlinx.datetime/DateTimeUnit.Companion.WEEK.|(){}[0] + final val YEAR // kotlinx.datetime/DateTimeUnit.Companion.YEAR|{}YEAR[0] + final fun (): kotlinx.datetime/DateTimeUnit.MonthBased // kotlinx.datetime/DateTimeUnit.Companion.YEAR.|(){}[0] + + final fun serializer(): kotlinx.serialization/KSerializer // kotlinx.datetime/DateTimeUnit.Companion.serializer|serializer(){}[0] + } +} + +final object kotlinx.datetime.serializers/DateBasedDateTimeUnitSerializer : kotlinx.serialization.internal/AbstractPolymorphicSerializer { // kotlinx.datetime.serializers/DateBasedDateTimeUnitSerializer|null[0] + final val baseClass // kotlinx.datetime.serializers/DateBasedDateTimeUnitSerializer.baseClass|{}baseClass[0] + final fun (): kotlin.reflect/KClass // kotlinx.datetime.serializers/DateBasedDateTimeUnitSerializer.baseClass.|(){}[0] + final val descriptor // kotlinx.datetime.serializers/DateBasedDateTimeUnitSerializer.descriptor|{}descriptor[0] + final fun (): kotlinx.serialization.descriptors/SerialDescriptor // kotlinx.datetime.serializers/DateBasedDateTimeUnitSerializer.descriptor.|(){}[0] + + final fun findPolymorphicSerializerOrNull(kotlinx.serialization.encoding/CompositeDecoder, kotlin/String?): kotlinx.serialization/DeserializationStrategy? // kotlinx.datetime.serializers/DateBasedDateTimeUnitSerializer.findPolymorphicSerializerOrNull|findPolymorphicSerializerOrNull(kotlinx.serialization.encoding.CompositeDecoder;kotlin.String?){}[0] + final fun findPolymorphicSerializerOrNull(kotlinx.serialization.encoding/Encoder, kotlinx.datetime/DateTimeUnit.DateBased): kotlinx.serialization/SerializationStrategy? // kotlinx.datetime.serializers/DateBasedDateTimeUnitSerializer.findPolymorphicSerializerOrNull|findPolymorphicSerializerOrNull(kotlinx.serialization.encoding.Encoder;kotlinx.datetime.DateTimeUnit.DateBased){}[0] +} + +final object kotlinx.datetime.serializers/DatePeriodComponentSerializer : kotlinx.serialization/KSerializer { // kotlinx.datetime.serializers/DatePeriodComponentSerializer|null[0] + final val descriptor // kotlinx.datetime.serializers/DatePeriodComponentSerializer.descriptor|{}descriptor[0] + final fun (): kotlinx.serialization.descriptors/SerialDescriptor // kotlinx.datetime.serializers/DatePeriodComponentSerializer.descriptor.|(){}[0] + + final fun deserialize(kotlinx.serialization.encoding/Decoder): kotlinx.datetime/DatePeriod // kotlinx.datetime.serializers/DatePeriodComponentSerializer.deserialize|deserialize(kotlinx.serialization.encoding.Decoder){}[0] + final fun serialize(kotlinx.serialization.encoding/Encoder, kotlinx.datetime/DatePeriod) // kotlinx.datetime.serializers/DatePeriodComponentSerializer.serialize|serialize(kotlinx.serialization.encoding.Encoder;kotlinx.datetime.DatePeriod){}[0] +} + +final object kotlinx.datetime.serializers/DatePeriodIso8601Serializer : kotlinx.serialization/KSerializer { // kotlinx.datetime.serializers/DatePeriodIso8601Serializer|null[0] + final val descriptor // kotlinx.datetime.serializers/DatePeriodIso8601Serializer.descriptor|{}descriptor[0] + final fun (): kotlinx.serialization.descriptors/SerialDescriptor // kotlinx.datetime.serializers/DatePeriodIso8601Serializer.descriptor.|(){}[0] + + final fun deserialize(kotlinx.serialization.encoding/Decoder): kotlinx.datetime/DatePeriod // kotlinx.datetime.serializers/DatePeriodIso8601Serializer.deserialize|deserialize(kotlinx.serialization.encoding.Decoder){}[0] + final fun serialize(kotlinx.serialization.encoding/Encoder, kotlinx.datetime/DatePeriod) // kotlinx.datetime.serializers/DatePeriodIso8601Serializer.serialize|serialize(kotlinx.serialization.encoding.Encoder;kotlinx.datetime.DatePeriod){}[0] +} + +final object kotlinx.datetime.serializers/DateTimePeriodComponentSerializer : kotlinx.serialization/KSerializer { // kotlinx.datetime.serializers/DateTimePeriodComponentSerializer|null[0] + final val descriptor // kotlinx.datetime.serializers/DateTimePeriodComponentSerializer.descriptor|{}descriptor[0] + final fun (): kotlinx.serialization.descriptors/SerialDescriptor // kotlinx.datetime.serializers/DateTimePeriodComponentSerializer.descriptor.|(){}[0] + + final fun deserialize(kotlinx.serialization.encoding/Decoder): kotlinx.datetime/DateTimePeriod // kotlinx.datetime.serializers/DateTimePeriodComponentSerializer.deserialize|deserialize(kotlinx.serialization.encoding.Decoder){}[0] + final fun serialize(kotlinx.serialization.encoding/Encoder, kotlinx.datetime/DateTimePeriod) // kotlinx.datetime.serializers/DateTimePeriodComponentSerializer.serialize|serialize(kotlinx.serialization.encoding.Encoder;kotlinx.datetime.DateTimePeriod){}[0] +} + +final object kotlinx.datetime.serializers/DateTimePeriodIso8601Serializer : kotlinx.serialization/KSerializer { // kotlinx.datetime.serializers/DateTimePeriodIso8601Serializer|null[0] + final val descriptor // kotlinx.datetime.serializers/DateTimePeriodIso8601Serializer.descriptor|{}descriptor[0] + final fun (): kotlinx.serialization.descriptors/SerialDescriptor // kotlinx.datetime.serializers/DateTimePeriodIso8601Serializer.descriptor.|(){}[0] + + final fun deserialize(kotlinx.serialization.encoding/Decoder): kotlinx.datetime/DateTimePeriod // kotlinx.datetime.serializers/DateTimePeriodIso8601Serializer.deserialize|deserialize(kotlinx.serialization.encoding.Decoder){}[0] + final fun serialize(kotlinx.serialization.encoding/Encoder, kotlinx.datetime/DateTimePeriod) // kotlinx.datetime.serializers/DateTimePeriodIso8601Serializer.serialize|serialize(kotlinx.serialization.encoding.Encoder;kotlinx.datetime.DateTimePeriod){}[0] +} + +final object kotlinx.datetime.serializers/DateTimeUnitSerializer : kotlinx.serialization.internal/AbstractPolymorphicSerializer { // kotlinx.datetime.serializers/DateTimeUnitSerializer|null[0] + final val baseClass // kotlinx.datetime.serializers/DateTimeUnitSerializer.baseClass|{}baseClass[0] + final fun (): kotlin.reflect/KClass // kotlinx.datetime.serializers/DateTimeUnitSerializer.baseClass.|(){}[0] + final val descriptor // kotlinx.datetime.serializers/DateTimeUnitSerializer.descriptor|{}descriptor[0] + final fun (): kotlinx.serialization.descriptors/SerialDescriptor // kotlinx.datetime.serializers/DateTimeUnitSerializer.descriptor.|(){}[0] + + final fun findPolymorphicSerializerOrNull(kotlinx.serialization.encoding/CompositeDecoder, kotlin/String?): kotlinx.serialization/DeserializationStrategy? // kotlinx.datetime.serializers/DateTimeUnitSerializer.findPolymorphicSerializerOrNull|findPolymorphicSerializerOrNull(kotlinx.serialization.encoding.CompositeDecoder;kotlin.String?){}[0] + final fun findPolymorphicSerializerOrNull(kotlinx.serialization.encoding/Encoder, kotlinx.datetime/DateTimeUnit): kotlinx.serialization/SerializationStrategy? // kotlinx.datetime.serializers/DateTimeUnitSerializer.findPolymorphicSerializerOrNull|findPolymorphicSerializerOrNull(kotlinx.serialization.encoding.Encoder;kotlinx.datetime.DateTimeUnit){}[0] +} + +final object kotlinx.datetime.serializers/DayBasedDateTimeUnitSerializer : kotlinx.serialization/KSerializer { // kotlinx.datetime.serializers/DayBasedDateTimeUnitSerializer|null[0] + final val descriptor // kotlinx.datetime.serializers/DayBasedDateTimeUnitSerializer.descriptor|{}descriptor[0] + final fun (): kotlinx.serialization.descriptors/SerialDescriptor // kotlinx.datetime.serializers/DayBasedDateTimeUnitSerializer.descriptor.|(){}[0] + + final fun deserialize(kotlinx.serialization.encoding/Decoder): kotlinx.datetime/DateTimeUnit.DayBased // kotlinx.datetime.serializers/DayBasedDateTimeUnitSerializer.deserialize|deserialize(kotlinx.serialization.encoding.Decoder){}[0] + final fun serialize(kotlinx.serialization.encoding/Encoder, kotlinx.datetime/DateTimeUnit.DayBased) // kotlinx.datetime.serializers/DayBasedDateTimeUnitSerializer.serialize|serialize(kotlinx.serialization.encoding.Encoder;kotlinx.datetime.DateTimeUnit.DayBased){}[0] +} + +final object kotlinx.datetime.serializers/DayOfWeekSerializer : kotlinx.serialization/KSerializer { // kotlinx.datetime.serializers/DayOfWeekSerializer|null[0] + final val descriptor // kotlinx.datetime.serializers/DayOfWeekSerializer.descriptor|{}descriptor[0] + final fun (): kotlinx.serialization.descriptors/SerialDescriptor // kotlinx.datetime.serializers/DayOfWeekSerializer.descriptor.|(){}[0] + + final fun deserialize(kotlinx.serialization.encoding/Decoder): kotlinx.datetime/DayOfWeek // kotlinx.datetime.serializers/DayOfWeekSerializer.deserialize|deserialize(kotlinx.serialization.encoding.Decoder){}[0] + final fun serialize(kotlinx.serialization.encoding/Encoder, kotlinx.datetime/DayOfWeek) // kotlinx.datetime.serializers/DayOfWeekSerializer.serialize|serialize(kotlinx.serialization.encoding.Encoder;kotlinx.datetime.DayOfWeek){}[0] +} + +final object kotlinx.datetime.serializers/FixedOffsetTimeZoneSerializer : kotlinx.serialization/KSerializer { // kotlinx.datetime.serializers/FixedOffsetTimeZoneSerializer|null[0] + final val descriptor // kotlinx.datetime.serializers/FixedOffsetTimeZoneSerializer.descriptor|{}descriptor[0] + final fun (): kotlinx.serialization.descriptors/SerialDescriptor // kotlinx.datetime.serializers/FixedOffsetTimeZoneSerializer.descriptor.|(){}[0] + + final fun deserialize(kotlinx.serialization.encoding/Decoder): kotlinx.datetime/FixedOffsetTimeZone // kotlinx.datetime.serializers/FixedOffsetTimeZoneSerializer.deserialize|deserialize(kotlinx.serialization.encoding.Decoder){}[0] + final fun serialize(kotlinx.serialization.encoding/Encoder, kotlinx.datetime/FixedOffsetTimeZone) // kotlinx.datetime.serializers/FixedOffsetTimeZoneSerializer.serialize|serialize(kotlinx.serialization.encoding.Encoder;kotlinx.datetime.FixedOffsetTimeZone){}[0] +} + +final object kotlinx.datetime.serializers/InstantComponentSerializer : kotlinx.serialization/KSerializer { // kotlinx.datetime.serializers/InstantComponentSerializer|null[0] + final val descriptor // kotlinx.datetime.serializers/InstantComponentSerializer.descriptor|{}descriptor[0] + final fun (): kotlinx.serialization.descriptors/SerialDescriptor // kotlinx.datetime.serializers/InstantComponentSerializer.descriptor.|(){}[0] + + final fun deserialize(kotlinx.serialization.encoding/Decoder): kotlinx.datetime/Instant // kotlinx.datetime.serializers/InstantComponentSerializer.deserialize|deserialize(kotlinx.serialization.encoding.Decoder){}[0] + final fun serialize(kotlinx.serialization.encoding/Encoder, kotlinx.datetime/Instant) // kotlinx.datetime.serializers/InstantComponentSerializer.serialize|serialize(kotlinx.serialization.encoding.Encoder;kotlinx.datetime.Instant){}[0] +} + +final object kotlinx.datetime.serializers/InstantIso8601Serializer : kotlinx.serialization/KSerializer { // kotlinx.datetime.serializers/InstantIso8601Serializer|null[0] + final val descriptor // kotlinx.datetime.serializers/InstantIso8601Serializer.descriptor|{}descriptor[0] + final fun (): kotlinx.serialization.descriptors/SerialDescriptor // kotlinx.datetime.serializers/InstantIso8601Serializer.descriptor.|(){}[0] + + final fun deserialize(kotlinx.serialization.encoding/Decoder): kotlinx.datetime/Instant // kotlinx.datetime.serializers/InstantIso8601Serializer.deserialize|deserialize(kotlinx.serialization.encoding.Decoder){}[0] + final fun serialize(kotlinx.serialization.encoding/Encoder, kotlinx.datetime/Instant) // kotlinx.datetime.serializers/InstantIso8601Serializer.serialize|serialize(kotlinx.serialization.encoding.Encoder;kotlinx.datetime.Instant){}[0] +} + +final object kotlinx.datetime.serializers/LocalDateComponentSerializer : kotlinx.serialization/KSerializer { // kotlinx.datetime.serializers/LocalDateComponentSerializer|null[0] + final val descriptor // kotlinx.datetime.serializers/LocalDateComponentSerializer.descriptor|{}descriptor[0] + final fun (): kotlinx.serialization.descriptors/SerialDescriptor // kotlinx.datetime.serializers/LocalDateComponentSerializer.descriptor.|(){}[0] + + final fun deserialize(kotlinx.serialization.encoding/Decoder): kotlinx.datetime/LocalDate // kotlinx.datetime.serializers/LocalDateComponentSerializer.deserialize|deserialize(kotlinx.serialization.encoding.Decoder){}[0] + final fun serialize(kotlinx.serialization.encoding/Encoder, kotlinx.datetime/LocalDate) // kotlinx.datetime.serializers/LocalDateComponentSerializer.serialize|serialize(kotlinx.serialization.encoding.Encoder;kotlinx.datetime.LocalDate){}[0] +} + +final object kotlinx.datetime.serializers/LocalDateIso8601Serializer : kotlinx.serialization/KSerializer { // kotlinx.datetime.serializers/LocalDateIso8601Serializer|null[0] + final val descriptor // kotlinx.datetime.serializers/LocalDateIso8601Serializer.descriptor|{}descriptor[0] + final fun (): kotlinx.serialization.descriptors/SerialDescriptor // kotlinx.datetime.serializers/LocalDateIso8601Serializer.descriptor.|(){}[0] + + final fun deserialize(kotlinx.serialization.encoding/Decoder): kotlinx.datetime/LocalDate // kotlinx.datetime.serializers/LocalDateIso8601Serializer.deserialize|deserialize(kotlinx.serialization.encoding.Decoder){}[0] + final fun serialize(kotlinx.serialization.encoding/Encoder, kotlinx.datetime/LocalDate) // kotlinx.datetime.serializers/LocalDateIso8601Serializer.serialize|serialize(kotlinx.serialization.encoding.Encoder;kotlinx.datetime.LocalDate){}[0] +} + +final object kotlinx.datetime.serializers/LocalDateTimeComponentSerializer : kotlinx.serialization/KSerializer { // kotlinx.datetime.serializers/LocalDateTimeComponentSerializer|null[0] + final val descriptor // kotlinx.datetime.serializers/LocalDateTimeComponentSerializer.descriptor|{}descriptor[0] + final fun (): kotlinx.serialization.descriptors/SerialDescriptor // kotlinx.datetime.serializers/LocalDateTimeComponentSerializer.descriptor.|(){}[0] + + final fun deserialize(kotlinx.serialization.encoding/Decoder): kotlinx.datetime/LocalDateTime // kotlinx.datetime.serializers/LocalDateTimeComponentSerializer.deserialize|deserialize(kotlinx.serialization.encoding.Decoder){}[0] + final fun serialize(kotlinx.serialization.encoding/Encoder, kotlinx.datetime/LocalDateTime) // kotlinx.datetime.serializers/LocalDateTimeComponentSerializer.serialize|serialize(kotlinx.serialization.encoding.Encoder;kotlinx.datetime.LocalDateTime){}[0] +} + +final object kotlinx.datetime.serializers/LocalDateTimeIso8601Serializer : kotlinx.serialization/KSerializer { // kotlinx.datetime.serializers/LocalDateTimeIso8601Serializer|null[0] + final val descriptor // kotlinx.datetime.serializers/LocalDateTimeIso8601Serializer.descriptor|{}descriptor[0] + final fun (): kotlinx.serialization.descriptors/SerialDescriptor // kotlinx.datetime.serializers/LocalDateTimeIso8601Serializer.descriptor.|(){}[0] + + final fun deserialize(kotlinx.serialization.encoding/Decoder): kotlinx.datetime/LocalDateTime // kotlinx.datetime.serializers/LocalDateTimeIso8601Serializer.deserialize|deserialize(kotlinx.serialization.encoding.Decoder){}[0] + final fun serialize(kotlinx.serialization.encoding/Encoder, kotlinx.datetime/LocalDateTime) // kotlinx.datetime.serializers/LocalDateTimeIso8601Serializer.serialize|serialize(kotlinx.serialization.encoding.Encoder;kotlinx.datetime.LocalDateTime){}[0] +} + +final object kotlinx.datetime.serializers/LocalTimeComponentSerializer : kotlinx.serialization/KSerializer { // kotlinx.datetime.serializers/LocalTimeComponentSerializer|null[0] + final val descriptor // kotlinx.datetime.serializers/LocalTimeComponentSerializer.descriptor|{}descriptor[0] + final fun (): kotlinx.serialization.descriptors/SerialDescriptor // kotlinx.datetime.serializers/LocalTimeComponentSerializer.descriptor.|(){}[0] + + final fun deserialize(kotlinx.serialization.encoding/Decoder): kotlinx.datetime/LocalTime // kotlinx.datetime.serializers/LocalTimeComponentSerializer.deserialize|deserialize(kotlinx.serialization.encoding.Decoder){}[0] + final fun serialize(kotlinx.serialization.encoding/Encoder, kotlinx.datetime/LocalTime) // kotlinx.datetime.serializers/LocalTimeComponentSerializer.serialize|serialize(kotlinx.serialization.encoding.Encoder;kotlinx.datetime.LocalTime){}[0] +} + +final object kotlinx.datetime.serializers/LocalTimeIso8601Serializer : kotlinx.serialization/KSerializer { // kotlinx.datetime.serializers/LocalTimeIso8601Serializer|null[0] + final val descriptor // kotlinx.datetime.serializers/LocalTimeIso8601Serializer.descriptor|{}descriptor[0] + final fun (): kotlinx.serialization.descriptors/SerialDescriptor // kotlinx.datetime.serializers/LocalTimeIso8601Serializer.descriptor.|(){}[0] + + final fun deserialize(kotlinx.serialization.encoding/Decoder): kotlinx.datetime/LocalTime // kotlinx.datetime.serializers/LocalTimeIso8601Serializer.deserialize|deserialize(kotlinx.serialization.encoding.Decoder){}[0] + final fun serialize(kotlinx.serialization.encoding/Encoder, kotlinx.datetime/LocalTime) // kotlinx.datetime.serializers/LocalTimeIso8601Serializer.serialize|serialize(kotlinx.serialization.encoding.Encoder;kotlinx.datetime.LocalTime){}[0] +} + +final object kotlinx.datetime.serializers/MonthBasedDateTimeUnitSerializer : kotlinx.serialization/KSerializer { // kotlinx.datetime.serializers/MonthBasedDateTimeUnitSerializer|null[0] + final val descriptor // kotlinx.datetime.serializers/MonthBasedDateTimeUnitSerializer.descriptor|{}descriptor[0] + final fun (): kotlinx.serialization.descriptors/SerialDescriptor // kotlinx.datetime.serializers/MonthBasedDateTimeUnitSerializer.descriptor.|(){}[0] + + final fun deserialize(kotlinx.serialization.encoding/Decoder): kotlinx.datetime/DateTimeUnit.MonthBased // kotlinx.datetime.serializers/MonthBasedDateTimeUnitSerializer.deserialize|deserialize(kotlinx.serialization.encoding.Decoder){}[0] + final fun serialize(kotlinx.serialization.encoding/Encoder, kotlinx.datetime/DateTimeUnit.MonthBased) // kotlinx.datetime.serializers/MonthBasedDateTimeUnitSerializer.serialize|serialize(kotlinx.serialization.encoding.Encoder;kotlinx.datetime.DateTimeUnit.MonthBased){}[0] +} + +final object kotlinx.datetime.serializers/MonthSerializer : kotlinx.serialization/KSerializer { // kotlinx.datetime.serializers/MonthSerializer|null[0] + final val descriptor // kotlinx.datetime.serializers/MonthSerializer.descriptor|{}descriptor[0] + final fun (): kotlinx.serialization.descriptors/SerialDescriptor // kotlinx.datetime.serializers/MonthSerializer.descriptor.|(){}[0] + + final fun deserialize(kotlinx.serialization.encoding/Decoder): kotlinx.datetime/Month // kotlinx.datetime.serializers/MonthSerializer.deserialize|deserialize(kotlinx.serialization.encoding.Decoder){}[0] + final fun serialize(kotlinx.serialization.encoding/Encoder, kotlinx.datetime/Month) // kotlinx.datetime.serializers/MonthSerializer.serialize|serialize(kotlinx.serialization.encoding.Encoder;kotlinx.datetime.Month){}[0] +} + +final object kotlinx.datetime.serializers/TimeBasedDateTimeUnitSerializer : kotlinx.serialization/KSerializer { // kotlinx.datetime.serializers/TimeBasedDateTimeUnitSerializer|null[0] + final val descriptor // kotlinx.datetime.serializers/TimeBasedDateTimeUnitSerializer.descriptor|{}descriptor[0] + final fun (): kotlinx.serialization.descriptors/SerialDescriptor // kotlinx.datetime.serializers/TimeBasedDateTimeUnitSerializer.descriptor.|(){}[0] + + final fun deserialize(kotlinx.serialization.encoding/Decoder): kotlinx.datetime/DateTimeUnit.TimeBased // kotlinx.datetime.serializers/TimeBasedDateTimeUnitSerializer.deserialize|deserialize(kotlinx.serialization.encoding.Decoder){}[0] + final fun serialize(kotlinx.serialization.encoding/Encoder, kotlinx.datetime/DateTimeUnit.TimeBased) // kotlinx.datetime.serializers/TimeBasedDateTimeUnitSerializer.serialize|serialize(kotlinx.serialization.encoding.Encoder;kotlinx.datetime.DateTimeUnit.TimeBased){}[0] +} + +final object kotlinx.datetime.serializers/TimeZoneSerializer : kotlinx.serialization/KSerializer { // kotlinx.datetime.serializers/TimeZoneSerializer|null[0] + final val descriptor // kotlinx.datetime.serializers/TimeZoneSerializer.descriptor|{}descriptor[0] + final fun (): kotlinx.serialization.descriptors/SerialDescriptor // kotlinx.datetime.serializers/TimeZoneSerializer.descriptor.|(){}[0] + + final fun deserialize(kotlinx.serialization.encoding/Decoder): kotlinx.datetime/TimeZone // kotlinx.datetime.serializers/TimeZoneSerializer.deserialize|deserialize(kotlinx.serialization.encoding.Decoder){}[0] + final fun serialize(kotlinx.serialization.encoding/Encoder, kotlinx.datetime/TimeZone) // kotlinx.datetime.serializers/TimeZoneSerializer.serialize|serialize(kotlinx.serialization.encoding.Encoder;kotlinx.datetime.TimeZone){}[0] +} + +final object kotlinx.datetime.serializers/UtcOffsetSerializer : kotlinx.serialization/KSerializer { // kotlinx.datetime.serializers/UtcOffsetSerializer|null[0] + final val descriptor // kotlinx.datetime.serializers/UtcOffsetSerializer.descriptor|{}descriptor[0] + final fun (): kotlinx.serialization.descriptors/SerialDescriptor // kotlinx.datetime.serializers/UtcOffsetSerializer.descriptor.|(){}[0] + + final fun deserialize(kotlinx.serialization.encoding/Decoder): kotlinx.datetime/UtcOffset // kotlinx.datetime.serializers/UtcOffsetSerializer.deserialize|deserialize(kotlinx.serialization.encoding.Decoder){}[0] + final fun serialize(kotlinx.serialization.encoding/Encoder, kotlinx.datetime/UtcOffset) // kotlinx.datetime.serializers/UtcOffsetSerializer.serialize|serialize(kotlinx.serialization.encoding.Encoder;kotlinx.datetime.UtcOffset){}[0] +} + +final val kotlinx.datetime/isDistantFuture // kotlinx.datetime/isDistantFuture|@kotlinx.datetime.Instant{}isDistantFuture[0] + final fun (kotlinx.datetime/Instant).(): kotlin/Boolean // kotlinx.datetime/isDistantFuture.|@kotlinx.datetime.Instant(){}[0] +final val kotlinx.datetime/isDistantPast // kotlinx.datetime/isDistantPast|@kotlinx.datetime.Instant{}isDistantPast[0] + final fun (kotlinx.datetime/Instant).(): kotlin/Boolean // kotlinx.datetime/isDistantPast.|@kotlinx.datetime.Instant(){}[0] +final val kotlinx.datetime/isoDayNumber // kotlinx.datetime/isoDayNumber|@kotlinx.datetime.DayOfWeek{}isoDayNumber[0] + final fun (kotlinx.datetime/DayOfWeek).(): kotlin/Int // kotlinx.datetime/isoDayNumber.|@kotlinx.datetime.DayOfWeek(){}[0] +final val kotlinx.datetime/number // kotlinx.datetime/number|@kotlinx.datetime.Month{}number[0] + final fun (kotlinx.datetime/Month).(): kotlin/Int // kotlinx.datetime/number.|@kotlinx.datetime.Month(){}[0] + +final fun (kotlin.time/Duration).kotlinx.datetime/toDateTimePeriod(): kotlinx.datetime/DateTimePeriod // kotlinx.datetime/toDateTimePeriod|toDateTimePeriod@kotlin.time.Duration(){}[0] +final fun (kotlin/String).kotlinx.datetime/toDatePeriod(): kotlinx.datetime/DatePeriod // kotlinx.datetime/toDatePeriod|toDatePeriod@kotlin.String(){}[0] +final fun (kotlin/String).kotlinx.datetime/toDateTimePeriod(): kotlinx.datetime/DateTimePeriod // kotlinx.datetime/toDateTimePeriod|toDateTimePeriod@kotlin.String(){}[0] +final fun (kotlin/String).kotlinx.datetime/toInstant(): kotlinx.datetime/Instant // kotlinx.datetime/toInstant|toInstant@kotlin.String(){}[0] +final fun (kotlin/String).kotlinx.datetime/toLocalDate(): kotlinx.datetime/LocalDate // kotlinx.datetime/toLocalDate|toLocalDate@kotlin.String(){}[0] +final fun (kotlin/String).kotlinx.datetime/toLocalDateTime(): kotlinx.datetime/LocalDateTime // kotlinx.datetime/toLocalDateTime|toLocalDateTime@kotlin.String(){}[0] +final fun (kotlin/String).kotlinx.datetime/toLocalTime(): kotlinx.datetime/LocalTime // kotlinx.datetime/toLocalTime|toLocalTime@kotlin.String(){}[0] +final fun (kotlinx.datetime.format/DateTimeComponents.Companion).kotlinx.datetime.format/parse(kotlin/CharSequence, kotlinx.datetime.format/DateTimeFormat): kotlinx.datetime.format/DateTimeComponents // kotlinx.datetime.format/parse|parse@kotlinx.datetime.format.DateTimeComponents.Companion(kotlin.CharSequence;kotlinx.datetime.format.DateTimeFormat){}[0] +final fun (kotlinx.datetime.format/DateTimeFormat).kotlinx.datetime.format/format(kotlin/Function1): kotlin/String // kotlinx.datetime.format/format|format@kotlinx.datetime.format.DateTimeFormat(kotlin.Function1){}[0] +final fun (kotlinx.datetime.format/DateTimeFormatBuilder).kotlinx.datetime.format/byUnicodePattern(kotlin/String) // kotlinx.datetime.format/byUnicodePattern|byUnicodePattern@kotlinx.datetime.format.DateTimeFormatBuilder(kotlin.String){}[0] +final fun (kotlinx.datetime.format/DateTimeFormatBuilder).kotlinx.datetime.format/char(kotlin/Char) // kotlinx.datetime.format/char|char@kotlinx.datetime.format.DateTimeFormatBuilder(kotlin.Char){}[0] +final fun (kotlinx.datetime/Clock).kotlinx.datetime/asTimeSource(): kotlin.time/TimeSource.WithComparableMarks // kotlinx.datetime/asTimeSource|asTimeSource@kotlinx.datetime.Clock(){}[0] +final fun (kotlinx.datetime/Clock).kotlinx.datetime/todayAt(kotlinx.datetime/TimeZone): kotlinx.datetime/LocalDate // kotlinx.datetime/todayAt|todayAt@kotlinx.datetime.Clock(kotlinx.datetime.TimeZone){}[0] +final fun (kotlinx.datetime/Clock).kotlinx.datetime/todayIn(kotlinx.datetime/TimeZone): kotlinx.datetime/LocalDate // kotlinx.datetime/todayIn|todayIn@kotlinx.datetime.Clock(kotlinx.datetime.TimeZone){}[0] +final fun (kotlinx.datetime/DatePeriod).kotlinx.datetime/plus(kotlinx.datetime/DatePeriod): kotlinx.datetime/DatePeriod // kotlinx.datetime/plus|plus@kotlinx.datetime.DatePeriod(kotlinx.datetime.DatePeriod){}[0] +final fun (kotlinx.datetime/DateTimePeriod).kotlinx.datetime/plus(kotlinx.datetime/DateTimePeriod): kotlinx.datetime/DateTimePeriod // kotlinx.datetime/plus|plus@kotlinx.datetime.DateTimePeriod(kotlinx.datetime.DateTimePeriod){}[0] +final fun (kotlinx.datetime/Instant).kotlinx.datetime/daysUntil(kotlinx.datetime/Instant, kotlinx.datetime/TimeZone): kotlin/Int // kotlinx.datetime/daysUntil|daysUntil@kotlinx.datetime.Instant(kotlinx.datetime.Instant;kotlinx.datetime.TimeZone){}[0] +final fun (kotlinx.datetime/Instant).kotlinx.datetime/format(kotlinx.datetime.format/DateTimeFormat, kotlinx.datetime/UtcOffset =...): kotlin/String // kotlinx.datetime/format|format@kotlinx.datetime.Instant(kotlinx.datetime.format.DateTimeFormat;kotlinx.datetime.UtcOffset){}[0] +final fun (kotlinx.datetime/Instant).kotlinx.datetime/minus(kotlin/Int, kotlinx.datetime/DateTimeUnit, kotlinx.datetime/TimeZone): kotlinx.datetime/Instant // kotlinx.datetime/minus|minus@kotlinx.datetime.Instant(kotlin.Int;kotlinx.datetime.DateTimeUnit;kotlinx.datetime.TimeZone){}[0] +final fun (kotlinx.datetime/Instant).kotlinx.datetime/minus(kotlin/Int, kotlinx.datetime/DateTimeUnit.TimeBased): kotlinx.datetime/Instant // kotlinx.datetime/minus|minus@kotlinx.datetime.Instant(kotlin.Int;kotlinx.datetime.DateTimeUnit.TimeBased){}[0] +final fun (kotlinx.datetime/Instant).kotlinx.datetime/minus(kotlin/Long, kotlinx.datetime/DateTimeUnit, kotlinx.datetime/TimeZone): kotlinx.datetime/Instant // kotlinx.datetime/minus|minus@kotlinx.datetime.Instant(kotlin.Long;kotlinx.datetime.DateTimeUnit;kotlinx.datetime.TimeZone){}[0] +final fun (kotlinx.datetime/Instant).kotlinx.datetime/minus(kotlin/Long, kotlinx.datetime/DateTimeUnit.TimeBased): kotlinx.datetime/Instant // kotlinx.datetime/minus|minus@kotlinx.datetime.Instant(kotlin.Long;kotlinx.datetime.DateTimeUnit.TimeBased){}[0] +final fun (kotlinx.datetime/Instant).kotlinx.datetime/minus(kotlinx.datetime/DateTimePeriod, kotlinx.datetime/TimeZone): kotlinx.datetime/Instant // kotlinx.datetime/minus|minus@kotlinx.datetime.Instant(kotlinx.datetime.DateTimePeriod;kotlinx.datetime.TimeZone){}[0] +final fun (kotlinx.datetime/Instant).kotlinx.datetime/minus(kotlinx.datetime/DateTimeUnit, kotlinx.datetime/TimeZone): kotlinx.datetime/Instant // kotlinx.datetime/minus|minus@kotlinx.datetime.Instant(kotlinx.datetime.DateTimeUnit;kotlinx.datetime.TimeZone){}[0] +final fun (kotlinx.datetime/Instant).kotlinx.datetime/minus(kotlinx.datetime/DateTimeUnit.TimeBased): kotlinx.datetime/Instant // kotlinx.datetime/minus|minus@kotlinx.datetime.Instant(kotlinx.datetime.DateTimeUnit.TimeBased){}[0] +final fun (kotlinx.datetime/Instant).kotlinx.datetime/minus(kotlinx.datetime/Instant, kotlinx.datetime/DateTimeUnit, kotlinx.datetime/TimeZone): kotlin/Long // kotlinx.datetime/minus|minus@kotlinx.datetime.Instant(kotlinx.datetime.Instant;kotlinx.datetime.DateTimeUnit;kotlinx.datetime.TimeZone){}[0] +final fun (kotlinx.datetime/Instant).kotlinx.datetime/minus(kotlinx.datetime/Instant, kotlinx.datetime/DateTimeUnit.TimeBased): kotlin/Long // kotlinx.datetime/minus|minus@kotlinx.datetime.Instant(kotlinx.datetime.Instant;kotlinx.datetime.DateTimeUnit.TimeBased){}[0] +final fun (kotlinx.datetime/Instant).kotlinx.datetime/minus(kotlinx.datetime/Instant, kotlinx.datetime/TimeZone): kotlinx.datetime/DateTimePeriod // kotlinx.datetime/minus|minus@kotlinx.datetime.Instant(kotlinx.datetime.Instant;kotlinx.datetime.TimeZone){}[0] +final fun (kotlinx.datetime/Instant).kotlinx.datetime/monthsUntil(kotlinx.datetime/Instant, kotlinx.datetime/TimeZone): kotlin/Int // kotlinx.datetime/monthsUntil|monthsUntil@kotlinx.datetime.Instant(kotlinx.datetime.Instant;kotlinx.datetime.TimeZone){}[0] +final fun (kotlinx.datetime/Instant).kotlinx.datetime/offsetIn(kotlinx.datetime/TimeZone): kotlinx.datetime/UtcOffset // kotlinx.datetime/offsetIn|offsetIn@kotlinx.datetime.Instant(kotlinx.datetime.TimeZone){}[0] +final fun (kotlinx.datetime/Instant).kotlinx.datetime/periodUntil(kotlinx.datetime/Instant, kotlinx.datetime/TimeZone): kotlinx.datetime/DateTimePeriod // kotlinx.datetime/periodUntil|periodUntil@kotlinx.datetime.Instant(kotlinx.datetime.Instant;kotlinx.datetime.TimeZone){}[0] +final fun (kotlinx.datetime/Instant).kotlinx.datetime/plus(kotlin/Int, kotlinx.datetime/DateTimeUnit, kotlinx.datetime/TimeZone): kotlinx.datetime/Instant // kotlinx.datetime/plus|plus@kotlinx.datetime.Instant(kotlin.Int;kotlinx.datetime.DateTimeUnit;kotlinx.datetime.TimeZone){}[0] +final fun (kotlinx.datetime/Instant).kotlinx.datetime/plus(kotlin/Int, kotlinx.datetime/DateTimeUnit.TimeBased): kotlinx.datetime/Instant // kotlinx.datetime/plus|plus@kotlinx.datetime.Instant(kotlin.Int;kotlinx.datetime.DateTimeUnit.TimeBased){}[0] +final fun (kotlinx.datetime/Instant).kotlinx.datetime/plus(kotlin/Long, kotlinx.datetime/DateTimeUnit, kotlinx.datetime/TimeZone): kotlinx.datetime/Instant // kotlinx.datetime/plus|plus@kotlinx.datetime.Instant(kotlin.Long;kotlinx.datetime.DateTimeUnit;kotlinx.datetime.TimeZone){}[0] +final fun (kotlinx.datetime/Instant).kotlinx.datetime/plus(kotlin/Long, kotlinx.datetime/DateTimeUnit.TimeBased): kotlinx.datetime/Instant // kotlinx.datetime/plus|plus@kotlinx.datetime.Instant(kotlin.Long;kotlinx.datetime.DateTimeUnit.TimeBased){}[0] +final fun (kotlinx.datetime/Instant).kotlinx.datetime/plus(kotlinx.datetime/DateTimePeriod, kotlinx.datetime/TimeZone): kotlinx.datetime/Instant // kotlinx.datetime/plus|plus@kotlinx.datetime.Instant(kotlinx.datetime.DateTimePeriod;kotlinx.datetime.TimeZone){}[0] +final fun (kotlinx.datetime/Instant).kotlinx.datetime/plus(kotlinx.datetime/DateTimeUnit, kotlinx.datetime/TimeZone): kotlinx.datetime/Instant // kotlinx.datetime/plus|plus@kotlinx.datetime.Instant(kotlinx.datetime.DateTimeUnit;kotlinx.datetime.TimeZone){}[0] +final fun (kotlinx.datetime/Instant).kotlinx.datetime/plus(kotlinx.datetime/DateTimeUnit.TimeBased): kotlinx.datetime/Instant // kotlinx.datetime/plus|plus@kotlinx.datetime.Instant(kotlinx.datetime.DateTimeUnit.TimeBased){}[0] +final fun (kotlinx.datetime/Instant).kotlinx.datetime/toLocalDateTime(kotlinx.datetime/TimeZone): kotlinx.datetime/LocalDateTime // kotlinx.datetime/toLocalDateTime|toLocalDateTime@kotlinx.datetime.Instant(kotlinx.datetime.TimeZone){}[0] +final fun (kotlinx.datetime/Instant).kotlinx.datetime/until(kotlinx.datetime/Instant, kotlinx.datetime/DateTimeUnit, kotlinx.datetime/TimeZone): kotlin/Long // kotlinx.datetime/until|until@kotlinx.datetime.Instant(kotlinx.datetime.Instant;kotlinx.datetime.DateTimeUnit;kotlinx.datetime.TimeZone){}[0] +final fun (kotlinx.datetime/Instant).kotlinx.datetime/until(kotlinx.datetime/Instant, kotlinx.datetime/DateTimeUnit.TimeBased): kotlin/Long // kotlinx.datetime/until|until@kotlinx.datetime.Instant(kotlinx.datetime.Instant;kotlinx.datetime.DateTimeUnit.TimeBased){}[0] +final fun (kotlinx.datetime/Instant).kotlinx.datetime/yearsUntil(kotlinx.datetime/Instant, kotlinx.datetime/TimeZone): kotlin/Int // kotlinx.datetime/yearsUntil|yearsUntil@kotlinx.datetime.Instant(kotlinx.datetime.Instant;kotlinx.datetime.TimeZone){}[0] +final fun (kotlinx.datetime/LocalDate).kotlinx.datetime/atStartOfDayIn(kotlinx.datetime/TimeZone): kotlinx.datetime/Instant // kotlinx.datetime/atStartOfDayIn|atStartOfDayIn@kotlinx.datetime.LocalDate(kotlinx.datetime.TimeZone){}[0] +final fun (kotlinx.datetime/LocalDate).kotlinx.datetime/atTime(kotlin/Int, kotlin/Int, kotlin/Int =..., kotlin/Int =...): kotlinx.datetime/LocalDateTime // kotlinx.datetime/atTime|atTime@kotlinx.datetime.LocalDate(kotlin.Int;kotlin.Int;kotlin.Int;kotlin.Int){}[0] +final fun (kotlinx.datetime/LocalDate).kotlinx.datetime/atTime(kotlinx.datetime/LocalTime): kotlinx.datetime/LocalDateTime // kotlinx.datetime/atTime|atTime@kotlinx.datetime.LocalDate(kotlinx.datetime.LocalTime){}[0] +final fun (kotlinx.datetime/LocalDate).kotlinx.datetime/daysUntil(kotlinx.datetime/LocalDate): kotlin/Int // kotlinx.datetime/daysUntil|daysUntil@kotlinx.datetime.LocalDate(kotlinx.datetime.LocalDate){}[0] +final fun (kotlinx.datetime/LocalDate).kotlinx.datetime/format(kotlinx.datetime.format/DateTimeFormat): kotlin/String // kotlinx.datetime/format|format@kotlinx.datetime.LocalDate(kotlinx.datetime.format.DateTimeFormat){}[0] +final fun (kotlinx.datetime/LocalDate).kotlinx.datetime/minus(kotlin/Int, kotlinx.datetime/DateTimeUnit.DateBased): kotlinx.datetime/LocalDate // kotlinx.datetime/minus|minus@kotlinx.datetime.LocalDate(kotlin.Int;kotlinx.datetime.DateTimeUnit.DateBased){}[0] +final fun (kotlinx.datetime/LocalDate).kotlinx.datetime/minus(kotlin/Long, kotlinx.datetime/DateTimeUnit.DateBased): kotlinx.datetime/LocalDate // kotlinx.datetime/minus|minus@kotlinx.datetime.LocalDate(kotlin.Long;kotlinx.datetime.DateTimeUnit.DateBased){}[0] +final fun (kotlinx.datetime/LocalDate).kotlinx.datetime/minus(kotlinx.datetime/DatePeriod): kotlinx.datetime/LocalDate // kotlinx.datetime/minus|minus@kotlinx.datetime.LocalDate(kotlinx.datetime.DatePeriod){}[0] +final fun (kotlinx.datetime/LocalDate).kotlinx.datetime/minus(kotlinx.datetime/DateTimeUnit.DateBased): kotlinx.datetime/LocalDate // kotlinx.datetime/minus|minus@kotlinx.datetime.LocalDate(kotlinx.datetime.DateTimeUnit.DateBased){}[0] +final fun (kotlinx.datetime/LocalDate).kotlinx.datetime/minus(kotlinx.datetime/LocalDate): kotlinx.datetime/DatePeriod // kotlinx.datetime/minus|minus@kotlinx.datetime.LocalDate(kotlinx.datetime.LocalDate){}[0] +final fun (kotlinx.datetime/LocalDate).kotlinx.datetime/monthsUntil(kotlinx.datetime/LocalDate): kotlin/Int // kotlinx.datetime/monthsUntil|monthsUntil@kotlinx.datetime.LocalDate(kotlinx.datetime.LocalDate){}[0] +final fun (kotlinx.datetime/LocalDate).kotlinx.datetime/periodUntil(kotlinx.datetime/LocalDate): kotlinx.datetime/DatePeriod // kotlinx.datetime/periodUntil|periodUntil@kotlinx.datetime.LocalDate(kotlinx.datetime.LocalDate){}[0] +final fun (kotlinx.datetime/LocalDate).kotlinx.datetime/plus(kotlin/Int, kotlinx.datetime/DateTimeUnit.DateBased): kotlinx.datetime/LocalDate // kotlinx.datetime/plus|plus@kotlinx.datetime.LocalDate(kotlin.Int;kotlinx.datetime.DateTimeUnit.DateBased){}[0] +final fun (kotlinx.datetime/LocalDate).kotlinx.datetime/plus(kotlin/Long, kotlinx.datetime/DateTimeUnit.DateBased): kotlinx.datetime/LocalDate // kotlinx.datetime/plus|plus@kotlinx.datetime.LocalDate(kotlin.Long;kotlinx.datetime.DateTimeUnit.DateBased){}[0] +final fun (kotlinx.datetime/LocalDate).kotlinx.datetime/plus(kotlinx.datetime/DatePeriod): kotlinx.datetime/LocalDate // kotlinx.datetime/plus|plus@kotlinx.datetime.LocalDate(kotlinx.datetime.DatePeriod){}[0] +final fun (kotlinx.datetime/LocalDate).kotlinx.datetime/plus(kotlinx.datetime/DateTimeUnit.DateBased): kotlinx.datetime/LocalDate // kotlinx.datetime/plus|plus@kotlinx.datetime.LocalDate(kotlinx.datetime.DateTimeUnit.DateBased){}[0] +final fun (kotlinx.datetime/LocalDate).kotlinx.datetime/until(kotlinx.datetime/LocalDate, kotlinx.datetime/DateTimeUnit.DateBased): kotlin/Long // kotlinx.datetime/until|until@kotlinx.datetime.LocalDate(kotlinx.datetime.LocalDate;kotlinx.datetime.DateTimeUnit.DateBased){}[0] +final fun (kotlinx.datetime/LocalDate).kotlinx.datetime/yearsUntil(kotlinx.datetime/LocalDate): kotlin/Int // kotlinx.datetime/yearsUntil|yearsUntil@kotlinx.datetime.LocalDate(kotlinx.datetime.LocalDate){}[0] +final fun (kotlinx.datetime/LocalDateTime).kotlinx.datetime/format(kotlinx.datetime.format/DateTimeFormat): kotlin/String // kotlinx.datetime/format|format@kotlinx.datetime.LocalDateTime(kotlinx.datetime.format.DateTimeFormat){}[0] +final fun (kotlinx.datetime/LocalDateTime).kotlinx.datetime/toInstant(kotlinx.datetime/TimeZone): kotlinx.datetime/Instant // kotlinx.datetime/toInstant|toInstant@kotlinx.datetime.LocalDateTime(kotlinx.datetime.TimeZone){}[0] +final fun (kotlinx.datetime/LocalDateTime).kotlinx.datetime/toInstant(kotlinx.datetime/UtcOffset): kotlinx.datetime/Instant // kotlinx.datetime/toInstant|toInstant@kotlinx.datetime.LocalDateTime(kotlinx.datetime.UtcOffset){}[0] +final fun (kotlinx.datetime/LocalTime).kotlinx.datetime/atDate(kotlin/Int, kotlin/Int, kotlin/Int =...): kotlinx.datetime/LocalDateTime // kotlinx.datetime/atDate|atDate@kotlinx.datetime.LocalTime(kotlin.Int;kotlin.Int;kotlin.Int){}[0] +final fun (kotlinx.datetime/LocalTime).kotlinx.datetime/atDate(kotlin/Int, kotlinx.datetime/Month, kotlin/Int =...): kotlinx.datetime/LocalDateTime // kotlinx.datetime/atDate|atDate@kotlinx.datetime.LocalTime(kotlin.Int;kotlinx.datetime.Month;kotlin.Int){}[0] +final fun (kotlinx.datetime/LocalTime).kotlinx.datetime/atDate(kotlinx.datetime/LocalDate): kotlinx.datetime/LocalDateTime // kotlinx.datetime/atDate|atDate@kotlinx.datetime.LocalTime(kotlinx.datetime.LocalDate){}[0] +final fun (kotlinx.datetime/LocalTime).kotlinx.datetime/format(kotlinx.datetime.format/DateTimeFormat): kotlin/String // kotlinx.datetime/format|format@kotlinx.datetime.LocalTime(kotlinx.datetime.format.DateTimeFormat){}[0] +final fun (kotlinx.datetime/TimeZone).kotlinx.datetime/offsetAt(kotlinx.datetime/Instant): kotlinx.datetime/UtcOffset // kotlinx.datetime/offsetAt|offsetAt@kotlinx.datetime.TimeZone(kotlinx.datetime.Instant){}[0] +final fun (kotlinx.datetime/UtcOffset).kotlinx.datetime/asTimeZone(): kotlinx.datetime/FixedOffsetTimeZone // kotlinx.datetime/asTimeZone|asTimeZone@kotlinx.datetime.UtcOffset(){}[0] +final fun (kotlinx.datetime/UtcOffset).kotlinx.datetime/format(kotlinx.datetime.format/DateTimeFormat): kotlin/String // kotlinx.datetime/format|format@kotlinx.datetime.UtcOffset(kotlinx.datetime.format.DateTimeFormat){}[0] +final fun <#A: kotlinx.datetime.format/DateTimeFormatBuilder> (#A).kotlinx.datetime.format/alternativeParsing(kotlin/Array>..., kotlin/Function1<#A, kotlin/Unit>) // kotlinx.datetime.format/alternativeParsing|alternativeParsing@0:0(kotlin.Array>...;kotlin.Function1<0:0,kotlin.Unit>){0§}[0] +final fun <#A: kotlinx.datetime.format/DateTimeFormatBuilder> (#A).kotlinx.datetime.format/optional(kotlin/String =..., kotlin/Function1<#A, kotlin/Unit>) // kotlinx.datetime.format/optional|optional@0:0(kotlin.String;kotlin.Function1<0:0,kotlin.Unit>){0§}[0] +final fun kotlinx.datetime/DateTimePeriod(kotlin/Int =..., kotlin/Int =..., kotlin/Int =..., kotlin/Int =..., kotlin/Int =..., kotlin/Int =..., kotlin/Long =...): kotlinx.datetime/DateTimePeriod // kotlinx.datetime/DateTimePeriod|DateTimePeriod(kotlin.Int;kotlin.Int;kotlin.Int;kotlin.Int;kotlin.Int;kotlin.Int;kotlin.Long){}[0] +final fun kotlinx.datetime/DayOfWeek(kotlin/Int): kotlinx.datetime/DayOfWeek // kotlinx.datetime/DayOfWeek|DayOfWeek(kotlin.Int){}[0] +final fun kotlinx.datetime/Month(kotlin/Int): kotlinx.datetime/Month // kotlinx.datetime/Month|Month(kotlin.Int){}[0] +final fun kotlinx.datetime/UtcOffset(): kotlinx.datetime/UtcOffset // kotlinx.datetime/UtcOffset|UtcOffset(){}[0] +final fun kotlinx.datetime/UtcOffset(kotlin/Int? =..., kotlin/Int? =..., kotlin/Int? =...): kotlinx.datetime/UtcOffset // kotlinx.datetime/UtcOffset|UtcOffset(kotlin.Int?;kotlin.Int?;kotlin.Int?){}[0] + +// Targets: [apple] +final fun (kotlinx.datetime/Instant).kotlinx.datetime/toNSDate(): platform.Foundation/NSDate // kotlinx.datetime/toNSDate|toNSDate@kotlinx.datetime.Instant(){}[0] + +// Targets: [apple] +final fun (kotlinx.datetime/LocalDate).kotlinx.datetime/toNSDateComponents(): platform.Foundation/NSDateComponents // kotlinx.datetime/toNSDateComponents|toNSDateComponents@kotlinx.datetime.LocalDate(){}[0] + +// Targets: [apple] +final fun (kotlinx.datetime/LocalDateTime).kotlinx.datetime/toNSDateComponents(): platform.Foundation/NSDateComponents // kotlinx.datetime/toNSDateComponents|toNSDateComponents@kotlinx.datetime.LocalDateTime(){}[0] + +// Targets: [apple] +final fun (kotlinx.datetime/TimeZone).kotlinx.datetime/toNSTimeZone(): platform.Foundation/NSTimeZone // kotlinx.datetime/toNSTimeZone|toNSTimeZone@kotlinx.datetime.TimeZone(){}[0] + +// Targets: [apple] +final fun (platform.Foundation/NSDate).kotlinx.datetime/toKotlinInstant(): kotlinx.datetime/Instant // kotlinx.datetime/toKotlinInstant|toKotlinInstant@platform.Foundation.NSDate(){}[0] + +// Targets: [apple] +final fun (platform.Foundation/NSTimeZone).kotlinx.datetime/toKotlinTimeZone(): kotlinx.datetime/TimeZone // kotlinx.datetime/toKotlinTimeZone|toKotlinTimeZone@platform.Foundation.NSTimeZone(){}[0] + +// Targets: [js] +abstract interface kotlinx.datetime.internal/InteropInterface // kotlinx.datetime.internal/InteropInterface|null[0] + +// Targets: [js] +final fun (kotlin.js/Date).kotlinx.datetime/toKotlinInstant(): kotlinx.datetime/Instant // kotlinx.datetime/toKotlinInstant|toKotlinInstant@kotlin.js.Date(){}[0] + +// Targets: [js] +final fun (kotlinx.datetime/Instant).kotlinx.datetime/toJSDate(): kotlin.js/Date // kotlinx.datetime/toJSDate|toJSDate@kotlinx.datetime.Instant(){}[0] + +// Targets: [wasmJs] +open annotation class kotlinx.datetime.internal/JsNonModule : kotlin/Annotation { // kotlinx.datetime.internal/JsNonModule|null[1] + constructor () // kotlinx.datetime.internal/JsNonModule.|(){}[1] +} + +// Targets: [wasmWasi] +abstract interface kotlinx.datetime.internal/TimeZonesProvider { // kotlinx.datetime.internal/TimeZonesProvider|null[0] + abstract fun getTimeZones(): kotlin.collections/Set // kotlinx.datetime.internal/TimeZonesProvider.getTimeZones|getTimeZones(){}[0] + abstract fun zoneDataByName(kotlin/String): kotlin/ByteArray // kotlinx.datetime.internal/TimeZonesProvider.zoneDataByName|zoneDataByName(kotlin.String){}[0] +} + +// Targets: [wasmWasi] +final fun kotlinx.datetime.internal/initializeTimeZonesProvider(kotlinx.datetime.internal/TimeZonesProvider) // kotlinx.datetime.internal/initializeTimeZonesProvider|initializeTimeZonesProvider(kotlinx.datetime.internal.TimeZonesProvider){}[0] diff --git a/fake-kotlinx-time/api/fake-kotlinx-time.api b/fake-kotlinx-time/api/fake-kotlinx-time.api new file mode 100644 index 000000000..c41e07cf5 --- /dev/null +++ b/fake-kotlinx-time/api/fake-kotlinx-time.api @@ -0,0 +1,49 @@ +public abstract interface class kotlinx/time/Clock { + public static final field Companion Lkotlinx/time/Clock$Companion; + public abstract fun now ()Lkotlinx/time/Instant; +} + +public final class kotlinx/time/Clock$Companion { +} + +public final class kotlinx/time/Clock$System : kotlinx/time/Clock { + public static final field INSTANCE Lkotlinx/time/Clock$System; + public fun now ()Lkotlinx/time/Instant; +} + +public final class kotlinx/time/ConvertersKt { + public static final fun toJavaInstant (Lkotlinx/time/Instant;)Ljava/time/Instant; + public static final fun toKotlinInstant (Ljava/time/Instant;)Lkotlinx/time/Instant; +} + +public final class kotlinx/time/Instant : java/lang/Comparable { + public static final field Companion Lkotlinx/time/Instant$Companion; + public synthetic fun compareTo (Ljava/lang/Object;)I + public fun compareTo (Lkotlinx/time/Instant;)I + public fun equals (Ljava/lang/Object;)Z + public final fun getEpochSeconds ()J + public final fun getNanosecondsOfSecond ()I + public fun hashCode ()I + public final fun minus-5sfh64U (Lkotlinx/time/Instant;)J + public final fun minus-LRDsOJo (J)Lkotlinx/time/Instant; + public final fun plus-LRDsOJo (J)Lkotlinx/time/Instant; + public final fun toEpochMilliseconds ()J + public fun toString ()Ljava/lang/String; +} + +public final class kotlinx/time/Instant$Companion { + public final fun fromEpochMilliseconds (J)Lkotlinx/time/Instant; + public final fun fromEpochSeconds (JI)Lkotlinx/time/Instant; + public final fun fromEpochSeconds (JJ)Lkotlinx/time/Instant; + public static synthetic fun fromEpochSeconds$default (Lkotlinx/time/Instant$Companion;JJILjava/lang/Object;)Lkotlinx/time/Instant; + public final fun getDISTANT_FUTURE ()Lkotlinx/time/Instant; + public final fun getDISTANT_PAST ()Lkotlinx/time/Instant; + public final fun now ()Lkotlinx/time/Instant; + public final fun parse (Ljava/lang/CharSequence;)Lkotlinx/time/Instant; +} + +public final class kotlinx/time/InstantKt { + public static final fun isDistantFuture (Lkotlinx/time/Instant;)Z + public static final fun isDistantPast (Lkotlinx/time/Instant;)Z +} + diff --git a/fake-kotlinx-time/api/fake-kotlinx-time.klib.api b/fake-kotlinx-time/api/fake-kotlinx-time.klib.api new file mode 100644 index 000000000..b8dda29b2 --- /dev/null +++ b/fake-kotlinx-time/api/fake-kotlinx-time.klib.api @@ -0,0 +1,57 @@ +// Klib ABI Dump +// Targets: [androidNativeArm32, androidNativeArm64, androidNativeX64, androidNativeX86, iosArm64, iosSimulatorArm64, iosX64, js, linuxArm32Hfp, linuxArm64, linuxX64, macosArm64, macosX64, mingwX64, tvosArm64, tvosSimulatorArm64, tvosX64, wasmJs, wasmWasi, watchosArm32, watchosArm64, watchosDeviceArm64, watchosSimulatorArm64, watchosX64] +// Rendering settings: +// - Signature version: 2 +// - Show manifest properties: true +// - Show declarations: true + +// Library unique name: +abstract interface kotlinx.time/Clock { // kotlinx.time/Clock|null[0] + abstract fun now(): kotlinx.time/Instant // kotlinx.time/Clock.now|now(){}[0] + + final object Companion // kotlinx.time/Clock.Companion|null[0] + + final object System : kotlinx.time/Clock { // kotlinx.time/Clock.System|null[0] + final fun now(): kotlinx.time/Instant // kotlinx.time/Clock.System.now|now(){}[0] + } +} + +final class kotlinx.time/Instant : kotlin/Comparable { // kotlinx.time/Instant|null[0] + final val epochSeconds // kotlinx.time/Instant.epochSeconds|{}epochSeconds[0] + final fun (): kotlin/Long // kotlinx.time/Instant.epochSeconds.|(){}[0] + final val nanosecondsOfSecond // kotlinx.time/Instant.nanosecondsOfSecond|{}nanosecondsOfSecond[0] + final fun (): kotlin/Int // kotlinx.time/Instant.nanosecondsOfSecond.|(){}[0] + + final fun compareTo(kotlinx.time/Instant): kotlin/Int // kotlinx.time/Instant.compareTo|compareTo(kotlinx.time.Instant){}[0] + final fun equals(kotlin/Any?): kotlin/Boolean // kotlinx.time/Instant.equals|equals(kotlin.Any?){}[0] + final fun hashCode(): kotlin/Int // kotlinx.time/Instant.hashCode|hashCode(){}[0] + final fun minus(kotlin.time/Duration): kotlinx.time/Instant // kotlinx.time/Instant.minus|minus(kotlin.time.Duration){}[0] + final fun minus(kotlinx.time/Instant): kotlin.time/Duration // kotlinx.time/Instant.minus|minus(kotlinx.time.Instant){}[0] + final fun plus(kotlin.time/Duration): kotlinx.time/Instant // kotlinx.time/Instant.plus|plus(kotlin.time.Duration){}[0] + final fun toEpochMilliseconds(): kotlin/Long // kotlinx.time/Instant.toEpochMilliseconds|toEpochMilliseconds(){}[0] + final fun toString(): kotlin/String // kotlinx.time/Instant.toString|toString(){}[0] + + final object Companion { // kotlinx.time/Instant.Companion|null[0] + final val DISTANT_FUTURE // kotlinx.time/Instant.Companion.DISTANT_FUTURE|{}DISTANT_FUTURE[0] + final fun (): kotlinx.time/Instant // kotlinx.time/Instant.Companion.DISTANT_FUTURE.|(){}[0] + final val DISTANT_PAST // kotlinx.time/Instant.Companion.DISTANT_PAST|{}DISTANT_PAST[0] + final fun (): kotlinx.time/Instant // kotlinx.time/Instant.Companion.DISTANT_PAST.|(){}[0] + + final fun fromEpochMilliseconds(kotlin/Long): kotlinx.time/Instant // kotlinx.time/Instant.Companion.fromEpochMilliseconds|fromEpochMilliseconds(kotlin.Long){}[0] + final fun fromEpochSeconds(kotlin/Long, kotlin/Int): kotlinx.time/Instant // kotlinx.time/Instant.Companion.fromEpochSeconds|fromEpochSeconds(kotlin.Long;kotlin.Int){}[0] + final fun fromEpochSeconds(kotlin/Long, kotlin/Long =...): kotlinx.time/Instant // kotlinx.time/Instant.Companion.fromEpochSeconds|fromEpochSeconds(kotlin.Long;kotlin.Long){}[0] + final fun now(): kotlinx.time/Instant // kotlinx.time/Instant.Companion.now|now(){}[0] + final fun parse(kotlin/CharSequence): kotlinx.time/Instant // kotlinx.time/Instant.Companion.parse|parse(kotlin.CharSequence){}[0] + } +} + +final val kotlinx.time/isDistantFuture // kotlinx.time/isDistantFuture|@kotlinx.time.Instant{}isDistantFuture[0] + final fun (kotlinx.time/Instant).(): kotlin/Boolean // kotlinx.time/isDistantFuture.|@kotlinx.time.Instant(){}[0] +final val kotlinx.time/isDistantPast // kotlinx.time/isDistantPast|@kotlinx.time.Instant{}isDistantPast[0] + final fun (kotlinx.time/Instant).(): kotlin/Boolean // kotlinx.time/isDistantPast.|@kotlinx.time.Instant(){}[0] + +// Targets: [js] +final fun (kotlin.js/Date).kotlinx.time/toKotlinInstant(): kotlinx.time/Instant // kotlinx.time/toKotlinInstant|toKotlinInstant@kotlin.js.Date(){}[0] + +// Targets: [js] +final fun (kotlinx.time/Instant).kotlinx.time/toJSDate(): kotlin.js/Date // kotlinx.time/toJSDate|toJSDate@kotlinx.time.Instant(){}[0] diff --git a/js-without-timezones/api/kotlinx-datetime-js-without-timezones.klib.api b/js-without-timezones/api/kotlinx-datetime-js-without-timezones.klib.api new file mode 100644 index 000000000..e69de29bb diff --git a/serialization/api/kotlinx-datetime-serialization.api b/serialization/api/kotlinx-datetime-serialization.api new file mode 100644 index 000000000..e69de29bb diff --git a/serialization/api/kotlinx-datetime-serialization.klib.api b/serialization/api/kotlinx-datetime-serialization.klib.api new file mode 100644 index 000000000..e69de29bb diff --git a/timezones/full/api/kotlinx-datetime-zoneinfo.klib.api b/timezones/full/api/kotlinx-datetime-zoneinfo.klib.api new file mode 100644 index 000000000..47db68f89 --- /dev/null +++ b/timezones/full/api/kotlinx-datetime-zoneinfo.klib.api @@ -0,0 +1,8 @@ +// Klib ABI Dump +// Targets: [wasmWasi] +// Rendering settings: +// - Signature version: 2 +// - Show manifest properties: true +// - Show declarations: true + +// Library unique name: From 114661dda52c66e4bba3407b2aff64676a73ec49 Mon Sep 17 00:00:00 2001 From: Dmitry Khalanskiy Date: Mon, 9 Dec 2024 16:05:06 +0100 Subject: [PATCH 7/7] Deprecate Instant while ensuring binary compatibility --- core/api/kotlinx-datetime.api | 50 ++ core/api/kotlinx-datetime.klib.api | 49 ++ core/build.gradle.kts | 1 + core/common/src/Clock.kt | 63 +- core/common/src/DeprecatedClock.kt | 144 ++++ core/common/src/DeprecatedInstant.kt | 645 ++++++++++++++++++ core/common/src/Instant.kt | 442 +----------- core/common/src/TimeZone.kt | 57 +- core/common/src/format/DateTimeComponents.kt | 19 +- ...ers.kt => DeprecatedInstantSerializers.kt} | 1 + core/common/test/ClockTimeSourceTest.kt | 4 +- .../test/DeprecatedClockTimeSourceTest.kt | 87 +++ core/common/test/DeprecatedInstantTest.kt | 640 +++++++++++++++++ core/common/test/InstantTest.kt | 107 +-- core/common/test/LocalDateTest.kt | 1 + core/common/test/LocalDateTimeTest.kt | 9 +- core/common/test/ReadmeTest.kt | 2 + core/common/test/TimeZoneTest.kt | 2 + .../format/DateTimeComponentsFormatTest.kt | 1 + .../test/format/DateTimeComponentsTest.kt | 1 + core/common/test/samples/ClockSamples.kt | 2 + core/common/test/samples/DayOfWeekSamples.kt | 1 + core/common/test/samples/InstantSamples.kt | 4 + core/common/test/samples/MonthSamples.kt | 1 + core/common/test/samples/TimeZoneSamples.kt | 2 + .../format/DateTimeComponentsSamples.kt | 1 + core/commonJs/src/internal/Platform.kt | 4 +- core/commonJs/test/JsJodaTimezoneTest.kt | 1 + core/commonKotlin/src/DeprecatedInstant.kt | 288 ++++++++ core/commonKotlin/src/Instant.kt | 451 +----------- core/commonKotlin/src/TimeZone.kt | 24 +- core/commonKotlin/src/ZonedDateTime.kt | 4 +- .../commonKotlin/src/internal/MonthDayTime.kt | 1 + core/commonKotlin/src/internal/OffsetInfo.kt | 2 +- core/commonKotlin/src/internal/Platform.kt | 3 - .../src/internal/RegionTimeZone.kt | 1 + .../src/internal/TimeZoneRules.kt | 5 +- .../test/InstantIsoStringsTest.kt | 420 ------------ .../test/ThreeTenBpInstantTest.kt | 96 --- core/commonKotlin/test/TimeZoneRulesTest.kt | 1 + core/darwin/src/Converters.kt | 13 +- core/darwin/test/ConvertersTest.kt | 2 + ...{Converters.kt => DeprecatedConverters.kt} | 1 + ...erTest.kt => DeprecatedJsConverterTest.kt} | 3 +- core/jvm/src/Converters.kt | 10 +- core/jvm/src/DeprecatedInstant.kt | 190 ++++++ core/jvm/src/Instant.kt | 105 +-- core/jvm/src/TimeZoneJvm.kt | 35 +- core/jvm/test/ConvertersTest.kt | 4 +- core/jvm/test/InstantParsing.kt | 2 + core/native/src/internal/Platform.kt | 23 - .../test/TimeZoneRulesCompleteTest.kt | 1 + core/wasmWasi/src/internal/Platform.kt | 39 -- .../windows/test/TimeZoneRulesCompleteTest.kt | 1 + ...atetime-js-test-without-timezones.klib.api | 0 .../test/TimezonesWithoutDatabaseTest.kt | 2 + ... => DeprecatedInstantSerializationTest.kt} | 4 +- serialization/common/test/IntegrationTest.kt | 4 +- 58 files changed, 2357 insertions(+), 1719 deletions(-) create mode 100644 core/common/src/DeprecatedClock.kt create mode 100644 core/common/src/DeprecatedInstant.kt rename core/common/src/serializers/{InstantSerializers.kt => DeprecatedInstantSerializers.kt} (98%) create mode 100644 core/common/test/DeprecatedClockTimeSourceTest.kt create mode 100644 core/common/test/DeprecatedInstantTest.kt create mode 100644 core/commonKotlin/src/DeprecatedInstant.kt delete mode 100644 core/commonKotlin/test/InstantIsoStringsTest.kt delete mode 100644 core/commonKotlin/test/ThreeTenBpInstantTest.kt rename core/js/src/{Converters.kt => DeprecatedConverters.kt} (94%) rename core/js/test/{JsConverterTest.kt => DeprecatedJsConverterTest.kt} (92%) create mode 100644 core/jvm/src/DeprecatedInstant.kt delete mode 100644 core/native/src/internal/Platform.kt delete mode 100644 core/wasmWasi/src/internal/Platform.kt create mode 100644 js-without-timezones/api/kotlinx-datetime-js-test-without-timezones.klib.api rename serialization/common/test/{InstantSerializationTest.kt => DeprecatedInstantSerializationTest.kt} (97%) diff --git a/core/api/kotlinx-datetime.api b/core/api/kotlinx-datetime.api index 829ee261a..fb1bb94a6 100644 --- a/core/api/kotlinx-datetime.api +++ b/core/api/kotlinx-datetime.api @@ -13,8 +13,11 @@ public final class kotlinx/datetime/Clock$System : kotlinx/datetime/Clock { public final class kotlinx/datetime/ClockKt { public static final fun asTimeSource (Lkotlinx/datetime/Clock;)Lkotlin/time/TimeSource$WithComparableMarks; + public static final fun asTimeSource (Lkotlinx/time/Clock;)Lkotlin/time/TimeSource$WithComparableMarks; public static final fun todayAt (Lkotlinx/datetime/Clock;Lkotlinx/datetime/TimeZone;)Lkotlinx/datetime/LocalDate; + public static final fun todayAt (Lkotlinx/time/Clock;Lkotlinx/datetime/TimeZone;)Lkotlinx/datetime/LocalDate; public static final fun todayIn (Lkotlinx/datetime/Clock;Lkotlinx/datetime/TimeZone;)Lkotlinx/datetime/LocalDate; + public static final fun todayIn (Lkotlinx/time/Clock;Lkotlinx/datetime/TimeZone;)Lkotlinx/datetime/LocalDate; } public final class kotlinx/datetime/ConvertersKt { @@ -171,6 +174,9 @@ public final class kotlinx/datetime/DayOfWeekKt { public static final fun getIsoDayNumber (Ljava/time/DayOfWeek;)I } +public final class kotlinx/datetime/DeprecationMarker { +} + public final class kotlinx/datetime/FixedOffsetTimeZone : kotlinx/datetime/TimeZone { public static final field Companion Lkotlinx/datetime/FixedOffsetTimeZone$Companion; public fun (Lkotlinx/datetime/UtcOffset;)V @@ -220,19 +226,30 @@ public final class kotlinx/datetime/Instant$Companion { public final class kotlinx/datetime/InstantJvmKt { public static final fun minus (Lkotlinx/datetime/Instant;ILkotlinx/datetime/DateTimeUnit;Lkotlinx/datetime/TimeZone;)Lkotlinx/datetime/Instant; + public static final fun minus (Lkotlinx/time/Instant;ILkotlinx/datetime/DateTimeUnit;Lkotlinx/datetime/TimeZone;)Lkotlinx/time/Instant; public static final fun periodUntil (Lkotlinx/datetime/Instant;Lkotlinx/datetime/Instant;Lkotlinx/datetime/TimeZone;)Lkotlinx/datetime/DateTimePeriod; + public static final fun periodUntil (Lkotlinx/time/Instant;Lkotlinx/time/Instant;Lkotlinx/datetime/TimeZone;)Lkotlinx/datetime/DateTimePeriod; public static final fun plus (Lkotlinx/datetime/Instant;ILkotlinx/datetime/DateTimeUnit;Lkotlinx/datetime/TimeZone;)Lkotlinx/datetime/Instant; public static final fun plus (Lkotlinx/datetime/Instant;JLkotlinx/datetime/DateTimeUnit$TimeBased;)Lkotlinx/datetime/Instant; public static final fun plus (Lkotlinx/datetime/Instant;JLkotlinx/datetime/DateTimeUnit;Lkotlinx/datetime/TimeZone;)Lkotlinx/datetime/Instant; public static final fun plus (Lkotlinx/datetime/Instant;Lkotlinx/datetime/DateTimePeriod;Lkotlinx/datetime/TimeZone;)Lkotlinx/datetime/Instant; public static final fun plus (Lkotlinx/datetime/Instant;Lkotlinx/datetime/DateTimeUnit;Lkotlinx/datetime/TimeZone;)Lkotlinx/datetime/Instant; + public static final fun plus (Lkotlinx/time/Instant;ILkotlinx/datetime/DateTimeUnit;Lkotlinx/datetime/TimeZone;)Lkotlinx/time/Instant; + public static final fun plus (Lkotlinx/time/Instant;JLkotlinx/datetime/DateTimeUnit$TimeBased;)Lkotlinx/time/Instant; + public static final fun plus (Lkotlinx/time/Instant;JLkotlinx/datetime/DateTimeUnit;Lkotlinx/datetime/TimeZone;)Lkotlinx/time/Instant; + public static final fun plus (Lkotlinx/time/Instant;Lkotlinx/datetime/DateTimePeriod;Lkotlinx/datetime/TimeZone;)Lkotlinx/time/Instant; + public static final fun plus (Lkotlinx/time/Instant;Lkotlinx/datetime/DateTimeUnit;Lkotlinx/datetime/TimeZone;)Lkotlinx/time/Instant; public static final fun until (Lkotlinx/datetime/Instant;Lkotlinx/datetime/Instant;Lkotlinx/datetime/DateTimeUnit;Lkotlinx/datetime/TimeZone;)J + public static final fun until (Lkotlinx/time/Instant;Lkotlinx/time/Instant;Lkotlinx/datetime/DateTimeUnit;Lkotlinx/datetime/TimeZone;)J } public final class kotlinx/datetime/InstantKt { public static final fun daysUntil (Lkotlinx/datetime/Instant;Lkotlinx/datetime/Instant;Lkotlinx/datetime/TimeZone;)I + public static final fun daysUntil (Lkotlinx/time/Instant;Lkotlinx/time/Instant;Lkotlinx/datetime/TimeZone;)I public static final fun format (Lkotlinx/datetime/Instant;Lkotlinx/datetime/format/DateTimeFormat;Lkotlinx/datetime/UtcOffset;)Ljava/lang/String; + public static final fun format (Lkotlinx/time/Instant;Lkotlinx/datetime/format/DateTimeFormat;Lkotlinx/datetime/UtcOffset;)Ljava/lang/String; public static synthetic fun format$default (Lkotlinx/datetime/Instant;Lkotlinx/datetime/format/DateTimeFormat;Lkotlinx/datetime/UtcOffset;ILjava/lang/Object;)Ljava/lang/String; + public static synthetic fun format$default (Lkotlinx/time/Instant;Lkotlinx/datetime/format/DateTimeFormat;Lkotlinx/datetime/UtcOffset;ILjava/lang/Object;)Ljava/lang/String; public static final fun isDistantFuture (Lkotlinx/datetime/Instant;)Z public static final fun isDistantPast (Lkotlinx/datetime/Instant;)Z public static final fun minus (Lkotlinx/datetime/Instant;ILkotlinx/datetime/DateTimeUnit$TimeBased;)Lkotlinx/datetime/Instant; @@ -244,12 +261,29 @@ public final class kotlinx/datetime/InstantKt { public static final fun minus (Lkotlinx/datetime/Instant;Lkotlinx/datetime/Instant;Lkotlinx/datetime/DateTimeUnit$TimeBased;)J public static final fun minus (Lkotlinx/datetime/Instant;Lkotlinx/datetime/Instant;Lkotlinx/datetime/DateTimeUnit;Lkotlinx/datetime/TimeZone;)J public static final fun minus (Lkotlinx/datetime/Instant;Lkotlinx/datetime/Instant;Lkotlinx/datetime/TimeZone;)Lkotlinx/datetime/DateTimePeriod; + public static final fun minus (Lkotlinx/time/Instant;ILkotlinx/datetime/DateTimeUnit$TimeBased;)Lkotlinx/time/Instant; + public static final fun minus (Lkotlinx/time/Instant;JLkotlinx/datetime/DateTimeUnit$TimeBased;)Lkotlinx/time/Instant; + public static final fun minus (Lkotlinx/time/Instant;JLkotlinx/datetime/DateTimeUnit;Lkotlinx/datetime/TimeZone;)Lkotlinx/time/Instant; + public static final fun minus (Lkotlinx/time/Instant;Lkotlinx/datetime/DateTimePeriod;Lkotlinx/datetime/TimeZone;)Lkotlinx/time/Instant; + public static final fun minus (Lkotlinx/time/Instant;Lkotlinx/datetime/DateTimeUnit$TimeBased;)Lkotlinx/time/Instant; + public static final fun minus (Lkotlinx/time/Instant;Lkotlinx/datetime/DateTimeUnit;Lkotlinx/datetime/TimeZone;)Lkotlinx/time/Instant; + public static final fun minus (Lkotlinx/time/Instant;Lkotlinx/time/Instant;Lkotlinx/datetime/DateTimeUnit$TimeBased;)J + public static final fun minus (Lkotlinx/time/Instant;Lkotlinx/time/Instant;Lkotlinx/datetime/DateTimeUnit;Lkotlinx/datetime/TimeZone;)J + public static final fun minus (Lkotlinx/time/Instant;Lkotlinx/time/Instant;Lkotlinx/datetime/TimeZone;)Lkotlinx/datetime/DateTimePeriod; public static final fun monthsUntil (Lkotlinx/datetime/Instant;Lkotlinx/datetime/Instant;Lkotlinx/datetime/TimeZone;)I + public static final fun monthsUntil (Lkotlinx/time/Instant;Lkotlinx/time/Instant;Lkotlinx/datetime/TimeZone;)I + public static final fun parse (Lkotlinx/time/Instant$Companion;Ljava/lang/CharSequence;Lkotlinx/datetime/format/DateTimeFormat;)Lkotlinx/time/Instant; public static final fun plus (Lkotlinx/datetime/Instant;ILkotlinx/datetime/DateTimeUnit$TimeBased;)Lkotlinx/datetime/Instant; public static final fun plus (Lkotlinx/datetime/Instant;Lkotlinx/datetime/DateTimeUnit$TimeBased;)Lkotlinx/datetime/Instant; + public static final fun plus (Lkotlinx/time/Instant;ILkotlinx/datetime/DateTimeUnit$TimeBased;)Lkotlinx/time/Instant; + public static final fun plus (Lkotlinx/time/Instant;Lkotlinx/datetime/DateTimeUnit$TimeBased;)Lkotlinx/time/Instant; + public static final fun toDeprecatedInstant (Lkotlinx/time/Instant;)Lkotlinx/datetime/Instant; public static final fun toInstant (Ljava/lang/String;)Lkotlinx/datetime/Instant; + public static final fun toNewInstant (Lkotlinx/datetime/Instant;)Lkotlinx/time/Instant; public static final fun until (Lkotlinx/datetime/Instant;Lkotlinx/datetime/Instant;Lkotlinx/datetime/DateTimeUnit$TimeBased;)J + public static final fun until (Lkotlinx/time/Instant;Lkotlinx/time/Instant;Lkotlinx/datetime/DateTimeUnit$TimeBased;)J public static final fun yearsUntil (Lkotlinx/datetime/Instant;Lkotlinx/datetime/Instant;Lkotlinx/datetime/TimeZone;)I + public static final fun yearsUntil (Lkotlinx/time/Instant;Lkotlinx/time/Instant;Lkotlinx/datetime/TimeZone;)I } public final class kotlinx/datetime/LocalDate : java/lang/Comparable { @@ -414,7 +448,10 @@ public class kotlinx/datetime/TimeZone { public final fun getId ()Ljava/lang/String; public fun hashCode ()I public final fun toInstant (Lkotlinx/datetime/LocalDateTime;)Lkotlinx/datetime/Instant; + public final fun toInstant (Lkotlinx/datetime/LocalDateTime;Lkotlinx/datetime/DeprecationMarker;)Lkotlinx/time/Instant; + public static synthetic fun toInstant$default (Lkotlinx/datetime/TimeZone;Lkotlinx/datetime/LocalDateTime;Lkotlinx/datetime/DeprecationMarker;ILjava/lang/Object;)Lkotlinx/time/Instant; public final fun toLocalDateTime (Lkotlinx/datetime/Instant;)Lkotlinx/datetime/LocalDateTime; + public final fun toLocalDateTime (Lkotlinx/time/Instant;)Lkotlinx/datetime/LocalDateTime; public fun toString ()Ljava/lang/String; } @@ -428,11 +465,21 @@ public final class kotlinx/datetime/TimeZone$Companion { public final class kotlinx/datetime/TimeZoneKt { public static final fun atStartOfDayIn (Lkotlinx/datetime/LocalDate;Lkotlinx/datetime/TimeZone;)Lkotlinx/datetime/Instant; + public static final fun atStartOfDayIn (Lkotlinx/datetime/LocalDate;Lkotlinx/datetime/TimeZone;Lkotlinx/datetime/DeprecationMarker;)Lkotlinx/time/Instant; + public static synthetic fun atStartOfDayIn$default (Lkotlinx/datetime/LocalDate;Lkotlinx/datetime/TimeZone;Lkotlinx/datetime/DeprecationMarker;ILjava/lang/Object;)Lkotlinx/time/Instant; public static final fun offsetAt (Lkotlinx/datetime/TimeZone;Lkotlinx/datetime/Instant;)Lkotlinx/datetime/UtcOffset; + public static final fun offsetAt (Lkotlinx/datetime/TimeZone;Lkotlinx/time/Instant;)Lkotlinx/datetime/UtcOffset; public static final fun offsetIn (Lkotlinx/datetime/Instant;Lkotlinx/datetime/TimeZone;)Lkotlinx/datetime/UtcOffset; + public static final fun offsetIn (Lkotlinx/time/Instant;Lkotlinx/datetime/TimeZone;)Lkotlinx/datetime/UtcOffset; public static final fun toInstant (Lkotlinx/datetime/LocalDateTime;Lkotlinx/datetime/TimeZone;)Lkotlinx/datetime/Instant; + public static final fun toInstant (Lkotlinx/datetime/LocalDateTime;Lkotlinx/datetime/TimeZone;Lkotlinx/datetime/DeprecationMarker;)Lkotlinx/time/Instant; public static final fun toInstant (Lkotlinx/datetime/LocalDateTime;Lkotlinx/datetime/UtcOffset;)Lkotlinx/datetime/Instant; + public static final fun toInstant (Lkotlinx/datetime/LocalDateTime;Lkotlinx/datetime/UtcOffset;Lkotlinx/datetime/DeprecationMarker;)Lkotlinx/time/Instant; + public static synthetic fun toInstant$default (Lkotlinx/datetime/LocalDateTime;Lkotlinx/datetime/TimeZone;Lkotlinx/datetime/DeprecationMarker;ILjava/lang/Object;)Lkotlinx/time/Instant; + public static synthetic fun toInstant$default (Lkotlinx/datetime/LocalDateTime;Lkotlinx/datetime/UtcOffset;Lkotlinx/datetime/DeprecationMarker;ILjava/lang/Object;)Lkotlinx/time/Instant; public static final fun toLocalDateTime (Lkotlinx/datetime/Instant;Lkotlinx/datetime/TimeZone;)Lkotlinx/datetime/LocalDateTime; + public static final fun toLocalDateTime (Lkotlinx/datetime/Instant;Lkotlinx/datetime/UtcOffset;)Lkotlinx/datetime/LocalDateTime; + public static final fun toLocalDateTime (Lkotlinx/time/Instant;Lkotlinx/datetime/TimeZone;)Lkotlinx/datetime/LocalDateTime; } public final class kotlinx/datetime/UtcOffset { @@ -504,6 +551,7 @@ public final class kotlinx/datetime/format/DateTimeComponents { public final fun setDateTime (Lkotlinx/datetime/LocalDateTime;)V public final fun setDateTimeOffset (Lkotlinx/datetime/Instant;Lkotlinx/datetime/UtcOffset;)V public final fun setDateTimeOffset (Lkotlinx/datetime/LocalDateTime;Lkotlinx/datetime/UtcOffset;)V + public final fun setDateTimeOffset (Lkotlinx/time/Instant;Lkotlinx/datetime/UtcOffset;)V public final fun setDayOfMonth (Ljava/lang/Integer;)V public final fun setDayOfWeek (Ljava/time/DayOfWeek;)V public final fun setDayOfYear (Ljava/lang/Integer;)V @@ -523,6 +571,8 @@ public final class kotlinx/datetime/format/DateTimeComponents { public final fun setTimeZoneId (Ljava/lang/String;)V public final fun setYear (Ljava/lang/Integer;)V public final fun toInstantUsingOffset ()Lkotlinx/datetime/Instant; + public final fun toInstantUsingOffset (Lkotlinx/datetime/DeprecationMarker;)Lkotlinx/time/Instant; + public static synthetic fun toInstantUsingOffset$default (Lkotlinx/datetime/format/DateTimeComponents;Lkotlinx/datetime/DeprecationMarker;ILjava/lang/Object;)Lkotlinx/time/Instant; public final fun toLocalDate ()Lkotlinx/datetime/LocalDate; public final fun toLocalDateTime ()Lkotlinx/datetime/LocalDateTime; public final fun toLocalTime ()Lkotlinx/datetime/LocalTime; diff --git a/core/api/kotlinx-datetime.klib.api b/core/api/kotlinx-datetime.klib.api index 8c66835f1..b18b01376 100644 --- a/core/api/kotlinx-datetime.klib.api +++ b/core/api/kotlinx-datetime.klib.api @@ -191,9 +191,11 @@ final class kotlinx.datetime.format/DateTimeComponents { // kotlinx.datetime.for final fun setDateTime(kotlinx.datetime/LocalDateTime) // kotlinx.datetime.format/DateTimeComponents.setDateTime|setDateTime(kotlinx.datetime.LocalDateTime){}[0] final fun setDateTimeOffset(kotlinx.datetime/Instant, kotlinx.datetime/UtcOffset) // kotlinx.datetime.format/DateTimeComponents.setDateTimeOffset|setDateTimeOffset(kotlinx.datetime.Instant;kotlinx.datetime.UtcOffset){}[0] final fun setDateTimeOffset(kotlinx.datetime/LocalDateTime, kotlinx.datetime/UtcOffset) // kotlinx.datetime.format/DateTimeComponents.setDateTimeOffset|setDateTimeOffset(kotlinx.datetime.LocalDateTime;kotlinx.datetime.UtcOffset){}[0] + final fun setDateTimeOffset(kotlinx.time/Instant, kotlinx.datetime/UtcOffset) // kotlinx.datetime.format/DateTimeComponents.setDateTimeOffset|setDateTimeOffset(kotlinx.time.Instant;kotlinx.datetime.UtcOffset){}[0] final fun setOffset(kotlinx.datetime/UtcOffset) // kotlinx.datetime.format/DateTimeComponents.setOffset|setOffset(kotlinx.datetime.UtcOffset){}[0] final fun setTime(kotlinx.datetime/LocalTime) // kotlinx.datetime.format/DateTimeComponents.setTime|setTime(kotlinx.datetime.LocalTime){}[0] final fun toInstantUsingOffset(): kotlinx.datetime/Instant // kotlinx.datetime.format/DateTimeComponents.toInstantUsingOffset|toInstantUsingOffset(){}[0] + final fun toInstantUsingOffset(kotlinx.datetime/DeprecationMarker =...): kotlinx.time/Instant // kotlinx.datetime.format/DateTimeComponents.toInstantUsingOffset|toInstantUsingOffset(kotlinx.datetime.DeprecationMarker){}[0] final fun toLocalDate(): kotlinx.datetime/LocalDate // kotlinx.datetime.format/DateTimeComponents.toLocalDate|toLocalDate(){}[0] final fun toLocalDateTime(): kotlinx.datetime/LocalDateTime // kotlinx.datetime.format/DateTimeComponents.toLocalDateTime|toLocalDateTime(){}[0] final fun toLocalTime(): kotlinx.datetime/LocalTime // kotlinx.datetime.format/DateTimeComponents.toLocalTime|toLocalTime(){}[0] @@ -276,6 +278,8 @@ final class kotlinx.datetime/DateTimeArithmeticException : kotlin/RuntimeExcepti constructor (kotlin/Throwable) // kotlinx.datetime/DateTimeArithmeticException.|(kotlin.Throwable){}[0] } +final class kotlinx.datetime/DeprecationMarker // kotlinx.datetime/DeprecationMarker|null[0] + final class kotlinx.datetime/FixedOffsetTimeZone : kotlinx.datetime/TimeZone { // kotlinx.datetime/FixedOffsetTimeZone|null[0] constructor (kotlinx.datetime/UtcOffset) // kotlinx.datetime/FixedOffsetTimeZone.|(kotlinx.datetime.UtcOffset){}[0] @@ -487,6 +491,8 @@ open class kotlinx.datetime/TimeZone { // kotlinx.datetime/TimeZone|null[0] final fun (kotlinx.datetime/Instant).toLocalDateTime(): kotlinx.datetime/LocalDateTime // kotlinx.datetime/TimeZone.toLocalDateTime|toLocalDateTime@kotlinx.datetime.Instant(){}[0] final fun (kotlinx.datetime/LocalDateTime).toInstant(): kotlinx.datetime/Instant // kotlinx.datetime/TimeZone.toInstant|toInstant@kotlinx.datetime.LocalDateTime(){}[0] + final fun (kotlinx.datetime/LocalDateTime).toInstant(kotlinx.datetime/DeprecationMarker =...): kotlinx.time/Instant // kotlinx.datetime/TimeZone.toInstant|toInstant@kotlinx.datetime.LocalDateTime(kotlinx.datetime.DeprecationMarker){}[0] + final fun (kotlinx.time/Instant).toLocalDateTime(): kotlinx.datetime/LocalDateTime // kotlinx.datetime/TimeZone.toLocalDateTime|toLocalDateTime@kotlinx.time.Instant(){}[0] open fun equals(kotlin/Any?): kotlin/Boolean // kotlinx.datetime/TimeZone.equals|equals(kotlin.Any?){}[0] open fun hashCode(): kotlin/Int // kotlinx.datetime/TimeZone.hashCode|hashCode(){}[0] open fun toString(): kotlin/String // kotlinx.datetime/TimeZone.toString|toString(){}[0] @@ -854,10 +860,13 @@ final fun (kotlinx.datetime/Instant).kotlinx.datetime/plus(kotlinx.datetime/Date final fun (kotlinx.datetime/Instant).kotlinx.datetime/plus(kotlinx.datetime/DateTimeUnit, kotlinx.datetime/TimeZone): kotlinx.datetime/Instant // kotlinx.datetime/plus|plus@kotlinx.datetime.Instant(kotlinx.datetime.DateTimeUnit;kotlinx.datetime.TimeZone){}[0] final fun (kotlinx.datetime/Instant).kotlinx.datetime/plus(kotlinx.datetime/DateTimeUnit.TimeBased): kotlinx.datetime/Instant // kotlinx.datetime/plus|plus@kotlinx.datetime.Instant(kotlinx.datetime.DateTimeUnit.TimeBased){}[0] final fun (kotlinx.datetime/Instant).kotlinx.datetime/toLocalDateTime(kotlinx.datetime/TimeZone): kotlinx.datetime/LocalDateTime // kotlinx.datetime/toLocalDateTime|toLocalDateTime@kotlinx.datetime.Instant(kotlinx.datetime.TimeZone){}[0] +final fun (kotlinx.datetime/Instant).kotlinx.datetime/toLocalDateTime(kotlinx.datetime/UtcOffset): kotlinx.datetime/LocalDateTime // kotlinx.datetime/toLocalDateTime|toLocalDateTime@kotlinx.datetime.Instant(kotlinx.datetime.UtcOffset){}[0] +final fun (kotlinx.datetime/Instant).kotlinx.datetime/toNewInstant(): kotlinx.time/Instant // kotlinx.datetime/toNewInstant|toNewInstant@kotlinx.datetime.Instant(){}[0] final fun (kotlinx.datetime/Instant).kotlinx.datetime/until(kotlinx.datetime/Instant, kotlinx.datetime/DateTimeUnit, kotlinx.datetime/TimeZone): kotlin/Long // kotlinx.datetime/until|until@kotlinx.datetime.Instant(kotlinx.datetime.Instant;kotlinx.datetime.DateTimeUnit;kotlinx.datetime.TimeZone){}[0] final fun (kotlinx.datetime/Instant).kotlinx.datetime/until(kotlinx.datetime/Instant, kotlinx.datetime/DateTimeUnit.TimeBased): kotlin/Long // kotlinx.datetime/until|until@kotlinx.datetime.Instant(kotlinx.datetime.Instant;kotlinx.datetime.DateTimeUnit.TimeBased){}[0] final fun (kotlinx.datetime/Instant).kotlinx.datetime/yearsUntil(kotlinx.datetime/Instant, kotlinx.datetime/TimeZone): kotlin/Int // kotlinx.datetime/yearsUntil|yearsUntil@kotlinx.datetime.Instant(kotlinx.datetime.Instant;kotlinx.datetime.TimeZone){}[0] final fun (kotlinx.datetime/LocalDate).kotlinx.datetime/atStartOfDayIn(kotlinx.datetime/TimeZone): kotlinx.datetime/Instant // kotlinx.datetime/atStartOfDayIn|atStartOfDayIn@kotlinx.datetime.LocalDate(kotlinx.datetime.TimeZone){}[0] +final fun (kotlinx.datetime/LocalDate).kotlinx.datetime/atStartOfDayIn(kotlinx.datetime/TimeZone, kotlinx.datetime/DeprecationMarker =...): kotlinx.time/Instant // kotlinx.datetime/atStartOfDayIn|atStartOfDayIn@kotlinx.datetime.LocalDate(kotlinx.datetime.TimeZone;kotlinx.datetime.DeprecationMarker){}[0] final fun (kotlinx.datetime/LocalDate).kotlinx.datetime/atTime(kotlin/Int, kotlin/Int, kotlin/Int =..., kotlin/Int =...): kotlinx.datetime/LocalDateTime // kotlinx.datetime/atTime|atTime@kotlinx.datetime.LocalDate(kotlin.Int;kotlin.Int;kotlin.Int;kotlin.Int){}[0] final fun (kotlinx.datetime/LocalDate).kotlinx.datetime/atTime(kotlinx.datetime/LocalTime): kotlinx.datetime/LocalDateTime // kotlinx.datetime/atTime|atTime@kotlinx.datetime.LocalDate(kotlinx.datetime.LocalTime){}[0] final fun (kotlinx.datetime/LocalDate).kotlinx.datetime/daysUntil(kotlinx.datetime/LocalDate): kotlin/Int // kotlinx.datetime/daysUntil|daysUntil@kotlinx.datetime.LocalDate(kotlinx.datetime.LocalDate){}[0] @@ -877,14 +886,48 @@ final fun (kotlinx.datetime/LocalDate).kotlinx.datetime/until(kotlinx.datetime/L final fun (kotlinx.datetime/LocalDate).kotlinx.datetime/yearsUntil(kotlinx.datetime/LocalDate): kotlin/Int // kotlinx.datetime/yearsUntil|yearsUntil@kotlinx.datetime.LocalDate(kotlinx.datetime.LocalDate){}[0] final fun (kotlinx.datetime/LocalDateTime).kotlinx.datetime/format(kotlinx.datetime.format/DateTimeFormat): kotlin/String // kotlinx.datetime/format|format@kotlinx.datetime.LocalDateTime(kotlinx.datetime.format.DateTimeFormat){}[0] final fun (kotlinx.datetime/LocalDateTime).kotlinx.datetime/toInstant(kotlinx.datetime/TimeZone): kotlinx.datetime/Instant // kotlinx.datetime/toInstant|toInstant@kotlinx.datetime.LocalDateTime(kotlinx.datetime.TimeZone){}[0] +final fun (kotlinx.datetime/LocalDateTime).kotlinx.datetime/toInstant(kotlinx.datetime/TimeZone, kotlinx.datetime/DeprecationMarker =...): kotlinx.time/Instant // kotlinx.datetime/toInstant|toInstant@kotlinx.datetime.LocalDateTime(kotlinx.datetime.TimeZone;kotlinx.datetime.DeprecationMarker){}[0] final fun (kotlinx.datetime/LocalDateTime).kotlinx.datetime/toInstant(kotlinx.datetime/UtcOffset): kotlinx.datetime/Instant // kotlinx.datetime/toInstant|toInstant@kotlinx.datetime.LocalDateTime(kotlinx.datetime.UtcOffset){}[0] +final fun (kotlinx.datetime/LocalDateTime).kotlinx.datetime/toInstant(kotlinx.datetime/UtcOffset, kotlinx.datetime/DeprecationMarker =...): kotlinx.time/Instant // kotlinx.datetime/toInstant|toInstant@kotlinx.datetime.LocalDateTime(kotlinx.datetime.UtcOffset;kotlinx.datetime.DeprecationMarker){}[0] final fun (kotlinx.datetime/LocalTime).kotlinx.datetime/atDate(kotlin/Int, kotlin/Int, kotlin/Int =...): kotlinx.datetime/LocalDateTime // kotlinx.datetime/atDate|atDate@kotlinx.datetime.LocalTime(kotlin.Int;kotlin.Int;kotlin.Int){}[0] final fun (kotlinx.datetime/LocalTime).kotlinx.datetime/atDate(kotlin/Int, kotlinx.datetime/Month, kotlin/Int =...): kotlinx.datetime/LocalDateTime // kotlinx.datetime/atDate|atDate@kotlinx.datetime.LocalTime(kotlin.Int;kotlinx.datetime.Month;kotlin.Int){}[0] final fun (kotlinx.datetime/LocalTime).kotlinx.datetime/atDate(kotlinx.datetime/LocalDate): kotlinx.datetime/LocalDateTime // kotlinx.datetime/atDate|atDate@kotlinx.datetime.LocalTime(kotlinx.datetime.LocalDate){}[0] final fun (kotlinx.datetime/LocalTime).kotlinx.datetime/format(kotlinx.datetime.format/DateTimeFormat): kotlin/String // kotlinx.datetime/format|format@kotlinx.datetime.LocalTime(kotlinx.datetime.format.DateTimeFormat){}[0] final fun (kotlinx.datetime/TimeZone).kotlinx.datetime/offsetAt(kotlinx.datetime/Instant): kotlinx.datetime/UtcOffset // kotlinx.datetime/offsetAt|offsetAt@kotlinx.datetime.TimeZone(kotlinx.datetime.Instant){}[0] +final fun (kotlinx.datetime/TimeZone).kotlinx.datetime/offsetAt(kotlinx.time/Instant): kotlinx.datetime/UtcOffset // kotlinx.datetime/offsetAt|offsetAt@kotlinx.datetime.TimeZone(kotlinx.time.Instant){}[0] final fun (kotlinx.datetime/UtcOffset).kotlinx.datetime/asTimeZone(): kotlinx.datetime/FixedOffsetTimeZone // kotlinx.datetime/asTimeZone|asTimeZone@kotlinx.datetime.UtcOffset(){}[0] final fun (kotlinx.datetime/UtcOffset).kotlinx.datetime/format(kotlinx.datetime.format/DateTimeFormat): kotlin/String // kotlinx.datetime/format|format@kotlinx.datetime.UtcOffset(kotlinx.datetime.format.DateTimeFormat){}[0] +final fun (kotlinx.time/Clock).kotlinx.datetime/asTimeSource(): kotlin.time/TimeSource.WithComparableMarks // kotlinx.datetime/asTimeSource|asTimeSource@kotlinx.time.Clock(){}[0] +final fun (kotlinx.time/Clock).kotlinx.datetime/todayAt(kotlinx.datetime/TimeZone): kotlinx.datetime/LocalDate // kotlinx.datetime/todayAt|todayAt@kotlinx.time.Clock(kotlinx.datetime.TimeZone){}[0] +final fun (kotlinx.time/Clock).kotlinx.datetime/todayIn(kotlinx.datetime/TimeZone): kotlinx.datetime/LocalDate // kotlinx.datetime/todayIn|todayIn@kotlinx.time.Clock(kotlinx.datetime.TimeZone){}[0] +final fun (kotlinx.time/Instant).kotlinx.datetime/daysUntil(kotlinx.time/Instant, kotlinx.datetime/TimeZone): kotlin/Int // kotlinx.datetime/daysUntil|daysUntil@kotlinx.time.Instant(kotlinx.time.Instant;kotlinx.datetime.TimeZone){}[0] +final fun (kotlinx.time/Instant).kotlinx.datetime/format(kotlinx.datetime.format/DateTimeFormat, kotlinx.datetime/UtcOffset =...): kotlin/String // kotlinx.datetime/format|format@kotlinx.time.Instant(kotlinx.datetime.format.DateTimeFormat;kotlinx.datetime.UtcOffset){}[0] +final fun (kotlinx.time/Instant).kotlinx.datetime/minus(kotlin/Int, kotlinx.datetime/DateTimeUnit, kotlinx.datetime/TimeZone): kotlinx.time/Instant // kotlinx.datetime/minus|minus@kotlinx.time.Instant(kotlin.Int;kotlinx.datetime.DateTimeUnit;kotlinx.datetime.TimeZone){}[0] +final fun (kotlinx.time/Instant).kotlinx.datetime/minus(kotlin/Int, kotlinx.datetime/DateTimeUnit.TimeBased): kotlinx.time/Instant // kotlinx.datetime/minus|minus@kotlinx.time.Instant(kotlin.Int;kotlinx.datetime.DateTimeUnit.TimeBased){}[0] +final fun (kotlinx.time/Instant).kotlinx.datetime/minus(kotlin/Long, kotlinx.datetime/DateTimeUnit, kotlinx.datetime/TimeZone): kotlinx.time/Instant // kotlinx.datetime/minus|minus@kotlinx.time.Instant(kotlin.Long;kotlinx.datetime.DateTimeUnit;kotlinx.datetime.TimeZone){}[0] +final fun (kotlinx.time/Instant).kotlinx.datetime/minus(kotlin/Long, kotlinx.datetime/DateTimeUnit.TimeBased): kotlinx.time/Instant // kotlinx.datetime/minus|minus@kotlinx.time.Instant(kotlin.Long;kotlinx.datetime.DateTimeUnit.TimeBased){}[0] +final fun (kotlinx.time/Instant).kotlinx.datetime/minus(kotlinx.datetime/DateTimePeriod, kotlinx.datetime/TimeZone): kotlinx.time/Instant // kotlinx.datetime/minus|minus@kotlinx.time.Instant(kotlinx.datetime.DateTimePeriod;kotlinx.datetime.TimeZone){}[0] +final fun (kotlinx.time/Instant).kotlinx.datetime/minus(kotlinx.datetime/DateTimeUnit, kotlinx.datetime/TimeZone): kotlinx.time/Instant // kotlinx.datetime/minus|minus@kotlinx.time.Instant(kotlinx.datetime.DateTimeUnit;kotlinx.datetime.TimeZone){}[0] +final fun (kotlinx.time/Instant).kotlinx.datetime/minus(kotlinx.datetime/DateTimeUnit.TimeBased): kotlinx.time/Instant // kotlinx.datetime/minus|minus@kotlinx.time.Instant(kotlinx.datetime.DateTimeUnit.TimeBased){}[0] +final fun (kotlinx.time/Instant).kotlinx.datetime/minus(kotlinx.time/Instant, kotlinx.datetime/DateTimeUnit, kotlinx.datetime/TimeZone): kotlin/Long // kotlinx.datetime/minus|minus@kotlinx.time.Instant(kotlinx.time.Instant;kotlinx.datetime.DateTimeUnit;kotlinx.datetime.TimeZone){}[0] +final fun (kotlinx.time/Instant).kotlinx.datetime/minus(kotlinx.time/Instant, kotlinx.datetime/DateTimeUnit.TimeBased): kotlin/Long // kotlinx.datetime/minus|minus@kotlinx.time.Instant(kotlinx.time.Instant;kotlinx.datetime.DateTimeUnit.TimeBased){}[0] +final fun (kotlinx.time/Instant).kotlinx.datetime/minus(kotlinx.time/Instant, kotlinx.datetime/TimeZone): kotlinx.datetime/DateTimePeriod // kotlinx.datetime/minus|minus@kotlinx.time.Instant(kotlinx.time.Instant;kotlinx.datetime.TimeZone){}[0] +final fun (kotlinx.time/Instant).kotlinx.datetime/monthsUntil(kotlinx.time/Instant, kotlinx.datetime/TimeZone): kotlin/Int // kotlinx.datetime/monthsUntil|monthsUntil@kotlinx.time.Instant(kotlinx.time.Instant;kotlinx.datetime.TimeZone){}[0] +final fun (kotlinx.time/Instant).kotlinx.datetime/offsetIn(kotlinx.datetime/TimeZone): kotlinx.datetime/UtcOffset // kotlinx.datetime/offsetIn|offsetIn@kotlinx.time.Instant(kotlinx.datetime.TimeZone){}[0] +final fun (kotlinx.time/Instant).kotlinx.datetime/periodUntil(kotlinx.time/Instant, kotlinx.datetime/TimeZone): kotlinx.datetime/DateTimePeriod // kotlinx.datetime/periodUntil|periodUntil@kotlinx.time.Instant(kotlinx.time.Instant;kotlinx.datetime.TimeZone){}[0] +final fun (kotlinx.time/Instant).kotlinx.datetime/plus(kotlin/Int, kotlinx.datetime/DateTimeUnit, kotlinx.datetime/TimeZone): kotlinx.time/Instant // kotlinx.datetime/plus|plus@kotlinx.time.Instant(kotlin.Int;kotlinx.datetime.DateTimeUnit;kotlinx.datetime.TimeZone){}[0] +final fun (kotlinx.time/Instant).kotlinx.datetime/plus(kotlin/Int, kotlinx.datetime/DateTimeUnit.TimeBased): kotlinx.time/Instant // kotlinx.datetime/plus|plus@kotlinx.time.Instant(kotlin.Int;kotlinx.datetime.DateTimeUnit.TimeBased){}[0] +final fun (kotlinx.time/Instant).kotlinx.datetime/plus(kotlin/Long, kotlinx.datetime/DateTimeUnit, kotlinx.datetime/TimeZone): kotlinx.time/Instant // kotlinx.datetime/plus|plus@kotlinx.time.Instant(kotlin.Long;kotlinx.datetime.DateTimeUnit;kotlinx.datetime.TimeZone){}[0] +final fun (kotlinx.time/Instant).kotlinx.datetime/plus(kotlin/Long, kotlinx.datetime/DateTimeUnit.TimeBased): kotlinx.time/Instant // kotlinx.datetime/plus|plus@kotlinx.time.Instant(kotlin.Long;kotlinx.datetime.DateTimeUnit.TimeBased){}[0] +final fun (kotlinx.time/Instant).kotlinx.datetime/plus(kotlinx.datetime/DateTimePeriod, kotlinx.datetime/TimeZone): kotlinx.time/Instant // kotlinx.datetime/plus|plus@kotlinx.time.Instant(kotlinx.datetime.DateTimePeriod;kotlinx.datetime.TimeZone){}[0] +final fun (kotlinx.time/Instant).kotlinx.datetime/plus(kotlinx.datetime/DateTimeUnit, kotlinx.datetime/TimeZone): kotlinx.time/Instant // kotlinx.datetime/plus|plus@kotlinx.time.Instant(kotlinx.datetime.DateTimeUnit;kotlinx.datetime.TimeZone){}[0] +final fun (kotlinx.time/Instant).kotlinx.datetime/plus(kotlinx.datetime/DateTimeUnit.TimeBased): kotlinx.time/Instant // kotlinx.datetime/plus|plus@kotlinx.time.Instant(kotlinx.datetime.DateTimeUnit.TimeBased){}[0] +final fun (kotlinx.time/Instant).kotlinx.datetime/toDeprecatedInstant(): kotlinx.datetime/Instant // kotlinx.datetime/toDeprecatedInstant|toDeprecatedInstant@kotlinx.time.Instant(){}[0] +final fun (kotlinx.time/Instant).kotlinx.datetime/toLocalDateTime(kotlinx.datetime/TimeZone): kotlinx.datetime/LocalDateTime // kotlinx.datetime/toLocalDateTime|toLocalDateTime@kotlinx.time.Instant(kotlinx.datetime.TimeZone){}[0] +final fun (kotlinx.time/Instant).kotlinx.datetime/until(kotlinx.time/Instant, kotlinx.datetime/DateTimeUnit, kotlinx.datetime/TimeZone): kotlin/Long // kotlinx.datetime/until|until@kotlinx.time.Instant(kotlinx.time.Instant;kotlinx.datetime.DateTimeUnit;kotlinx.datetime.TimeZone){}[0] +final fun (kotlinx.time/Instant).kotlinx.datetime/until(kotlinx.time/Instant, kotlinx.datetime/DateTimeUnit.TimeBased): kotlin/Long // kotlinx.datetime/until|until@kotlinx.time.Instant(kotlinx.time.Instant;kotlinx.datetime.DateTimeUnit.TimeBased){}[0] +final fun (kotlinx.time/Instant).kotlinx.datetime/yearsUntil(kotlinx.time/Instant, kotlinx.datetime/TimeZone): kotlin/Int // kotlinx.datetime/yearsUntil|yearsUntil@kotlinx.time.Instant(kotlinx.time.Instant;kotlinx.datetime.TimeZone){}[0] +final fun (kotlinx.time/Instant.Companion).kotlinx.datetime/parse(kotlin/CharSequence, kotlinx.datetime.format/DateTimeFormat): kotlinx.time/Instant // kotlinx.datetime/parse|parse@kotlinx.time.Instant.Companion(kotlin.CharSequence;kotlinx.datetime.format.DateTimeFormat){}[0] final fun <#A: kotlinx.datetime.format/DateTimeFormatBuilder> (#A).kotlinx.datetime.format/alternativeParsing(kotlin/Array>..., kotlin/Function1<#A, kotlin/Unit>) // kotlinx.datetime.format/alternativeParsing|alternativeParsing@0:0(kotlin.Array>...;kotlin.Function1<0:0,kotlin.Unit>){0§}[0] final fun <#A: kotlinx.datetime.format/DateTimeFormatBuilder> (#A).kotlinx.datetime.format/optional(kotlin/String =..., kotlin/Function1<#A, kotlin/Unit>) // kotlinx.datetime.format/optional|optional@0:0(kotlin.String;kotlin.Function1<0:0,kotlin.Unit>){0§}[0] final fun kotlinx.datetime/DateTimePeriod(kotlin/Int =..., kotlin/Int =..., kotlin/Int =..., kotlin/Int =..., kotlin/Int =..., kotlin/Int =..., kotlin/Long =...): kotlinx.datetime/DateTimePeriod // kotlinx.datetime/DateTimePeriod|DateTimePeriod(kotlin.Int;kotlin.Int;kotlin.Int;kotlin.Int;kotlin.Int;kotlin.Int;kotlin.Long){}[0] @@ -905,9 +948,15 @@ final fun (kotlinx.datetime/LocalDateTime).kotlinx.datetime/toNSDateComponents() // Targets: [apple] final fun (kotlinx.datetime/TimeZone).kotlinx.datetime/toNSTimeZone(): platform.Foundation/NSTimeZone // kotlinx.datetime/toNSTimeZone|toNSTimeZone@kotlinx.datetime.TimeZone(){}[0] +// Targets: [apple] +final fun (kotlinx.time/Instant).kotlinx.datetime/toNSDate(): platform.Foundation/NSDate // kotlinx.datetime/toNSDate|toNSDate@kotlinx.time.Instant(){}[0] + // Targets: [apple] final fun (platform.Foundation/NSDate).kotlinx.datetime/toKotlinInstant(): kotlinx.datetime/Instant // kotlinx.datetime/toKotlinInstant|toKotlinInstant@platform.Foundation.NSDate(){}[0] +// Targets: [apple] +final fun (platform.Foundation/NSDate).kotlinx.datetime/toKotlinInstant(kotlinx.datetime/DeprecationMarker =...): kotlinx.time/Instant // kotlinx.datetime/toKotlinInstant|toKotlinInstant@platform.Foundation.NSDate(kotlinx.datetime.DeprecationMarker){}[0] + // Targets: [apple] final fun (platform.Foundation/NSTimeZone).kotlinx.datetime/toKotlinTimeZone(): kotlinx.datetime/TimeZone // kotlinx.datetime/toKotlinTimeZone|toKotlinTimeZone@platform.Foundation.NSTimeZone(){}[0] diff --git a/core/build.gradle.kts b/core/build.gradle.kts index 063221406..590cfcb4a 100644 --- a/core/build.gradle.kts +++ b/core/build.gradle.kts @@ -165,6 +165,7 @@ kotlin { commonMain { dependencies { compileOnly("org.jetbrains.kotlinx:kotlinx-serialization-core:$serializationVersion") + api(project(":fake-kotlinx-time")) } } diff --git a/core/common/src/Clock.kt b/core/common/src/Clock.kt index e9cd9137e..475f44f8d 100644 --- a/core/common/src/Clock.kt +++ b/core/common/src/Clock.kt @@ -3,62 +3,17 @@ * Use of this source code is governed by the Apache 2.0 License that can be found in the LICENSE.txt file. */ +@file:JvmMultifileClass +@file:JvmName("ClockKt") package kotlinx.datetime import kotlin.time.* - -/** - * A source of [Instant] values. - * - * See [Clock.System][Clock.System] for the clock instance that queries the operating system. - * - * It is not recommended to use [Clock.System] directly in the implementation. Instead, you can pass a - * [Clock] explicitly to the necessary functions or classes. - * This way, tests can be written deterministically by providing custom [Clock] implementations - * to the system under test. - */ -public interface Clock { - /** - * Returns the [Instant] corresponding to the current time, according to this clock. - * - * Calling [now] later is not guaranteed to return a larger [Instant]. - * In particular, for [Clock.System], the opposite is completely expected, - * and it must be taken into account. - * See the [System] documentation for details. - * - * Even though [Instant] is defined to be on the UTC-SLS time scale, which enforces a specific way of handling - * leap seconds, [now] is not guaranteed to handle leap seconds in any specific way. - */ - public fun now(): Instant - - /** - * The [Clock] instance that queries the platform-specific system clock as its source of time knowledge. - * - * Successive calls to [now] will not necessarily return increasing [Instant] values, and when they do, - * these increases will not necessarily correspond to the elapsed time. - * - * For example, when using [Clock.System], the following could happen: - * - [now] returns `2023-01-02T22:35:01Z`. - * - The system queries the Internet and recognizes that its clock needs adjusting. - * - [now] returns `2023-01-02T22:32:05Z`. - * - * When you need predictable intervals between successive measurements, consider using [TimeSource.Monotonic]. - * - * For improved testability, you should avoid using [Clock.System] directly in the implementation - * and pass a [Clock] explicitly instead. For example: - * - * @sample kotlinx.datetime.test.samples.ClockSamples.system - * @sample kotlinx.datetime.test.samples.ClockSamples.dependencyInjection - */ - public object System : Clock { - override fun now(): Instant = @Suppress("DEPRECATION_ERROR") Instant.now() - } - - /** A companion object used purely for namespacing. */ - public companion object { - - } -} +import kotlinx.time.Clock +import kotlinx.time.Instant +import kotlinx.time.isDistantFuture +import kotlin.jvm.JvmMultifileClass +import kotlin.jvm.JvmName +import kotlin.time.Duration.Companion.seconds /** * Returns the current date at the given [time zone][timeZone], according to [this Clock][this]. @@ -104,7 +59,7 @@ private class InstantTimeMark(private val instant: Instant, private val clock: C override fun toString(): String = "InstantTimeMark($instant, $clock)" - private fun Instant.isSaturated() = this == Instant.MAX || this == Instant.MIN + private fun Instant.isSaturated() = this.plus(1.seconds) == this || this.plus((-1).seconds) == this private fun Instant.saturatingAdd(duration: Duration): Instant { if (isSaturated()) { if (duration.isInfinite() && duration.isPositive() != this.isDistantFuture) { diff --git a/core/common/src/DeprecatedClock.kt b/core/common/src/DeprecatedClock.kt new file mode 100644 index 000000000..6b8029dee --- /dev/null +++ b/core/common/src/DeprecatedClock.kt @@ -0,0 +1,144 @@ +/* + * Copyright 2019-2024 JetBrains s.r.o. and contributors. + * Use of this source code is governed by the Apache 2.0 License that can be found in the LICENSE.txt file. + */ + +@file:Suppress("DEPRECATION_ERROR") +@file:JvmMultifileClass +@file:JvmName("ClockKt") +package kotlinx.datetime + +import kotlin.jvm.JvmMultifileClass +import kotlin.jvm.JvmName +import kotlin.time.ComparableTimeMark +import kotlin.time.Duration +import kotlin.time.Duration.Companion.seconds +import kotlin.time.ExperimentalTime +import kotlin.time.TimeSource + +/** + * A source of [Instant] values. + * + * See [Clock.System][Clock.System] for the clock instance that queries the operating system. + * + * It is not recommended to use [Clock.System] directly in the implementation. Instead, you can pass a + * [Clock] explicitly to the necessary functions or classes. + * This way, tests can be written deterministically by providing custom [Clock] implementations + * to the system under test. + */ +@Deprecated( + "Use kotlin.time.Clock instead", + ReplaceWith("kotlinx.time.Clock", "kotlinx.time.Clock"), + level = DeprecationLevel.ERROR +) +public interface Clock { + /** + * Returns the [Instant] corresponding to the current time, according to this clock. + * + * Calling [now] later is not guaranteed to return a larger [Instant]. + * In particular, for [Clock.System], the opposite is completely expected, + * and it must be taken into account. + * See the [System] documentation for details. + * + * Even though [Instant] is defined to be on the UTC-SLS time scale, which enforces a specific way of handling + * leap seconds, [now] is not guaranteed to handle leap seconds in any specific way. + */ + public fun now(): Instant + + /** + * The [Clock] instance that queries the platform-specific system clock as its source of time knowledge. + * + * Successive calls to [now] will not necessarily return increasing [Instant] values, and when they do, + * these increases will not necessarily correspond to the elapsed time. + * + * For example, when using [Clock.System], the following could happen: + * - [now] returns `2023-01-02T22:35:01Z`. + * - The system queries the Internet and recognizes that its clock needs adjusting. + * - [now] returns `2023-01-02T22:32:05Z`. + * + * When you need predictable intervals between successive measurements, consider using [TimeSource.Monotonic]. + * + * For improved testability, you should avoid using [Clock.System] directly in the implementation + * and pass a [Clock] explicitly instead. For example: + * + * @sample kotlinx.datetime.test.samples.ClockSamples.system + * @sample kotlinx.datetime.test.samples.ClockSamples.dependencyInjection + */ + public object System : Clock { + override fun now(): Instant = kotlinx.time.Clock.System.now().toDeprecatedInstant() + } + + /** A companion object used purely for namespacing. */ + public companion object { + + } +} + +/** + * Returns the current date at the given [time zone][timeZone], according to [this Clock][this]. + * + * The time zone is important because the current date is not the same in all time zones at the same instant. + * + * @sample kotlinx.datetime.test.samples.ClockSamples.todayIn + */ +public fun Clock.todayIn(timeZone: TimeZone): LocalDate = + now().toNewInstant().toLocalDateTime(timeZone).date + +/** + * Returns a [TimeSource] that uses this [Clock] to mark a time instant and to find the amount of time elapsed since that mark. + * + * **Pitfall**: using this function with [Clock.System] is error-prone + * because [Clock.System] is not well suited for measuring time intervals. + * Please only use this conversion function on the [Clock] instances that are fully controlled programmatically. + */ +@ExperimentalTime +public fun Clock.asTimeSource(): TimeSource.WithComparableMarks = object : TimeSource.WithComparableMarks { + override fun markNow(): ComparableTimeMark = DeprecatedInstantTimeMark(now(), this@asTimeSource) +} + +@ExperimentalTime +private class DeprecatedInstantTimeMark(private val instant: Instant, private val clock: Clock) : ComparableTimeMark { + override fun elapsedNow(): Duration = saturatingDiff(clock.now(), instant) + + override fun plus(duration: Duration): ComparableTimeMark = + DeprecatedInstantTimeMark(instant.saturatingAdd(duration), clock) + override fun minus(duration: Duration): ComparableTimeMark = + DeprecatedInstantTimeMark(instant.saturatingAdd(-duration), clock) + + override fun minus(other: ComparableTimeMark): Duration { + if (other !is DeprecatedInstantTimeMark || other.clock != this.clock) { + throw IllegalArgumentException("Subtracting or comparing time marks from different time sources is not possible: $this and $other") + } + return saturatingDiff(this.instant, other.instant) + } + + override fun equals(other: Any?): Boolean { + return other is DeprecatedInstantTimeMark && this.clock == other.clock && this.instant == other.instant + } + + override fun hashCode(): Int = instant.hashCode() + + override fun toString(): String = "InstantTimeMark($instant, $clock)" + + private fun Instant.isSaturated() = this.plus(1.seconds) == this || this.plus((-1).seconds) == this + private fun Instant.saturatingAdd(duration: Duration): Instant { + if (isSaturated()) { + if (duration.isInfinite() && duration.isPositive() != this.isDistantFuture) { + throw IllegalArgumentException("Summing infinities of different signs") + } + return this + } + return this + duration + } + private fun saturatingDiff(instant1: Instant, instant2: Instant): Duration = when { + instant1 == instant2 -> + Duration.ZERO + instant1.isSaturated() || instant2.isSaturated() -> + (instant1 - instant2) * Double.POSITIVE_INFINITY + else -> + instant1 - instant2 + } +} + +@Deprecated("Use Clock.todayIn instead", ReplaceWith("this.todayIn(timeZone)"), DeprecationLevel.WARNING) +public fun Clock.todayAt(timeZone: TimeZone): LocalDate = todayIn(timeZone) diff --git a/core/common/src/DeprecatedInstant.kt b/core/common/src/DeprecatedInstant.kt new file mode 100644 index 000000000..f4d6cedd4 --- /dev/null +++ b/core/common/src/DeprecatedInstant.kt @@ -0,0 +1,645 @@ +/* + * Copyright 2019-2024 JetBrains s.r.o. and contributors. + * Use of this source code is governed by the Apache 2.0 License that can be found in the LICENSE.txt file. + */ + +@file:Suppress("DEPRECATION_ERROR") +@file:JvmMultifileClass +@file:JvmName("InstantKt") +package kotlinx.datetime + +import kotlinx.datetime.format.DateTimeComponents +import kotlinx.datetime.format.DateTimeFormat +import kotlinx.datetime.format.format +import kotlinx.datetime.format.parse +import kotlinx.datetime.internal.NANOS_PER_ONE +import kotlinx.datetime.internal.clampToInt +import kotlinx.datetime.internal.multiplyAddAndDivide +import kotlinx.datetime.serializers.InstantIso8601Serializer +import kotlinx.serialization.Serializable +import kotlin.jvm.JvmMultifileClass +import kotlin.jvm.JvmName +import kotlin.time.Duration +import kotlin.time.Duration.Companion.days +import kotlin.time.TimeSource + +public fun Instant.toNewInstant(): kotlinx.time.Instant = + kotlinx.time.Instant.fromEpochSeconds(epochSeconds, nanosecondsOfSecond) + +public fun kotlinx.time.Instant.toDeprecatedInstant(): Instant = + Instant.fromEpochSeconds(epochSeconds, nanosecondsOfSecond) + +@Deprecated( + "Use kotlin.time.Instant instead", + ReplaceWith("kotlinx.time.Instant", "kotlinx.time.Instant"), + level = DeprecationLevel.ERROR +) +@Serializable(with = InstantIso8601Serializer::class) +public expect class Instant : Comparable { + + /** + * The number of seconds from the epoch instant `1970-01-01T00:00:00Z` rounded down to a [Long] number. + * + * The difference between the rounded number of seconds and the actual number of seconds + * is returned by [nanosecondsOfSecond] property expressed in nanoseconds. + * + * Note that this number doesn't include leap seconds added or removed since the epoch. + * + * @see fromEpochSeconds + * @sample kotlinx.datetime.test.samples.InstantSamples.epochSeconds + */ + public val epochSeconds: Long + + /** + * The number of nanoseconds by which this instant is later than [epochSeconds] from the epoch instant. + * + * The value is always non-negative and lies in the range `0..999_999_999`. + * + * @see fromEpochSeconds + * @sample kotlinx.datetime.test.samples.InstantSamples.nanosecondsOfSecond + */ + public val nanosecondsOfSecond: Int + + /** + * Returns the number of milliseconds from the epoch instant `1970-01-01T00:00:00Z`. + * + * Any fractional part of a millisecond is rounded toward zero to the whole number of milliseconds. + * + * If the result does not fit in [Long], + * returns [Long.MAX_VALUE] for a positive result or [Long.MIN_VALUE] for a negative result. + * + * @see fromEpochMilliseconds + * @sample kotlinx.datetime.test.samples.InstantSamples.toEpochMilliseconds + */ + public fun toEpochMilliseconds(): Long + + /** + * Returns an instant that is the result of adding the specified [duration] to this instant. + * + * If the [duration] is positive, the returned instant is later than this instant. + * If the [duration] is negative, the returned instant is earlier than this instant. + * + * The return value is clamped to the boundaries of [Instant] if the result exceeds them. + * + * **Pitfall**: [Duration.Companion.days] are multiples of 24 hours and are not calendar-based. + * Consider using the [plus] overload that accepts a multiple of a [DateTimeUnit] instead for calendar-based + * operations instead of using [Duration]. + * For an explanation of why some days are not 24 hours, see [DateTimeUnit.DayBased]. + * + * @sample kotlinx.datetime.test.samples.InstantSamples.plusDuration + */ + public operator fun plus(duration: Duration): Instant + + /** + * Returns an instant that is the result of subtracting the specified [duration] from this instant. + * + * If the [duration] is positive, the returned instant is earlier than this instant. + * If the [duration] is negative, the returned instant is later than this instant. + * + * The return value is clamped to the boundaries of [Instant] if the result exceeds them. + * + * **Pitfall**: [Duration.Companion.days] are multiples of 24 hours and are not calendar-based. + * Consider using the [minus] overload that accepts a multiple of a [DateTimeUnit] instead for calendar-based + * operations instead of using [Duration]. + * For an explanation of why some days are not 24 hours, see [DateTimeUnit.DayBased]. + * + * @sample kotlinx.datetime.test.samples.InstantSamples.minusDuration + */ + public operator fun minus(duration: Duration): Instant + + // questionable + /** + * Returns the [Duration] between two instants: [other] and `this`. + * + * The duration returned is positive if this instant is later than the other, + * and negative if this instant is earlier than the other. + * + * The result is never clamped, but note that for instants that are far apart, + * the value returned may represent the duration between them inexactly due to the loss of precision. + * + * Note that sources of [Instant] values (in particular, [Clock]) are not guaranteed to be in sync with each other + * or even monotonic, so the result of this operation may be negative even if the other instant was observed later + * than this one, or vice versa. + * For measuring time intervals, consider using [TimeSource.Monotonic]. + * + * @sample kotlinx.datetime.test.samples.InstantSamples.minusInstant + */ + public operator fun minus(other: Instant): Duration + + /** + * Compares `this` instant with the [other] instant. + * Returns zero if this instant represents the same moment as the other (meaning they are equal to one another), + * a negative number if this instant is earlier than the other, + * and a positive number if this instant is later than the other. + * + * @sample kotlinx.datetime.test.samples.InstantSamples.compareToSample + */ + public override operator fun compareTo(other: Instant): Int + + /** + * Converts this instant to the ISO 8601 string representation, for example, `2023-01-02T23:40:57.120Z`. + * + * The representation uses the UTC-SLS time scale instead of UTC. + * In practice, this means that leap second handling will not be readjusted to the UTC. + * Leap seconds will not be added or skipped, so it is impossible to acquire a string + * where the component for seconds is 60, and for any day, it's possible to observe 23:59:59. + * + * @see parse + * @see DateTimeComponents.Formats.ISO_DATE_TIME_OFFSET for a very similar format. The difference is that + * [DateTimeComponents.Formats.ISO_DATE_TIME_OFFSET] will not add trailing zeros for readability to the + * fractional part of the second. + * @sample kotlinx.datetime.test.samples.InstantSamples.toStringSample + */ + public override fun toString(): String + + + public companion object { + @Deprecated("Use Clock.System.now() instead", ReplaceWith("Clock.System.now()", "kotlinx.datetime.Clock"), level = DeprecationLevel.ERROR) + public fun now(): Instant + + /** + * Returns an [Instant] that is [epochMilliseconds] number of milliseconds from the epoch instant `1970-01-01T00:00:00Z`. + * + * Every value of [epochMilliseconds] is guaranteed to be representable as an [Instant]. + * + * Note that [Instant] also supports nanosecond precision via [fromEpochSeconds]. + * + * @see Instant.toEpochMilliseconds + * @sample kotlinx.datetime.test.samples.InstantSamples.fromEpochMilliseconds + */ + public fun fromEpochMilliseconds(epochMilliseconds: Long): Instant + + /** + * Returns an [Instant] that is the [epochSeconds] number of seconds from the epoch instant `1970-01-01T00:00:00Z` + * and the [nanosecondAdjustment] number of nanoseconds from the whole second. + * + * The return value is clamped to the boundaries of [Instant] if the result exceeds them. + * In any case, it is guaranteed that instants between [DISTANT_PAST] and [DISTANT_FUTURE] can be represented. + * + * [fromEpochMilliseconds] is a similar function for when input data only has millisecond precision. + * + * @see Instant.epochSeconds + * @see Instant.nanosecondsOfSecond + * @sample kotlinx.datetime.test.samples.InstantSamples.fromEpochSeconds + */ + public fun fromEpochSeconds(epochSeconds: Long, nanosecondAdjustment: Long = 0): Instant + + /** + * Returns an [Instant] that is the [epochSeconds] number of seconds from the epoch instant `1970-01-01T00:00:00Z` + * and the [nanosecondAdjustment] number of nanoseconds from the whole second. + * + * The return value is clamped to the boundaries of [Instant] if the result exceeds them. + * In any case, it is guaranteed that instants between [DISTANT_PAST] and [DISTANT_FUTURE] can be represented. + * + * [fromEpochMilliseconds] is a similar function for when input data only has millisecond precision. + * + * @see Instant.epochSeconds + * @see Instant.nanosecondsOfSecond + * @sample kotlinx.datetime.test.samples.InstantSamples.fromEpochSecondsIntNanos + */ + public fun fromEpochSeconds(epochSeconds: Long, nanosecondAdjustment: Int): Instant + + /** + * A shortcut for calling [DateTimeFormat.parse], followed by [DateTimeComponents.toInstantUsingOffset]. + * + * Parses a string that represents an instant, including date and time components and a mandatory + * time zone offset and returns the parsed [Instant] value. + * + * The string is considered to represent time on the UTC-SLS time scale instead of UTC. + * In practice, this means that, even if there is a leap second on the given day, it will not affect how the + * time is parsed, even if it's in the last 1000 seconds of the day. + * Instead, even if there is a negative leap second on the given day, 23:59:59 is still considered a valid time. + * 23:59:60 is invalid on UTC-SLS, so parsing it will fail. + * + * If the format is not specified, [DateTimeComponents.Formats.ISO_DATE_TIME_OFFSET] is used. + * `2023-01-02T23:40:57.120Z` is an example of a string in this format. + * + * @throws IllegalArgumentException if the text cannot be parsed or the boundaries of [Instant] are exceeded. + * + * @see Instant.toString for formatting using the default format. + * @see Instant.format for formatting using a custom format. + * @sample kotlinx.datetime.test.samples.InstantSamples.parsing + */ + public fun parse( + input: CharSequence, + format: DateTimeFormat = DateTimeComponents.Formats.ISO_DATE_TIME_OFFSET + ): Instant + + /** + * An instant value that is far in the past. + * + * All instants in the range `DISTANT_PAST..DISTANT_FUTURE` can be [converted][Instant.toLocalDateTime] to + * [LocalDateTime] without exceptions in every time zone. + * + * [isDistantPast] returns true for this value and all earlier ones. + */ + public val DISTANT_PAST: Instant // -100001-12-31T23:59:59.999999999Z + + /** + * An instant value that is far in the future. + * + * All instants in the range `DISTANT_PAST..DISTANT_FUTURE` can be [converted][Instant.toLocalDateTime] to + * [LocalDateTime] without exceptions in every time zone. + * + * [isDistantFuture] returns true for this value and all later ones. + */ + public val DISTANT_FUTURE: Instant // +100000-01-01T00:00:00Z + + internal val MIN: Instant + internal val MAX: Instant + } +} + +/** + * Returns true if the instant is [Instant.DISTANT_PAST] or earlier. + * + * @sample kotlinx.datetime.test.samples.InstantSamples.isDistantPast + */ +public val Instant.isDistantPast: Boolean + get() = this <= Instant.DISTANT_PAST + +/** + * Returns true if the instant is [Instant.DISTANT_FUTURE] or later. + * + * @sample kotlinx.datetime.test.samples.InstantSamples.isDistantFuture + */ +public val Instant.isDistantFuture: Boolean + get() = this >= Instant.DISTANT_FUTURE + +/** + * @suppress + */ +@Deprecated("Removed to support more idiomatic code. See https://github.com/Kotlin/kotlinx-datetime/issues/339", ReplaceWith("Instant.parse(this)"), DeprecationLevel.WARNING) +public fun String.toInstant(): Instant = Instant.parse(this) + +/** + * Returns an instant that is the result of adding components of [DateTimePeriod] to this instant. The components are + * added in the order from the largest units to the smallest, i.e., from years to nanoseconds. + * + * - If the [DateTimePeriod] only contains time-based components, please consider adding a [Duration] instead, + * as in `Clock.System.now() + 5.hours`. + * Then, it will not be necessary to pass the [timeZone]. + * - If the [DateTimePeriod] only has a single non-zero component (only the months or only the days), + * please consider using a multiple of [DateTimeUnit.DAY] or [DateTimeUnit.MONTH], like in + * `Clock.System.now().plus(5, DateTimeUnit.DAY, TimeZone.currentSystemDefault())`. + * + * @throws DateTimeArithmeticException if this value or the results of intermediate computations are too large to fit in + * [LocalDateTime]. + * @sample kotlinx.datetime.test.samples.InstantSamples.plusPeriod + */ +public expect fun Instant.plus(period: DateTimePeriod, timeZone: TimeZone): Instant + +/** + * Returns an instant that is the result of subtracting components of [DateTimePeriod] from this instant. The components + * are subtracted in the order from the largest units to the smallest, i.e., from years to nanoseconds. + * + * - If the [DateTimePeriod] only contains time-based components, please consider subtracting a [Duration] instead, + * as in `Clock.System.now() - 5.hours`. + * Then, it is not necessary to pass the [timeZone]. + * - If the [DateTimePeriod] only has a single non-zero component (only the months or only the days), + * please consider using a multiple of [DateTimeUnit.DAY] or [DateTimeUnit.MONTH], as in + * `Clock.System.now().minus(5, DateTimeUnit.DAY, TimeZone.currentSystemDefault())`. + * + * @throws DateTimeArithmeticException if this value or the results of intermediate computations are too large to fit in + * [LocalDateTime]. + * @sample kotlinx.datetime.test.samples.InstantSamples.minusPeriod + */ +public fun Instant.minus(period: DateTimePeriod, timeZone: TimeZone): Instant = + /* An overflow can happen for any component, but we are only worried about nanoseconds, as having an overflow in + any other component means that `plus` will throw due to the minimum value of the numeric type overflowing the + `Instant` limits. */ + if (period.totalNanoseconds != Long.MIN_VALUE) { + val negatedPeriod = with(period) { buildDateTimePeriod(-totalMonths, -days, -totalNanoseconds) } + plus(negatedPeriod, timeZone) + } else { + val negatedPeriod = with(period) { buildDateTimePeriod(-totalMonths, -days, -(totalNanoseconds+1)) } + plus(negatedPeriod, timeZone).plus(1, DateTimeUnit.NANOSECOND) + } + +/** + * Returns a [DateTimePeriod] representing the difference between `this` and [other] instants. + * + * The components of [DateTimePeriod] are calculated so that adding it to `this` instant results in the [other] instant. + * + * All components of the [DateTimePeriod] returned are: + * - Positive or zero if this instant is earlier than the other. + * - Negative or zero if this instant is later than the other. + * - Exactly zero if this instant is equal to the other. + * + * @throws DateTimeArithmeticException if `this` or [other] instant is too large to fit in [LocalDateTime]. + * @sample kotlinx.datetime.test.samples.InstantSamples.periodUntil + */ +public expect fun Instant.periodUntil(other: Instant, timeZone: TimeZone): DateTimePeriod + +/** + * Returns the whole number of the specified date or time [units][unit] between `this` and [other] instants + * in the specified [timeZone]. + * + * The value returned is: + * - Positive or zero if this instant is earlier than the other. + * - Negative or zero if this instant is later than the other. + * - Zero if this instant is equal to the other. + * + * If the result does not fit in [Long], returns [Long.MAX_VALUE] for a positive result or [Long.MIN_VALUE] for a negative result. + * + * @throws DateTimeArithmeticException if `this` or [other] instant is too large to fit in [LocalDateTime]. + * @sample kotlinx.datetime.test.samples.InstantSamples.untilAsDateTimeUnit + */ +public expect fun Instant.until(other: Instant, unit: DateTimeUnit, timeZone: TimeZone): Long + +/** + * Returns the whole number of the specified time [units][unit] between `this` and [other] instants. + * + * The value returned is: + * - Positive or zero if this instant is earlier than the other. + * - Negative or zero if this instant is later than the other. + * - Zero if this instant is equal to the other. + * + * If the result does not fit in [Long], returns [Long.MAX_VALUE] for a positive result or [Long.MIN_VALUE] for a negative result. + * + * @sample kotlinx.datetime.test.samples.InstantSamples.untilAsTimeBasedUnit + */ +public fun Instant.until(other: Instant, unit: DateTimeUnit.TimeBased): Long = + try { + multiplyAddAndDivide(other.epochSeconds - epochSeconds, + NANOS_PER_ONE.toLong(), + (other.nanosecondsOfSecond - nanosecondsOfSecond).toLong(), + unit.nanoseconds) + } catch (_: ArithmeticException) { + if (this < other) Long.MAX_VALUE else Long.MIN_VALUE + } + +/** + * Returns the number of whole days between two instants in the specified [timeZone]. + * + * If the result does not fit in [Int], returns [Int.MAX_VALUE] for a positive result or [Int.MIN_VALUE] for a negative result. + * + * @see Instant.until + * @throws DateTimeArithmeticException if `this` or [other] instant is too large to fit in [LocalDateTime]. + * @sample kotlinx.datetime.test.samples.InstantSamples.daysUntil + */ +public fun Instant.daysUntil(other: Instant, timeZone: TimeZone): Int = + until(other, DateTimeUnit.DAY, timeZone).clampToInt() + +/** + * Returns the number of whole months between two instants in the specified [timeZone]. + * + * If the result does not fit in [Int], returns [Int.MAX_VALUE] for a positive result or [Int.MIN_VALUE] for a negative result. + * + * @see Instant.until + * @throws DateTimeArithmeticException if `this` or [other] instant is too large to fit in [LocalDateTime]. + * @sample kotlinx.datetime.test.samples.InstantSamples.monthsUntil + */ +public fun Instant.monthsUntil(other: Instant, timeZone: TimeZone): Int = + until(other, DateTimeUnit.MONTH, timeZone).clampToInt() + +/** + * Returns the number of whole years between two instants in the specified [timeZone]. + * + * If the result does not fit in [Int], returns [Int.MAX_VALUE] for a positive result or [Int.MIN_VALUE] for a negative result. + * + * @see Instant.until + * @throws DateTimeArithmeticException if `this` or [other] instant is too large to fit in [LocalDateTime]. + * @sample kotlinx.datetime.test.samples.InstantSamples.yearsUntil + */ +public fun Instant.yearsUntil(other: Instant, timeZone: TimeZone): Int = + until(other, DateTimeUnit.YEAR, timeZone).clampToInt() + +/** + * Returns a [DateTimePeriod] representing the difference between [other] and `this` instants. + * + * The components of [DateTimePeriod] are calculated so that adding it back to the `other` instant results in this instant. + * + * All components of the [DateTimePeriod] returned are: + * - Negative or zero if this instant is earlier than the other. + * - Positive or zero if this instant is later than the other. + * - Exactly zero if this instant is equal to the other. + * + * @throws DateTimeArithmeticException if `this` or [other] instant is too large to fit in [LocalDateTime]. + * @see Instant.periodUntil + * @sample kotlinx.datetime.test.samples.InstantSamples.minusInstantInZone + */ +public fun Instant.minus(other: Instant, timeZone: TimeZone): DateTimePeriod = + other.periodUntil(this, timeZone) + + +/** + * Returns an instant that is the result of adding one [unit] to this instant + * in the specified [timeZone]. + * + * The returned instant is later than this instant. + * + * @throws DateTimeArithmeticException if this value or the result is too large to fit in [LocalDateTime]. + */ +@Deprecated("Use the plus overload with an explicit number of units", ReplaceWith("this.plus(1, unit, timeZone)")) +public expect fun Instant.plus(unit: DateTimeUnit, timeZone: TimeZone): Instant + +/** + * Returns an instant that is the result of subtracting one [unit] from this instant + * in the specified [timeZone]. + * + * The returned instant is earlier than this instant. + * + * @throws DateTimeArithmeticException if this value or the result is too large to fit in [LocalDateTime]. + */ +@Deprecated("Use the minus overload with an explicit number of units", ReplaceWith("this.minus(1, unit, timeZone)")) +public fun Instant.minus(unit: DateTimeUnit, timeZone: TimeZone): Instant = + plus(-1, unit, timeZone) + +/** + * Returns an instant that is the result of adding one [unit] to this instant. + * + * The returned instant is later than this instant. + * + * The return value is clamped to the boundaries of [Instant] if the result exceeds them. + */ +@Deprecated("Use the plus overload with an explicit number of units", ReplaceWith("this.plus(1, unit)")) +public fun Instant.plus(unit: DateTimeUnit.TimeBased): Instant = + plus(1L, unit) + +/** + * Returns an instant that is the result of subtracting one [unit] from this instant. + * + * The returned instant is earlier than this instant. + * + * The return value is clamped to the boundaries of [Instant] if the result exceeds them. + */ +@Deprecated("Use the minus overload with an explicit number of units", ReplaceWith("this.minus(1, unit)")) +public fun Instant.minus(unit: DateTimeUnit.TimeBased): Instant = + plus(-1L, unit) + +/** + * Returns an instant that is the result of adding the [value] number of the specified [unit] to this instant + * in the specified [timeZone]. + * + * If the [value] is positive, the returned instant is later than this instant. + * If the [value] is negative, the returned instant is earlier than this instant. + * + * Note that the time zone does not need to be passed when the [unit] is a time-based unit. + * It is also not needed when adding date-based units to a [LocalDate][LocalDate.plus]. + * + * @throws DateTimeArithmeticException if this value or the result is too large to fit in [LocalDateTime]. + * @sample kotlinx.datetime.test.samples.InstantSamples.plusDateTimeUnit + */ +public expect fun Instant.plus(value: Int, unit: DateTimeUnit, timeZone: TimeZone): Instant + +/** + * Returns an instant that is the result of subtracting the [value] number of the specified [unit] from this instant + * in the specified [timeZone]. + * + * If the [value] is positive, the returned instant is earlier than this instant. + * If the [value] is negative, the returned instant is later than this instant. + * + * Note that the time zone does not need to be passed when the [unit] is a time-based unit. + * It is also not needed when subtracting date-based units from a [LocalDate]. + * + * If the [value] is positive, the returned instant is earlier than this instant. + * If the [value] is negative, the returned instant is later than this instant. + * + * @throws DateTimeArithmeticException if this value or the result is too large to fit in [LocalDateTime]. + * @sample kotlinx.datetime.test.samples.InstantSamples.minusDateTimeUnit + */ +public expect fun Instant.minus(value: Int, unit: DateTimeUnit, timeZone: TimeZone): Instant + +/** + * Returns an instant that is the result of adding the [value] number of the specified [unit] to this instant. + * + * If the [value] is positive, the returned instant is later than this instant. + * If the [value] is negative, the returned instant is earlier than this instant. + * + * The return value is clamped to the boundaries of [Instant] if the result exceeds them. + * + * @sample kotlinx.datetime.test.samples.InstantSamples.plusTimeBasedUnit + */ +public fun Instant.plus(value: Int, unit: DateTimeUnit.TimeBased): Instant = + plus(value.toLong(), unit) + +/** + * Returns an instant that is the result of subtracting the [value] number of the specified [unit] from this instant. + * + * If the [value] is positive, the returned instant is earlier than this instant. + * If the [value] is negative, the returned instant is later than this instant. + * + * The return value is clamped to the boundaries of [Instant] if the result exceeds them. + * + * @sample kotlinx.datetime.test.samples.InstantSamples.minusTimeBasedUnit + */ +public fun Instant.minus(value: Int, unit: DateTimeUnit.TimeBased): Instant = + minus(value.toLong(), unit) + +/** + * Returns an instant that is the result of adding the [value] number of the specified [unit] to this instant + * in the specified [timeZone]. + * + * If the [value] is positive, the returned instant is later than this instant. + * If the [value] is negative, the returned instant is earlier than this instant. + * + * Note that the time zone does not need to be passed when the [unit] is a time-based unit. + * It is also not needed when adding date-based units to a [LocalDate]. + * + * @throws DateTimeArithmeticException if this value or the result is too large to fit in [LocalDateTime]. + * @sample kotlinx.datetime.test.samples.InstantSamples.plusDateTimeUnit + */ +public expect fun Instant.plus(value: Long, unit: DateTimeUnit, timeZone: TimeZone): Instant + +/** + * Returns an instant that is the result of subtracting the [value] number of the specified [unit] from this instant + * in the specified [timeZone]. + * + * If the [value] is positive, the returned instant is earlier than this instant. + * If the [value] is negative, the returned instant is later than this instant. + * + * Note that the time zone does not need to be passed when the [unit] is a time-based unit. + * It is also not needed when subtracting date-based units from a [LocalDate]. + * + * @throws DateTimeArithmeticException if this value or the result is too large to fit in [LocalDateTime]. + * @sample kotlinx.datetime.test.samples.InstantSamples.minusDateTimeUnit + */ +public fun Instant.minus(value: Long, unit: DateTimeUnit, timeZone: TimeZone): Instant = + if (value != Long.MIN_VALUE) { + plus(-value, unit, timeZone) + } else { + plus(-(value + 1), unit, timeZone).plus(1, unit, timeZone) + } + +/** + * Returns an instant that is the result of adding the [value] number of the specified [unit] to this instant. + * + * If the [value] is positive, the returned instant is later than this instant. + * If the [value] is negative, the returned instant is earlier than this instant. + * + * The return value is clamped to the boundaries of [Instant] if the result exceeds them. + * + * @sample kotlinx.datetime.test.samples.InstantSamples.plusTimeBasedUnit + */ +public expect fun Instant.plus(value: Long, unit: DateTimeUnit.TimeBased): Instant + +/** + * Returns an instant that is the result of subtracting the [value] number of the specified [unit] from this instant. + * + * If the [value] is positive, the returned instant is earlier than this instant. + * If the [value] is negative, the returned instant is later than this instant. + * + * The return value is clamped to the boundaries of [Instant] if the result exceeds them. + * + * @sample kotlinx.datetime.test.samples.InstantSamples.minusTimeBasedUnit + */ +public fun Instant.minus(value: Long, unit: DateTimeUnit.TimeBased): Instant = + if (value != Long.MIN_VALUE) { + plus(-value, unit) + } else { + plus(-(value + 1), unit).plus(1, unit) + } + +/** + * Returns the whole number of the specified date or time [units][unit] between [other] and `this` instants + * in the specified [timeZone]. + * + * The value returned is negative or zero if this instant is earlier than the other, + * and positive or zero if this instant is later than the other. + * + * If the result does not fit in [Long], returns [Long.MAX_VALUE] for a positive result or [Long.MIN_VALUE] for a negative result. + * + * @throws DateTimeArithmeticException if `this` or [other] instant is too large to fit in [LocalDateTime]. + * @see Instant.until for the same operation but with swapped arguments. + * @sample kotlinx.datetime.test.samples.InstantSamples.minusAsDateTimeUnit + */ +public fun Instant.minus(other: Instant, unit: DateTimeUnit, timeZone: TimeZone): Long = + other.until(this, unit, timeZone) + +/** + * Returns the whole number of the specified time [units][unit] between [other] and `this` instants. + * + * The value returned is negative or zero if this instant is earlier than the other, + * and positive or zero if this instant is later than the other. + * + * If the result does not fit in [Long], returns [Long.MAX_VALUE] for a positive result or [Long.MIN_VALUE] for a negative result. + * + * @see Instant.until for the same operation but with swapped arguments. + * @sample kotlinx.datetime.test.samples.InstantSamples.minusAsTimeBasedUnit + */ +public fun Instant.minus(other: Instant, unit: DateTimeUnit.TimeBased): Long = + other.until(this, unit) + +/** + * Formats this value using the given [format] using the given [offset]. + * + * Equivalent to calling [DateTimeFormat.format] on [format] and using [DateTimeComponents.setDateTimeOffset] in + * the lambda. + * + * [DateTimeComponents.Formats.ISO_DATE_TIME_OFFSET] is a format very similar to the one used by [toString]. + * The only difference is that [Instant.toString] adds trailing zeros to the fraction-of-second component so that the + * number of digits after a dot is a multiple of three. + * + * @sample kotlinx.datetime.test.samples.InstantSamples.formatting + */ +public fun Instant.format(format: DateTimeFormat, offset: UtcOffset = UtcOffset.ZERO): String { + val instant = this + return format.format { setDateTimeOffset(instant.toNewInstant(), offset) } +} + +public class DeprecationMarker private constructor() { + internal companion object { + internal val INSTANCE = DeprecationMarker() + } +} diff --git a/core/common/src/Instant.kt b/core/common/src/Instant.kt index 3deedd946..7130daaa0 100644 --- a/core/common/src/Instant.kt +++ b/core/common/src/Instant.kt @@ -3,435 +3,49 @@ * Use of this source code is governed by the Apache 2.0 License that can be found in the LICENSE.txt file. */ +@file:JvmMultifileClass +@file:JvmName("InstantKt") package kotlinx.datetime import kotlinx.datetime.format.* import kotlinx.datetime.internal.* -import kotlinx.datetime.serializers.InstantIso8601Serializer -import kotlinx.datetime.serializers.InstantComponentSerializer -import kotlinx.serialization.Serializable import kotlin.time.* +import kotlinx.time.Instant +import kotlin.jvm.JvmMultifileClass +import kotlin.jvm.JvmName /** - * A moment in time. + * A shortcut for calling [DateTimeFormat.parse], followed by [DateTimeComponents.toInstantUsingOffset]. * - * A point in time must be uniquely identified in a way that is independent of a time zone. - * For example, `1970-01-01, 00:00:00` does not represent a moment in time since this would happen at different times - * in different time zones: someone in Tokyo would think it is already `1970-01-01` several hours earlier than someone in - * Berlin would. To represent such entities, use [LocalDateTime]. - * In contrast, "the moment the clocks in London first showed 00:00 on Jan 1, 2000" is a specific moment - * in time, as is "1970-01-01, 00:00:00 UTC+0", so it can be represented as an [Instant]. + * Parses a string that represents an instant, including date and time components and a mandatory + * time zone offset and returns the parsed [Instant] value. * - * `Instant` uses the UTC-SLS (smeared leap second) time scale. This time scale doesn't contain instants - * corresponding to leap seconds, but instead "smears" positive and negative leap seconds among the last 1000 seconds - * of the day when a leap second happens. + * The string is considered to represent time on the UTC-SLS time scale instead of UTC. + * In practice, this means that, even if there is a leap second on the given day, it will not affect how the + * time is parsed, even if it's in the last 1000 seconds of the day. + * Instead, even if there is a negative leap second on the given day, 23:59:59 is still considered a valid time. + * 23:59:60 is invalid on UTC-SLS, so parsing it will fail. * - * ### Obtaining the current moment + * [Instant.parse] is equivalent to calling this function with the + * [DateTimeComponents.Formats.ISO_DATE_TIME_OFFSET] format. + * `2023-01-02T23:40:57.120Z` is an example of a string in this format. * - * The [Clock] interface is the primary way to obtain the current moment: + * @throws IllegalArgumentException if the text cannot be parsed or the boundaries of [Instant] are exceeded. * - * ``` - * val clock: Clock = Clock.System - * val instant = clock.now() - * ``` - * - * The [Clock.System] implementation uses the platform-specific system clock to obtain the current moment. - * Note that this clock is not guaranteed to be monotonic, and the user or the system may adjust it at any time, - * so it should not be used for measuring time intervals. - * For that, consider using [TimeSource.Monotonic] and [TimeMark] instead of [Clock.System] and [Instant]. - * - * ### Obtaining human-readable representations - * - * #### Date and time - * - * [Instant] is essentially the number of seconds and nanoseconds since a designated moment in time, - * stored as something like `1709898983.123456789`. - * [Instant] does not contain information about the day or time, as this depends on the time zone. - * To work with this information for a specific time zone, obtain a [LocalDateTime] using [Instant.toLocalDateTime]: - * - * ``` - * val instant = Instant.fromEpochSeconds(1709898983, 123456789) - * instant.toLocalDateTime(TimeZone.of("Europe/Berlin")) // 2024-03-08T12:56:23.123456789 - * instant.toLocalDateTime(TimeZone.UTC) // 2024-03-08T11:56:23.123456789 - * ``` - * - * For values very far in the past or the future, this conversion may fail. - * The specific range of values that can be converted to [LocalDateTime] is unspecified, but at least - * [DISTANT_PAST], [DISTANT_FUTURE], and all values between them are included in that range. - * - * #### Date or time separately - * - * To obtain a [LocalDate] or [LocalTime], first, obtain a [LocalDateTime], and then use its [LocalDateTime.date] - * and [LocalDateTime.time] properties: - * - * ``` - * val instant = Instant.fromEpochSeconds(1709898983, 123456789) - * instant.toLocalDateTime(TimeZone.of("Europe/Berlin")).date // 2024-03-08 - * ``` - * - * ### Arithmetic operations - * - * #### Elapsed-time-based - * - * The [plus] and [minus] operators can be used to add [Duration]s to and subtract them from an [Instant]: - * - * ``` - * Clock.System.now() + 5.seconds // 5 seconds from now - * ``` - * - * Durations can also be represented as multiples of some [time-based datetime unit][DateTimeUnit.TimeBased]: - * - * ``` - * Clock.System.now().plus(4, DateTimeUnit.HOUR) // 4 hours from now - * ``` - * - * Also, there is a [minus] operator that returns the [Duration] representing the difference between two instants: - * - * ``` - * val start = Clock.System.now() - * val concertStart = LocalDateTime(2023, 1, 1, 20, 0, 0).toInstant(TimeZone.of("Europe/Berlin")) - * val timeUntilConcert = concertStart - start - * ``` - * - * #### Calendar-based - * - * Since [Instant] represents a point in time, it is always well-defined what the result of arithmetic operations on it - * is, including the cases when a calendar is used. - * This is not the case for [LocalDateTime], where the result of arithmetic operations depends on the time zone. - * See the [LocalDateTime] documentation for more details. - * - * Adding and subtracting calendar-based units can be done using the [plus] and [minus] operators, - * requiring a [TimeZone]: - * - * ``` - * // One day from now in Berlin - * Clock.System.now().plus(1, DateTimeUnit.DAY, TimeZone.of("Europe/Berlin")) - * - * // A day and two hours short from two months later in Berlin - * Clock.System.now().plus(DateTimePeriod(months = 2, days = -1, hours = -2), TimeZone.of("Europe/Berlin")) - * ``` - * - * The difference between [Instant] values in terms of calendar-based units can be obtained using the [periodUntil] - * method: - * - * ``` - * val start = Clock.System.now() - * val concertStart = LocalDateTime(2023, 1, 1, 20, 0, 0).toInstant(TimeZone.of("Europe/Berlin")) - * val timeUntilConcert = start.periodUntil(concertStart, TimeZone.of("Europe/Berlin")) - * // Two months, three days, four hours, and five minutes until the concert - * ``` - * - * Or the [Instant.until] method, as well as [Instant.daysUntil], [Instant.monthsUntil], - * and [Instant.yearsUntil] extension functions: - * - * ``` - * val start = Clock.System.now() - * val concertStart = LocalDateTime(2023, 1, 1, 20, 0, 0).toInstant(TimeZone.of("Europe/Berlin")) - * val timeUntilConcert = start.until(concertStart, DateTimeUnit.DAY, TimeZone.of("Europe/Berlin")) - * // 63 days until the concert, rounded down - * ``` - * - * ### Platform specifics - * - * On the JVM, there are `Instant.toJavaInstant()` and `java.time.Instant.toKotlinInstant()` - * extension functions to convert between `kotlinx.datetime` and `java.time` objects used for the same purpose. - * Similarly, on the Darwin platforms, there are `Instant.toNSDate()` and `NSDate.toKotlinInstant()` - * extension functions. - * - * ### Construction, serialization, and deserialization - * - * [fromEpochSeconds] can be used to construct an instant from the number of seconds since - * `1970-01-01T00:00:00Z` (the Unix epoch). - * [epochSeconds] and [nanosecondsOfSecond] can be used to obtain the number of seconds and nanoseconds since the epoch. - * - * ``` - * val instant = Instant.fromEpochSeconds(1709898983, 123456789) - * instant.epochSeconds // 1709898983 - * instant.nanosecondsOfSecond // 123456789 - * ``` - * - * [fromEpochMilliseconds] allows constructing an instant from the number of milliseconds since the epoch. - * [toEpochMilliseconds] can be used to obtain the number of milliseconds since the epoch. - * Note that [Instant] supports nanosecond precision, so converting to milliseconds is a lossy operation. - * - * ``` - * val instant1 = Instant.fromEpochSeconds(1709898983, 123456789) - * instant1.nanosecondsOfSecond // 123456789 - * val milliseconds = instant1.toEpochMilliseconds() // 1709898983123 - * val instant2 = Instant.fromEpochMilliseconds(milliseconds) - * instant2.nanosecondsOfSecond // 123000000 - * ``` - * - * [parse] and [toString] methods can be used to obtain an [Instant] from and convert it to a string in the - * ISO 8601 extended format. - * - * ``` - * val instant = Instant.parse("2023-01-02T22:35:01+01:00") - * instant.toString() // 2023-01-02T21:35:01Z - * ``` - * - * During parsing, the UTC offset is not returned separately. - * If the UTC offset is important, use [DateTimeComponents] with [DateTimeComponents.Formats.ISO_DATE_TIME_OFFSET] to - * parse the string instead. - * - * [Instant.parse] and [Instant.format] also accept custom formats: - * - * ``` - * val customFormat = DateTimeComponents.Format { - * date(LocalDate.Formats.ISO) - * char(' ') - * time(LocalTime.Formats.ISO) - * char(' ') - * offset(UtcOffset.Formats.ISO) - * } - * val instant = Instant.parse("2023-01-02 22:35:01.14 +01:00", customFormat) - * instant.format(customFormat, offset = UtcOffset(hours = 2)) // 2023-01-02 23:35:01.14 +02:00 - * ``` - * - * Additionally, there are several `kotlinx-serialization` serializers for [Instant]: - * - [InstantIso8601Serializer] for the ISO 8601 extended format. - * - [InstantComponentSerializer] for an object with components. - * - * @see LocalDateTime for a user-visible representation of moments in time in an unspecified time zone. + * @see Instant.toString for formatting using the default format. + * @see Instant.format for formatting using a custom format. + * @see Instant.parse for parsing an ISO string without involving `kotlinx-datetime`. + * @sample kotlinx.datetime.test.samples.InstantSamples.parsing */ -@Serializable(with = InstantIso8601Serializer::class) -public expect class Instant : Comparable { - - /** - * The number of seconds from the epoch instant `1970-01-01T00:00:00Z` rounded down to a [Long] number. - * - * The difference between the rounded number of seconds and the actual number of seconds - * is returned by [nanosecondsOfSecond] property expressed in nanoseconds. - * - * Note that this number doesn't include leap seconds added or removed since the epoch. - * - * @see fromEpochSeconds - * @sample kotlinx.datetime.test.samples.InstantSamples.epochSeconds - */ - public val epochSeconds: Long - - /** - * The number of nanoseconds by which this instant is later than [epochSeconds] from the epoch instant. - * - * The value is always non-negative and lies in the range `0..999_999_999`. - * - * @see fromEpochSeconds - * @sample kotlinx.datetime.test.samples.InstantSamples.nanosecondsOfSecond - */ - public val nanosecondsOfSecond: Int - - /** - * Returns the number of milliseconds from the epoch instant `1970-01-01T00:00:00Z`. - * - * Any fractional part of a millisecond is rounded toward zero to the whole number of milliseconds. - * - * If the result does not fit in [Long], - * returns [Long.MAX_VALUE] for a positive result or [Long.MIN_VALUE] for a negative result. - * - * @see fromEpochMilliseconds - * @sample kotlinx.datetime.test.samples.InstantSamples.toEpochMilliseconds - */ - public fun toEpochMilliseconds(): Long - - /** - * Returns an instant that is the result of adding the specified [duration] to this instant. - * - * If the [duration] is positive, the returned instant is later than this instant. - * If the [duration] is negative, the returned instant is earlier than this instant. - * - * The return value is clamped to the boundaries of [Instant] if the result exceeds them. - * - * **Pitfall**: [Duration.Companion.days] are multiples of 24 hours and are not calendar-based. - * Consider using the [plus] overload that accepts a multiple of a [DateTimeUnit] instead for calendar-based - * operations instead of using [Duration]. - * For an explanation of why some days are not 24 hours, see [DateTimeUnit.DayBased]. - * - * @sample kotlinx.datetime.test.samples.InstantSamples.plusDuration - */ - public operator fun plus(duration: Duration): Instant - - /** - * Returns an instant that is the result of subtracting the specified [duration] from this instant. - * - * If the [duration] is positive, the returned instant is earlier than this instant. - * If the [duration] is negative, the returned instant is later than this instant. - * - * The return value is clamped to the boundaries of [Instant] if the result exceeds them. - * - * **Pitfall**: [Duration.Companion.days] are multiples of 24 hours and are not calendar-based. - * Consider using the [minus] overload that accepts a multiple of a [DateTimeUnit] instead for calendar-based - * operations instead of using [Duration]. - * For an explanation of why some days are not 24 hours, see [DateTimeUnit.DayBased]. - * - * @sample kotlinx.datetime.test.samples.InstantSamples.minusDuration - */ - public operator fun minus(duration: Duration): Instant - - // questionable - /** - * Returns the [Duration] between two instants: [other] and `this`. - * - * The duration returned is positive if this instant is later than the other, - * and negative if this instant is earlier than the other. - * - * The result is never clamped, but note that for instants that are far apart, - * the value returned may represent the duration between them inexactly due to the loss of precision. - * - * Note that sources of [Instant] values (in particular, [Clock]) are not guaranteed to be in sync with each other - * or even monotonic, so the result of this operation may be negative even if the other instant was observed later - * than this one, or vice versa. - * For measuring time intervals, consider using [TimeSource.Monotonic]. - * - * @sample kotlinx.datetime.test.samples.InstantSamples.minusInstant - */ - public operator fun minus(other: Instant): Duration - - /** - * Compares `this` instant with the [other] instant. - * Returns zero if this instant represents the same moment as the other (meaning they are equal to one another), - * a negative number if this instant is earlier than the other, - * and a positive number if this instant is later than the other. - * - * @sample kotlinx.datetime.test.samples.InstantSamples.compareToSample - */ - public override operator fun compareTo(other: Instant): Int - - /** - * Converts this instant to the ISO 8601 string representation, for example, `2023-01-02T23:40:57.120Z`. - * - * The representation uses the UTC-SLS time scale instead of UTC. - * In practice, this means that leap second handling will not be readjusted to the UTC. - * Leap seconds will not be added or skipped, so it is impossible to acquire a string - * where the component for seconds is 60, and for any day, it's possible to observe 23:59:59. - * - * @see parse - * @see DateTimeComponents.Formats.ISO_DATE_TIME_OFFSET for a very similar format. The difference is that - * [DateTimeComponents.Formats.ISO_DATE_TIME_OFFSET] will not add trailing zeros for readability to the - * fractional part of the second. - * @sample kotlinx.datetime.test.samples.InstantSamples.toStringSample - */ - public override fun toString(): String - - - public companion object { - @Deprecated("Use Clock.System.now() instead", ReplaceWith("Clock.System.now()", "kotlinx.datetime.Clock"), level = DeprecationLevel.ERROR) - public fun now(): Instant - - /** - * Returns an [Instant] that is [epochMilliseconds] number of milliseconds from the epoch instant `1970-01-01T00:00:00Z`. - * - * Every value of [epochMilliseconds] is guaranteed to be representable as an [Instant]. - * - * Note that [Instant] also supports nanosecond precision via [fromEpochSeconds]. - * - * @see Instant.toEpochMilliseconds - * @sample kotlinx.datetime.test.samples.InstantSamples.fromEpochMilliseconds - */ - public fun fromEpochMilliseconds(epochMilliseconds: Long): Instant - - /** - * Returns an [Instant] that is the [epochSeconds] number of seconds from the epoch instant `1970-01-01T00:00:00Z` - * and the [nanosecondAdjustment] number of nanoseconds from the whole second. - * - * The return value is clamped to the boundaries of [Instant] if the result exceeds them. - * In any case, it is guaranteed that instants between [DISTANT_PAST] and [DISTANT_FUTURE] can be represented. - * - * [fromEpochMilliseconds] is a similar function for when input data only has millisecond precision. - * - * @see Instant.epochSeconds - * @see Instant.nanosecondsOfSecond - * @sample kotlinx.datetime.test.samples.InstantSamples.fromEpochSeconds - */ - public fun fromEpochSeconds(epochSeconds: Long, nanosecondAdjustment: Long = 0): Instant - - /** - * Returns an [Instant] that is the [epochSeconds] number of seconds from the epoch instant `1970-01-01T00:00:00Z` - * and the [nanosecondAdjustment] number of nanoseconds from the whole second. - * - * The return value is clamped to the boundaries of [Instant] if the result exceeds them. - * In any case, it is guaranteed that instants between [DISTANT_PAST] and [DISTANT_FUTURE] can be represented. - * - * [fromEpochMilliseconds] is a similar function for when input data only has millisecond precision. - * - * @see Instant.epochSeconds - * @see Instant.nanosecondsOfSecond - * @sample kotlinx.datetime.test.samples.InstantSamples.fromEpochSecondsIntNanos - */ - public fun fromEpochSeconds(epochSeconds: Long, nanosecondAdjustment: Int): Instant - - /** - * A shortcut for calling [DateTimeFormat.parse], followed by [DateTimeComponents.toInstantUsingOffset]. - * - * Parses a string that represents an instant, including date and time components and a mandatory - * time zone offset and returns the parsed [Instant] value. - * - * The string is considered to represent time on the UTC-SLS time scale instead of UTC. - * In practice, this means that, even if there is a leap second on the given day, it will not affect how the - * time is parsed, even if it's in the last 1000 seconds of the day. - * Instead, even if there is a negative leap second on the given day, 23:59:59 is still considered a valid time. - * 23:59:60 is invalid on UTC-SLS, so parsing it will fail. - * - * If the format is not specified, [DateTimeComponents.Formats.ISO_DATE_TIME_OFFSET] is used. - * `2023-01-02T23:40:57.120Z` is an example of a string in this format. - * - * @throws IllegalArgumentException if the text cannot be parsed or the boundaries of [Instant] are exceeded. - * - * @see Instant.toString for formatting using the default format. - * @see Instant.format for formatting using a custom format. - * @sample kotlinx.datetime.test.samples.InstantSamples.parsing - */ - public fun parse( - input: CharSequence, - format: DateTimeFormat = DateTimeComponents.Formats.ISO_DATE_TIME_OFFSET - ): Instant - - /** - * An instant value that is far in the past. - * - * All instants in the range `DISTANT_PAST..DISTANT_FUTURE` can be [converted][Instant.toLocalDateTime] to - * [LocalDateTime] without exceptions in every time zone. - * - * [isDistantPast] returns true for this value and all earlier ones. - */ - public val DISTANT_PAST: Instant // -100001-12-31T23:59:59.999999999Z - - /** - * An instant value that is far in the future. - * - * All instants in the range `DISTANT_PAST..DISTANT_FUTURE` can be [converted][Instant.toLocalDateTime] to - * [LocalDateTime] without exceptions in every time zone. - * - * [isDistantFuture] returns true for this value and all later ones. - */ - public val DISTANT_FUTURE: Instant // +100000-01-01T00:00:00Z - - internal val MIN: Instant - internal val MAX: Instant - } +public fun Instant.Companion.parse( + input: CharSequence, + format: DateTimeFormat, +): Instant = try { + format.parse(input).toInstantUsingOffset() +} catch (e: IllegalArgumentException) { + throw DateTimeFormatException("Failed to parse an instant from '$input'", e) } -/** - * Returns true if the instant is [Instant.DISTANT_PAST] or earlier. - * - * @sample kotlinx.datetime.test.samples.InstantSamples.isDistantPast - */ -public val Instant.isDistantPast: Boolean - get() = this <= Instant.DISTANT_PAST - -/** - * Returns true if the instant is [Instant.DISTANT_FUTURE] or later. - * - * @sample kotlinx.datetime.test.samples.InstantSamples.isDistantFuture - */ -public val Instant.isDistantFuture: Boolean - get() = this >= Instant.DISTANT_FUTURE - -/** - * @suppress - */ -@Deprecated("Removed to support more idiomatic code. See https://github.com/Kotlin/kotlinx-datetime/issues/339", ReplaceWith("Instant.parse(this)"), DeprecationLevel.WARNING) -public fun String.toInstant(): Instant = Instant.parse(this) - /** * Returns an instant that is the result of adding components of [DateTimePeriod] to this instant. The components are * added in the order from the largest units to the smallest, i.e., from years to nanoseconds. diff --git a/core/common/src/TimeZone.kt b/core/common/src/TimeZone.kt index 808a8ad69..3636920e2 100644 --- a/core/common/src/TimeZone.kt +++ b/core/common/src/TimeZone.kt @@ -10,6 +10,7 @@ package kotlinx.datetime import kotlinx.datetime.serializers.* import kotlinx.serialization.Serializable +import kotlinx.time.Instant /** * A time zone, provides the conversion between [Instant] and [LocalDateTime] values @@ -126,6 +127,11 @@ public expect open class TimeZone { */ public fun Instant.toLocalDateTime(): LocalDateTime + @PublishedApi + @Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE", "DEPRECATION_ERROR") + @kotlin.internal.LowPriorityInOverloadResolution + internal fun kotlinx.datetime.Instant.toLocalDateTime(): LocalDateTime + /** * Returns an instant that corresponds to this civil datetime value in the time zone provided as an implicit receiver. * @@ -141,7 +147,12 @@ public expect open class TimeZone { * @see Instant.toLocalDateTime * @sample kotlinx.datetime.test.samples.TimeZoneSamples.toInstantWithTwoReceivers */ - public fun LocalDateTime.toInstant(): Instant + public fun LocalDateTime.toInstant(deprecationMarker: DeprecationMarker = DeprecationMarker.INSTANCE): Instant + + @PublishedApi + @Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE", "DEPRECATION_ERROR") + @kotlin.internal.LowPriorityInOverloadResolution + internal fun LocalDateTime.toInstant(): kotlinx.datetime.Instant } /** @@ -195,6 +206,11 @@ public typealias ZoneOffset = FixedOffsetTimeZone */ public expect fun TimeZone.offsetAt(instant: Instant): UtcOffset +@PublishedApi +@Suppress("DEPRECATION_ERROR") +internal fun TimeZone.offsetAt(instant: kotlinx.datetime.Instant): UtcOffset = + offsetAt(instant.toNewInstant()) + /** * Returns a civil datetime value that this instant has in the specified [timeZone]. * @@ -208,6 +224,11 @@ public expect fun TimeZone.offsetAt(instant: Instant): UtcOffset */ public expect fun Instant.toLocalDateTime(timeZone: TimeZone): LocalDateTime +@PublishedApi +@Suppress("DEPRECATION_ERROR") +internal fun kotlinx.datetime.Instant.toLocalDateTime(timeZone: TimeZone): LocalDateTime = + toNewInstant().toLocalDateTime(timeZone) + /** * Returns a civil datetime value that this instant has in the specified [UTC offset][offset]. * @@ -221,6 +242,11 @@ public expect fun Instant.toLocalDateTime(timeZone: TimeZone): LocalDateTime */ internal expect fun Instant.toLocalDateTime(offset: UtcOffset): LocalDateTime +@PublishedApi +@Suppress("DEPRECATION_ERROR") +internal fun kotlinx.datetime.Instant.toLocalDateTime(offset: UtcOffset): LocalDateTime = + toNewInstant().toLocalDateTime(offset) + /** * Finds the offset from UTC the specified [timeZone] has at this instant of physical time. * @@ -235,6 +261,11 @@ internal expect fun Instant.toLocalDateTime(offset: UtcOffset): LocalDateTime public fun Instant.offsetIn(timeZone: TimeZone): UtcOffset = timeZone.offsetAt(this) +@PublishedApi +@Suppress("DEPRECATION_ERROR") +internal fun kotlinx.datetime.Instant.offsetIn(timeZone: TimeZone): UtcOffset = + timeZone.offsetAt(toNewInstant()) + /** * Returns an instant that corresponds to this civil datetime value in the specified [timeZone]. * @@ -250,7 +281,13 @@ public fun Instant.offsetIn(timeZone: TimeZone): UtcOffset = * @see Instant.toLocalDateTime * @sample kotlinx.datetime.test.samples.TimeZoneSamples.localDateTimeToInstantInZone */ -public expect fun LocalDateTime.toInstant(timeZone: TimeZone): Instant +public expect fun LocalDateTime.toInstant(timeZone: TimeZone, deprecationMarker: DeprecationMarker = DeprecationMarker.INSTANCE): Instant + +@PublishedApi +@Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE", "DEPRECATION_ERROR") +@kotlin.internal.LowPriorityInOverloadResolution +internal fun LocalDateTime.toInstant(timeZone: TimeZone): kotlinx.datetime.Instant = + toInstant(timeZone).toDeprecatedInstant() /** * Returns an instant that corresponds to this civil datetime value that happens at the specified [UTC offset][offset]. @@ -258,7 +295,13 @@ public expect fun LocalDateTime.toInstant(timeZone: TimeZone): Instant * @see Instant.toLocalDateTime * @sample kotlinx.datetime.test.samples.TimeZoneSamples.localDateTimeToInstantInOffset */ -public expect fun LocalDateTime.toInstant(offset: UtcOffset): Instant +public expect fun LocalDateTime.toInstant(offset: UtcOffset, deprecationMarker: DeprecationMarker = DeprecationMarker.INSTANCE): Instant + +@PublishedApi +@Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE", "DEPRECATION_ERROR") +@kotlin.internal.LowPriorityInOverloadResolution +internal fun LocalDateTime.toInstant(offset: UtcOffset): kotlinx.datetime.Instant = + toInstant(offset).toDeprecatedInstant() /** * Returns an instant that corresponds to the start of this date in the specified [timeZone]. @@ -273,4 +316,10 @@ public expect fun LocalDateTime.toInstant(offset: UtcOffset): Instant * * @sample kotlinx.datetime.test.samples.TimeZoneSamples.atStartOfDayIn */ -public expect fun LocalDate.atStartOfDayIn(timeZone: TimeZone): Instant +public expect fun LocalDate.atStartOfDayIn(timeZone: TimeZone, deprecationMarker: DeprecationMarker = DeprecationMarker.INSTANCE): Instant + +@PublishedApi +@Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE", "DEPRECATION_ERROR") +@kotlin.internal.LowPriorityInOverloadResolution +internal fun LocalDate.atStartOfDayIn(timeZone: TimeZone): kotlinx.datetime.Instant = + atStartOfDayIn(timeZone).toDeprecatedInstant() diff --git a/core/common/src/format/DateTimeComponents.kt b/core/common/src/format/DateTimeComponents.kt index 436fa4992..e2a535fb2 100644 --- a/core/common/src/format/DateTimeComponents.kt +++ b/core/common/src/format/DateTimeComponents.kt @@ -12,6 +12,7 @@ import kotlinx.datetime.internal.format.* import kotlinx.datetime.internal.format.parser.Copyable import kotlinx.datetime.internal.safeMultiply import kotlin.reflect.* +import kotlinx.time.Instant /** * A collection of datetime fields used specifically for parsing and formatting. @@ -244,6 +245,12 @@ public class DateTimeComponents internal constructor(internal val contents: Date year = year!! + ((instant.epochSeconds / SECONDS_PER_10000_YEARS) * 10000).toInt() } + @PublishedApi + @Suppress("DEPRECATION_ERROR") + internal fun setDateTimeOffset(instant: kotlinx.datetime.Instant, utcOffset: UtcOffset) { + setDateTimeOffset(instant.toNewInstant(), utcOffset) + } + /** * Writes the contents of the specified [localDateTime] and [utcOffset] to this [DateTimeComponents]. * @@ -474,7 +481,7 @@ public class DateTimeComponents internal constructor(internal val contents: Date * with one another. * @sample kotlinx.datetime.test.samples.format.DateTimeComponentsSamples.toInstantUsingOffset */ - public fun toInstantUsingOffset(): Instant { + public fun toInstantUsingOffset(deprecationMarker: DeprecationMarker = DeprecationMarker.INSTANCE): Instant { val offset = toUtcOffset() val time = toLocalTime() val truncatedDate = contents.date.copy() @@ -492,10 +499,16 @@ public class DateTimeComponents internal constructor(internal val contents: Date } catch (e: ArithmeticException) { throw DateTimeFormatException("The parsed date is outside the range representable by Instant", e) } - if (totalSeconds < Instant.MIN.epochSeconds || totalSeconds > Instant.MAX.epochSeconds) + val result = Instant.fromEpochSeconds(totalSeconds, nanosecond ?: 0) + if (result.epochSeconds != totalSeconds) throw DateTimeFormatException("The parsed date is outside the range representable by Instant") - return Instant.fromEpochSeconds(totalSeconds, nanosecond ?: 0) + return result } + + @PublishedApi + @Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE", "DEPRECATION_ERROR") + @kotlin.internal.LowPriorityInOverloadResolution + internal fun toInstantUsingOffset(): kotlinx.datetime.Instant = toInstantUsingOffset().toDeprecatedInstant() } /** diff --git a/core/common/src/serializers/InstantSerializers.kt b/core/common/src/serializers/DeprecatedInstantSerializers.kt similarity index 98% rename from core/common/src/serializers/InstantSerializers.kt rename to core/common/src/serializers/DeprecatedInstantSerializers.kt index c64bdf475..9d4d67f58 100644 --- a/core/common/src/serializers/InstantSerializers.kt +++ b/core/common/src/serializers/DeprecatedInstantSerializers.kt @@ -3,6 +3,7 @@ * Use of this source code is governed by the Apache 2.0 License that can be found in the LICENSE.txt file. */ +@file:Suppress("DEPRECATION_ERROR") package kotlinx.datetime.serializers import kotlinx.datetime.Instant diff --git a/core/common/test/ClockTimeSourceTest.kt b/core/common/test/ClockTimeSourceTest.kt index 561cd2221..a7f647a31 100644 --- a/core/common/test/ClockTimeSourceTest.kt +++ b/core/common/test/ClockTimeSourceTest.kt @@ -10,6 +10,8 @@ import kotlin.test.* import kotlin.time.* import kotlin.time.Duration.Companion.days import kotlin.time.Duration.Companion.nanoseconds +import kotlinx.time.Clock +import kotlinx.time.Instant @OptIn(ExperimentalTime::class) class ClockTimeSourceTest { @@ -43,7 +45,7 @@ class ClockTimeSourceTest { clock.instant -= 2.days assertEquals(-1.days, mark.elapsedNow()) - clock.instant = Instant.MAX + clock.instant = Instant.fromEpochSeconds(Long.MAX_VALUE) assertEquals(Duration.INFINITE, mark.elapsedNow()) } diff --git a/core/common/test/DeprecatedClockTimeSourceTest.kt b/core/common/test/DeprecatedClockTimeSourceTest.kt new file mode 100644 index 000000000..75030f3aa --- /dev/null +++ b/core/common/test/DeprecatedClockTimeSourceTest.kt @@ -0,0 +1,87 @@ +/* + * Copyright 2019-2023 JetBrains s.r.o. and contributors. + * Use of this source code is governed by the Apache 2.0 License that can be found in the LICENSE.txt file. + */ + +@file:Suppress("DEPRECATION_ERROR") +package kotlinx.datetime.test + +import kotlinx.datetime.* +import kotlin.test.* +import kotlin.time.* +import kotlin.time.Duration.Companion.days +import kotlin.time.Duration.Companion.nanoseconds + +@OptIn(ExperimentalTime::class) +class DeprecatedClockTimeSourceTest { + @Test + fun arithmetic() { + val timeSource = Clock.System.asTimeSource() + val mark0 = timeSource.markNow() + + val markPast = mark0 - 1.days + val markFuture = mark0 + 1.days + + assertTrue(markPast < mark0) + assertTrue(markFuture > mark0) + assertEquals(mark0, markPast + 1.days) + assertEquals(2.days, markFuture - markPast) + } + + @Test + fun elapsed() { + val clock = object : Clock { + var instant = Clock.System.now() + override fun now(): Instant = instant + } + val timeSource = clock.asTimeSource() + val mark = timeSource.markNow() + assertEquals(Duration.ZERO, mark.elapsedNow()) + + clock.instant += 1.days + assertEquals(1.days, mark.elapsedNow()) + + clock.instant -= 2.days + assertEquals(-1.days, mark.elapsedNow()) + + clock.instant = Instant.fromEpochSeconds(Long.MAX_VALUE) + assertEquals(Duration.INFINITE, mark.elapsedNow()) + } + + @Test + fun differentSources() { + val mark1 = Clock.System.asTimeSource().markNow() + val mark2 = object : Clock { + override fun now(): Instant = Instant.DISTANT_FUTURE + }.asTimeSource().markNow() + assertNotEquals(mark1, mark2) + assertFailsWith { mark1 - mark2 } + assertFailsWith { mark1 compareTo mark2 } + } + + @Test + fun saturation() { + val mark0 = Clock.System.asTimeSource().markNow() + + val markFuture = mark0 + Duration.INFINITE + val markPast = mark0 - Duration.INFINITE + + for (delta in listOf(Duration.ZERO, 1.nanoseconds, 1.days)) { + assertEquals(markFuture, markFuture - delta) + assertEquals(markFuture, markFuture + delta) + + assertEquals(markPast, markPast - delta) + assertEquals(markPast, markPast + delta) + } + val infinitePairs = listOf(markFuture to markPast, markFuture to mark0, mark0 to markPast) + for ((later, earlier) in infinitePairs) { + assertEquals(Duration.INFINITE, later - earlier) + assertEquals(-Duration.INFINITE, earlier - later) + } + assertEquals(Duration.ZERO, markFuture - markFuture) + assertEquals(Duration.ZERO, markPast - markPast) + + assertFailsWith { markFuture - Duration.INFINITE } + assertFailsWith { markPast + Duration.INFINITE } + } +} diff --git a/core/common/test/DeprecatedInstantTest.kt b/core/common/test/DeprecatedInstantTest.kt new file mode 100644 index 000000000..43be6ae20 --- /dev/null +++ b/core/common/test/DeprecatedInstantTest.kt @@ -0,0 +1,640 @@ +/* + * Copyright 2019-2020 JetBrains s.r.o. + * Use of this source code is governed by the Apache 2.0 License that can be found in the LICENSE.txt file. + */ + +@file:Suppress("DEPRECATION_ERROR") +package kotlinx.datetime.test + +import kotlinx.datetime.* +import kotlinx.datetime.format.* +import kotlinx.datetime.internal.* +import kotlin.random.* +import kotlin.test.* +import kotlin.time.* +import kotlin.time.Duration.Companion.days +import kotlin.time.Duration.Companion.hours +import kotlin.time.Duration.Companion.milliseconds +import kotlin.time.Duration.Companion.nanoseconds +import kotlin.time.Duration.Companion.seconds + +class DeprecatedInstantTest { + + @Test + fun testNow() { + val instant = Clock.System.now() + val millis = instant.toEpochMilliseconds() + + assertTrue(millis > 1_500_000_000_000L) + + println(instant) + println(instant.toEpochMilliseconds()) + + val millisInstant = Instant.fromEpochMilliseconds(millis) + + assertEquals(millis, millisInstant.toEpochMilliseconds()) + + val notEqualInstant = Instant.fromEpochMilliseconds(millis + 1) + assertNotEquals(notEqualInstant, instant) + } + + @Test + fun instantArithmetic() { + val instant = Clock.System.now().toEpochMilliseconds().let { Instant.fromEpochMilliseconds(it) } // round to millis + val diffMillis = Random.nextLong(1000, 1_000_000_000) + val diff = diffMillis.milliseconds + + val nextInstant = (instant.toEpochMilliseconds() + diffMillis).let { Instant.fromEpochMilliseconds(it) } + + assertEquals(diff, nextInstant - instant) + assertEquals(nextInstant, instant + diff) + assertEquals(instant, nextInstant - diff) + + println("this: $instant, next: $nextInstant, diff: ${diff.toIsoString()}") + } + + @Test + fun instantToLocalDTConversion() { + val now = Clock.System.now() + println(now.toLocalDateTime(TimeZone.UTC)) + println(now.toLocalDateTime(TimeZone.currentSystemDefault())) + } + + /* Based on the ThreeTenBp project. + * Copyright (c) 2007-present, Stephen Colebourne & Michael Nascimento Santos + */ + @Test + fun parseIsoString() { + val instants = arrayOf( + Triple("1970-01-01T00:00:00Z", 0, 0), + Triple("1970-01-01t00:00:00Z", 0, 0), + Triple("1970-01-01T00:00:00z", 0, 0), + Triple("1970-01-01T00:00:00.0Z", 0, 0), + Triple("1970-01-01T00:00:00.000000000Z", 0, 0), + Triple("1970-01-01T00:00:00.000000001Z", 0, 1), + Triple("1970-01-01T00:00:00.100000000Z", 0, 100000000), + Triple("1970-01-01T00:00:01Z", 1, 0), + Triple("1970-01-01T00:01:00Z", 60, 0), + Triple("1970-01-01T00:01:01Z", 61, 0), + Triple("1970-01-01T00:01:01.000000001Z", 61, 1), + Triple("1970-01-01T01:00:00.000000000Z", 3600, 0), + Triple("1970-01-01T01:01:01.000000001Z", 3661, 1), + Triple("1970-01-02T01:01:01.100000000Z", 90061, 100000000)) + instants.forEach { + val (str, seconds, nanos) = it + val instant = Instant.parse(str) + assertEquals(seconds.toLong() * 1000 + nanos / 1000000, instant.toEpochMilliseconds()) + } + + assertInvalidFormat { Instant.parse("1970-01-01T23:59:60Z")} + assertInvalidFormat { Instant.parse("1970-01-01T24:00:00Z")} + assertInvalidFormat { Instant.parse("1970-01-01T23:59Z")} + assertInvalidFormat { Instant.parse("x") } + assertInvalidFormat { Instant.parse("12020-12-31T23:59:59.000000000Z") } + // this string represents an Instant that is currently larger than Instant.MAX any of the implementations: + assertInvalidFormat { Instant.parse("+1000000001-12-31T23:59:59.000000000Z") } + } + + @Test + fun parseStringsWithOffsets() { + val strings = arrayOf( + Pair("2020-01-01T00:01:01.02+18:00", "2019-12-31T06:01:01.020Z"), + Pair("2020-01-01T00:01:01.123456789-17:59:59", "2020-01-01T18:01:00.123456789Z"), + Pair("2020-01-01T00:01:01.010203040+17:59:59", "2019-12-31T06:01:02.010203040Z"), + Pair("2020-01-01T00:01:01.010203040+17:59", "2019-12-31T06:02:01.010203040Z"), + Pair("2020-01-01T00:01:01+00", "2020-01-01T00:01:01Z"), + ) + strings.forEach { (str, strInZ) -> + val instant = Instant.parse(str) + assertEquals(Instant.parse(strInZ), instant, str) + assertEquals(strInZ, instant.toString(), str) + } + assertInvalidFormat { Instant.parse("2020-01-01T00:01:01+18:01") } + assertInvalidFormat { Instant.parse("2020-01-01T00:01:01+1801") } + assertInvalidFormat { Instant.parse("2020-01-01T00:01:01+0") } + assertInvalidFormat { Instant.parse("2020-01-01T00:01:01+") } + assertInvalidFormat { Instant.parse("2020-01-01T00:01:01") } + assertInvalidFormat { Instant.parse("2020-01-01T00:01:01+000000") } + + val instants = listOf( + Instant.DISTANT_FUTURE, + Instant.DISTANT_PAST, + Instant.fromEpochSeconds(0, 0)) + + val offsetStrings = listOf( + "Z", + "+03:12:14", + "-03:12:14", + "+02:35", + "-02:35", + "+04", + "-04", + ) + + val offsetFormat = UtcOffset.Format { + optional("Z") { + offsetHours() + optional { + char(':'); offsetMinutesOfHour() + optional { char(':'); offsetSecondsOfMinute() } + } + } + } + val offsets = offsetStrings.map { UtcOffset.parse(it, offsetFormat) } + + for (instant in instants) { + for (offsetIx in offsets.indices) { + val str = instant.format(DateTimeComponents.Formats.ISO_DATE_TIME_OFFSET, offsets[offsetIx]) + val offsetString = offsets[offsetIx].toString() + assertEquals(offsetString, offsetString.commonSuffixWith(str)) + assertEquals(instant, Instant.parse(str, DateTimeComponents.Formats.ISO_DATE_TIME_OFFSET)) + assertEquals(instant, Instant.parse(str)) + } + } + } + + @Test + fun instantCalendarArithmetic() { + val zone = TimeZone.of("Europe/Berlin") + + fun expectBetween(instant1: Instant, instant2: Instant, expected: Long, unit: DateTimeUnit) { + assertEquals(expected, instant1.until(instant2, unit, zone), "i1.until(i2)") + assertEquals(expected, -instant2.until(instant1, unit, zone), "i2.until(i1)") + assertEquals(expected, instant2.minus(instant1, unit, zone), "i2.minus(i1)") + assertEquals(expected, -instant1.minus(instant2, unit, zone), "i1.minus(i2)") + + for (timeUnit in listOf(DateTimeUnit.MICROSECOND, DateTimeUnit.MILLISECOND, DateTimeUnit.SECOND, DateTimeUnit.MINUTE, DateTimeUnit.HOUR)) { + val diff = instant2.minus(instant1, timeUnit, zone) + assertEquals(instant2 - instant1, timeUnit.duration * diff.toDouble()) + assertEquals(instant2, instant1.plus(diff, timeUnit, zone)) + assertEquals(instant1, instant2.minus(diff, timeUnit, zone)) + assertEquals(instant2, instant1.plus(diff, timeUnit)) + assertEquals(instant1, instant2.minus(diff, timeUnit)) + } + } + + val instant1 = LocalDateTime(2019, Month.OCTOBER, 27, 2, 59).toInstant(zone).toDeprecatedInstant() + checkComponents(instant1.toLocalDateTime(zone), 2019, 10, 27, 2, 59) + + val instant2 = instant1.plus(DateTimePeriod(hours = 24), zone) + checkComponents(instant2.toLocalDateTime(zone), 2019, 10, 28, 1, 59) + expectBetween(instant1, instant2, 24, DateTimeUnit.HOUR) + assertEquals(instant1, instant2.minus(DateTimePeriod(hours = 24), zone)) + + val instant3 = instant1.plus(1, DateTimeUnit.DAY, zone) + checkComponents(instant3.toLocalDateTime(zone), 2019, 10, 28, 2, 59) + expectBetween(instant1, instant3, 25, DateTimeUnit.HOUR) + expectBetween(instant1, instant3, 1, DateTimeUnit.DAY) + assertEquals(1, instant1.daysUntil(instant3, zone)) + assertEquals(instant1.minus(1, DateTimeUnit.HOUR), instant2.minus(1, DateTimeUnit.DAY, zone)) + + val instant4 = instant1.plus(14, DateTimeUnit.MONTH, zone) + checkComponents(instant4.toLocalDateTime(zone), 2020, 12, 27, 2, 59) + expectBetween(instant1, instant4, 1, DateTimeUnit.YEAR) + expectBetween(instant1, instant4, 4, DateTimeUnit.QUARTER) + expectBetween(instant1, instant4, 14, DateTimeUnit.MONTH) + expectBetween(instant1, instant4, 61, DateTimeUnit.WEEK) + expectBetween(instant1, instant4, 366 + 31 + 30, DateTimeUnit.DAY) + expectBetween(instant1, instant4, (366 + 31 + 30) * 24 + 1, DateTimeUnit.HOUR) + assertEquals(instant1.plus(1, DateTimeUnit.HOUR), instant4.minus(14, DateTimeUnit.MONTH, zone)) + + val period = DateTimePeriod(days = 1, hours = 1) + val instant5 = instant1.plus(period, zone) + checkComponents(instant5.toLocalDateTime(zone), 2019, 10, 28, 3, 59) + assertEquals(period, instant1.periodUntil(instant5, zone)) + assertEquals(period, instant5.minus(instant1, zone)) + assertEquals(26.hours, instant5.minus(instant1)) + assertEquals(instant1.plus(1, DateTimeUnit.HOUR), instant5.minus(period, zone)) + + val instant6 = instant1.plus(23, DateTimeUnit.HOUR, zone) + checkComponents(instant6.toLocalDateTime(zone), 2019, 10, 28, 0, 59) + expectBetween(instant1, instant6, 23, DateTimeUnit.HOUR) + expectBetween(instant1, instant6, 0, DateTimeUnit.DAY) + assertEquals(instant1, instant6.minus(23, DateTimeUnit.HOUR, zone)) + } + + @Test + fun addingMultiplesOf2_32() { + val pow2_32 = 1L shl 32 + val instant1 = Instant.fromEpochSeconds(0) + val instant2 = instant1.plus(pow2_32, DateTimeUnit.NANOSECOND, TimeZone.UTC) + assertEquals(pow2_32 / NANOS_PER_ONE, instant2.epochSeconds) + assertEquals(pow2_32 % NANOS_PER_ONE, instant2.nanosecondsOfSecond.toLong()) + + val instant3 = instant1.plus(pow2_32, DateTimeUnit.SECOND, TimeZone.UTC) + assertEquals(pow2_32, instant3.epochSeconds) + } + + @Test + fun unitMultiplesUntil() { + val unit1000days = DateTimeUnit.DAY * 1000 + val unit4years = DateTimeUnit.YEAR * 4 // longer than 1000-DAY + + val zone = TimeZone.UTC + val min = LocalDateTime.MIN.toInstant(zone) + val max = LocalDateTime.MAX.toInstant(zone) + val diffDays = min.until(max, unit1000days, zone) + val diffYears = min.until(max, unit4years, zone) + assertTrue(diffDays in 0..Int.MAX_VALUE, "difference in $unit1000days should fit in Int, was $diffDays") + assertTrue(diffDays > diffYears, "difference in $unit1000days unit must be more than in $unit4years unit, was $diffDays $diffYears") + + val unit500ns = DateTimeUnit.NANOSECOND * 500 + val start = Instant.parse("1700-01-01T00:00:00Z") + val end = start.plus(300, DateTimeUnit.YEAR, zone) + val diffNs = start.until(end, unit500ns, zone) + val diffUs = start.until(end, DateTimeUnit.MICROSECOND, zone) + assertEquals(diffUs * 2, diffNs) + + assertEquals(end, start.plus(diffNs, unit500ns, zone)) + assertEquals(start, end.plus(-diffUs, DateTimeUnit.MICROSECOND, zone)) + } + + @Test + fun instantOffset() { + val zone = TimeZone.of("Europe/Berlin") + val instant1 = LocalDateTime(2019, 10, 27, 2, 59, 0, 0).toInstant(zone) + val ldt1 = instant1.toLocalDateTime(zone) + val offset1 = instant1.offsetIn(zone) + checkComponents(ldt1, 2019, 10, 27, 2, 59) + assertEquals(instant1, ldt1.toInstant(offset1)) + + val instant2 = instant1 + 1.hours + val ldt2 = instant2.toLocalDateTime(zone) + val offset2 = instant2.offsetIn(zone) + assertEquals(ldt1, ldt2) + assertEquals(instant2, ldt2.toInstant(offset2)) + assertNotEquals(offset1, offset2) + assertEquals(offset1.totalSeconds.seconds, offset2.totalSeconds.seconds + 1.hours) + + val instant3 = instant2 - 2.hours + val offset3 = instant3.offsetIn(zone) + assertEquals(offset1, offset3) + + // without the minus, this test fails on JVM + (Instant.MAX - (2 * 365).days).offsetIn(zone) + } + + @Test + fun changingTimeZoneRules() { + val start = Instant.parse("1991-01-25T23:15:15.855Z") + val end = Instant.parse("2006-04-24T22:07:32.561Z") + val diff = start.periodUntil(end, TimeZone.of("Europe/Moscow")) + val end2 = start.plus(diff, TimeZone.of("Europe/Moscow")) + assertEquals(end, end2) + } + + @Test + fun diffInvariant() { + repeat(STRESS_TEST_ITERATIONS) { + val millis1 = Random.nextLong(2_000_000_000_000L) + val millis2 = Random.nextLong(2_000_000_000_000L) + val instant1 = Instant.fromEpochMilliseconds(millis1) + val instant2 = Instant.fromEpochMilliseconds(millis2) + + val diff = instant1.periodUntil(instant2, TimeZone.currentSystemDefault()) + val instant3 = instant1.plus(diff, TimeZone.currentSystemDefault()) + + if (instant2 != instant3) + println("start: $instant1, end: $instant2, start + diff: $instant3, diff: $diff") + } + } + + @Test + fun diffInvariantSameAsDate() { + repeat(STRESS_TEST_ITERATIONS) { + val millis1 = Random.nextLong(2_000_000_000_000L) + val millis2 = Random.nextLong(2_000_000_000_000L) + with(TimeZone.UTC) TZ@ { + val date1 = Instant.fromEpochMilliseconds(millis1).toLocalDateTime().date + val date2 = Instant.fromEpochMilliseconds(millis2).toLocalDateTime().date + val instant1 = date1.atStartOfDayIn(this@TZ) + val instant2 = date2.atStartOfDayIn(this@TZ) + + val diff1 = instant1.periodUntil(instant2, this@TZ) + val diff2 = date1.periodUntil(date2) + + if (diff1 != diff2) + throw AssertionError( + "start: $instant1, end: $instant2, diff by instants: $diff1, diff by dates: $diff2" + ) + } + } + } + + + @Test + fun zoneDependentDiff() { + val instant1 = Instant.parse("2019-04-01T00:00:00Z") + val instant2 = Instant.parse("2019-05-01T04:00:00Z") + + for (zone in (-12..12 step 3).map { h -> TimeZone.of("${if (h >= 0) "+" else ""}$h") }) { + val dt1 = instant1.toLocalDateTime(zone) + val dt2 = instant2.toLocalDateTime(zone) + val diff = instant1.periodUntil(instant2, zone) + println("diff between $dt1 and $dt2 at zone $zone: $diff") + } + } + + /* Based on the ThreeTenBp project. + * Copyright (c) 2007-present, Stephen Colebourne & Michael Nascimento Santos + */ + @Test + fun nanosecondAdjustment() { + for (i in -2..2L) { + for (j in 0..9) { + val t: Instant = Instant.fromEpochSeconds(i, j) + val t2: Instant = Instant.fromEpochSeconds(i, j.toLong()) + assertEquals(i, t.epochSeconds) + assertEquals(j, t.nanosecondsOfSecond) + assertEquals(t, t2) + } + for (j in -10..-1) { + val t: Instant = Instant.fromEpochSeconds(i, j) + val t2: Instant = Instant.fromEpochSeconds(i, j.toLong()) + assertEquals(i - 1, t.epochSeconds) + assertEquals(j + 1000000000, t.nanosecondsOfSecond) + assertEquals(t, t2) + } + for (j in 999_999_990..999_999_999) { + val t: Instant = Instant.fromEpochSeconds(i, j) + val t2: Instant = Instant.fromEpochSeconds(i, j.toLong()) + assertEquals(i, t.epochSeconds) + assertEquals(j, t.nanosecondsOfSecond) + assertEquals(t, t2) + } + } + val t = Instant.fromEpochSeconds(0, Int.MAX_VALUE) + assertEquals((Int.MAX_VALUE / 1_000_000_000).toLong(), t.epochSeconds) + assertEquals(Int.MAX_VALUE % 1_000_000_000, t.nanosecondsOfSecond) + val t2 = Instant.fromEpochSeconds(0, Long.MAX_VALUE) + assertEquals(Long.MAX_VALUE / 1_000_000_000, t2.epochSeconds) + assertEquals((Long.MAX_VALUE % 1_000_000_000).toInt(), t2.nanosecondsOfSecond) + } + + /* Based on the ThreeTenBp project. + * Copyright (c) 2007-present, Stephen Colebourne & Michael Nascimento Santos + */ + @Test + fun strings() { + assertEquals("0000-01-02T00:00:00Z", LocalDateTime(0, 1, 2, 0, 0, 0, 0).toInstant(TimeZone.UTC).toString()) + assertEquals("0000-01-01T12:30:00Z", LocalDateTime(0, 1, 1, 12, 30, 0, 0).toInstant(TimeZone.UTC).toString()) + assertEquals("0000-01-01T00:00:00.000000001Z", LocalDateTime(0, 1, 1, 0, 0, 0, 1).toInstant(TimeZone.UTC).toString()) + assertEquals("0000-01-01T00:00:00Z", LocalDateTime(0, 1, 1, 0, 0, 0, 0).toInstant(TimeZone.UTC).toString()) + assertEquals("-0001-12-31T23:59:59.999999999Z", LocalDateTime(-1, 12, 31, 23, 59, 59, 999999999).toInstant(TimeZone.UTC).toString()) + assertEquals("-0001-12-31T12:30:00Z", LocalDateTime(-1, 12, 31, 12, 30, 0, 0).toInstant(TimeZone.UTC).toString()) + assertEquals("-0001-12-30T12:30:00Z", LocalDateTime(-1, 12, 30, 12, 30, 0, 0).toInstant(TimeZone.UTC).toString()) + assertEquals("-9999-01-02T12:30:00Z", LocalDateTime(-9999, 1, 2, 12, 30, 0, 0).toInstant(TimeZone.UTC).toString()) + assertEquals("-9999-01-01T12:30:00Z", LocalDateTime(-9999, 1, 1, 12, 30, 0, 0).toInstant(TimeZone.UTC).toString()) + assertEquals("-9999-01-01T00:00:00Z", LocalDateTime(-9999, 1, 1, 0, 0, 0, 0).toInstant(TimeZone.UTC).toString()) + assertEquals("-10000-12-31T23:59:59.999999999Z", LocalDateTime(-10000, 12, 31, 23, 59, 59, 999999999).toInstant(TimeZone.UTC).toString()) + assertEquals("-10000-12-31T12:30:00Z", LocalDateTime(-10000, 12, 31, 12, 30, 0, 0).toInstant(TimeZone.UTC).toString()) + assertEquals("-10000-12-30T12:30:00Z", LocalDateTime(-10000, 12, 30, 12, 30, 0, 0).toInstant(TimeZone.UTC).toString()) + assertEquals("-15000-12-31T12:30:00Z", LocalDateTime(-15000, 12, 31, 12, 30, 0, 0).toInstant(TimeZone.UTC).toString()) + assertEquals("-19999-01-02T12:30:00Z", LocalDateTime(-19999, 1, 2, 12, 30, 0, 0).toInstant(TimeZone.UTC).toString()) + assertEquals("-19999-01-01T12:30:00Z", LocalDateTime(-19999, 1, 1, 12, 30, 0, 0).toInstant(TimeZone.UTC).toString()) + assertEquals("-19999-01-01T00:00:00Z", LocalDateTime(-19999, 1, 1, 0, 0, 0, 0).toInstant(TimeZone.UTC).toString()) + assertEquals("-20000-12-31T23:59:59.999999999Z", LocalDateTime(-20000, 12, 31, 23, 59, 59, 999999999).toInstant(TimeZone.UTC).toString()) + assertEquals("-20000-12-31T12:30:00Z", LocalDateTime(-20000, 12, 31, 12, 30, 0, 0).toInstant(TimeZone.UTC).toString()) + assertEquals("-20000-12-30T12:30:00Z", LocalDateTime(-20000, 12, 30, 12, 30, 0, 0).toInstant(TimeZone.UTC).toString()) + assertEquals("-25000-12-31T12:30:00Z", LocalDateTime(-25000, 12, 31, 12, 30, 0, 0).toInstant(TimeZone.UTC).toString()) + assertEquals("9999-12-30T12:30:00Z", LocalDateTime(9999, 12, 30, 12, 30, 0, 0).toInstant(TimeZone.UTC).toString()) + assertEquals("9999-12-31T12:30:00Z", LocalDateTime(9999, 12, 31, 12, 30, 0, 0).toInstant(TimeZone.UTC).toString()) + assertEquals("9999-12-31T23:59:59.999999999Z", LocalDateTime(9999, 12, 31, 23, 59, 59, 999999999).toInstant(TimeZone.UTC).toString()) + assertEquals("+10000-01-01T00:00:00Z", LocalDateTime(10000, 1, 1, 0, 0, 0, 0).toInstant(TimeZone.UTC).toString()) + assertEquals("+10000-01-01T12:30:00Z", LocalDateTime(10000, 1, 1, 12, 30, 0, 0).toInstant(TimeZone.UTC).toString()) + assertEquals("+10000-01-02T12:30:00Z", LocalDateTime(10000, 1, 2, 12, 30, 0, 0).toInstant(TimeZone.UTC).toString()) + assertEquals("+15000-12-31T12:30:00Z", LocalDateTime(15000, 12, 31, 12, 30, 0, 0).toInstant(TimeZone.UTC).toString()) + assertEquals("+19999-12-30T12:30:00Z", LocalDateTime(19999, 12, 30, 12, 30, 0, 0).toInstant(TimeZone.UTC).toString()) + assertEquals("+19999-12-31T12:30:00Z", LocalDateTime(19999, 12, 31, 12, 30, 0, 0).toInstant(TimeZone.UTC).toString()) + assertEquals("+19999-12-31T23:59:59.999999999Z", LocalDateTime(19999, 12, 31, 23, 59, 59, 999999999).toInstant(TimeZone.UTC).toString()) + assertEquals("+20000-01-01T00:00:00Z", LocalDateTime(20000, 1, 1, 0, 0, 0, 0).toInstant(TimeZone.UTC).toString()) + assertEquals("+20000-01-01T12:30:00Z", LocalDateTime(20000, 1, 1, 12, 30, 0, 0).toInstant(TimeZone.UTC).toString()) + assertEquals("+20000-01-02T12:30:00Z", LocalDateTime(20000, 1, 2, 12, 30, 0, 0).toInstant(TimeZone.UTC).toString()) + assertEquals("+25000-12-31T12:30:00Z", LocalDateTime(25000, 12, 31, 12, 30, 0, 0).toInstant(TimeZone.UTC).toString()) + assertEquals("+19999-12-31T23:59:59.009999999Z", LocalDateTime(19999, 12, 31, 23, 59, 59, 9999999).toInstant(TimeZone.UTC).toString()) + assertEquals("+19999-12-31T23:59:59.999999Z", LocalDateTime(19999, 12, 31, 23, 59, 59, 999999000).toInstant(TimeZone.UTC).toString()) + assertEquals("+19999-12-31T23:59:59.009999Z", LocalDateTime(19999, 12, 31, 23, 59, 59, 9999000).toInstant(TimeZone.UTC).toString()) + assertEquals("+19999-12-31T23:59:59.123Z", LocalDateTime(19999, 12, 31, 23, 59, 59, 123000000).toInstant(TimeZone.UTC).toString()) + assertEquals("+19999-12-31T23:59:59.100Z", LocalDateTime(19999, 12, 31, 23, 59, 59, 100000000).toInstant(TimeZone.UTC).toString()) + assertEquals("+19999-12-31T23:59:59.020Z", LocalDateTime(19999, 12, 31, 23, 59, 59, 20000000).toInstant(TimeZone.UTC).toString()) + assertEquals("+19999-12-31T23:59:59.003Z", LocalDateTime(19999, 12, 31, 23, 59, 59, 3000000).toInstant(TimeZone.UTC).toString()) + assertEquals("+19999-12-31T23:59:59.000400Z", LocalDateTime(19999, 12, 31, 23, 59, 59, 400000).toInstant(TimeZone.UTC).toString()) + assertEquals("+19999-12-31T23:59:59.000050Z", LocalDateTime(19999, 12, 31, 23, 59, 59, 50000).toInstant(TimeZone.UTC).toString()) + assertEquals("+19999-12-31T23:59:59.000006Z", LocalDateTime(19999, 12, 31, 23, 59, 59, 6000).toInstant(TimeZone.UTC).toString()) + assertEquals("+19999-12-31T23:59:59.000000700Z", LocalDateTime(19999, 12, 31, 23, 59, 59, 700).toInstant(TimeZone.UTC).toString()) + assertEquals("+19999-12-31T23:59:59.000000080Z", LocalDateTime(19999, 12, 31, 23, 59, 59, 80).toInstant(TimeZone.UTC).toString()) + assertEquals("+19999-12-31T23:59:59.000000009Z", LocalDateTime(19999, 12, 31, 23, 59, 59, 9).toInstant(TimeZone.UTC).toString()) + } + + @Test + fun distantPastAndFuture() { + val distantFutureString = "+100000-01-01T00:00:00Z" + val distantPastString = "-100001-12-31T23:59:59.999999999Z" + assertEquals(distantFutureString, Instant.DISTANT_FUTURE.toString()) + assertEquals(Instant.DISTANT_FUTURE, distantFutureString.toInstant()) + assertEquals(distantPastString, Instant.DISTANT_PAST.toString()) + assertEquals(Instant.DISTANT_PAST, distantPastString.toInstant()) + assertTrue(Instant.DISTANT_PAST.isDistantPast) + assertTrue(Instant.DISTANT_FUTURE.isDistantFuture) + assertFalse(Instant.DISTANT_PAST.isDistantFuture) + assertFalse(Instant.DISTANT_FUTURE.isDistantPast) + assertFalse((Instant.DISTANT_PAST + 1.nanoseconds).isDistantPast) + assertFalse((Instant.DISTANT_FUTURE - 1.nanoseconds).isDistantFuture) + assertTrue((Instant.DISTANT_PAST - 1.nanoseconds).isDistantPast) + assertTrue((Instant.DISTANT_FUTURE + 1.nanoseconds).isDistantFuture) + assertTrue(Instant.MAX.isDistantFuture) + assertFalse(Instant.MAX.isDistantPast) + assertTrue(Instant.MIN.isDistantPast) + assertFalse(Instant.MIN.isDistantFuture) + } + +} + +class DeprecatedInstantRangeTest { + private val UTC = TimeZone.UTC + private val maxValidInstant = LocalDateTime.MAX.toInstant(UTC).toDeprecatedInstant() + private val minValidInstant = LocalDateTime.MIN.toInstant(UTC).toDeprecatedInstant() + + private val largePositiveLongs = listOf(Long.MAX_VALUE, Long.MAX_VALUE - 1, Long.MAX_VALUE - 50) + private val largeNegativeLongs = listOf(Long.MIN_VALUE, Long.MIN_VALUE + 1, Long.MIN_VALUE + 50) + + private val largePositiveInstants = listOf(Instant.MAX, Instant.MAX - 1.seconds, Instant.MAX - 50.seconds) + private val largeNegativeInstants = listOf(Instant.MIN, Instant.MIN + 1.seconds, Instant.MIN + 50.seconds) + + private val smallInstants = listOf( + Instant.fromEpochMilliseconds(0), + Instant.fromEpochMilliseconds(1003), + Instant.fromEpochMilliseconds(253112) + ) + + + @Test + fun epochMillisecondsClamping() { + /* Any number of milliseconds in Long is representable as an Instant */ + for (instant in largePositiveInstants) { + assertEquals(Long.MAX_VALUE, instant.toEpochMilliseconds(), "$instant") + } + for (instant in largeNegativeInstants) { + assertEquals(Long.MIN_VALUE, instant.toEpochMilliseconds(), "$instant") + } + for (milliseconds in largePositiveLongs + largeNegativeLongs) { + assertEquals(milliseconds, Instant.fromEpochMilliseconds(milliseconds).toEpochMilliseconds(), + "$milliseconds") + } + } + + @Test + fun epochSecondsClamping() { + // fromEpochSeconds + // On all platforms Long.MAX_VALUE of seconds is not a valid instant. + for (seconds in largePositiveLongs) { + assertEquals(Instant.MAX, Instant.fromEpochSeconds(seconds, 35)) + } + for (seconds in largeNegativeLongs) { + assertEquals(Instant.MIN, Instant.fromEpochSeconds(seconds, 35)) + } + for (instant in largePositiveInstants + smallInstants + largeNegativeInstants) { + assertEquals(instant, Instant.fromEpochSeconds(instant.epochSeconds, instant.nanosecondsOfSecond.toLong())) + } + } + + @Test + fun durationArithmeticClamping() { + val longDurations = listOf(Duration.INFINITE) + + for (duration in longDurations) { + for (instant in smallInstants + largeNegativeInstants + largePositiveInstants) { + assertEquals(Instant.MAX, instant + duration) + } + for (instant in smallInstants + largeNegativeInstants + largePositiveInstants) { + assertEquals(Instant.MIN, instant - duration) + } + } + assertEquals(Instant.MAX, (Instant.MAX - 4.seconds) + 5.seconds) + assertEquals(Instant.MIN, (Instant.MIN + 10.seconds) - 12.seconds) + } + + @Test + fun periodArithmeticOutOfRange() { + // Instant.plus(DateTimePeriod(), TimeZone) + // Arithmetic overflow + for (instant in largePositiveInstants) { + assertArithmeticFails("$instant") { instant.plus(DateTimePeriod(nanoseconds = Long.MAX_VALUE), UTC) } + } + for (instant in largeNegativeInstants) { + assertArithmeticFails("$instant") { instant.plus(DateTimePeriod(nanoseconds = Long.MIN_VALUE), UTC) } + } + // Arithmetic overflow in an Int + for (instant in smallInstants + listOf(maxValidInstant)) { + assertEquals(instant.epochSeconds + Int.MIN_VALUE, + instant.plus(Int.MIN_VALUE, DateTimeUnit.SECOND, UTC).epochSeconds) + assertEquals(instant.epochSeconds - Int.MAX_VALUE, + instant.minus(Int.MAX_VALUE, DateTimeUnit.SECOND, UTC).epochSeconds) + } + for (instant in smallInstants + listOf(minValidInstant)) { + assertEquals(instant.epochSeconds + Int.MAX_VALUE, + instant.plus(Int.MAX_VALUE, DateTimeUnit.SECOND, UTC).epochSeconds) + assertEquals(instant.epochSeconds - Int.MIN_VALUE, + instant.minus(Int.MIN_VALUE, DateTimeUnit.SECOND, UTC).epochSeconds) + } + // Overflowing a LocalDateTime in input + maxValidInstant.plus(DateTimePeriod(nanoseconds = -1), UTC) + minValidInstant.plus(DateTimePeriod(nanoseconds = 1), UTC) + assertArithmeticFails { (maxValidInstant + 1.nanoseconds).plus(DateTimePeriod(nanoseconds = -2), UTC) } + assertArithmeticFails { (minValidInstant - 1.nanoseconds).plus(DateTimePeriod(nanoseconds = 2), UTC) } + // Overflowing a LocalDateTime in result + assertArithmeticFails { maxValidInstant.plus(DateTimePeriod(nanoseconds = 1), UTC) } + assertArithmeticFails { minValidInstant.plus(DateTimePeriod(nanoseconds = -1), UTC) } + // Overflowing a LocalDateTime in intermediate computations + assertArithmeticFails { maxValidInstant.plus(DateTimePeriod(days = 1, nanoseconds = -1_000_000_001), UTC) } + assertArithmeticFails { maxValidInstant.plus(DateTimePeriod(months = 1, days = -48), UTC) } + } + + @Test + fun unitArithmeticOutOfRange() { + // Instant.plus(Long, DateTimeUnit, TimeZone) + // Arithmetic overflow + for (instant in smallInstants + largeNegativeInstants + largePositiveInstants) { + assertArithmeticFails("$instant") { instant.plus(Long.MAX_VALUE, DateTimeUnit.SECOND, UTC) } + assertArithmeticFails("$instant") { instant.plus(Long.MIN_VALUE, DateTimeUnit.SECOND, UTC) } + assertArithmeticFails("$instant") { instant.plus(Long.MAX_VALUE, DateTimeUnit.YEAR, UTC) } + assertArithmeticFails("$instant") { instant.plus(Long.MIN_VALUE, DateTimeUnit.YEAR, UTC) } + } + for (instant in smallInstants) { + instant.plus(2 * Int.MAX_VALUE.toLong(), DateTimeUnit.DAY, UTC) + instant.plus(2 * Int.MIN_VALUE.toLong(), DateTimeUnit.DAY, UTC) + instant.plus(2 * Int.MAX_VALUE.toLong(), DateTimeUnit.MONTH, UTC) + instant.plus(2 * Int.MIN_VALUE.toLong(), DateTimeUnit.MONTH, UTC) + } + // Overflowing a LocalDateTime in input + maxValidInstant.plus(-1, DateTimeUnit.NANOSECOND, UTC) + minValidInstant.plus(1, DateTimeUnit.NANOSECOND, UTC) + assertArithmeticFails { (maxValidInstant + 1.nanoseconds).plus(-2, DateTimeUnit.NANOSECOND, UTC) } + assertArithmeticFails { (minValidInstant - 1.nanoseconds).plus(2, DateTimeUnit.NANOSECOND, UTC) } + // Overflowing a LocalDateTime in result + assertArithmeticFails { maxValidInstant.plus(1, DateTimeUnit.NANOSECOND, UTC) } + assertArithmeticFails { maxValidInstant.plus(1, DateTimeUnit.YEAR, UTC) } + assertArithmeticFails { minValidInstant.plus(-1, DateTimeUnit.NANOSECOND, UTC) } + assertArithmeticFails { minValidInstant.plus(-1, DateTimeUnit.YEAR, UTC) } + } + + @Test + fun timeBasedUnitArithmeticOutOfRange() { + // Instant.plus(Long, DateTimeUnit.TimeBased) + // Arithmetic overflow + for (instant in smallInstants + largeNegativeInstants + largePositiveInstants) { + assertEquals(Instant.MAX, instant.plus(Long.MAX_VALUE, DateTimeUnit.SECOND)) + assertEquals(Instant.MIN, instant.plus(Long.MIN_VALUE, DateTimeUnit.SECOND)) + } + // Overflow of Instant boundaries + for (instant in smallInstants + largeNegativeInstants + largePositiveInstants) { + assertEquals(Instant.MAX, instant.plus(Instant.MAX.epochSeconds - instant.epochSeconds + 1, DateTimeUnit.SECOND)) + assertEquals(Instant.MIN, instant.plus(Instant.MIN.epochSeconds - instant.epochSeconds - 1, DateTimeUnit.SECOND)) + } + } + + + @Test + fun periodUntilOutOfRange() { + // Instant.periodUntil + maxValidInstant.periodUntil(maxValidInstant, UTC) + assertArithmeticFails { (maxValidInstant + 1.nanoseconds).periodUntil(maxValidInstant, UTC) } + assertArithmeticFails { minValidInstant.periodUntil(minValidInstant - 1.nanoseconds, UTC) } + } + + @Test + fun unitsUntilClamping() { + // Arithmetic overflow of the resulting number + assertEquals(Long.MAX_VALUE, minValidInstant.until(maxValidInstant, DateTimeUnit.NANOSECOND, UTC)) + assertEquals(Long.MIN_VALUE, maxValidInstant.until(minValidInstant, DateTimeUnit.NANOSECOND, UTC)) + assertEquals(Long.MAX_VALUE, minValidInstant.until(maxValidInstant, DateTimeUnit.NANOSECOND)) + assertEquals(Long.MIN_VALUE, maxValidInstant.until(minValidInstant, DateTimeUnit.NANOSECOND)) + } + + @Test + fun unitsUntilOutOfRange() { + // Instant.until + // Overflowing a LocalDateTime in input + assertArithmeticFails { (maxValidInstant + 1.nanoseconds).until(maxValidInstant, DateTimeUnit.NANOSECOND, UTC) } + assertArithmeticFails { maxValidInstant.until(maxValidInstant + 1.nanoseconds, DateTimeUnit.NANOSECOND, UTC) } + // Overloads without a TimeZone should not fail on overflowing a LocalDateTime + (maxValidInstant + 1.nanoseconds).until(maxValidInstant, DateTimeUnit.NANOSECOND) + maxValidInstant.until(maxValidInstant + 1.nanoseconds, DateTimeUnit.NANOSECOND) + } + + // https://github.com/Kotlin/kotlinx-datetime/issues/263 + @Test + fun addSmallDurationsToLargeInstants() { + for (smallDuration in listOf(1.nanoseconds, 999_999.nanoseconds, 1.seconds - 1.nanoseconds)) { + assertEquals(expected = Instant.MAX, actual = Instant.MAX + smallDuration) + assertEquals(expected = Instant.MIN, actual = Instant.MIN - smallDuration) + } + } + + @Test + fun subtractInstants() { + val max = Instant.fromEpochSeconds(31494816403199L) + val min = Instant.fromEpochSeconds(-31619119219200L) + assertEquals(max.epochSeconds - min.epochSeconds, (max - min).inWholeSeconds) + } +} diff --git a/core/common/test/InstantTest.kt b/core/common/test/InstantTest.kt index 8bc41f019..3479cc530 100644 --- a/core/common/test/InstantTest.kt +++ b/core/common/test/InstantTest.kt @@ -16,6 +16,10 @@ import kotlin.time.Duration.Companion.hours import kotlin.time.Duration.Companion.milliseconds import kotlin.time.Duration.Companion.nanoseconds import kotlin.time.Duration.Companion.seconds +import kotlinx.time.Clock +import kotlinx.time.Instant +import kotlinx.time.isDistantFuture +import kotlinx.time.isDistantPast class InstantTest { @@ -59,99 +63,6 @@ class InstantTest { println(now.toLocalDateTime(TimeZone.currentSystemDefault())) } - /* Based on the ThreeTenBp project. - * Copyright (c) 2007-present, Stephen Colebourne & Michael Nascimento Santos - */ - @Test - fun parseIsoString() { - val instants = arrayOf( - Triple("1970-01-01T00:00:00Z", 0, 0), - Triple("1970-01-01t00:00:00Z", 0, 0), - Triple("1970-01-01T00:00:00z", 0, 0), - Triple("1970-01-01T00:00:00.0Z", 0, 0), - Triple("1970-01-01T00:00:00.000000000Z", 0, 0), - Triple("1970-01-01T00:00:00.000000001Z", 0, 1), - Triple("1970-01-01T00:00:00.100000000Z", 0, 100000000), - Triple("1970-01-01T00:00:01Z", 1, 0), - Triple("1970-01-01T00:01:00Z", 60, 0), - Triple("1970-01-01T00:01:01Z", 61, 0), - Triple("1970-01-01T00:01:01.000000001Z", 61, 1), - Triple("1970-01-01T01:00:00.000000000Z", 3600, 0), - Triple("1970-01-01T01:01:01.000000001Z", 3661, 1), - Triple("1970-01-02T01:01:01.100000000Z", 90061, 100000000)) - instants.forEach { - val (str, seconds, nanos) = it - val instant = Instant.parse(str) - assertEquals(seconds.toLong() * 1000 + nanos / 1000000, instant.toEpochMilliseconds()) - } - - assertInvalidFormat { Instant.parse("1970-01-01T23:59:60Z")} - assertInvalidFormat { Instant.parse("1970-01-01T24:00:00Z")} - assertInvalidFormat { Instant.parse("1970-01-01T23:59Z")} - assertInvalidFormat { Instant.parse("x") } - assertInvalidFormat { Instant.parse("12020-12-31T23:59:59.000000000Z") } - // this string represents an Instant that is currently larger than Instant.MAX any of the implementations: - assertInvalidFormat { Instant.parse("+1000000001-12-31T23:59:59.000000000Z") } - } - - @Test - fun parseStringsWithOffsets() { - val strings = arrayOf( - Pair("2020-01-01T00:01:01.02+18:00", "2019-12-31T06:01:01.020Z"), - Pair("2020-01-01T00:01:01.123456789-17:59:59", "2020-01-01T18:01:00.123456789Z"), - Pair("2020-01-01T00:01:01.010203040+17:59:59", "2019-12-31T06:01:02.010203040Z"), - Pair("2020-01-01T00:01:01.010203040+17:59", "2019-12-31T06:02:01.010203040Z"), - Pair("2020-01-01T00:01:01+00", "2020-01-01T00:01:01Z"), - ) - strings.forEach { (str, strInZ) -> - val instant = Instant.parse(str) - assertEquals(Instant.parse(strInZ), instant, str) - assertEquals(strInZ, instant.toString(), str) - } - assertInvalidFormat { Instant.parse("2020-01-01T00:01:01+18:01") } - assertInvalidFormat { Instant.parse("2020-01-01T00:01:01+1801") } - assertInvalidFormat { Instant.parse("2020-01-01T00:01:01+0") } - assertInvalidFormat { Instant.parse("2020-01-01T00:01:01+") } - assertInvalidFormat { Instant.parse("2020-01-01T00:01:01") } - assertInvalidFormat { Instant.parse("2020-01-01T00:01:01+000000") } - - val instants = listOf( - Instant.DISTANT_FUTURE, - Instant.DISTANT_PAST, - Instant.fromEpochSeconds(0, 0)) - - val offsetStrings = listOf( - "Z", - "+03:12:14", - "-03:12:14", - "+02:35", - "-02:35", - "+04", - "-04", - ) - - val offsetFormat = UtcOffset.Format { - optional("Z") { - offsetHours() - optional { - char(':'); offsetMinutesOfHour() - optional { char(':'); offsetSecondsOfMinute() } - } - } - } - val offsets = offsetStrings.map { UtcOffset.parse(it, offsetFormat) } - - for (instant in instants) { - for (offsetIx in offsets.indices) { - val str = instant.format(DateTimeComponents.Formats.ISO_DATE_TIME_OFFSET, offsets[offsetIx]) - val offsetString = offsets[offsetIx].toString() - assertEquals(offsetString, offsetString.commonSuffixWith(str)) - assertEquals(instant, Instant.parse(str, DateTimeComponents.Formats.ISO_DATE_TIME_OFFSET)) - assertEquals(instant, Instant.parse(str)) - } - } - } - @Test fun instantCalendarArithmetic() { val zone = TimeZone.of("Europe/Berlin") @@ -428,9 +339,9 @@ class InstantTest { val distantFutureString = "+100000-01-01T00:00:00Z" val distantPastString = "-100001-12-31T23:59:59.999999999Z" assertEquals(distantFutureString, Instant.DISTANT_FUTURE.toString()) - assertEquals(Instant.DISTANT_FUTURE, distantFutureString.toInstant()) + assertEquals(Instant.DISTANT_FUTURE, distantFutureString.let(Instant::parse)) assertEquals(distantPastString, Instant.DISTANT_PAST.toString()) - assertEquals(Instant.DISTANT_PAST, distantPastString.toInstant()) + assertEquals(Instant.DISTANT_PAST, distantPastString.let(Instant::parse)) assertTrue(Instant.DISTANT_PAST.isDistantPast) assertTrue(Instant.DISTANT_FUTURE.isDistantFuture) assertFalse(Instant.DISTANT_PAST.isDistantFuture) @@ -638,3 +549,9 @@ class InstantRangeTest { assertEquals(max.epochSeconds - min.epochSeconds, (max - min).inWholeSeconds) } } + +private val maxInstant = Instant.fromEpochSeconds(Long.MAX_VALUE) +private val minInstant = Instant.fromEpochSeconds(Long.MIN_VALUE) + +internal val Instant.Companion.MAX get() = maxInstant +internal val Instant.Companion.MIN get() = minInstant diff --git a/core/common/test/LocalDateTest.kt b/core/common/test/LocalDateTest.kt index 6d4065626..7e47e155d 100644 --- a/core/common/test/LocalDateTest.kt +++ b/core/common/test/LocalDateTest.kt @@ -7,6 +7,7 @@ package kotlinx.datetime.test import kotlinx.datetime.* import kotlinx.datetime.internal.* +import kotlinx.time.Clock import kotlin.random.* import kotlin.test.* diff --git a/core/common/test/LocalDateTimeTest.kt b/core/common/test/LocalDateTimeTest.kt index 906ef9339..5587e0aeb 100644 --- a/core/common/test/LocalDateTimeTest.kt +++ b/core/common/test/LocalDateTimeTest.kt @@ -6,7 +6,8 @@ package kotlinx.datetime.test import kotlinx.datetime.* -import kotlinx.datetime.Clock +import kotlinx.time.Clock +import kotlinx.time.Instant import kotlin.test.* import kotlin.time.* import kotlin.time.Duration.Companion.hours @@ -50,8 +51,8 @@ class LocalDateTimeTest { val diff = with(TimeZone.UTC) { ldt2.toInstant() - ldt1.toInstant() } assertEquals(with(Duration) { 1.hours + 7.minutes - 15.seconds + 400100.microseconds }, diff) - assertFailsWith { (Instant.MAX - 3.days).toLocalDateTime(TimeZone.UTC) } - assertFailsWith { (Instant.MIN + 6.hours).toLocalDateTime(TimeZone.UTC) } + assertFailsWith { (Instant.fromEpochSeconds(Long.MAX_VALUE) - 3.days).toLocalDateTime(TimeZone.UTC) } + assertFailsWith { (Instant.fromEpochSeconds(Long.MIN_VALUE) + 6.hours).toLocalDateTime(TimeZone.UTC) } } @Test @@ -67,7 +68,7 @@ class LocalDateTimeTest { val instant = Instant.parse("2019-10-01T18:43:15.100500Z") val datetime = instant.toLocalDateTime(TimeZone.UTC) checkComponents(datetime, 2019, 10, 1, 18, 43, 15, 100500000) - assertFailsWith { Instant.MAX.toLocalDateTime(TimeZone.UTC) } + assertFailsWith { Instant.fromEpochSeconds(Long.MAX_VALUE).toLocalDateTime(TimeZone.UTC) } } @Test diff --git a/core/common/test/ReadmeTest.kt b/core/common/test/ReadmeTest.kt index 696ff1510..490cbe837 100644 --- a/core/common/test/ReadmeTest.kt +++ b/core/common/test/ReadmeTest.kt @@ -9,6 +9,8 @@ import kotlinx.datetime.* import kotlinx.datetime.format.* import kotlin.test.* import kotlin.time.* +import kotlinx.time.Clock +import kotlinx.time.Instant /** * Tests the code snippets in the README.md file. diff --git a/core/common/test/TimeZoneTest.kt b/core/common/test/TimeZoneTest.kt index f59bd2cec..add0fa443 100644 --- a/core/common/test/TimeZoneTest.kt +++ b/core/common/test/TimeZoneTest.kt @@ -9,6 +9,8 @@ package kotlinx.datetime.test import kotlinx.datetime.* import kotlin.test.* +import kotlinx.time.Clock +import kotlinx.time.Instant class TimeZoneTest { diff --git a/core/common/test/format/DateTimeComponentsFormatTest.kt b/core/common/test/format/DateTimeComponentsFormatTest.kt index 07973d757..07a5862d4 100644 --- a/core/common/test/format/DateTimeComponentsFormatTest.kt +++ b/core/common/test/format/DateTimeComponentsFormatTest.kt @@ -8,6 +8,7 @@ package kotlinx.datetime.test.format import kotlinx.datetime.* import kotlinx.datetime.format.* import kotlin.test.* +import kotlinx.time.Instant class DateTimeComponentsFormatTest { diff --git a/core/common/test/format/DateTimeComponentsTest.kt b/core/common/test/format/DateTimeComponentsTest.kt index c61841265..e5c78b850 100644 --- a/core/common/test/format/DateTimeComponentsTest.kt +++ b/core/common/test/format/DateTimeComponentsTest.kt @@ -8,6 +8,7 @@ package kotlinx.datetime.test.format import kotlinx.datetime.* import kotlinx.datetime.format.* import kotlin.test.* +import kotlinx.time.Clock class DateTimeComponentsTest { @Test diff --git a/core/common/test/samples/ClockSamples.kt b/core/common/test/samples/ClockSamples.kt index 83a3a1563..66d47b3c0 100644 --- a/core/common/test/samples/ClockSamples.kt +++ b/core/common/test/samples/ClockSamples.kt @@ -7,6 +7,8 @@ package kotlinx.datetime.test.samples import kotlinx.datetime.* import kotlin.test.* +import kotlinx.time.Clock +import kotlinx.time.Instant class ClockSamples { @Test diff --git a/core/common/test/samples/DayOfWeekSamples.kt b/core/common/test/samples/DayOfWeekSamples.kt index c8393ae98..4d4e1e0cd 100644 --- a/core/common/test/samples/DayOfWeekSamples.kt +++ b/core/common/test/samples/DayOfWeekSamples.kt @@ -7,6 +7,7 @@ package kotlinx.datetime.test.samples import kotlinx.datetime.* import kotlin.test.* +import kotlinx.time.Clock class DayOfWeekSamples { diff --git a/core/common/test/samples/InstantSamples.kt b/core/common/test/samples/InstantSamples.kt index 8fec354b7..7e2a483cf 100644 --- a/core/common/test/samples/InstantSamples.kt +++ b/core/common/test/samples/InstantSamples.kt @@ -10,6 +10,10 @@ import kotlinx.datetime.format.* import kotlin.random.* import kotlin.test.* import kotlin.time.Duration.Companion.hours +import kotlinx.time.Clock +import kotlinx.time.Instant +import kotlinx.time.isDistantFuture +import kotlinx.time.isDistantPast class InstantSamples { diff --git a/core/common/test/samples/MonthSamples.kt b/core/common/test/samples/MonthSamples.kt index 70128845a..3efe3d46b 100644 --- a/core/common/test/samples/MonthSamples.kt +++ b/core/common/test/samples/MonthSamples.kt @@ -7,6 +7,7 @@ package kotlinx.datetime.test.samples import kotlinx.datetime.* import kotlin.test.* +import kotlinx.time.Clock class MonthSamples { diff --git a/core/common/test/samples/TimeZoneSamples.kt b/core/common/test/samples/TimeZoneSamples.kt index 71ef13fb6..3456a9599 100644 --- a/core/common/test/samples/TimeZoneSamples.kt +++ b/core/common/test/samples/TimeZoneSamples.kt @@ -8,6 +8,8 @@ package kotlinx.datetime.test.samples import kotlinx.datetime.* import kotlinx.datetime.format.* import kotlin.test.* +import kotlinx.time.Instant +import kotlinx.time.Clock class TimeZoneSamples { diff --git a/core/common/test/samples/format/DateTimeComponentsSamples.kt b/core/common/test/samples/format/DateTimeComponentsSamples.kt index e3e81daa9..bebe0251b 100644 --- a/core/common/test/samples/format/DateTimeComponentsSamples.kt +++ b/core/common/test/samples/format/DateTimeComponentsSamples.kt @@ -8,6 +8,7 @@ package kotlinx.datetime.test.samples.format import kotlinx.datetime.* import kotlinx.datetime.format.* import kotlin.test.* +import kotlinx.time.Instant class DateTimeComponentsSamples { diff --git a/core/commonJs/src/internal/Platform.kt b/core/commonJs/src/internal/Platform.kt index c5ba347b7..ac759d3b0 100644 --- a/core/commonJs/src/internal/Platform.kt +++ b/core/commonJs/src/internal/Platform.kt @@ -8,6 +8,7 @@ package kotlinx.datetime.internal import kotlinx.datetime.* import kotlinx.datetime.UtcOffset import kotlinx.datetime.internal.JSJoda.ZoneId +import kotlinx.time.Instant private val tzdb: Result = runCatching { /** @@ -139,10 +140,7 @@ internal fun rulesForId(zoneId: String): TimeZoneRules? = tzdb.getOrThrow()?.rul internal actual fun getAvailableZoneIds(): Set = tzdb.getOrThrow()?.availableTimeZoneIds() ?: setOf("UTC") -internal actual fun currentTime(): Instant = Instant.fromEpochMilliseconds(Date().getTime().toLong()) - internal external class Date() { constructor(milliseconds: Double) - fun getTime(): Double fun getTimezoneOffset(): Double } diff --git a/core/commonJs/test/JsJodaTimezoneTest.kt b/core/commonJs/test/JsJodaTimezoneTest.kt index 138ed28ac..635fbf749 100644 --- a/core/commonJs/test/JsJodaTimezoneTest.kt +++ b/core/commonJs/test/JsJodaTimezoneTest.kt @@ -12,6 +12,7 @@ import kotlin.time.Duration.Companion.milliseconds import kotlin.time.Duration.Companion.seconds import kotlinx.datetime.test.JSJoda.Instant as jtInstant import kotlinx.datetime.test.JSJoda.ZoneId as jtZoneId +import kotlinx.time.Instant class JsJodaTimezoneTest { @Test diff --git a/core/commonKotlin/src/DeprecatedInstant.kt b/core/commonKotlin/src/DeprecatedInstant.kt new file mode 100644 index 000000000..7818fed19 --- /dev/null +++ b/core/commonKotlin/src/DeprecatedInstant.kt @@ -0,0 +1,288 @@ +/* + * Copyright 2019-2024 JetBrains s.r.o. and contributors. + * Use of this source code is governed by the Apache 2.0 License that can be found in the LICENSE.txt file. + */ + +@file:Suppress("DEPRECATION_ERROR") +@file:JvmMultifileClass +@file:JvmName("InstantKt") +package kotlinx.datetime + +import kotlinx.datetime.format.* +import kotlinx.datetime.internal.* +import kotlinx.datetime.internal.MILLIS_PER_ONE +import kotlinx.datetime.internal.NANOS_PER_MILLI +import kotlinx.datetime.internal.NANOS_PER_ONE +import kotlinx.datetime.internal.safeAdd +import kotlinx.datetime.internal.safeMultiply +import kotlinx.datetime.serializers.InstantIso8601Serializer +import kotlinx.serialization.Serializable +import kotlin.jvm.JvmMultifileClass +import kotlin.jvm.JvmName +import kotlin.time.Duration +import kotlin.time.Duration.Companion.nanoseconds +import kotlin.time.Duration.Companion.seconds + +/** + * The minimum supported epoch second. + */ +private const val MIN_SECOND = -31557014167219200L // -1000000000-01-01T00:00:00Z + +/** + * The maximum supported epoch second. + */ +private const val MAX_SECOND = 31556889864403199L // +1000000000-12-31T23:59:59Z + +@Deprecated( + "Use kotlin.time.Instant instead", + ReplaceWith("kotlinx.time.Instant", "kotlinx.time.Instant"), + level = DeprecationLevel.ERROR +) +@Serializable(with = InstantIso8601Serializer::class) +public actual class Instant internal constructor(public actual val epochSeconds: Long, public actual val nanosecondsOfSecond: Int) : Comparable { + + init { + require(epochSeconds in MIN_SECOND..MAX_SECOND) { "Instant exceeds minimum or maximum instant" } + } + + // org.threeten.bp.Instant#toEpochMilli + public actual fun toEpochMilliseconds(): Long = try { + if (epochSeconds >= 0) { + val millis = safeMultiply(epochSeconds, MILLIS_PER_ONE.toLong()) + safeAdd(millis, (nanosecondsOfSecond / NANOS_PER_MILLI).toLong()) + } else { + // prevent an overflow in seconds * 1000 + // instead of going form the second farther away from 0 + // going toward 0 + // we go from the second closer to 0 away from 0 + // that way we always stay in the valid long range + // seconds + 1 can not overflow because it is negative + val millis = safeMultiply(epochSeconds + 1, MILLIS_PER_ONE.toLong()) + safeAdd(millis, (nanosecondsOfSecond / NANOS_PER_MILLI - MILLIS_PER_ONE).toLong()) + } + } catch (_: ArithmeticException) { + if (epochSeconds > 0) Long.MAX_VALUE else Long.MIN_VALUE + } + + // org.threeten.bp.Instant#plus(long, long) + /** + * @throws ArithmeticException if arithmetic overflow occurs + * @throws IllegalArgumentException if the boundaries of Instant are overflown + */ + internal fun plus(secondsToAdd: Long, nanosToAdd: Long): Instant { + if ((secondsToAdd or nanosToAdd) == 0L) { + return this + } + val newEpochSeconds: Long = safeAdd(safeAdd(epochSeconds, secondsToAdd), (nanosToAdd / NANOS_PER_ONE)) + val newNanosToAdd = nanosToAdd % NANOS_PER_ONE + val nanoAdjustment = (nanosecondsOfSecond + newNanosToAdd) // safe int+NANOS_PER_ONE + return fromEpochSecondsThrowing(newEpochSeconds, nanoAdjustment) + } + + public actual operator fun plus(duration: Duration): Instant = duration.toComponents { secondsToAdd, nanosecondsToAdd -> + try { + plus(secondsToAdd, nanosecondsToAdd.toLong()) + } catch (_: IllegalArgumentException) { + if (duration.isPositive()) MAX else MIN + } catch (_: ArithmeticException) { + if (duration.isPositive()) MAX else MIN + } + } + + public actual operator fun minus(duration: Duration): Instant = plus(-duration) + + public actual operator fun minus(other: Instant): Duration = + (this.epochSeconds - other.epochSeconds).seconds + // won't overflow given the instant bounds + (this.nanosecondsOfSecond - other.nanosecondsOfSecond).nanoseconds + + actual override fun compareTo(other: Instant): Int { + val s = epochSeconds.compareTo(other.epochSeconds) + if (s != 0) { + return s + } + return nanosecondsOfSecond.compareTo(other.nanosecondsOfSecond) + } + + override fun equals(other: Any?): Boolean = + this === other || other is Instant && this.epochSeconds == other.epochSeconds && this.nanosecondsOfSecond == other.nanosecondsOfSecond + + // org.threeten.bp.Instant#hashCode + override fun hashCode(): Int = + (epochSeconds xor (epochSeconds ushr 32)).toInt() + 51 * nanosecondsOfSecond + + // org.threeten.bp.format.DateTimeFormatterBuilder.InstantPrinterParser#print + actual override fun toString(): String = format(ISO_DATE_TIME_OFFSET_WITH_TRAILING_ZEROS) + + public actual companion object { + internal actual val MIN = Instant(MIN_SECOND, 0) + internal actual val MAX = Instant(MAX_SECOND, 999_999_999) + + @Deprecated("Use Clock.System.now() instead", ReplaceWith("Clock.System.now()", "kotlinx.datetime.Clock"), level = DeprecationLevel.ERROR) + public actual fun now(): Instant = Clock.System.now() + + // org.threeten.bp.Instant#ofEpochMilli + public actual fun fromEpochMilliseconds(epochMilliseconds: Long): Instant { + val epochSeconds = epochMilliseconds.floorDiv(MILLIS_PER_ONE.toLong()) + val nanosecondsOfSecond = (epochMilliseconds.mod(MILLIS_PER_ONE.toLong()) * NANOS_PER_MILLI).toInt() + return when { + epochSeconds < MIN_SECOND -> MIN + epochSeconds > MAX_SECOND -> MAX + else -> fromEpochSeconds(epochSeconds, nanosecondsOfSecond) + } + } + + /** + * @throws ArithmeticException if arithmetic overflow occurs + * @throws IllegalArgumentException if the boundaries of Instant are overflown + */ + private fun fromEpochSecondsThrowing(epochSeconds: Long, nanosecondAdjustment: Long): Instant { + val secs = safeAdd(epochSeconds, nanosecondAdjustment.floorDiv(NANOS_PER_ONE.toLong())) + val nos = nanosecondAdjustment.mod(NANOS_PER_ONE.toLong()).toInt() + return Instant(secs, nos) + } + + // org.threeten.bp.Instant#ofEpochSecond(long, long) + public actual fun fromEpochSeconds(epochSeconds: Long, nanosecondAdjustment: Long): Instant = + try { + fromEpochSecondsThrowing(epochSeconds, nanosecondAdjustment) + } catch (_: ArithmeticException) { + if (epochSeconds > 0) MAX else MIN + } catch (_: IllegalArgumentException) { + if (epochSeconds > 0) MAX else MIN + } + + public actual fun fromEpochSeconds(epochSeconds: Long, nanosecondAdjustment: Int): Instant = + fromEpochSeconds(epochSeconds, nanosecondAdjustment.toLong()) + + public actual fun parse(input: CharSequence, format: DateTimeFormat): Instant = try { + format.parse(input).toInstantUsingOffset().toDeprecatedInstant() + } catch (e: IllegalArgumentException) { + throw DateTimeFormatException("Failed to parse an instant from '$input'", e) + } + + @Deprecated("This overload is only kept for binary compatibility", level = DeprecationLevel.HIDDEN) + public fun parse(isoString: String): Instant = parse(input = isoString) + + public actual val DISTANT_PAST: Instant = fromEpochSeconds(DISTANT_PAST_SECONDS, 999_999_999) + + public actual val DISTANT_FUTURE: Instant = fromEpochSeconds(DISTANT_FUTURE_SECONDS, 0) + } + +} + +private fun Instant.toZonedDateTimeFailing(zone: TimeZone): ZonedDateTime = try { + toZonedDateTime(zone) +} catch (e: IllegalArgumentException) { + throw DateTimeArithmeticException("Can not convert instant $this to LocalDateTime to perform computations", e) +} + +/** + * @throws IllegalArgumentException if the [Instant] exceeds the boundaries of [LocalDateTime] + */ +private fun Instant.toZonedDateTime(zone: TimeZone): ZonedDateTime { + val currentOffset = zone.offsetAtImpl(this.toNewInstant()) + return ZonedDateTime(toNewInstant().toLocalDateTimeImpl(currentOffset), zone, currentOffset) +} + +/** Check that [Instant] fits in [ZonedDateTime]. + * This is done on the results of computations for consistency with other platforms. + */ +private fun Instant.check(zone: TimeZone): Instant = this@check.also { + toZonedDateTimeFailing(zone) +} + +public actual fun Instant.plus(period: DateTimePeriod, timeZone: TimeZone): Instant = try { + with(period) { + val withDate = toZonedDateTimeFailing(timeZone) + .run { if (totalMonths != 0L) plus(totalMonths, DateTimeUnit.MONTH) else this } + .run { if (days != 0) plus(days.toLong(), DateTimeUnit.DAY) else this } + withDate.toDeprecatedInstant() + .run { if (totalNanoseconds != 0L) plus(0, totalNanoseconds).check(timeZone) else this } + }.check(timeZone) +} catch (e: ArithmeticException) { + throw DateTimeArithmeticException("Arithmetic overflow when adding CalendarPeriod to an Instant", e) +} catch (e: IllegalArgumentException) { + throw DateTimeArithmeticException("Boundaries of Instant exceeded when adding CalendarPeriod", e) +} + +@Deprecated("Use the plus overload with an explicit number of units", ReplaceWith("this.plus(1, unit, timeZone)")) +public actual fun Instant.plus(unit: DateTimeUnit, timeZone: TimeZone): Instant = + plus(1L, unit, timeZone) +public actual fun Instant.plus(value: Int, unit: DateTimeUnit, timeZone: TimeZone): Instant = + plus(value.toLong(), unit, timeZone) +public actual fun Instant.minus(value: Int, unit: DateTimeUnit, timeZone: TimeZone): Instant = + plus(-value.toLong(), unit, timeZone) +public actual fun Instant.plus(value: Long, unit: DateTimeUnit, timeZone: TimeZone): Instant = try { + when (unit) { + is DateTimeUnit.DateBased -> + toZonedDateTimeFailing(timeZone).plus(value, unit).toDeprecatedInstant() + is DateTimeUnit.TimeBased -> + check(timeZone).plus(value, unit).check(timeZone) + } +} catch (e: ArithmeticException) { + throw DateTimeArithmeticException("Arithmetic overflow when adding to an Instant", e) +} catch (e: IllegalArgumentException) { + throw DateTimeArithmeticException("Boundaries of Instant exceeded when adding a value", e) +} + +public actual fun Instant.plus(value: Long, unit: DateTimeUnit.TimeBased): Instant = + try { + multiplyAndDivide(value, unit.nanoseconds, NANOS_PER_ONE.toLong()).let { (seconds, nanoseconds) -> + plus(seconds, nanoseconds) + } + } catch (_: ArithmeticException) { + if (value > 0) Instant.MAX else Instant.MIN + } catch (_: IllegalArgumentException) { + if (value > 0) Instant.MAX else Instant.MIN + } + +public actual fun Instant.periodUntil(other: Instant, timeZone: TimeZone): DateTimePeriod { + var thisLdt = toZonedDateTimeFailing(timeZone) + val otherLdt = other.toZonedDateTimeFailing(timeZone) + + val months = thisLdt.until(otherLdt, DateTimeUnit.MONTH) // `until` on dates never fails + thisLdt = thisLdt.plus(months, DateTimeUnit.MONTH) // won't throw: thisLdt + months <= otherLdt, which is known to be valid + val days = thisLdt.until(otherLdt, DateTimeUnit.DAY) // `until` on dates never fails + thisLdt = thisLdt.plus(days, DateTimeUnit.DAY) // won't throw: thisLdt + days <= otherLdt + val nanoseconds = thisLdt.until(otherLdt, DateTimeUnit.NANOSECOND) // |otherLdt - thisLdt| < 24h + + return buildDateTimePeriod(months, days.toInt(), nanoseconds) +} + +public actual fun Instant.until(other: Instant, unit: DateTimeUnit, timeZone: TimeZone): Long = + when (unit) { + is DateTimeUnit.DateBased -> + toZonedDateTimeFailing(timeZone).dateTime.until(other.toZonedDateTimeFailing(timeZone).dateTime, unit) + .toLong() + is DateTimeUnit.TimeBased -> { + check(timeZone); other.check(timeZone) + until(other, unit) + } + } + +private fun ZonedDateTime.toDeprecatedInstant(): Instant = + Instant.fromEpochSeconds(dateTime.toEpochSecond(offset), dateTime.nanosecond) + +private val ISO_DATE_TIME_OFFSET_WITH_TRAILING_ZEROS = DateTimeComponents.Format { + date(ISO_DATE) + alternativeParsing({ + char('t') + }) { + char('T') + } + hour() + char(':') + minute() + char(':') + second() + optional { + char('.') + secondFractionInternal(1, 9, FractionalSecondDirective.GROUP_BY_THREE) + } + isoOffset( + zOnZero = true, + useSeparator = true, + outputMinute = WhenToOutput.IF_NONZERO, + outputSecond = WhenToOutput.IF_NONZERO + ) +} diff --git a/core/commonKotlin/src/Instant.kt b/core/commonKotlin/src/Instant.kt index 5035e8da3..0bfa06295 100644 --- a/core/commonKotlin/src/Instant.kt +++ b/core/commonKotlin/src/Instant.kt @@ -8,14 +8,10 @@ package kotlinx.datetime -import kotlinx.datetime.format.* import kotlinx.datetime.internal.* -import kotlinx.datetime.serializers.InstantIso8601Serializer -import kotlinx.serialization.Serializable -import kotlin.time.* +import kotlinx.time.Instant import kotlin.time.Duration.Companion.nanoseconds import kotlin.time.Duration.Companion.seconds -import kotlin.math.absoluteValue public actual enum class DayOfWeek { MONDAY, @@ -27,412 +23,6 @@ public actual enum class DayOfWeek { SUNDAY; } -/** - * The minimum supported epoch second. - */ -private const val MIN_SECOND = -31557014167219200L // -1000000000-01-01T00:00:00Z - -/** - * The maximum supported epoch second. - */ -private const val MAX_SECOND = 31556889864403199L // +1000000000-12-31T23:59:59 - -@Serializable(with = InstantIso8601Serializer::class) -public actual class Instant internal constructor(public actual val epochSeconds: Long, public actual val nanosecondsOfSecond: Int) : Comparable { - - init { - require(epochSeconds in MIN_SECOND..MAX_SECOND) { "Instant exceeds minimum or maximum instant" } - } - - // org.threeten.bp.Instant#toEpochMilli - public actual fun toEpochMilliseconds(): Long = try { - if (epochSeconds >= 0) { - val millis = safeMultiply(epochSeconds, MILLIS_PER_ONE.toLong()) - safeAdd(millis, (nanosecondsOfSecond / NANOS_PER_MILLI).toLong()) - } else { - // prevent an overflow in seconds * 1000 - // instead of going form the second farther away from 0 - // going toward 0 - // we go from the second closer to 0 away from 0 - // that way we always stay in the valid long range - // seconds + 1 can not overflow because it is negative - val millis = safeMultiply(epochSeconds + 1, MILLIS_PER_ONE.toLong()) - safeAdd(millis, (nanosecondsOfSecond / NANOS_PER_MILLI - MILLIS_PER_ONE).toLong()) - } - } catch (_: ArithmeticException) { - if (epochSeconds > 0) Long.MAX_VALUE else Long.MIN_VALUE - } - - // org.threeten.bp.Instant#plus(long, long) - /** - * @throws ArithmeticException if arithmetic overflow occurs - * @throws IllegalArgumentException if the boundaries of Instant are overflown - */ - internal fun plus(secondsToAdd: Long, nanosToAdd: Long): Instant { - if ((secondsToAdd or nanosToAdd) == 0L) { - return this - } - val newEpochSeconds: Long = safeAdd(safeAdd(epochSeconds, secondsToAdd), (nanosToAdd / NANOS_PER_ONE)) - val newNanosToAdd = nanosToAdd % NANOS_PER_ONE - val nanoAdjustment = (nanosecondsOfSecond + newNanosToAdd) // safe int+NANOS_PER_ONE - return fromEpochSecondsThrowing(newEpochSeconds, nanoAdjustment) - } - - public actual operator fun plus(duration: Duration): Instant = duration.toComponents { secondsToAdd, nanosecondsToAdd -> - try { - plus(secondsToAdd, nanosecondsToAdd.toLong()) - } catch (_: IllegalArgumentException) { - if (duration.isPositive()) MAX else MIN - } catch (_: ArithmeticException) { - if (duration.isPositive()) MAX else MIN - } - } - - public actual operator fun minus(duration: Duration): Instant = plus(-duration) - - public actual operator fun minus(other: Instant): Duration = - (this.epochSeconds - other.epochSeconds).seconds + // won't overflow given the instant bounds - (this.nanosecondsOfSecond - other.nanosecondsOfSecond).nanoseconds - - actual override fun compareTo(other: Instant): Int { - val s = epochSeconds.compareTo(other.epochSeconds) - if (s != 0) { - return s - } - return nanosecondsOfSecond.compareTo(other.nanosecondsOfSecond) - } - - override fun equals(other: Any?): Boolean = - this === other || other is Instant && this.epochSeconds == other.epochSeconds && this.nanosecondsOfSecond == other.nanosecondsOfSecond - - // org.threeten.bp.Instant#hashCode - override fun hashCode(): Int = - (epochSeconds xor (epochSeconds ushr 32)).toInt() + 51 * nanosecondsOfSecond - - // org.threeten.bp.format.DateTimeFormatterBuilder.InstantPrinterParser#print - actual override fun toString(): String = format(ISO_DATE_TIME_OFFSET_WITH_TRAILING_ZEROS) - - public actual companion object { - internal actual val MIN = Instant(MIN_SECOND, 0) - internal actual val MAX = Instant(MAX_SECOND, 999_999_999) - - @Deprecated("Use Clock.System.now() instead", ReplaceWith("Clock.System.now()", "kotlinx.datetime.Clock"), level = DeprecationLevel.ERROR) - public actual fun now(): Instant = currentTime() - - // org.threeten.bp.Instant#ofEpochMilli - public actual fun fromEpochMilliseconds(epochMilliseconds: Long): Instant { - val epochSeconds = epochMilliseconds.floorDiv(MILLIS_PER_ONE.toLong()) - val nanosecondsOfSecond = (epochMilliseconds.mod(MILLIS_PER_ONE.toLong()) * NANOS_PER_MILLI).toInt() - return when { - epochSeconds < MIN_SECOND -> MIN - epochSeconds > MAX_SECOND -> MAX - else -> fromEpochSeconds(epochSeconds, nanosecondsOfSecond) - } - } - - /** - * @throws ArithmeticException if arithmetic overflow occurs - * @throws IllegalArgumentException if the boundaries of Instant are overflown - */ - private fun fromEpochSecondsThrowing(epochSeconds: Long, nanosecondAdjustment: Long): Instant { - val secs = safeAdd(epochSeconds, nanosecondAdjustment.floorDiv(NANOS_PER_ONE.toLong())) - val nos = nanosecondAdjustment.mod(NANOS_PER_ONE.toLong()).toInt() - return Instant(secs, nos) - } - - // org.threeten.bp.Instant#ofEpochSecond(long, long) - public actual fun fromEpochSeconds(epochSeconds: Long, nanosecondAdjustment: Long): Instant = - try { - fromEpochSecondsThrowing(epochSeconds, nanosecondAdjustment) - } catch (_: ArithmeticException) { - if (epochSeconds > 0) MAX else MIN - } catch (_: IllegalArgumentException) { - if (epochSeconds > 0) MAX else MIN - } - - public actual fun fromEpochSeconds(epochSeconds: Long, nanosecondAdjustment: Int): Instant = - fromEpochSeconds(epochSeconds, nanosecondAdjustment.toLong()) - - public actual fun parse(input: CharSequence, format: DateTimeFormat): Instant = try { - format.parse(input).toInstantUsingOffset() - } catch (e: IllegalArgumentException) { - throw DateTimeFormatException("Failed to parse an instant from '$input'", e) - } - - @Deprecated("This overload is only kept for binary compatibility", level = DeprecationLevel.HIDDEN) - public fun parse(isoString: String): Instant = parse(input = isoString) - - public actual val DISTANT_PAST: Instant = fromEpochSeconds(DISTANT_PAST_SECONDS, 999_999_999) - - public actual val DISTANT_FUTURE: Instant = fromEpochSeconds(DISTANT_FUTURE_SECONDS, 0) - } - -} - -private class UnboundedLocalDateTime( - val year: Int, - val month: Int, - val day: Int, - val hour: Int, - val minute: Int, - val second: Int, - val nanosecond: Int, -) { - fun toInstant(offsetSeconds: Int): Instant { - val epochSeconds = run { - // org.threeten.bp.LocalDate#toEpochDay - val epochDays = run { - val y = year.toLong() - var total = 365 * y - if (y >= 0) { - total += (y + 3) / 4 - (y + 99) / 100 + (y + 399) / 400 - } else { - total -= y / -4 - y / -100 + y / -400 - } - total += ((367 * month - 362) / 12) - total += day - 1 - if (month > 2) { - total-- - if (!isLeapYear(year)) { - total-- - } - } - total - DAYS_0000_TO_1970 - } - // org.threeten.bp.LocalTime#toSecondOfDay - val daySeconds = hour * SECONDS_PER_HOUR + minute * SECONDS_PER_MINUTE + second - // org.threeten.bp.chrono.ChronoLocalDateTime#toEpochSecond - epochDays * 86400L + daySeconds - offsetSeconds - } - if (epochSeconds < Instant.MIN.epochSeconds || epochSeconds > Instant.MAX.epochSeconds) - throw DateTimeFormatException( - "The parsed date is outside the range representable by Instant (Unix epoch second $epochSeconds)" - ) - return Instant.fromEpochSeconds(epochSeconds, nanosecond) - } - - override fun toString(): String = "UnboundedLocalDateTime($year-$month-$day $hour:$minute:$second.$nanosecond)" - - companion object { - fun fromInstant(instant: Instant, offsetSeconds: Int): UnboundedLocalDateTime { - val localSecond: Long = instant.epochSeconds + offsetSeconds - val epochDays = localSecond.floorDiv(SECONDS_PER_DAY.toLong()) - val secsOfDay = localSecond.mod(SECONDS_PER_DAY.toLong()).toInt() - val year: Int - val month: Int - val day: Int - // org.threeten.bp.LocalDate#toEpochDay - run { - var zeroDay = epochDays + DAYS_0000_TO_1970 - // find the march-based year - zeroDay -= 60 // adjust to 0000-03-01 so leap day is at end of four year cycle - - var adjust = 0L - if (zeroDay < 0) { // adjust negative years to positive for calculation - val adjustCycles = (zeroDay + 1) / DAYS_PER_CYCLE - 1 - adjust = adjustCycles * 400 - zeroDay += -adjustCycles * DAYS_PER_CYCLE - } - var yearEst = ((400 * zeroDay.toLong() + 591) / DAYS_PER_CYCLE) - var doyEst = zeroDay - (365 * yearEst + yearEst / 4 - yearEst / 100 + yearEst / 400) - if (doyEst < 0) { // fix estimate - yearEst-- - doyEst = zeroDay - (365 * yearEst + yearEst / 4 - yearEst / 100 + yearEst / 400) - } - yearEst += adjust // reset any negative year - - val marchDoy0 = doyEst.toInt() - - // convert march-based values back to january-based - val marchMonth0 = (marchDoy0 * 5 + 2) / 153 - month = (marchMonth0 + 2) % 12 + 1 - day = marchDoy0 - (marchMonth0 * 306 + 5) / 10 + 1 - year = (yearEst + marchMonth0 / 10).toInt() - } - val hours = (secsOfDay / SECONDS_PER_HOUR) - val secondWithoutHours = secsOfDay - hours * SECONDS_PER_HOUR - val minutes = (secondWithoutHours / SECONDS_PER_MINUTE) - val second = secondWithoutHours - minutes * SECONDS_PER_MINUTE - return UnboundedLocalDateTime(year, month, day, hours, minutes, second, instant.nanosecondsOfSecond) - } - } -} - -internal fun parseIso(isoString: String): Instant { - fun parseFailure(error: String): Nothing { - throw IllegalArgumentException("$error when parsing an Instant from $isoString") - } - inline fun expect(what: String, where: Int, predicate: (Char) -> Boolean) { - val c = isoString[where] - if (!predicate(c)) { - parseFailure("Expected $what, but got $c at position $where") - } - } - val s = isoString - var i = 0 - require(s.isNotEmpty()) { "An empty string is not a valid Instant" } - val yearSign = when (val c = s[i]) { - '+', '-' -> { ++i; c } - else -> ' ' - } - val yearStart = i - var absYear = 0 - while (i < s.length && s[i] in '0'..'9') { - absYear = absYear * 10 + (s[i] - '0') - ++i - } - val year = when { - i > yearStart + 10 -> { - parseFailure("Expected at most 10 digits for the year number, got ${i - yearStart} digits") - } - i == yearStart + 10 && s[yearStart] >= '2' -> { - parseFailure("Expected at most 9 digits for the year number or year 1000000000, got ${i - yearStart}") - } - i - yearStart < 4 -> { - parseFailure("The year number must be padded to 4 digits, got ${i - yearStart} digits") - } - else -> { - if (yearSign == '+' && i - yearStart == 4) { - parseFailure("The '+' sign at the start is only valid for year numbers longer than 4 digits") - } - if (yearSign == ' ' && i - yearStart != 4) { - parseFailure("A '+' or '-' sign is required for year numbers longer than 4 digits") - } - if (yearSign == '-') -absYear else absYear - } - } - // reading at least -MM-DDTHH:MM:SSZ - // 0123456789012345 16 chars - if (s.length < i + 16) { - parseFailure("The input string is too short") - } - expect("'-'", i) { it == '-' } - expect("'-'", i + 3) { it == '-' } - expect("'T' or 't'", i + 6) { it == 'T' || it == 't' } - expect("':'", i + 9) { it == ':' } - expect("':'", i + 12) { it == ':' } - for (j in asciiDigitPositionsInIsoStringAfterYear) { - expect("an ASCII digit", i + j) { it in '0'..'9' } - } - fun twoDigitNumber(index: Int) = (s[index] - '0') * 10 + (s[index + 1] - '0') - val month = twoDigitNumber(i + 1) - val day = twoDigitNumber(i + 4) - val hour = twoDigitNumber(i + 7) - val minute = twoDigitNumber(i + 10) - val second = twoDigitNumber(i + 13) - val nanosecond = if (s[i + 15] == '.') { - val fractionStart = i + 16 - i = fractionStart - var fraction = 0 - while (i < s.length && s[i] in '0'..'9') { - fraction = fraction * 10 + (s[i] - '0') - ++i - } - if (i - fractionStart in 1..9) { - fraction * POWERS_OF_TEN[fractionStart + 9 - i] - } else { - parseFailure("1..9 digits are supported for the fraction of the second, got {i - fractionStart}") - } - } else { - i += 15 - 0 - } - val offsetSeconds = when (val sign = s.getOrNull(i)) { - null -> { - parseFailure("The UTC offset at the end of the string is missing") - } - 'z', 'Z' -> if (s.length == i + 1) { - 0 - } else { - parseFailure("Extra text after the instant at position ${i + 1}") - } - '-', '+' -> { - val offsetStrLength = s.length - i - if (offsetStrLength % 3 != 0) { parseFailure("Invalid UTC offset string '${s.substring(i)}'") } - if (offsetStrLength > 9) { parseFailure("The UTC offset string '${s.substring(i)}' is too long") } - for (j in colonsInIsoOffsetString) { - if (s.getOrNull(i + j) ?: break != ':') - parseFailure("Expected ':' at index ${i + j}, got '${s[i + j]}'") - } - for (j in asciiDigitsInIsoOffsetString) { - if (s.getOrNull(i + j) ?: break !in '0'..'9') - parseFailure("Expected a digit at index ${i + j}, got '${s[i + j]}'") - } - val offsetHour = twoDigitNumber(i + 1) - val offsetMinute = if (offsetStrLength > 3) { twoDigitNumber(i + 4) } else { 0 } - val offsetSecond = if (offsetStrLength > 6) { twoDigitNumber(i + 7) } else { 0 } - if (offsetMinute > 59) { parseFailure("Expected offset-minute-of-hour in 0..59, got $offsetMinute") } - if (offsetSecond > 59) { parseFailure("Expected offset-second-of-minute in 0..59, got $offsetSecond") } - if (offsetHour > 17 && !(offsetHour == 18 && offsetMinute == 0 && offsetSecond == 0)) { - parseFailure("Expected an offset in -18:00..+18:00, got $sign$offsetHour:$offsetMinute:$offsetSecond") - } - (offsetHour * 3600 + offsetMinute * 60 + offsetSecond) * if (sign == '-') -1 else 1 - } - else -> { - parseFailure("Expected the UTC offset at position $i, got '$sign'") - } - } - if (month !in 1..12) { parseFailure("Expected a month number in 1..12, got $month") } - if (day !in 1..month.monthLength(isLeapYear(year))) { - parseFailure("Expected a valid day-of-month for $year-$month, got $day") - } - if (hour > 23) { parseFailure("Expected hour in 0..23, got $hour") } - if (minute > 59) { parseFailure("Expected minute-of-hour in 0..59, got $minute") } - if (second > 59) { parseFailure("Expected second-of-minute in 0..59, got $second") } - return UnboundedLocalDateTime(year, month, day, hour, minute, second, nanosecond).toInstant(offsetSeconds) -} - -private val asciiDigitPositionsInIsoStringAfterYear by lazy { listOf(1, 2, 4, 5, 7, 8, 10, 11, 13, 14) } -private val colonsInIsoOffsetString by lazy { listOf(3, 6) } -private val asciiDigitsInIsoOffsetString by lazy { listOf(1, 2, 4, 5, 7, 8) } - -internal fun formatIso(instant: Instant): String = buildString { - val ldt = UnboundedLocalDateTime.fromInstant(instant, 0) - fun Appendable.appendTwoDigits(number: Int) { - if (number < 10) append('0') - append(number) - } - run { - val number = ldt.year - when { - number.absoluteValue < 1_000 -> { - val innerBuilder = StringBuilder() - if (number >= 0) { - innerBuilder.append((number + 10_000)).deleteAt(0) - } else { - innerBuilder.append((number - 10_000)).deleteAt(1) - } - append(innerBuilder) - } - else -> { - if (number >= 10_000) append('+') - append(number) - } - } - } - append('-') - appendTwoDigits(ldt.month) - append('-') - appendTwoDigits(ldt.day) - append('T') - appendTwoDigits(ldt.hour) - append(':') - appendTwoDigits(ldt.minute) - append(':') - appendTwoDigits(ldt.second) - if (ldt.nanosecond != 0) { - append('.') - var zerosToStrip = 0 - while (ldt.nanosecond % POWERS_OF_TEN[zerosToStrip + 1] == 0) { - ++zerosToStrip - } - zerosToStrip -= zerosToStrip % 3 // rounding down to a multiple of 3 - val numberToOutput = ldt.nanosecond / POWERS_OF_TEN[zerosToStrip] - append((numberToOutput + POWERS_OF_TEN[9 - zerosToStrip]).toString().substring(1)) - } - append('Z') -} - private fun Instant.toZonedDateTimeFailing(zone: TimeZone): ZonedDateTime = try { toZonedDateTime(zone) } catch (e: IllegalArgumentException) { @@ -460,7 +50,14 @@ public actual fun Instant.plus(period: DateTimePeriod, timeZone: TimeZone): Inst .run { if (totalMonths != 0L) plus(totalMonths, DateTimeUnit.MONTH) else this } .run { if (days != 0) plus(days.toLong(), DateTimeUnit.DAY) else this } withDate.toInstant() - .run { if (totalNanoseconds != 0L) plus(0, totalNanoseconds).check(timeZone) else this } + .run { + if (totalNanoseconds != 0L) + // we don't add nanoseconds directly, as `totalNanoseconds.nanoseconds` can hit `Duration.INFINITE` + plus((totalNanoseconds / NANOS_PER_ONE).seconds) + .plus((totalNanoseconds % NANOS_PER_ONE).nanoseconds) + .check(timeZone) + else this + } }.check(timeZone) } catch (e: ArithmeticException) { throw DateTimeArithmeticException("Arithmetic overflow when adding CalendarPeriod to an Instant", e) @@ -491,12 +88,12 @@ public actual fun Instant.plus(value: Long, unit: DateTimeUnit, timeZone: TimeZo public actual fun Instant.plus(value: Long, unit: DateTimeUnit.TimeBased): Instant = try { multiplyAndDivide(value, unit.nanoseconds, NANOS_PER_ONE.toLong()).let { (seconds, nanoseconds) -> - plus(seconds, nanoseconds) + plus(seconds.seconds).plus(nanoseconds.nanoseconds) } } catch (_: ArithmeticException) { - if (value > 0) Instant.MAX else Instant.MIN + Instant.fromEpochSeconds(if (value > 0) Long.MAX_VALUE else Long.MIN_VALUE) } catch (_: IllegalArgumentException) { - if (value > 0) Instant.MAX else Instant.MIN + Instant.fromEpochSeconds(if (value > 0) Long.MAX_VALUE else Long.MIN_VALUE) } public actual fun Instant.periodUntil(other: Instant, timeZone: TimeZone): DateTimePeriod { @@ -522,27 +119,3 @@ public actual fun Instant.until(other: Instant, unit: DateTimeUnit, timeZone: Ti until(other, unit) } } - -private val ISO_DATE_TIME_OFFSET_WITH_TRAILING_ZEROS = DateTimeComponents.Format { - date(ISO_DATE) - alternativeParsing({ - char('t') - }) { - char('T') - } - hour() - char(':') - minute() - char(':') - second() - optional { - char('.') - secondFractionInternal(1, 9, FractionalSecondDirective.GROUP_BY_THREE) - } - isoOffset( - zOnZero = true, - useSeparator = true, - outputMinute = WhenToOutput.IF_NONZERO, - outputSecond = WhenToOutput.IF_NONZERO - ) -} diff --git a/core/commonKotlin/src/TimeZone.kt b/core/commonKotlin/src/TimeZone.kt index 4aa6aa5e3..76256a263 100644 --- a/core/commonKotlin/src/TimeZone.kt +++ b/core/commonKotlin/src/TimeZone.kt @@ -12,6 +12,7 @@ import kotlinx.datetime.format.* import kotlinx.datetime.internal.* import kotlinx.datetime.serializers.* import kotlinx.serialization.Serializable +import kotlinx.time.Instant @Serializable(with = TimeZoneSerializer::class) public actual open class TimeZone internal constructor() { @@ -80,7 +81,20 @@ public actual open class TimeZone internal constructor() { get() = error("Should be overridden") public actual fun Instant.toLocalDateTime(): LocalDateTime = instantToLocalDateTime(this) - public actual fun LocalDateTime.toInstant(): Instant = localDateTimeToInstant(this) + public actual fun LocalDateTime.toInstant(deprecationMarker: DeprecationMarker): Instant = + localDateTimeToInstant(this) + + @PublishedApi + @Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE", "DEPRECATION_ERROR") + @kotlin.internal.LowPriorityInOverloadResolution + internal actual fun kotlinx.datetime.Instant.toLocalDateTime(): LocalDateTime = + toNewInstant().toLocalDateTime() + + @PublishedApi + @Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE", "DEPRECATION_ERROR") + @kotlin.internal.LowPriorityInOverloadResolution + internal actual fun LocalDateTime.toInstant(): kotlinx.datetime.Instant = + toInstant(this@TimeZone).toDeprecatedInstant() internal open fun atStartOfDay(date: LocalDate): Instant = error("Should be overridden") //value.atStartOfDay(date) internal open fun offsetAtImpl(instant: Instant): UtcOffset = error("Should be overridden") @@ -147,13 +161,13 @@ internal fun Instant.toLocalDateTimeImpl(offset: UtcOffset): LocalDateTime { return LocalDateTime(date, time) } -public actual fun LocalDateTime.toInstant(timeZone: TimeZone): Instant = +public actual fun LocalDateTime.toInstant(timeZone: TimeZone, deprecationMarker: DeprecationMarker): Instant = timeZone.localDateTimeToInstant(this) -public actual fun LocalDateTime.toInstant(offset: UtcOffset): Instant = - Instant(this.toEpochSecond(offset), this.nanosecond) +public actual fun LocalDateTime.toInstant(offset: UtcOffset, deprecationMarker: DeprecationMarker): Instant = + Instant.fromEpochSeconds(this.toEpochSecond(offset), this.nanosecond) -public actual fun LocalDate.atStartOfDayIn(timeZone: TimeZone): Instant = +public actual fun LocalDate.atStartOfDayIn(timeZone: TimeZone, deprecationMarker: DeprecationMarker): Instant = timeZone.atStartOfDay(this) private val lenientOffsetFormat = UtcOffsetFormat.build { diff --git a/core/commonKotlin/src/ZonedDateTime.kt b/core/commonKotlin/src/ZonedDateTime.kt index effd5acd0..c10011ded 100644 --- a/core/commonKotlin/src/ZonedDateTime.kt +++ b/core/commonKotlin/src/ZonedDateTime.kt @@ -8,6 +8,8 @@ package kotlinx.datetime +import kotlinx.time.Instant + internal class ZonedDateTime(val dateTime: LocalDateTime, private val zone: TimeZone, val offset: UtcOffset) { /** * @throws IllegalArgumentException if the result exceeds the boundaries @@ -46,7 +48,7 @@ internal class ZonedDateTime(val dateTime: LocalDateTime, private val zone: Time } internal fun ZonedDateTime.toInstant(): Instant = - Instant(dateTime.toEpochSecond(offset), dateTime.nanosecond) + Instant.fromEpochSeconds(dateTime.toEpochSecond(offset), dateTime.nanosecond) // org.threeten.bp.ZonedDateTime#until diff --git a/core/commonKotlin/src/internal/MonthDayTime.kt b/core/commonKotlin/src/internal/MonthDayTime.kt index 65d331dd9..4be2e994f 100644 --- a/core/commonKotlin/src/internal/MonthDayTime.kt +++ b/core/commonKotlin/src/internal/MonthDayTime.kt @@ -6,6 +6,7 @@ package kotlinx.datetime.internal import kotlinx.datetime.* +import kotlinx.time.Instant /** * A rule expressing how to create a date in a given year. diff --git a/core/commonKotlin/src/internal/OffsetInfo.kt b/core/commonKotlin/src/internal/OffsetInfo.kt index a89946da8..4685475dc 100644 --- a/core/commonKotlin/src/internal/OffsetInfo.kt +++ b/core/commonKotlin/src/internal/OffsetInfo.kt @@ -6,6 +6,7 @@ package kotlinx.datetime.internal import kotlinx.datetime.* +import kotlinx.time.Instant internal sealed interface OffsetInfo { data class Gap( @@ -43,4 +44,3 @@ internal fun OffsetInfo(transitionInstant: Instant, offsetBefore: UtcOffset, off } else { OffsetInfo.Overlap(transitionInstant, offsetBefore, offsetAfter) } - diff --git a/core/commonKotlin/src/internal/Platform.kt b/core/commonKotlin/src/internal/Platform.kt index 8227af253..d941eeded 100644 --- a/core/commonKotlin/src/internal/Platform.kt +++ b/core/commonKotlin/src/internal/Platform.kt @@ -5,7 +5,6 @@ package kotlinx.datetime.internal -import kotlinx.datetime.Instant import kotlinx.datetime.TimeZone internal expect fun timeZoneById(zoneId: String): TimeZone @@ -13,5 +12,3 @@ internal expect fun timeZoneById(zoneId: String): TimeZone internal expect fun getAvailableZoneIds(): Set internal expect fun currentSystemDefaultZone(): Pair - -internal expect fun currentTime(): Instant diff --git a/core/commonKotlin/src/internal/RegionTimeZone.kt b/core/commonKotlin/src/internal/RegionTimeZone.kt index d4e7bc6b6..9bec9e27f 100644 --- a/core/commonKotlin/src/internal/RegionTimeZone.kt +++ b/core/commonKotlin/src/internal/RegionTimeZone.kt @@ -6,6 +6,7 @@ package kotlinx.datetime.internal import kotlinx.datetime.* +import kotlinx.time.Instant internal class RegionTimeZone(private val tzid: TimeZoneRules, override val id: String) : TimeZone() { diff --git a/core/commonKotlin/src/internal/TimeZoneRules.kt b/core/commonKotlin/src/internal/TimeZoneRules.kt index 0d56aceb7..0c9bd948a 100644 --- a/core/commonKotlin/src/internal/TimeZoneRules.kt +++ b/core/commonKotlin/src/internal/TimeZoneRules.kt @@ -5,8 +5,11 @@ package kotlinx.datetime.internal -import kotlinx.datetime.* +import kotlinx.datetime.LocalDateTime +import kotlinx.datetime.UtcOffset +import kotlinx.datetime.toLocalDateTime import kotlin.math.* +import kotlinx.time.Instant internal class TimeZoneRules( /** diff --git a/core/commonKotlin/test/InstantIsoStringsTest.kt b/core/commonKotlin/test/InstantIsoStringsTest.kt deleted file mode 100644 index 6b9390e37..000000000 --- a/core/commonKotlin/test/InstantIsoStringsTest.kt +++ /dev/null @@ -1,420 +0,0 @@ -/* - * Copyright 2019-2024 JetBrains s.r.o. and contributors. - * Use of this source code is governed by the Apache 2.0 License that can be found in the LICENSE.txt file. - */ - -package kotlinx.datetime - -import kotlin.math.absoluteValue -import kotlin.time.Duration.Companion.seconds -import kotlin.test.* - -class InstantIsoStringsTest { - - @Test - fun parseDates() { - fun Int.zeroPadded(digits: Int): String = when { - this >= 0 -> toString().padStart(digits, '0') - else -> "-${absoluteValue.toString().padStart(digits, '0')}" - } - fun localDateToString(year: Int, month: Int, day: Int) = - "${year.zeroPadded(4)}-${month.zeroPadded(2)}-${day.zeroPadded(2)}" - // only works for 1-4-digit years - fun assertMonthBoundariesAreCorrect(year: Int, month: Int, lastDayOfMonth: Int) { - val validString = "${localDateToString(year, month, lastDayOfMonth)}T23:59:59Z" - val invalidString = "${localDateToString(year, month, lastDayOfMonth + 1)}T23:59:59Z" - parseInstant(validString) // shouldn't throw - assertInvalidFormat(invalidString) { parseInstant(invalidString) } - } - val nonLeapYears = listOf( - 1970, 1971, 1973, 1974, 1975, 2021, 2022, 2023, 2100, 1100, 1, 2, 3, 5, -1, -2, -1971, 100, -100 - ) - val leapYears = listOf( - 0, 1972, 1976, 1980, 2000, 1200, 400, -400, -4, -8, - ) - for ((month, lastDayOfMonth) in arrayOf( - 1 to 31, 3 to 31, 4 to 30, 5 to 31, 6 to 30, - 7 to 31, 8 to 31, 9 to 30, 10 to 31, 11 to 30, 12 to 31, - )) { - for (year in nonLeapYears + leapYears) { - assertMonthBoundariesAreCorrect(year, month, lastDayOfMonth) - } - } - for (leapYear in leapYears) { - assertMonthBoundariesAreCorrect(leapYear, 2, 29) - } - for (nonLeapYear in nonLeapYears) { - assertMonthBoundariesAreCorrect(nonLeapYear, 2, 28) - } - } - - @Test - fun parseIsoString() { - for ((str, seconds, nanos) in arrayOf>( - // all components are taken into account - Triple("1970-01-01T00:00:00Z", 0, 0), - Triple("1970-01-01T00:00:00.000000001Z", 0, 1), - Triple("1970-01-01T00:00:00.100Z", 0, 100000000), - Triple("1970-01-01T00:00:01Z", 1, 0), - Triple("1970-01-01T00:01:00Z", 60, 0), - Triple("1970-01-01T00:01:01Z", 61, 0), - Triple("1970-01-01T00:01:01.000000001Z", 61, 1), - Triple("1970-01-01T01:00:00Z", 3600, 0), - Triple("1970-01-01T01:01:01.000000001Z", 3661, 1), - Triple("1970-01-02T01:01:01.100Z", 90061, 100000000), - Triple("1970-02-02T01:01:01.100Z", 31 * 86400 + 90061, 100000000), - Triple("1971-02-02T01:01:01.100Z", (365 + 31) * 86400 + 90061, 100000000), - // how many digits get output for various precision of the sub-second portion - Triple("1970-01-01T00:00:00.100Z", 0, 100_000_000), - Triple("1970-01-01T00:00:00.010Z", 0, 10_000_000), - Triple("1970-01-01T00:00:00.001Z", 0, 1_000_000), - Triple("1970-01-01T00:00:00.000100Z", 0, 100_000), - Triple("1970-01-01T00:00:00.000010Z", 0, 10_000), - Triple("1970-01-01T00:00:00.000001Z", 0, 1_000), - Triple("1970-01-01T00:00:00.000000100Z", 0, 100), - Triple("1970-01-01T00:00:00.000000010Z", 0, 10), - Triple("1970-01-01T00:00:00.000000001Z", 0, 1), - // random data queried from java.time - Triple("+51861-09-21T11:07:43.782719883Z", 1574430692863, 782719883), - Triple("+395069-04-30T01:28:37.454777349Z", 12405016603717, 454777349), - Triple("-551259-03-05T08:01:36.195722269Z", -17458215523104, 195722269), - Triple("+498403-02-11T17:47:05.156642423Z", 15665915958425, 156642423), - Triple("+283686-10-14T23:00:25.666521845Z", 8890123158025, 666521845), - Triple("-910329-04-04T09:27:54.456784744Z", -28789367639526, 456784744), - Triple("-37222-03-21T18:04:37.006055123Z", -1236773166923, 6055123), - Triple("-189377-03-30T01:37:14.288808090Z", -6038320515766, 288808090), - Triple("-67394-03-24T03:19:41.794404047Z", -2188909341619, 794404047), - Triple("-870649-05-27T13:47:39.925150102Z", -27537183223941, 925150102), - Triple("+94020-04-10T14:51:21.569206089Z", 2904826114281, 569206089), - Triple("-945485-07-11T23:28:58.240153828Z", -29898775384262, 240153828), - Triple("-73722-02-22T11:19:54.364548772Z", -2388604250406, 364548772), - Triple("-645899-05-17T16:44:21.522135477Z", -20444759104539, 522135477), - Triple("-702594-10-20T10:13:53.212104714Z", -22233867083167, 212104714), - Triple("-442579-11-22T01:35:44.591216727Z", -14028583357456, 591216727), - Triple("-849915-06-25T01:28:27.625015449Z", -26882878833093, 625015449), - Triple("-481897-08-13T05:44:47.077814711Z", -15269348340913, 77814711), - Triple("+295919-02-07T15:47:37.850981753Z", 9276137682457, 850981753), - Triple("+967334-01-15T15:08:10.235167075Z", 30463946694490, 235167075), - Triple("+774237-04-30T16:00:32.810606451Z", 24370403011232, 810606451), - Triple("+792959-05-03T08:18:31.616194572Z", 24961212490711, 616194572), - Triple("-261823-02-16T03:17:35.085815500Z", -8324498983345, 85815500), - Triple("+931062-03-22T17:04:54.135075640Z", 29319318637494, 135075640), - Triple("+623320-01-26T03:08:05.121769356Z", 19607914264085, 121769356), - Triple("+322804-03-06T11:31:24.788006817Z", 10124548774284, 788006817), - Triple("-784322-04-03T21:25:19.666588404Z", -24812970806081, 666588404), - Triple("+403293-01-07T05:59:41.601460200Z", 12664531288781, 601460200), - Triple("-835821-06-01T00:52:15.782852248Z", -26438117296065, 782852248), - Triple("+222483-07-15T08:29:55.019931345Z", 6958735086595, 19931345), - Triple("-663595-09-05T04:36:24.110433196Z", -21003181356216, 110433196), - Triple("+166626-02-15T22:16:34.070665743Z", 5196045449794, 70665743), - Triple("-517158-01-02T22:52:24.155574933Z", -16382097162456, 155574933), - Triple("+850155-01-02T10:25:31.349473798Z", 26766133467931, 349473798), - Triple("-967697-04-25T20:43:33.328060156Z", -30599725115787, 328060156), - Triple("+437131-04-26T07:32:58.134219875Z", 13732364705578, 134219875), - Triple("+372920-11-25T13:38:22.852562723Z", 11706079786702, 852562723), - Triple("+169255-09-07T11:28:18.481625778Z", 5279026303698, 481625778), - Triple("-980786-08-18T17:05:22.581779094Z", -31012764044078, 581779094), - Triple("+182945-05-25T20:39:24.545585221Z", 5711031952764, 545585221), - Triple("+300811-12-15T02:53:38.676752671Z", 9430541175218, 676752671), - Triple("-807816-01-18T18:04:26.291749218Z", -25554376389334, 291749218), - Triple("-53033-12-30T22:02:01.398533618Z", -1735695568679, 398533618), - Triple("-354903-06-14T10:08:46.111648055Z", -11261809864274, 111648055), - Triple("+842009-03-11T23:58:06.537554993Z", 26509076495886, 537554993), - Triple("-391976-11-09T04:16:17.862484469Z", -12431707962223, 862484469), - Triple("-733019-10-28T17:07:13.450343935Z", -23193986539967, 450343935), - Triple("+595280-03-05T23:36:27.765851400Z", 18723060833787, 765851400), - Triple("-930296-07-17T03:33:33.094509320Z", -29419456335987, 94509320), - Triple("+609508-02-29T10:58:02.703241053Z", 19172052557882, 703241053), - Triple("+996233-06-25T06:01:55.647461964Z", 31375924927315, 647461964), - Triple("-93200-12-06T21:29:56.140938343Z", -3003245692204, 140938343), - Triple("+794143-07-02T09:49:35.585085194Z", 24998581100975, 585085194), - Triple("-783550-12-31T17:10:16.577723428Z", -24788585371784, 577723428), - Triple("-240168-11-03T17:22:09.108424624Z", -7641110702271, 108424624), - Triple("+613419-02-15T12:00:07.012460989Z", 19295470641607, 12460989), - Triple("-521405-03-25T02:03:46.552711998Z", -16516112536574, 552711998), - Triple("-938829-01-22T16:48:43.582709371Z", -29688747030677, 582709371), - Triple("+916785-05-16T21:54:45.983221956Z", 28868784818085, 983221956), - Triple("+482425-06-09T04:24:32.683186155Z", 15161709183872, 683186155), - Triple("+622585-08-20T05:45:52.555088343Z", 19584737819152, 555088343), - Triple("-451048-11-02T01:49:29.076392891Z", -14295840847831, 76392891), - Triple("+721083-09-17T00:31:34.648020241Z", 22693036811494, 648020241), - Triple("+235979-10-28T12:07:33.706273641Z", 7384636728453, 706273641), - Triple("+285234-04-12T18:30:25.215363003Z", 8938957285825, 215363003), - Triple("-917176-03-10T10:03:25.943265324Z", -29005440213395, 943265324), - Triple("-381932-09-05T02:47:17.004960541Z", -12114755529163, 4960541), - Triple("-52158-11-11T09:38:45.489915403Z", -1708087530075, 489915403), - Triple("-584290-11-15T20:15:24.377620606Z", -18500551127076, 377620606), - Triple("-645616-05-05T17:36:59.941608628Z", -20435829488581, 941608628), - Triple("+794405-06-22T21:08:20.853641989Z", 25006848239300, 853641989), - Triple("+986590-08-01T05:15:25.827177433Z", 31071624470125, 827177433), - Triple("+527158-02-06T12:34:35.088546391Z", 16573335654875, 88546391), - Triple("-513116-05-01T07:28:44.448204123Z", -16254533665876, 448204123), - Triple("+397065-10-19T21:59:05.831855226Z", 12468019211945, 831855226), - Triple("+312769-04-26T11:33:07.802217284Z", 9807879123187, 802217284), - Triple("+682473-04-14T01:00:38.067076018Z", 21474609498038, 67076018), - Triple("+731560-02-15T02:15:06.599802467Z", 23023640456106, 599802467), - Triple("-877354-10-27T22:55:02.723751549Z", -27748759338298, 723751549), - Triple("-746193-01-02T07:19:56.258497483Z", -23609743807204, 258497483), - Triple("-822112-07-28T08:55:19.319285417Z", -26005498038281, 319285417), - Triple("-400365-04-30T00:05:51.210582736Z", -12696455980449, 210582736), - Triple("+436254-07-11T18:08:06.937065549Z", 13704695921286, 937065549), - Triple("-340854-01-07T03:17:32.367173472Z", -10818479997748, 367173472), - Triple("-985221-04-25T22:57:01.511559459Z", -31152729085379, 511559459), - Triple("+859861-09-01T02:21:20.289341591Z", 27072446149280, 289341591), - Triple("-0131-07-16T10:47:54.756333457Z", -66284140326, 756333457), - Triple("-327041-11-18T22:55:21.885337272Z", -10382556503079, 885337272), - Triple("-268616-05-06T10:27:54.420166505Z", -8538858480726, 420166505), - Triple("-228012-05-16T15:26:54.680432991Z", -7257519160386, 680432991), - Triple("+857168-09-12T13:29:36.945689251Z", 26987464272576, 945689251), - Triple("-974181-04-12T08:47:35.627678735Z", -30804341526745, 627678735), - Triple("-435700-10-20T22:33:13.897477229Z", -13811505874007, 897477229), - Triple("-507467-01-19T23:06:05.156792267Z", -16076277276835, 156792267), - Triple("-382257-11-19T08:00:10.407963305Z", -12125005142390, 407963305), - Triple("+83082-01-04T20:18:56.409867424Z", 2559647852336, 409867424), - Triple("-916839-09-12T22:45:39.091941363Z", -28994789466861, 91941363), - Triple("-147771-05-07T08:31:34.950238979Z", -4725358615706, 950238979), - // random enormous values queried from java.time - Triple("+639727757-10-17T17:26:30.359003681Z", 20187795978609990, 359003681), - Triple("-375448814-11-11T03:04:48.637504595Z", -11848082341899312, 637504595), - Triple("+99162559-10-16T11:21:05.057717803Z", 3129205972303265, 57717803), - Triple("-826813174-05-22T21:35:39.018693830Z", -26091765799784661, 18693830), - Triple("+254125623-04-28T11:42:22.659957596Z", 8019367929949342, 659957596), - Triple("+540978343-01-03T20:31:36.945404442Z", 17071565436102696, 945404442), - Triple("+988277529-06-06T13:25:41.942771350Z", 31186964391657941, 942771350), - Triple("+566487909-09-04T08:09:46.007490076Z", 17876569606963786, 7490076), - Triple("+442225124-02-16T18:23:29.975096773Z", 13955214848034209, 975096773), - Triple("-399250586-02-19T22:58:29.585918917Z", -12599193741267691, 585918917), - Triple("-190217791-04-28T00:49:29.270751921Z", -6002755857213031, 270751921), - Triple("-716173704-05-24T22:05:33.639928802Z", -22600321355469267, 639928802), - Triple("+910504788-10-26T05:23:43.517192887Z", 28732693749312223, 517192887), - Triple("+515896807-08-17T06:36:25.012343642Z", 16280068627982185, 12343642), - Triple("-623794742-03-20T17:09:07.396143995Z", -19685122891483853, 396143995), - Triple("-416781718-10-06T01:41:32.866307162Z", -13152422812544308, 866307162), - Triple("+287346593-09-30T23:30:55.109337183Z", 9067720499134255, 109337183), - Triple("-819839065-09-06T07:25:58.953784983Z", -25871684167672442, 953784983), - Triple("+673467211-06-05T02:15:40.712392732Z", 21252510297310540, 712392732), - Triple("+982441727-04-13T12:12:06.776817565Z", 31002804263391126, 776817565), - )) { - val instant = parseInstant(str) - assertEquals( - Instant.fromEpochSeconds(seconds, nanos), instant, - "Parsed $instant from $str, with Unix time = `$seconds + 10^-9 * $nanos`" - ) - assertEquals(str, displayInstant(instant)) - } - // non-canonical strings are parsed as well, but formatted differently - for ((str, seconds, nanos) in arrayOf( - // upper, lower case, trailing zeros - Triple("2024-07-15T14:06:29.461245000z", 1721052389, 461245000), - Triple("2024-07-15t14:06:29.4612450z", 1721052389, 461245000), - // current time - Triple("2024-07-15T16:06:29.461245691+02:00", 1721052389, 461245691), - )) { - val instant = parseInstant(str) - assertEquals( - seconds.toLong() * 1000 + nanos / 1000000, instant.toEpochMilliseconds(), - "Parsed $instant from $str, with Unix time = `$seconds + 10^-9 * $nanos`" - ) - } - } - - @Test - fun nonParseableInstantStrings() { - for (nonIsoString in listOf( - // empty string - "", - // a non-empty but clearly unsuitable string - "x", - // something other than a sign at the beginning - " 1970-01-01T00:00:00Z", - // too many digits for the year - "+1234567890-01-01T00:00:00Z", - "-1234567890-01-01T00:00:00Z", - // not enough padding for the year - "003-01-01T00:00:00Z", - "-003-01-01T00:00:00Z", - // a plus sign even though there is only 4 digits - "+1970-01-01T00:00:00Z", - // too many digits without padding - "11970-01-01T00:00:00Z", - // incorrect separators between the components - "1970/01-01T00:00:00Z", - "1970-01/01T00:00:00Z", - "1970-01-01 00:00:00Z", - "1970-01-01T00-00:00Z", - "1970-01-01T00:00-00Z", - // non-digits where digits are expected - "1970-X1-01T00:00:00Z", - "1970-1X-01T00:00:00Z", - "1970-11-X1T00:00:00Z", - "1970-11-1XT00:00:00Z", - "1970-11-10TX0:00:00Z", - "1970-11-10T0X:00:00Z", - "1970-11-10T00:X0:00Z", - "1970-11-10T00:0X:00Z", - "1970-11-10T00:00:X0Z", - "1970-11-10T00:00:0XZ", - // a non-ascii digit - "1970-11-10T00:00:0٩Z", - // not enough components - "1970-11-10T00:00Z", - // not enough components, even if the length is sufficient - "1970-11-10T00:00+01:15", - // a dot without any fraction of the second following it - "1970-11-10T00:00:00.Z", - // too many digits in the fraction of the second - "1970-11-10T00:00:00.1234567890Z", - // out-of-range values - "1970-00-10T00:00:00Z", - "1970-13-10T00:00:00Z", - "1970-01-32T00:00:00Z", - "1970-02-29T00:00:00Z", - "1972-02-30T00:00:00Z", - "2000-02-30T00:00:00Z", - "2100-02-29T00:00:00Z", - "2004-02-30T00:00:00Z", - "2005-02-29T00:00:00Z", - "2005-04-31T00:00:00Z", - "2005-04-01T24:00:00Z", - "2005-04-01T00:60:00Z", - "2005-04-01T00:00:60Z", - // leap second - "1970-01-01T23:59:60Z", - // lack of padding - "1970-1-10T00:00:00+05:00", - "1970-10-1T00:00:00+05:00", - "1970-10-10T0:00:00+05:00", - "1970-10-10T00:0:00+05:00", - "1970-10-10T00:00:0+05:00", - // no offset - "1970-02-03T04:05:06.123456789", - // some invalid single-character offsets - "1970-02-03T04:05:06.123456789A", - "1970-02-03T04:05:06.123456789+", - "1970-02-03T04:05:06.123456789-", - // too many components in the offset - "1970-02-03T04:05:06.123456789+03:02:01:00", - "1970-02-03T04:05:06.123456789+03:02:01.02", - // single-digit offset - "1970-02-03T04:05:06.123456789+3", - // incorrect sign in the offset - "1970-02-03T04:05:06.123456789 03", - // non-digits in the offset - "1970-02-03T04:05:06.123456789+X3", - "1970-02-03T04:05:06.123456789+1X", - "1970-02-03T04:05:06.123456789+X3:12", - "1970-02-03T04:05:06.123456789+1X:12", - "1970-02-03T04:05:06.123456789+X3:12", - "1970-02-03T04:05:06.123456789+13:X2", - "1970-02-03T04:05:06.123456789+13:1X", - "1970-02-03T04:05:06.123456789+X3:12:59", - "1970-02-03T04:05:06.123456789+1X:12:59", - "1970-02-03T04:05:06.123456789+X3:12:59", - "1970-02-03T04:05:06.123456789+13:X2:59", - "1970-02-03T04:05:06.123456789+13:1X:59", - "1970-02-03T04:05:06.123456789+13:12:X9", - "1970-02-03T04:05:06.123456789+13:12:5X", - // incorrect separators in the offset - "1970-02-03T04:05:06.123456789+13/12", - "1970-02-03T04:05:06.123456789+13/12:59", - "1970-02-03T04:05:06.123456789+13:12/59", - "1970-02-03T04:05:06.123456789+0130", - "1970-02-03T04:05:06.123456789-0130", - // incorrect field length - "1970-02-03T04:05:06.123456789-18:001", - // out-of-range offsets - "1970-02-03T04:05:06.123456789+18:12:59", - "1970-02-03T04:05:06.123456789-18:12:59", - "1970-02-03T04:05:06.123456789+18:00:01", - "1970-02-03T04:05:06.123456789-18:00:01", - "1970-02-03T04:05:06.123456789+18:01", - "1970-02-03T04:05:06.123456789-18:01", - "1970-02-03T04:05:06.123456789+19", - "1970-02-03T04:05:06.123456789-19", - // out-of-range fields of the offset - "1970-02-03T04:05:06.123456789+01:12:60", - "1970-02-03T04:05:06.123456789-01:12:60", - "1970-02-03T04:05:06.123456789+01:60", - "1970-02-03T04:05:06.123456789-01:60", - // lack of padding in the offset - "1970-02-03T04:05:06.123456789+1:12:50", - "1970-02-03T04:05:06.123456789+01:2:60", - "1970-02-03T04:05:06.123456789+01:12:6", - )) { - assertInvalidFormat(nonIsoString) { parseInstant(nonIsoString) } - } - // this string represents an Instant that is currently larger than Instant.MAX any of the implementations: - assertInvalidFormat { parseInstant ("+1000000001-12-31T23:59:59.000000000Z") } - } - - @Test - fun parseStringsWithOffsets() { - val strings = arrayOf( - Pair("2020-01-01T00:01:01.02+18:00", "2019-12-31T06:01:01.020Z"), - Pair("2020-01-01T00:01:01.123456789-17:59:59", "2020-01-01T18:01:00.123456789Z"), - Pair("2020-01-01T00:01:01.010203040+17:59:59", "2019-12-31T06:01:02.010203040Z"), - Pair("2020-01-01T00:01:01.010203040+17:59", "2019-12-31T06:02:01.010203040Z"), - Pair("2020-01-01T00:01:01+00", "2020-01-01T00:01:01Z"), - ) - strings.forEach { (str, strInZ) -> - val instant = parseInstant(str) - assertEquals(parseInstant(strInZ), instant, str) - assertEquals(strInZ, displayInstant(instant), str) - } - assertInvalidFormat { parseInstant("2020-01-01T00:01:01+18:01") } - assertInvalidFormat { parseInstant("2020-01-01T00:01:01+1801") } - assertInvalidFormat { parseInstant("2020-01-01T00:01:01+0") } - assertInvalidFormat { parseInstant("2020-01-01T00:01:01+") } - assertInvalidFormat { parseInstant("2020-01-01T00:01:01") } - assertInvalidFormat { parseInstant("2020-01-01T00:01:01+000000") } - - val instants = listOf( - Instant.DISTANT_FUTURE, - Instant.DISTANT_PAST, - Instant.fromEpochSeconds(0, 0), - parseInstant("2020-01-02T03:04:05.6789Z"), - Instant.MAX, - Instant.MIN, - ) - - val offsets = listOf( - 0 to "Z", - 3 * 3600 + 12 * 60 + 14 to "+03:12:14", - - 3 * 3600 - 12 * 60 - 14 to "-03:12:14", - 2 * 3600 + 35 * 60 to "+02:35", - - 2 * 3600 - 35 * 60 to "-02:35", - 4 * 3600 to "+04", - - 4 * 3600 to "-04", - ) - - for (instant in instants) { - for ((offsetSeconds, offsetString) in offsets) { - if (instant == Instant.MAX && offsetSeconds < 0 || - instant == Instant.MIN && offsetSeconds > 0 - ) continue - val newInstant = parseInstant("${instant.toString().dropLast(1)}$offsetString") - assertEquals(newInstant, instant.minus(offsetSeconds.seconds)) - } - } - } - - private fun parseInstant(isoString: String): Instant { - // return Instant.parse(isoString) - return parseIso(isoString) - } - - private fun displayInstant(instant: Instant): String { - // return instant.toString() - return formatIso(instant) - } -} - - -@Suppress("INVISIBLE_REFERENCE", "INVISIBLE_MEMBER") -@kotlin.internal.InlineOnly -private fun assertInvalidFormat(message: String? = null, f: () -> T) { - assertFailsWith(message) { - val result = f() - fail(result.toString()) - } -} diff --git a/core/commonKotlin/test/ThreeTenBpInstantTest.kt b/core/commonKotlin/test/ThreeTenBpInstantTest.kt deleted file mode 100644 index baf538cef..000000000 --- a/core/commonKotlin/test/ThreeTenBpInstantTest.kt +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Copyright 2019-2020 JetBrains s.r.o. - * Use of this source code is governed by the Apache 2.0 License that can be found in the LICENSE.txt file. - */ -/* Based on the ThreeTenBp project. - * Copyright (c) 2007-present, Stephen Colebourne & Michael Nascimento Santos - */ - -package kotlinx.datetime.test - -import kotlinx.datetime.* -import kotlin.test.* - -class ThreeTenBpInstantTest { - - @Test - fun instantComparisons() { - val instants = arrayOf( - Instant.fromEpochSeconds(-2L, 0), - Instant.fromEpochSeconds(-2L, 999999998), - Instant.fromEpochSeconds(-2L, 999999999), - Instant.fromEpochSeconds(-1L, 0), - Instant.fromEpochSeconds(-1L, 1), - Instant.fromEpochSeconds(-1L, 999999998), - Instant.fromEpochSeconds(-1L, 999999999), - Instant.fromEpochSeconds(0L, 0), - Instant.fromEpochSeconds(0L, 1), - Instant.fromEpochSeconds(0L, 2), - Instant.fromEpochSeconds(0L, 999999999), - Instant.fromEpochSeconds(1L, 0), - Instant.fromEpochSeconds(2L, 0) - ) - for (i in instants.indices) { - val a = instants[i] - for (j in instants.indices) { - val b = instants[j] - when { - i < j -> { - assertTrue(a < b, "$a <=> $b") - assertNotEquals(a, b, "$a <=> $b") - } - i > j -> { - assertTrue(a > b, "$a <=> $b") - assertNotEquals(a, b, "$a <=> $b") - } - else -> { - assertEquals(0, a.compareTo(b), "$a <=> $b") - assertEquals(a, b, "$a <=> $b") - } - } - } - } - } - - @Test - fun instantEquals() { - val test5a: Instant = Instant.fromEpochSeconds(5L, 20) - val test5b: Instant = Instant.fromEpochSeconds(5L, 20) - val test5n: Instant = Instant.fromEpochSeconds(5L, 30) - val test6: Instant = Instant.fromEpochSeconds(6L, 20) - assertEquals(true, test5a == test5a) - assertEquals(true, test5a == test5b) - assertEquals(false, test5a == test5n) - assertEquals(false, test5a == test6) - assertEquals(true, test5b == test5a) - assertEquals(true, test5b == test5b) - assertEquals(false, test5b == test5n) - assertEquals(false, test5b == test6) - assertEquals(false, test5n == test5a) - assertEquals(false, test5n == test5b) - assertEquals(true, test5n == test5n) - assertEquals(false, test5n == test6) - assertEquals(false, test6 == test5a) - assertEquals(false, test6 == test5b) - assertEquals(false, test6 == test5n) - assertEquals(true, test6 == test6) - } - - @Test - fun toEpochMilliseconds() { - assertEquals(Instant.fromEpochSeconds(1L, 1000000).toEpochMilliseconds(), 1001L) - assertEquals(Instant.fromEpochSeconds(1L, 2000000).toEpochMilliseconds(), 1002L) - assertEquals(Instant.fromEpochSeconds(1L, 567).toEpochMilliseconds(), 1000L) - assertEquals(Instant.fromEpochSeconds(Long.MAX_VALUE / 1_000_000).toEpochMilliseconds(), Long.MAX_VALUE / 1_000_000 * 1000) - assertEquals(Instant.fromEpochSeconds(Long.MIN_VALUE / 1_000_000).toEpochMilliseconds(), Long.MIN_VALUE / 1_000_000 * 1000) - assertEquals(Instant.fromEpochSeconds(0L, -1000000).toEpochMilliseconds(), -1L) - assertEquals(Instant.fromEpochSeconds(0L, 1000000).toEpochMilliseconds(), 1) - assertEquals(Instant.fromEpochSeconds(0L, 999999).toEpochMilliseconds(), 0) - assertEquals(Instant.fromEpochSeconds(0L, 1).toEpochMilliseconds(), 0) - assertEquals(Instant.fromEpochSeconds(0L, 0).toEpochMilliseconds(), 0) - assertEquals(Instant.fromEpochSeconds(0L, -1).toEpochMilliseconds(), -1L) - assertEquals(Instant.fromEpochSeconds(0L, -999999).toEpochMilliseconds(), -1L) - assertEquals(Instant.fromEpochSeconds(0L, -1000000).toEpochMilliseconds(), -1L) - assertEquals(Instant.fromEpochSeconds(0L, -1000001).toEpochMilliseconds(), -2L) - } -} diff --git a/core/commonKotlin/test/TimeZoneRulesTest.kt b/core/commonKotlin/test/TimeZoneRulesTest.kt index c4666a62b..03742b2d7 100644 --- a/core/commonKotlin/test/TimeZoneRulesTest.kt +++ b/core/commonKotlin/test/TimeZoneRulesTest.kt @@ -8,6 +8,7 @@ package kotlinx.datetime.test import kotlinx.datetime.* import kotlinx.datetime.internal.* import kotlin.test.* +import kotlinx.time.Instant class TimeZoneRulesTest { @Test diff --git a/core/darwin/src/Converters.kt b/core/darwin/src/Converters.kt index 6ffdfe653..7f9df981d 100644 --- a/core/darwin/src/Converters.kt +++ b/core/darwin/src/Converters.kt @@ -10,6 +10,7 @@ package kotlinx.datetime import kotlinx.cinterop.* import kotlinx.datetime.internal.NANOS_PER_ONE import platform.Foundation.* +import kotlinx.time.Instant /** * Converts the [Instant] to an instance of [NSDate]. @@ -25,6 +26,10 @@ public fun Instant.toNSDate(): NSDate { return NSDate.dateWithTimeIntervalSince1970(secs) } +@PublishedApi +@Suppress("DEPRECATION_ERROR") +internal fun kotlinx.datetime.Instant.toNSDate(): NSDate = toNewInstant().toNSDate() + /** * Converts the [NSDate] to the corresponding [Instant]. * @@ -33,13 +38,19 @@ public fun Instant.toNSDate(): NSDate { * For example, if the [NSDate] only has millisecond or microsecond precision logically, * due to conversion artifacts in [Double] values, the result may include non-zero nanoseconds. */ -public fun NSDate.toKotlinInstant(): Instant { +public fun NSDate.toKotlinInstant(deprecationMarker: DeprecationMarker = DeprecationMarker.INSTANCE): Instant { val secs = timeIntervalSince1970() val fullSeconds = secs.toLong() val nanos = (secs - fullSeconds) * NANOS_PER_ONE return Instant.fromEpochSeconds(fullSeconds, nanos.toLong()) } +@PublishedApi +@Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE", "DEPRECATION_ERROR") +@kotlin.internal.LowPriorityInOverloadResolution +internal fun NSDate.toKotlinInstant(): kotlinx.datetime.Instant = + toKotlinInstant().toDeprecatedInstant() + /** * Converts the [TimeZone] to [NSTimeZone]. * diff --git a/core/darwin/test/ConvertersTest.kt b/core/darwin/test/ConvertersTest.kt index 9efe07750..38ccb959c 100644 --- a/core/darwin/test/ConvertersTest.kt +++ b/core/darwin/test/ConvertersTest.kt @@ -11,6 +11,8 @@ import platform.Foundation.* import kotlin.math.* import kotlin.random.* import kotlin.test.* +import kotlinx.time.Clock +import kotlinx.time.Instant class ConvertersTest { diff --git a/core/js/src/Converters.kt b/core/js/src/DeprecatedConverters.kt similarity index 94% rename from core/js/src/Converters.kt rename to core/js/src/DeprecatedConverters.kt index a4a8c9df9..544e2abc9 100644 --- a/core/js/src/Converters.kt +++ b/core/js/src/DeprecatedConverters.kt @@ -3,6 +3,7 @@ * Use of this source code is governed by the Apache 2.0 License that can be found in the LICENSE.txt file. */ +@file:Suppress("DEPRECATION_ERROR") package kotlinx.datetime import kotlin.js.* diff --git a/core/js/test/JsConverterTest.kt b/core/js/test/DeprecatedJsConverterTest.kt similarity index 92% rename from core/js/test/JsConverterTest.kt rename to core/js/test/DeprecatedJsConverterTest.kt index de5eeb6f3..f90b33d0e 100644 --- a/core/js/test/JsConverterTest.kt +++ b/core/js/test/DeprecatedJsConverterTest.kt @@ -3,13 +3,14 @@ * Use of this source code is governed by the Apache 2.0 License that can be found in the LICENSE.txt file. */ +@file:Suppress("DEPRECATION_ERROR") package kotlinx.datetime.test import kotlinx.datetime.* import kotlin.js.* import kotlin.test.* -class JsConverterTest { +class DeprecatedJsConverterTest { @Test fun toJSDateTest() { val releaseInstant = Instant.parse("2016-02-15T00:00:00Z") diff --git a/core/jvm/src/Converters.kt b/core/jvm/src/Converters.kt index 6db8339d3..751c87230 100644 --- a/core/jvm/src/Converters.kt +++ b/core/jvm/src/Converters.kt @@ -8,12 +8,17 @@ package kotlinx.datetime /** * Converts this [kotlinx.datetime.Instant][Instant] value to a [java.time.Instant][java.time.Instant] value. */ -public fun Instant.toJavaInstant(): java.time.Instant = this.value +@PublishedApi +@Suppress("DEPRECATION_ERROR") +internal fun Instant.toJavaInstant(): java.time.Instant = this.value /** * Converts this [java.time.Instant][java.time.Instant] value to a [kotlinx.datetime.Instant][Instant] value. */ -public fun java.time.Instant.toKotlinInstant(): Instant = Instant(this) +@PublishedApi +@Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE", "DEPRECATION_ERROR") +@kotlin.internal.LowPriorityInOverloadResolution +internal fun java.time.Instant.toKotlinInstant(): Instant = Instant(this) /** @@ -92,4 +97,3 @@ public fun UtcOffset.toJavaZoneOffset(): java.time.ZoneOffset = this.zoneOffset * Converts this [java.time.ZoneOffset][java.time.ZoneOffset] value to a [kotlinx.datetime.UtcOffset][UtcOffset] value. */ public fun java.time.ZoneOffset.toKotlinUtcOffset(): UtcOffset = UtcOffset(this) - diff --git a/core/jvm/src/DeprecatedInstant.kt b/core/jvm/src/DeprecatedInstant.kt new file mode 100644 index 000000000..05ab31e7d --- /dev/null +++ b/core/jvm/src/DeprecatedInstant.kt @@ -0,0 +1,190 @@ +/* + * Copyright 2019-2024 JetBrains s.r.o. and contributors. + * Use of this source code is governed by the Apache 2.0 License that can be found in the LICENSE.txt file. + */ + +@file:Suppress("DEPRECATION_ERROR") +@file:JvmMultifileClass +@file:JvmName("InstantJvmKt") +package kotlinx.datetime + +import kotlinx.datetime.format.DateTimeComponents +import kotlinx.datetime.format.DateTimeFormat +import kotlinx.datetime.internal.NANOS_PER_ONE +import kotlinx.datetime.internal.multiplyAndDivide +import kotlinx.datetime.internal.safeMultiply +import kotlinx.datetime.serializers.InstantIso8601Serializer +import kotlinx.serialization.Serializable +import java.time.Clock +import java.time.DateTimeException +import java.time.temporal.ChronoUnit +import kotlin.time.Duration +import kotlin.time.Duration.Companion.nanoseconds +import kotlin.time.Duration.Companion.seconds + +@Deprecated( + "Use kotlin.time.Instant instead", + ReplaceWith("kotlinx.time.Instant", "kotlinx.time.Instant"), + level = DeprecationLevel.ERROR +) +@Serializable(with = InstantIso8601Serializer::class) +public actual class Instant internal constructor(internal val value: java.time.Instant) : Comparable { + + public actual val epochSeconds: Long + get() = value.epochSecond + public actual val nanosecondsOfSecond: Int + get() = value.nano + + public actual fun toEpochMilliseconds(): Long = try { + value.toEpochMilli() + } catch (e: ArithmeticException) { + if (value.isAfter(java.time.Instant.EPOCH)) Long.MAX_VALUE else Long.MIN_VALUE + } + + public actual operator fun plus(duration: Duration): Instant = duration.toComponents { seconds, nanoseconds -> + try { + Instant(value.plusSeconds(seconds).plusNanos(nanoseconds.toLong())) + } catch (e: java.lang.Exception) { + if (e !is ArithmeticException && e !is DateTimeException) throw e + if (duration.isPositive()) MAX else MIN + } + } + + public actual operator fun minus(duration: Duration): Instant = plus(-duration) + + public actual operator fun minus(other: Instant): Duration = + (this.value.epochSecond - other.value.epochSecond).seconds + // won't overflow given the instant bounds + (this.value.nano - other.value.nano).nanoseconds + + public actual override operator fun compareTo(other: Instant): Int = this.value.compareTo(other.value) + + override fun equals(other: Any?): Boolean = + (this === other) || (other is Instant && this.value == other.value) + + override fun hashCode(): Int = value.hashCode() + + actual override fun toString(): String = value.toString() + + public actual companion object { + @Deprecated("Use Clock.System.now() instead", ReplaceWith("Clock.System.now()", "kotlinx.datetime.Clock"), level = DeprecationLevel.ERROR) + public actual fun now(): Instant = + Instant(Clock.systemUTC().instant()) + + public actual fun fromEpochMilliseconds(epochMilliseconds: Long): Instant = + Instant(java.time.Instant.ofEpochMilli(epochMilliseconds)) + + // TODO: implement a custom parser to 1) help DCE get rid of the formatting machinery 2) move Instant to stdlib + public actual fun parse(input: CharSequence, format: DateTimeFormat): Instant = try { + /** + * Can't use built-in Java Time's handling of `Instant.parse` because it supports 24:00:00 and + * 23:59:60, and also doesn't support non-`Z` UTC offsets on older JDKs. + * Can't use custom Java Time's formats because Java 8 doesn't support the UTC offset format with + * optional minutes and seconds and `:` between them: + * https://docs.oracle.com/javase/8/docs/api/java/time/format/DateTimeFormatterBuilder.html#appendOffset-java.lang.String-java.lang.String- + */ + format.parse(input).toInstantUsingOffset().toDeprecatedInstant() + } catch (e: IllegalArgumentException) { + throw DateTimeFormatException("Failed to parse an instant from '$input'", e) + } + + @Deprecated("This overload is only kept for binary compatibility", level = DeprecationLevel.HIDDEN) + public fun parse(isoString: String): Instant = parse(input = isoString) + + public actual fun fromEpochSeconds(epochSeconds: Long, nanosecondAdjustment: Long): Instant = try { + Instant(java.time.Instant.ofEpochSecond(epochSeconds, nanosecondAdjustment)) + } catch (e: Exception) { + if (e !is ArithmeticException && e !is DateTimeException) throw e + if (epochSeconds > 0) MAX else MIN + } + + public actual fun fromEpochSeconds(epochSeconds: Long, nanosecondAdjustment: Int): Instant = + fromEpochSeconds(epochSeconds, nanosecondAdjustment.toLong()) + + public actual val DISTANT_PAST: Instant = Instant(java.time.Instant.ofEpochSecond(DISTANT_PAST_SECONDS, 999_999_999)) + public actual val DISTANT_FUTURE: Instant = Instant(java.time.Instant.ofEpochSecond(DISTANT_FUTURE_SECONDS, 0)) + + internal actual val MIN: Instant = Instant(java.time.Instant.MIN) + internal actual val MAX: Instant = Instant(java.time.Instant.MAX) + } +} + +private fun Instant.atZone(zone: TimeZone): java.time.ZonedDateTime = try { + value.atZone(zone.zoneId) +} catch (e: DateTimeException) { + throw DateTimeArithmeticException(e) +} + +public actual fun Instant.plus(period: DateTimePeriod, timeZone: TimeZone): Instant { + try { + val thisZdt = atZone(timeZone) + return with(period) { + thisZdt + .run { if (totalMonths != 0L) plusMonths(totalMonths) else this } + .run { if (days != 0) plusDays(days.toLong()) else this } + .run { if (totalNanoseconds != 0L) plusNanos(totalNanoseconds) else this } + }.toInstant().let(::Instant) + } catch (e: DateTimeException) { + throw DateTimeArithmeticException(e) + } +} + +@Deprecated("Use the plus overload with an explicit number of units", ReplaceWith("this.plus(1, unit, timeZone)")) +public actual fun Instant.plus(unit: DateTimeUnit, timeZone: TimeZone): Instant = + plus(1L, unit, timeZone) + +public actual fun Instant.plus(value: Int, unit: DateTimeUnit, timeZone: TimeZone): Instant = + plus(value.toLong(), unit, timeZone) + +public actual fun Instant.minus(value: Int, unit: DateTimeUnit, timeZone: TimeZone): Instant = + plus(-value.toLong(), unit, timeZone) + +public actual fun Instant.plus(value: Long, unit: DateTimeUnit, timeZone: TimeZone): Instant = + try { + val thisZdt = atZone(timeZone) + when (unit) { + is DateTimeUnit.TimeBased -> + plus(value, unit).value.also { it.atZone(timeZone.zoneId) } + is DateTimeUnit.DayBased -> + thisZdt.plusDays(safeMultiply(value, unit.days.toLong())).toInstant() + is DateTimeUnit.MonthBased -> + thisZdt.plusMonths(safeMultiply(value, unit.months.toLong())).toInstant() + }.let(::Instant) + } catch (e: Exception) { + if (e !is DateTimeException && e !is ArithmeticException) throw e + throw DateTimeArithmeticException("Instant $this cannot be represented as local date when adding $value $unit to it", e) + } + +public actual fun Instant.plus(value: Long, unit: DateTimeUnit.TimeBased): Instant = + try { + multiplyAndDivide(value, unit.nanoseconds, NANOS_PER_ONE.toLong()).let { (d, r) -> + Instant(this.value.plusSeconds(d).plusNanos(r)) + } + } catch (e: Exception) { + if (e !is DateTimeException && e !is ArithmeticException) throw e + if (value > 0) Instant.MAX else Instant.MIN + } + +public actual fun Instant.periodUntil(other: Instant, timeZone: TimeZone): DateTimePeriod { + var thisZdt = this.atZone(timeZone) + val otherZdt = other.atZone(timeZone) + + val months = thisZdt.until(otherZdt, ChronoUnit.MONTHS); thisZdt = thisZdt.plusMonths(months) + val days = thisZdt.until(otherZdt, ChronoUnit.DAYS); thisZdt = thisZdt.plusDays(days) + val nanoseconds = thisZdt.until(otherZdt, ChronoUnit.NANOS) + + return buildDateTimePeriod(months, days.toInt(), nanoseconds) +} + +public actual fun Instant.until(other: Instant, unit: DateTimeUnit, timeZone: TimeZone): Long = try { + val thisZdt = this.atZone(timeZone) + val otherZdt = other.atZone(timeZone) + when(unit) { + is DateTimeUnit.TimeBased -> until(other, unit) + is DateTimeUnit.DayBased -> thisZdt.until(otherZdt, ChronoUnit.DAYS) / unit.days + is DateTimeUnit.MonthBased -> thisZdt.until(otherZdt, ChronoUnit.MONTHS) / unit.months + } +} catch (e: DateTimeException) { + throw DateTimeArithmeticException(e) +} catch (e: ArithmeticException) { + if (this.value < other.value) Long.MAX_VALUE else Long.MIN_VALUE +} diff --git a/core/jvm/src/Instant.kt b/core/jvm/src/Instant.kt index 53c670449..69988c086 100644 --- a/core/jvm/src/Instant.kt +++ b/core/jvm/src/Instant.kt @@ -2,106 +2,23 @@ * Copyright 2019-2020 JetBrains s.r.o. * Use of this source code is governed by the Apache 2.0 License that can be found in the LICENSE.txt file. */ +@file:JvmMultifileClass @file:JvmName("InstantJvmKt") package kotlinx.datetime -import kotlinx.datetime.format.* import kotlinx.datetime.internal.safeMultiply import kotlinx.datetime.internal.* -import kotlinx.datetime.serializers.InstantIso8601Serializer -import kotlinx.serialization.Serializable import java.time.DateTimeException import java.time.temporal.* -import kotlin.time.* +import kotlinx.time.Instant +import kotlinx.time.toJavaInstant +import kotlinx.time.toKotlinInstant import kotlin.time.Duration.Companion.nanoseconds import kotlin.time.Duration.Companion.seconds -import java.time.Instant as jtInstant -import java.time.Clock as jtClock - -@Serializable(with = InstantIso8601Serializer::class) -public actual class Instant internal constructor(internal val value: jtInstant) : Comparable { - - public actual val epochSeconds: Long - get() = value.epochSecond - public actual val nanosecondsOfSecond: Int - get() = value.nano - - public actual fun toEpochMilliseconds(): Long = try { - value.toEpochMilli() - } catch (e: ArithmeticException) { - if (value.isAfter(java.time.Instant.EPOCH)) Long.MAX_VALUE else Long.MIN_VALUE - } - - public actual operator fun plus(duration: Duration): Instant = duration.toComponents { seconds, nanoseconds -> - try { - Instant(value.plusSeconds(seconds).plusNanos(nanoseconds.toLong())) - } catch (e: java.lang.Exception) { - if (e !is ArithmeticException && e !is DateTimeException) throw e - if (duration.isPositive()) MAX else MIN - } - } - - public actual operator fun minus(duration: Duration): Instant = plus(-duration) - - public actual operator fun minus(other: Instant): Duration = - (this.value.epochSecond - other.value.epochSecond).seconds + // won't overflow given the instant bounds - (this.value.nano - other.value.nano).nanoseconds - - public actual override operator fun compareTo(other: Instant): Int = this.value.compareTo(other.value) - - override fun equals(other: Any?): Boolean = - (this === other) || (other is Instant && this.value == other.value) - - override fun hashCode(): Int = value.hashCode() - - actual override fun toString(): String = value.toString() - - public actual companion object { - @Deprecated("Use Clock.System.now() instead", ReplaceWith("Clock.System.now()", "kotlinx.datetime.Clock"), level = DeprecationLevel.ERROR) - public actual fun now(): Instant = - Instant(jtClock.systemUTC().instant()) - - public actual fun fromEpochMilliseconds(epochMilliseconds: Long): Instant = - Instant(jtInstant.ofEpochMilli(epochMilliseconds)) - - // TODO: implement a custom parser to 1) help DCE get rid of the formatting machinery 2) move Instant to stdlib - public actual fun parse(input: CharSequence, format: DateTimeFormat): Instant = try { - /** - * Can't use built-in Java Time's handling of `Instant.parse` because it supports 24:00:00 and - * 23:59:60, and also doesn't support non-`Z` UTC offsets on older JDKs. - * Can't use custom Java Time's formats because Java 8 doesn't support the UTC offset format with - * optional minutes and seconds and `:` between them: - * https://docs.oracle.com/javase/8/docs/api/java/time/format/DateTimeFormatterBuilder.html#appendOffset-java.lang.String-java.lang.String- - */ - format.parse(input).toInstantUsingOffset() - } catch (e: IllegalArgumentException) { - throw DateTimeFormatException("Failed to parse an instant from '$input'", e) - } - - @Deprecated("This overload is only kept for binary compatibility", level = DeprecationLevel.HIDDEN) - public fun parse(isoString: String): Instant = parse(input = isoString) - - public actual fun fromEpochSeconds(epochSeconds: Long, nanosecondAdjustment: Long): Instant = try { - Instant(jtInstant.ofEpochSecond(epochSeconds, nanosecondAdjustment)) - } catch (e: Exception) { - if (e !is ArithmeticException && e !is DateTimeException) throw e - if (epochSeconds > 0) MAX else MIN - } - - public actual fun fromEpochSeconds(epochSeconds: Long, nanosecondAdjustment: Int): Instant = - fromEpochSeconds(epochSeconds, nanosecondAdjustment.toLong()) - - public actual val DISTANT_PAST: Instant = Instant(jtInstant.ofEpochSecond(DISTANT_PAST_SECONDS, 999_999_999)) - public actual val DISTANT_FUTURE: Instant = Instant(jtInstant.ofEpochSecond(DISTANT_FUTURE_SECONDS, 0)) - - internal actual val MIN: Instant = Instant(jtInstant.MIN) - internal actual val MAX: Instant = Instant(jtInstant.MAX) - } -} private fun Instant.atZone(zone: TimeZone): java.time.ZonedDateTime = try { - value.atZone(zone.zoneId) + toJavaInstant().atZone(zone.zoneId) } catch (e: DateTimeException) { throw DateTimeArithmeticException(e) } @@ -114,7 +31,7 @@ public actual fun Instant.plus(period: DateTimePeriod, timeZone: TimeZone): Inst .run { if (totalMonths != 0L) plusMonths(totalMonths) else this } .run { if (days != 0) plusDays(days.toLong()) else this } .run { if (totalNanoseconds != 0L) plusNanos(totalNanoseconds) else this } - }.toInstant().let(::Instant) + }.toInstant().toKotlinInstant() } catch (e: DateTimeException) { throw DateTimeArithmeticException(e) } @@ -135,12 +52,12 @@ public actual fun Instant.plus(value: Long, unit: DateTimeUnit, timeZone: TimeZo val thisZdt = atZone(timeZone) when (unit) { is DateTimeUnit.TimeBased -> - plus(value, unit).value.also { it.atZone(timeZone.zoneId) } + plus(value, unit).toJavaInstant().also { it.atZone(timeZone.zoneId) } is DateTimeUnit.DayBased -> thisZdt.plusDays(safeMultiply(value, unit.days.toLong())).toInstant() is DateTimeUnit.MonthBased -> thisZdt.plusMonths(safeMultiply(value, unit.months.toLong())).toInstant() - }.let(::Instant) + }.toKotlinInstant() } catch (e: Exception) { if (e !is DateTimeException && e !is ArithmeticException) throw e throw DateTimeArithmeticException("Instant $this cannot be represented as local date when adding $value $unit to it", e) @@ -149,11 +66,11 @@ public actual fun Instant.plus(value: Long, unit: DateTimeUnit, timeZone: TimeZo public actual fun Instant.plus(value: Long, unit: DateTimeUnit.TimeBased): Instant = try { multiplyAndDivide(value, unit.nanoseconds, NANOS_PER_ONE.toLong()).let { (d, r) -> - Instant(this.value.plusSeconds(d).plusNanos(r)) + this.plus(d.seconds).plus(r.nanoseconds) } } catch (e: Exception) { if (e !is DateTimeException && e !is ArithmeticException) throw e - if (value > 0) Instant.MAX else Instant.MIN + Instant.fromEpochSeconds(if (value > 0) Long.MAX_VALUE else Long.MIN_VALUE) } public actual fun Instant.periodUntil(other: Instant, timeZone: TimeZone): DateTimePeriod { @@ -178,5 +95,5 @@ public actual fun Instant.until(other: Instant, unit: DateTimeUnit, timeZone: Ti } catch (e: DateTimeException) { throw DateTimeArithmeticException(e) } catch (e: ArithmeticException) { - if (this.value < other.value) Long.MAX_VALUE else Long.MIN_VALUE + if (this < other) Long.MAX_VALUE else Long.MIN_VALUE } diff --git a/core/jvm/src/TimeZoneJvm.kt b/core/jvm/src/TimeZoneJvm.kt index cd90993ef..482a82b3b 100644 --- a/core/jvm/src/TimeZoneJvm.kt +++ b/core/jvm/src/TimeZoneJvm.kt @@ -13,6 +13,9 @@ import kotlinx.serialization.Serializable import java.time.DateTimeException import java.time.ZoneId import java.time.ZoneOffset as jtZoneOffset +import kotlinx.time.Instant +import kotlinx.time.toJavaInstant +import kotlinx.time.toKotlinInstant @Serializable(with = TimeZoneSerializer::class) public actual open class TimeZone internal constructor(internal val zoneId: ZoneId) { @@ -21,7 +24,19 @@ public actual open class TimeZone internal constructor(internal val zoneId: Zone // experimental member-extensions public actual fun Instant.toLocalDateTime(): LocalDateTime = toLocalDateTime(this@TimeZone) - public actual fun LocalDateTime.toInstant(): Instant = toInstant(this@TimeZone) + public actual fun LocalDateTime.toInstant(deprecationMarker: DeprecationMarker): Instant = toInstant(this@TimeZone) + + @PublishedApi + @Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE", "DEPRECATION_ERROR") + @kotlin.internal.LowPriorityInOverloadResolution + internal actual fun kotlinx.datetime.Instant.toLocalDateTime(): LocalDateTime = + toNewInstant().toLocalDateTime() + + @PublishedApi + @Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE", "DEPRECATION_ERROR") + @kotlin.internal.LowPriorityInOverloadResolution + internal actual fun LocalDateTime.toInstant(): kotlinx.datetime.Instant = + toInstant(this@TimeZone).toDeprecatedInstant() actual override fun equals(other: Any?): Boolean = (this === other) || (other is TimeZone && this.zoneId == other.zoneId) @@ -74,26 +89,26 @@ internal constructor(public actual val offset: UtcOffset, zoneId: ZoneId): TimeZ } public actual fun TimeZone.offsetAt(instant: Instant): UtcOffset = - zoneId.rules.getOffset(instant.value).let(::UtcOffset) + zoneId.rules.getOffset(instant.toJavaInstant()).let(::UtcOffset) public actual fun Instant.toLocalDateTime(timeZone: TimeZone): LocalDateTime = try { - java.time.LocalDateTime.ofInstant(this.value, timeZone.zoneId).let(::LocalDateTime) + java.time.LocalDateTime.ofInstant(this.toJavaInstant(), timeZone.zoneId).let(::LocalDateTime) } catch (e: DateTimeException) { throw DateTimeArithmeticException(e) } internal actual fun Instant.toLocalDateTime(offset: UtcOffset): LocalDateTime = try { - java.time.LocalDateTime.ofInstant(this.value, offset.zoneOffset).let(::LocalDateTime) + java.time.LocalDateTime.ofInstant(this.toJavaInstant(), offset.zoneOffset).let(::LocalDateTime) } catch (e: DateTimeException) { throw DateTimeArithmeticException(e) } -public actual fun LocalDateTime.toInstant(timeZone: TimeZone): Instant = - this.value.atZone(timeZone.zoneId).toInstant().let(::Instant) +public actual fun LocalDateTime.toInstant(timeZone: TimeZone, deprecationMarker: DeprecationMarker): Instant = + this.value.atZone(timeZone.zoneId).toInstant().toKotlinInstant() -public actual fun LocalDateTime.toInstant(offset: UtcOffset): Instant = - this.value.toInstant(offset.zoneOffset).let(::Instant) +public actual fun LocalDateTime.toInstant(offset: UtcOffset, deprecationMarker: DeprecationMarker): Instant = + this.value.toInstant(offset.zoneOffset).toKotlinInstant() -public actual fun LocalDate.atStartOfDayIn(timeZone: TimeZone): Instant = - this.value.atStartOfDay(timeZone.zoneId).toInstant().let(::Instant) +public actual fun LocalDate.atStartOfDayIn(timeZone: TimeZone, deprecationMarker: DeprecationMarker): Instant = + this.value.atStartOfDay(timeZone.zoneId).toInstant().toKotlinInstant() diff --git a/core/jvm/test/ConvertersTest.kt b/core/jvm/test/ConvertersTest.kt index 18d759938..1cfa81566 100644 --- a/core/jvm/test/ConvertersTest.kt +++ b/core/jvm/test/ConvertersTest.kt @@ -14,6 +14,8 @@ import java.time.LocalDate as JTLocalDate import java.time.Period as JTPeriod import java.time.ZoneId import java.time.ZoneOffset as JTZoneOffset +import kotlinx.time.Instant +import kotlinx.time.* class ConvertersTest { @@ -26,7 +28,7 @@ class ConvertersTest { assertEquals(ktInstant, jtInstant.toKotlinInstant()) assertEquals(jtInstant, ktInstant.toJavaInstant()) - assertEquals(ktInstant, jtInstant.toString().toInstant()) + assertEquals(ktInstant, jtInstant.toString().let(Instant::parse)) assertEquals(jtInstant, ktInstant.toString().let(JTInstant::parse)) } diff --git a/core/jvm/test/InstantParsing.kt b/core/jvm/test/InstantParsing.kt index 4c29b81e9..aac16b775 100644 --- a/core/jvm/test/InstantParsing.kt +++ b/core/jvm/test/InstantParsing.kt @@ -2,6 +2,8 @@ package kotlinx.datetime import kotlinx.datetime.format.* import kotlin.test.* +import kotlinx.time.Instant +import kotlinx.time.* class InstantParsing { @Test diff --git a/core/native/src/internal/Platform.kt b/core/native/src/internal/Platform.kt deleted file mode 100644 index 63b890610..000000000 --- a/core/native/src/internal/Platform.kt +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright 2019-2024 JetBrains s.r.o. and contributors. - * Use of this source code is governed by the Apache 2.0 License that can be found in the LICENSE.txt file. - */ - -package kotlinx.datetime.internal - -import kotlinx.cinterop.* -import kotlinx.datetime.Instant -import platform.posix.* - -@OptIn(ExperimentalForeignApi::class, UnsafeNumber::class) -internal actual fun currentTime(): Instant = memScoped { - val tm = alloc() - val error = clock_gettime(CLOCK_REALTIME.convert(), tm.ptr) - check(error == 0) { "Error when reading the system clock: ${strerror(errno)?.toKString() ?: "Unknown error"}" } - try { - require(tm.tv_nsec in 0 until NANOS_PER_ONE) - Instant(tm.tv_sec.convert(), tm.tv_nsec.convert()) - } catch (e: IllegalArgumentException) { - throw IllegalStateException("The readings from the system clock (${tm.tv_sec} seconds, ${tm.tv_nsec} nanoseconds) are not representable as an Instant") - } -} \ No newline at end of file diff --git a/core/tzdbOnFilesystem/test/TimeZoneRulesCompleteTest.kt b/core/tzdbOnFilesystem/test/TimeZoneRulesCompleteTest.kt index 43437708a..40fea247f 100644 --- a/core/tzdbOnFilesystem/test/TimeZoneRulesCompleteTest.kt +++ b/core/tzdbOnFilesystem/test/TimeZoneRulesCompleteTest.kt @@ -11,6 +11,7 @@ import kotlinx.datetime.internal.* import platform.posix.* import kotlin.io.encoding.* import kotlin.test.* +import kotlinx.time.Instant class TimeZoneRulesCompleteTest { @OptIn(ExperimentalEncodingApi::class) diff --git a/core/wasmWasi/src/internal/Platform.kt b/core/wasmWasi/src/internal/Platform.kt deleted file mode 100644 index 1403cae7d..000000000 --- a/core/wasmWasi/src/internal/Platform.kt +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright 2019-2024 JetBrains s.r.o. and contributors. - * Use of this source code is governed by the Apache 2.0 License that can be found in the LICENSE.txt file. - */ - -package kotlinx.datetime.internal - -import kotlinx.datetime.Instant -import kotlin.wasm.WasmImport -import kotlin.wasm.unsafe.UnsafeWasmMemoryApi -import kotlin.wasm.unsafe.withScopedMemoryAllocator - -/** - * Return the time value of a clock. Note: This is similar to `clock_gettime` in POSIX. - */ -@WasmImport("wasi_snapshot_preview1", "clock_time_get") -private external fun wasiRawClockTimeGet(clockId: Int, precision: Long, resultPtr: Int): Int - -private const val CLOCKID_REALTIME = 0 - -@OptIn(UnsafeWasmMemoryApi::class) -private fun clockTimeGet(): Long = withScopedMemoryAllocator { allocator -> - val rp0 = allocator.allocate(8) - val ret = wasiRawClockTimeGet( - clockId = CLOCKID_REALTIME, - precision = 1, - resultPtr = rp0.address.toInt() - ) - if (ret == 0) { - rp0.loadLong() - } else { - error("WASI call failed with $ret") - } -} - -internal actual fun currentTime(): Instant = clockTimeGet().let { time -> - // Instant.MAX and Instant.MIN are never going to be exceeded using just the Long number of nanoseconds - Instant(time.floorDiv(NANOS_PER_ONE.toLong()), time.mod(NANOS_PER_ONE.toLong()).toInt()) -} diff --git a/core/windows/test/TimeZoneRulesCompleteTest.kt b/core/windows/test/TimeZoneRulesCompleteTest.kt index 34d3b8684..cd1aa32bf 100644 --- a/core/windows/test/TimeZoneRulesCompleteTest.kt +++ b/core/windows/test/TimeZoneRulesCompleteTest.kt @@ -13,6 +13,7 @@ import kotlinx.datetime.internal.* import platform.windows.* import kotlin.test.* import kotlin.time.Duration.Companion.milliseconds +import kotlinx.time.Instant class TimeZoneRulesCompleteTest { diff --git a/js-without-timezones/api/kotlinx-datetime-js-test-without-timezones.klib.api b/js-without-timezones/api/kotlinx-datetime-js-test-without-timezones.klib.api new file mode 100644 index 000000000..e69de29bb diff --git a/js-without-timezones/common/test/TimezonesWithoutDatabaseTest.kt b/js-without-timezones/common/test/TimezonesWithoutDatabaseTest.kt index 4fd7aef51..8924bf52d 100644 --- a/js-without-timezones/common/test/TimezonesWithoutDatabaseTest.kt +++ b/js-without-timezones/common/test/TimezonesWithoutDatabaseTest.kt @@ -9,6 +9,8 @@ import kotlinx.datetime.* import kotlin.test.* import kotlinx.datetime.test.JSJoda.ZoneId as jtZoneId import kotlinx.datetime.test.JSJoda.Instant as jtInstant +import kotlinx.time.Instant +import kotlinx.time.Clock class TimezonesWithoutDatabaseTest { @Test diff --git a/serialization/common/test/InstantSerializationTest.kt b/serialization/common/test/DeprecatedInstantSerializationTest.kt similarity index 97% rename from serialization/common/test/InstantSerializationTest.kt rename to serialization/common/test/DeprecatedInstantSerializationTest.kt index dea5c2dbd..74686eae7 100644 --- a/serialization/common/test/InstantSerializationTest.kt +++ b/serialization/common/test/DeprecatedInstantSerializationTest.kt @@ -2,6 +2,8 @@ * Copyright 2019-2020 JetBrains s.r.o. * Use of this source code is governed by the Apache 2.0 License that can be found in the LICENSE.txt file. */ + +@file:Suppress("DEPRECATION_ERROR") package kotlinx.datetime.serialization.test import kotlinx.datetime.* @@ -10,7 +12,7 @@ import kotlinx.serialization.* import kotlinx.serialization.json.* import kotlin.test.* -class InstantSerializationTest { +class DeprecatedInstantSerializationTest { private fun iso8601Serialization(serializer: KSerializer) { for ((instant, json) in listOf( diff --git a/serialization/common/test/IntegrationTest.kt b/serialization/common/test/IntegrationTest.kt index 0452a10fd..c721e1bd6 100644 --- a/serialization/common/test/IntegrationTest.kt +++ b/serialization/common/test/IntegrationTest.kt @@ -3,13 +3,13 @@ * Use of this source code is governed by the Apache 2.0 License that can be found in the LICENSE.txt file. */ +@file:Suppress("DEPRECATION_ERROR") package kotlinx.datetime.serialization.test import kotlinx.datetime.* import kotlinx.datetime.serializers.* import kotlinx.serialization.Contextual import kotlinx.serialization.Serializable -import kotlinx.serialization.decodeFromString import kotlinx.serialization.encodeToString import kotlinx.serialization.json.Json import kotlinx.serialization.modules.SerializersModule @@ -127,4 +127,4 @@ class IntegrationTest { assertEquals(dummyValue, format.decodeFromString(json)) assertEquals(json, format.encodeToString(dummyValue)) } -} \ No newline at end of file +}