diff --git a/README.md b/README.md index a39615466..9e446ee45 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # kotlinx-datetime [![Kotlin Alpha](https://kotl.in/badges/alpha.svg)](https://kotlinlang.org/docs/components-stability.html) -[![JetBrains official project](https://jb.gg/badges/official.svg)](https://confluence.jetbrains.com/display/ALL/JetBrains+on+GitHub) +[![JetBrains official project](https://jb.gg/badges/official.svg)](https://confluence.jetbrains.com/display/ALL/JetBrains+on+GitHub) [![GitHub license](https://img.shields.io/badge/license-Apache%20License%202.0-blue.svg?style=flat)](http://www.apache.org/licenses/LICENSE-2.0) [![Maven Central](https://img.shields.io/maven-central/v/org.jetbrains.kotlinx/kotlinx-datetime.svg?label=Maven%20Central)](https://search.maven.org/search?q=g:org.jetbrains.kotlinx%20AND%20a:kotlinx-datetime) [![Kotlin](https://img.shields.io/badge/kotlin-1.9.21-blue.svg?logo=kotlin)](http://kotlinlang.org) @@ -21,78 +21,59 @@ all-encompassing and lacks some domain-specific utilities that special-purpose a We chose convenience over generality, so the API surface this library provides is as minimal as possible to meet the use-cases. -The library puts a clear boundary between the physical time of an instant and the local, time-zone dependent -civil time, consisting of components such as year, month, etc that people use when talking about time. +The library puts a clear boundary between the physical time of an instant and the local, time-zone dependent +civil time, consisting of components such as year, month, etc that people use when talking about time. We intentionally avoid entities in the library that mix both together and could be misused. -However, there are convenience operations that take, for example, a physical instant and perform a calendar-based +However, there are convenience operations that take, for example, a physical instant and perform a calendar-based adjustment (such as adding a month); all such operations explicitly take a time-zone information as parameter to clearly state that their result depends on the civil time-zone rules which are subject to change at any time. The library is based on the ISO 8601 international standard, other ways to represent dates and times are out of -its scope. Internationalization (such as locale-specific month and day names) is out the scope, too. +its scope. Internationalization (such as locale-specific month and day names) is out the scope, too. ## Types The library provides a basic set of types for working with date and time: -- `Instant` to represent a moment on the UTC-SLS time scale; -- `Clock` to obtain the current instant; -- `LocalDateTime` to represent date and time components without a reference to the particular time zone; +- `LocalDateTime` to represent date and time components without a reference to the particular time zone; - `LocalDate` to represent the components of date only; - `LocalTime` to represent the components of time only; -- `TimeZone` and `FixedOffsetTimeZone` provide time zone information to convert between `Instant` and `LocalDateTime`; +- `TimeZone` and `FixedOffsetTimeZone` provide time zone information to convert between + `kotlin.time.Instant` and `LocalDateTime`; - `Month` and `DayOfWeek` enums; - `DateTimePeriod` to represent a difference between two instants decomposed into date and time units; - `DatePeriod` is a subclass of `DateTimePeriod` with zero time components, it represents a difference between two LocalDate values decomposed into date units. -- `DateTimeUnit` provides a set of predefined date and time units to use in arithmetic operations on `Instant` and `LocalDate`. +- `DateTimeUnit` provides a set of predefined date and time units to use in arithmetic operations + on `kotlin.time.Instant` and `LocalDate`. - `UtcOffset` represents the amount of time the local datetime at a particular time zone differs from the datetime at UTC. ### Type use-cases Here is some basic advice on how to choose which of the date-carrying types to use in what cases: -- Use `Instant` to represent a timestamp of the event that had already happened in the past (like a timestamp of - a log entry) or will definitely happen in a well-defined instant of time in the future not far away from now +- Use `kotlin.time.Instant` to represent a timestamp of the event that had already happened in the past + (like a timestamp of a log entry) or will definitely happen in a well-defined instant of time in the future + not far away from now (like an order confirmation deadline in 1 hour from now). - -- Use `LocalDateTime` to represent a time of the event that is scheduled to happen in the far future at a certain - local time (like a scheduled meeting in a few months from now). You'll have to keep track of the `TimeZone` of - the scheduled event separately. Try to avoid converting future events to `Instant` in advance, because time-zone - rules might change unexpectedly in the future. In this [blog post](https://codeblog.jonskeet.uk/2019/03/27/storing-utc-is-not-a-silver-bullet/), you can read more about why it's not always + +- Use `LocalDateTime` to represent a time of the event that is scheduled to happen in the far future at a certain + local time (like a scheduled meeting in a few months from now). You'll have to keep track of the `TimeZone` of + the scheduled event separately. Try to avoid converting future events to `Instant` in advance, because time-zone + rules might change unexpectedly in the future. In this [blog post](https://codeblog.jonskeet.uk/2019/03/27/storing-utc-is-not-a-silver-bullet/), you can read more about why it's not always a good idea to use `Instant` everywhere. - - Also, use `LocalDateTime` to decode an `Instant` to its local datetime components for display and UIs. - + + Also use `LocalDateTime` to decode an `Instant` to its local datetime components for display and UIs. + - Use `LocalDate` to represent the date of an event that does not have a specific time associated with it (like a birth date). - Use `LocalTime` to represent the time of an event that does not have a specific date associated with it. - + ## Operations With the above types you can get the following operations done. - -### Getting the current moment of time - -The current moment of time can be captured with the `Instant` type. -To obtain an `Instant` corresponding to the current moment of time, -use `now()` function of the `Clock` interface: - -```kotlin -val clock: Clock = ... -val currentMoment = clock.now() -``` - -An instance of `Clock` can be injected through the function/class parameters, -or you can use its default implementation `Clock.System` that represents the system clock: - -```kotlin -val currentMoment = Clock.System.now() -``` - - ### Converting an instant to local date and time components An `Instant` is just a counter of high resolution time intervals since the beginning of time scale. @@ -107,7 +88,7 @@ val datetimeInUtc: LocalDateTime = currentMoment.toLocalDateTime(TimeZone.UTC) val datetimeInSystemZone: LocalDateTime = currentMoment.toLocalDateTime(TimeZone.currentSystemDefault()) ``` -A `LocalDateTime` instance exposes familiar components of the Gregorian calendar: +A `LocalDateTime` instance exposes familiar components of the Gregorian calendar: `year`, `month`, `day`, `hour`, and so on up to `nanosecond`. The property `dayOfWeek` shows what weekday that date is, and `dayOfYear` shows the day number since the beginning of a year. @@ -167,32 +148,24 @@ val timeWithNanos = LocalTime(hour = 23, minute = 59, second = 12, nanosecond = val hourMinute = LocalTime(hour = 12, minute = 13) ``` -### Converting instant to and from unix time - -An `Instant` can be converted to a number of milliseconds since the Unix/POSIX epoch with the `toEpochMilliseconds()` function. -To convert back, use the companion object function `Instant.fromEpochMilliseconds(Long)`. - ### Converting instant and local datetime to and from the ISO 8601 string `Instant`, `LocalDateTime`, `LocalDate` and `LocalTime` provide shortcuts for parsing and formatting them using the extended ISO 8601 format. -The `toString()` function is used to convert the value to a string in that format, and -the `parse` function in companion object is used to parse a string representation back. +The `toString()` function is used to convert the value to a string in that format, and +the `parse` function in companion object is used to parse a string representation back. ```kotlin -val instantNow = Clock.System.now() -instantNow.toString() // returns something like 2015-12-31T12:30:00Z -val instantBefore = Instant.parse("2010-06-01T22:19:44.475Z") +val localDateTime = LocalDateTime(2025, 3, 21, 12, 27, 35, 124365453) +localDateTime.toString() // 2025-03-21T12:27:35.124365453 +val sameLocalDateTime = LocalDateTime.parse("2025-03-21T12:27:35.124365453") ``` -`LocalDateTime` uses a similar format, but without `Z` UTC time zone designator in the end. - `LocalDate` uses a format with just year, month, and date components, e.g. `2010-06-01`. `LocalTime` uses a format with just hour, minute, second and (if non-zero) nanosecond components, e.g. `12:01:03`. ```kotlin -LocalDateTime.parse("2010-06-01T22:19:44") LocalDate.parse("2010-06-01") LocalTime.parse("12:01:03") LocalTime.parse("12:00:03.999") @@ -315,6 +288,9 @@ DateTimeComponents.Formats.RFC_1123.format { ### Instant arithmetic +The `Instant` arithmetic operations that don't involve the calendar are available purely in the standard library +and do not require using `kotlinx-datetime`: + ```kotlin val now = Clock.System.now() val instantInThePast: Instant = Instant.parse("2020-01-01T00:00:00Z") @@ -322,16 +298,13 @@ val durationSinceThen: Duration = now - instantInThePast val equidistantInstantInTheFuture: Instant = now + durationSinceThen ``` -`Duration` is a type from the experimental `kotlin.time` package in the Kotlin standard library. -This type holds the amount of time that can be represented in different time units: from nanoseconds to 24H days. - To get the calendar difference between two instants you can use the `Instant.periodUntil(Instant, TimeZone)` function. ```kotlin val period: DateTimePeriod = instantInThePast.periodUntil(Clock.System.now(), TimeZone.UTC) ``` -A `DateTimePeriod` represents a difference between two particular moments as a sum of calendar components, +A `DateTimePeriod` represents a difference between two particular moments as a sum of calendar components, like "2 years, 3 months, 10 days, and 22 hours". The difference can be calculated as an integer amount of specified date or time units: @@ -350,7 +323,7 @@ val tomorrow = now.plus(2, DateTimeUnit.DAY, systemTZ) val threeYearsAndAMonthLater = now.plus(DateTimePeriod(years = 3, months = 1), systemTZ) ``` -Note that `plus` and `...until` operations require a `TimeZone` as a parameter because the calendar interval between +Note that `plus` and `...until` operations require a `TimeZone` as a parameter because the calendar interval between two particular instants can be different, when calculated in different time zones. ### Date arithmetic @@ -395,16 +368,14 @@ val localDateTimeTwoDaysLater = instantTwoDaysLater.toLocalDateTime(timeZone) ## Implementation -The implementation of datetime types, such as `Instant`, `LocalDateTime`, `TimeZone` and so on, relies on: +The implementation of datetime types, such as `LocalDateTime`, `TimeZone` and so on, relies on: - in JVM: [`java.time`](https://docs.oracle.com/javase/8/docs/api/java/time/package-summary.html) API; -- in Js and Wasm-Js: [`js-joda`](https://js-joda.github.io/js-joda/) library; -- in Native: based on the [ThreeTen backport project](https://www.threeten.org/threetenbp/) - - time zone support is provided by [date](https://github.com/HowardHinnant/date/) C++ library; +- the other platforms: based on the [ThreeTen backport project](https://www.threeten.org/threetenbp/) + - time zone support on JS and Wasm/JS is provided by the [`js-joda`](https://js-joda.github.io/js-joda/) library. ## Known/open issues, work TBD -- [x] Some kind of `Clock` interface is needed as a pluggable replacement for `Instant.now()`. - [ ] Flexible locale-neutral parsing and formatting facilities are needed to support various datetime interchange formats that are used in practice (in particular, various RFCs). @@ -414,11 +385,56 @@ The implementation of datetime types, such as `Instant`, `LocalDateTime`, `TimeZ The library is published to Maven Central. -The library is compatible with the Kotlin Standard Library not lower than `1.9.0`. +The library is compatible with the Kotlin Standard Library not lower than `2.1.20`. -If you target Android devices running **below API 26**, you need to use Android Gradle plugin 4.0 or newer +If you target Android devices running **below API 26**, you need to use Android Gradle plugin 4.0 or newer and enable [core library desugaring](https://developer.android.com/studio/write/java8-support#library-desugaring). +### Deprecation of `Instant` + +`kotlinx-datetime` versions earlier than `0.7.0` used to provide `kotlinx.datetime.Instant` +and `kotlinx.datetime.Clock`. +The Kotlin standard library started including its own, identical `kotlin.time.Instant` and `kotlin.time.Clock`, +as it became evident that `Instant` was also useful outside the datetime contexts. + +Here is the recommended procedure for migrating from `kotlinx-datetime` version `0.6.x` or earlier to `0.7.0`: + +* First, simply try upgrading to `0.7.0`. + If your project has a dependency on `kotlinx-datetime`, but doesn't have dependencies on other libraries that are + *themselves* reliant on an older `kotlinx-datetime`, you are good to go: the code should compile and run. + This applies both to applications and to libraries! +* If your project depends on other libraries that themselves use an older version of `kotlinx-datetime`, + then your code may fail at runtime with a `ClassNotFoundException` + for `kotlinx.datetime.Instant` or `kotlinx.datetime.Clock`, or maybe even fail to compile. + In that case, please check if the affected libraries you have as dependencies have already published a new release + adapted to use `Instant` and `Clock` from `kotlin.time`. +* If all else fails, use the *compatibility release* of `kotlinx-datetime`. + Instead of the version `0.7.0`, use `0.7.0-0.6.x-compat`. + This artifact still contains `kotlinx.datetime.Instant` and `kotlinx.datetime.Clock`, + ensuring that third-party libraries reliant on them can still be used. + This artifact is less straightforward to use than `0.7.0`, so only resort to it when libraries you don't control + require that the removed classes still exist. + +Tips for fixing compilation errors: + +* If you encounter resolution ambiguity errors for `Instant` or `Clock`, + see if you have `import kotlin.time.*` along with `import kotlinx.datetime.*`. + Since both libraries have a `Clock` and an `Instant`, you have to manually add + `import kotlin.time.Instant` and/or `import kotlin.time.Clock` explicitly. +* When using the compatibility release of `kotlinx-datetime`, you may encounter errors like + "required `kotlinx.datetime.Instant`, found `kotlin.time.Instant`" or vice versa. + - First, please check if you have imported a `kotlinx.datetime` class when a `kotlin.time` class would work. + The final goal is getting rid of `kotlinx.datetime.Instant`, so limit its usage as much as possible! + - If you have no choice but to use an `Instant` or `Clock` from `kotlinx-datetime` (for example, because a third-party + library accepts a `kotlinx.datetime.Instant` as a parameter or returns it as a function result), + you can use the compatibility functions: + * `kotlin.time.Instant.toDeprecatedInstant(): kotlinx.datetime.Instant` + * `kotlin.time.Clock.toDeprecatedClock(): kotlinx.datetime.Clock` + * `kotlinx.datetime.Instant.toStdlibInstant(): kotlin.time.Instant` + * `kotlinx.datetime.Clock.toStdlibClock(): kotlin.time.Clock` + +> Compatibility releases will be published for all `0.7.x` versions of `kotlinx-datetime`, but not longer. + ### Gradle - Add the Maven Central repository if it is not already there: @@ -537,10 +553,10 @@ Add a dependency to the `` element. Note that you need to use the ## Building -The project requires JDK 8 to build classes and to run tests. +The project requires JDK 8 to build classes and to run tests. Gradle will try to find it among the installed JDKs or [provision](https://docs.gradle.org/current/userguide/toolchains.html#sec:provisioning) it automatically if it couldn't be found. -The path to JDK 8 can be additionally specified with the environment variable `JDK_8`. -For local builds, you can use a later version of JDK if you don't have that +The path to JDK 8 can be additionally specified with the environment variable `JDK_8`. +For local builds, you can use a later version of JDK if you don't have that version installed. Specify the version of this JDK with the `java.mainToolchainVersion` Gradle property. After that, the project can be opened in IDEA and built with Gradle. diff --git a/core/api/kotlinx-datetime.api b/core/api/kotlinx-datetime.api index 1f554210f..df3741be7 100644 --- a/core/api/kotlinx-datetime.api +++ b/core/api/kotlinx-datetime.api @@ -12,9 +12,15 @@ public final class kotlinx/datetime/Clock$System : kotlinx/datetime/Clock { } public final class kotlinx/datetime/ClockKt { + public static final fun asClock (Lkotlin/time/TimeSource;Lkotlin/time/Instant;)Lkotlin/time/Clock; public static final fun asClock (Lkotlin/time/TimeSource;Lkotlinx/datetime/Instant;)Lkotlinx/datetime/Clock; + public static final fun asTimeSource (Lkotlin/time/Clock;)Lkotlin/time/TimeSource$WithComparableMarks; public static final fun asTimeSource (Lkotlinx/datetime/Clock;)Lkotlin/time/TimeSource$WithComparableMarks; + public static final fun toDeprecatedClock (Lkotlin/time/Clock;)Lkotlinx/datetime/Clock; + public static final fun toStdlibClock (Lkotlinx/datetime/Clock;)Lkotlin/time/Clock; + public static final fun todayAt (Lkotlin/time/Clock;Lkotlinx/datetime/TimeZone;)Lkotlinx/datetime/LocalDate; public static final fun todayAt (Lkotlinx/datetime/Clock;Lkotlinx/datetime/TimeZone;)Lkotlinx/datetime/LocalDate; + public static final fun todayIn (Lkotlin/time/Clock;Lkotlinx/datetime/TimeZone;)Lkotlinx/datetime/LocalDate; public static final fun todayIn (Lkotlinx/datetime/Clock;Lkotlinx/datetime/TimeZone;)Lkotlinx/datetime/LocalDate; } @@ -239,22 +245,42 @@ public final class kotlinx/datetime/Instant$Companion { } public final class kotlinx/datetime/InstantJvmKt { + public static final fun minus (Lkotlin/time/Instant;ILkotlinx/datetime/DateTimeUnit;Lkotlinx/datetime/TimeZone;)Lkotlin/time/Instant; public static final fun minus (Lkotlinx/datetime/Instant;ILkotlinx/datetime/DateTimeUnit;Lkotlinx/datetime/TimeZone;)Lkotlinx/datetime/Instant; + public static final fun periodUntil (Lkotlin/time/Instant;Lkotlin/time/Instant;Lkotlinx/datetime/TimeZone;)Lkotlinx/datetime/DateTimePeriod; public static final fun periodUntil (Lkotlinx/datetime/Instant;Lkotlinx/datetime/Instant;Lkotlinx/datetime/TimeZone;)Lkotlinx/datetime/DateTimePeriod; + public static final fun plus (Lkotlin/time/Instant;ILkotlinx/datetime/DateTimeUnit;Lkotlinx/datetime/TimeZone;)Lkotlin/time/Instant; + public static final fun plus (Lkotlin/time/Instant;JLkotlinx/datetime/DateTimeUnit$TimeBased;)Lkotlin/time/Instant; + public static final fun plus (Lkotlin/time/Instant;JLkotlinx/datetime/DateTimeUnit;Lkotlinx/datetime/TimeZone;)Lkotlin/time/Instant; + public static final fun plus (Lkotlin/time/Instant;Lkotlinx/datetime/DateTimePeriod;Lkotlinx/datetime/TimeZone;)Lkotlin/time/Instant; + public static final fun plus (Lkotlin/time/Instant;Lkotlinx/datetime/DateTimeUnit;Lkotlinx/datetime/TimeZone;)Lkotlin/time/Instant; 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 (Lkotlin/time/Instant;Lkotlin/time/Instant;Lkotlinx/datetime/DateTimeUnit;Lkotlinx/datetime/TimeZone;)J 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 (Lkotlin/time/Instant;Lkotlin/time/Instant;Lkotlinx/datetime/TimeZone;)I public static final fun daysUntil (Lkotlinx/datetime/Instant;Lkotlinx/datetime/Instant;Lkotlinx/datetime/TimeZone;)I + public static final fun format (Lkotlin/time/Instant;Lkotlinx/datetime/format/DateTimeFormat;Lkotlinx/datetime/UtcOffset;)Ljava/lang/String; public static final fun format (Lkotlinx/datetime/Instant;Lkotlinx/datetime/format/DateTimeFormat;Lkotlinx/datetime/UtcOffset;)Ljava/lang/String; + public static synthetic fun format$default (Lkotlin/time/Instant;Lkotlinx/datetime/format/DateTimeFormat;Lkotlinx/datetime/UtcOffset;ILjava/lang/Object;)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 (Lkotlin/time/Instant;ILkotlinx/datetime/DateTimeUnit$TimeBased;)Lkotlin/time/Instant; + public static final fun minus (Lkotlin/time/Instant;JLkotlinx/datetime/DateTimeUnit$TimeBased;)Lkotlin/time/Instant; + public static final fun minus (Lkotlin/time/Instant;JLkotlinx/datetime/DateTimeUnit;Lkotlinx/datetime/TimeZone;)Lkotlin/time/Instant; + public static final fun minus (Lkotlin/time/Instant;Lkotlin/time/Instant;Lkotlinx/datetime/DateTimeUnit$TimeBased;)J + public static final fun minus (Lkotlin/time/Instant;Lkotlin/time/Instant;Lkotlinx/datetime/DateTimeUnit;Lkotlinx/datetime/TimeZone;)J + public static final fun minus (Lkotlin/time/Instant;Lkotlin/time/Instant;Lkotlinx/datetime/TimeZone;)Lkotlinx/datetime/DateTimePeriod; + public static final fun minus (Lkotlin/time/Instant;Lkotlinx/datetime/DateTimePeriod;Lkotlinx/datetime/TimeZone;)Lkotlin/time/Instant; + public static final fun minus (Lkotlin/time/Instant;Lkotlinx/datetime/DateTimeUnit$TimeBased;)Lkotlin/time/Instant; + public static final fun minus (Lkotlin/time/Instant;Lkotlinx/datetime/DateTimeUnit;Lkotlinx/datetime/TimeZone;)Lkotlin/time/Instant; 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; @@ -264,11 +290,19 @@ 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 monthsUntil (Lkotlin/time/Instant;Lkotlin/time/Instant;Lkotlinx/datetime/TimeZone;)I public static final fun monthsUntil (Lkotlinx/datetime/Instant;Lkotlinx/datetime/Instant;Lkotlinx/datetime/TimeZone;)I + public static final fun parse (Lkotlin/time/Instant$Companion;Ljava/lang/CharSequence;Lkotlinx/datetime/format/DateTimeFormat;)Lkotlin/time/Instant; + public static final fun plus (Lkotlin/time/Instant;ILkotlinx/datetime/DateTimeUnit$TimeBased;)Lkotlin/time/Instant; + public static final fun plus (Lkotlin/time/Instant;Lkotlinx/datetime/DateTimeUnit$TimeBased;)Lkotlin/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 toDeprecatedInstant (Lkotlin/time/Instant;)Lkotlinx/datetime/Instant; public static final fun toInstant (Ljava/lang/String;)Lkotlinx/datetime/Instant; + public static final fun toStdlibInstant (Lkotlinx/datetime/Instant;)Lkotlin/time/Instant; + public static final fun until (Lkotlin/time/Instant;Lkotlin/time/Instant;Lkotlinx/datetime/DateTimeUnit$TimeBased;)J public static final fun until (Lkotlinx/datetime/Instant;Lkotlinx/datetime/Instant;Lkotlinx/datetime/DateTimeUnit$TimeBased;)J + public static final fun yearsUntil (Lkotlin/time/Instant;Lkotlin/time/Instant;Lkotlinx/datetime/TimeZone;)I public static final fun yearsUntil (Lkotlinx/datetime/Instant;Lkotlinx/datetime/Instant;Lkotlinx/datetime/TimeZone;)I } @@ -472,6 +506,9 @@ public final class kotlinx/datetime/MonthKt { public static final fun getNumber (Lkotlinx/datetime/Month;)I } +public final class kotlinx/datetime/OverloadMarker { +} + public final class kotlinx/datetime/Ser : java/io/Externalizable { public static final field Companion Lkotlinx/datetime/Ser$Companion; public static final field DATE_TAG I @@ -493,6 +530,9 @@ 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/OverloadMarker;)Lkotlin/time/Instant; + public static synthetic fun toInstant$default (Lkotlinx/datetime/TimeZone;Lkotlinx/datetime/LocalDateTime;Lkotlinx/datetime/OverloadMarker;ILjava/lang/Object;)Lkotlin/time/Instant; + public final fun toLocalDateTime (Lkotlin/time/Instant;)Lkotlinx/datetime/LocalDateTime; public final fun toLocalDateTime (Lkotlinx/datetime/Instant;)Lkotlinx/datetime/LocalDateTime; public fun toString ()Ljava/lang/String; } @@ -507,11 +547,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/OverloadMarker;)Lkotlin/time/Instant; + public static synthetic fun atStartOfDayIn$default (Lkotlinx/datetime/LocalDate;Lkotlinx/datetime/TimeZone;Lkotlinx/datetime/OverloadMarker;ILjava/lang/Object;)Lkotlin/time/Instant; + public static final fun offsetAt (Lkotlinx/datetime/TimeZone;Lkotlin/time/Instant;)Lkotlinx/datetime/UtcOffset; public static final fun offsetAt (Lkotlinx/datetime/TimeZone;Lkotlinx/datetime/Instant;)Lkotlinx/datetime/UtcOffset; + public static final fun offsetIn (Lkotlin/time/Instant;Lkotlinx/datetime/TimeZone;)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/TimeZone;Lkotlinx/datetime/OverloadMarker;)Lkotlin/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/OverloadMarker;)Lkotlin/time/Instant; + public static synthetic fun toInstant$default (Lkotlinx/datetime/LocalDateTime;Lkotlinx/datetime/TimeZone;Lkotlinx/datetime/OverloadMarker;ILjava/lang/Object;)Lkotlin/time/Instant; + public static synthetic fun toInstant$default (Lkotlinx/datetime/LocalDateTime;Lkotlinx/datetime/UtcOffset;Lkotlinx/datetime/OverloadMarker;ILjava/lang/Object;)Lkotlin/time/Instant; + public static final fun toLocalDateTime (Lkotlin/time/Instant;Lkotlinx/datetime/TimeZone;)Lkotlinx/datetime/LocalDateTime; 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 final class kotlinx/datetime/UtcOffset : java/io/Serializable { @@ -582,6 +632,7 @@ public final class kotlinx/datetime/format/DateTimeComponents { 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 (Lkotlin/time/Instant;Lkotlinx/datetime/UtcOffset;)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 setDay (Ljava/lang/Integer;)V @@ -604,6 +655,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/OverloadMarker;)Lkotlin/time/Instant; + public static synthetic fun toInstantUsingOffset$default (Lkotlinx/datetime/format/DateTimeComponents;Lkotlinx/datetime/OverloadMarker;ILjava/lang/Object;)Lkotlin/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 129b736dc..becf95d15 100644 --- a/core/api/kotlinx-datetime.klib.api +++ b/core/api/kotlinx-datetime.klib.api @@ -193,11 +193,13 @@ final class kotlinx.datetime.format/DateTimeComponents { // kotlinx.datetime.for 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(kotlin.time/Instant, kotlinx.datetime/UtcOffset) // kotlinx.datetime.format/DateTimeComponents.setDateTimeOffset|setDateTimeOffset(kotlin.time.Instant;kotlinx.datetime.UtcOffset){}[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 toInstantUsingOffset(kotlinx.datetime/OverloadMarker = ...): kotlin.time/Instant // kotlinx.datetime.format/DateTimeComponents.toInstantUsingOffset|toInstantUsingOffset(kotlinx.datetime.OverloadMarker){}[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] @@ -461,6 +463,8 @@ final class kotlinx.datetime/LocalTime : kotlin/Comparable(): kotlin/Int // kotlinx.datetime/UtcOffset.totalSeconds.|(){}[0] @@ -493,8 +497,10 @@ 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 (kotlin.time/Instant).toLocalDateTime(): kotlinx.datetime/LocalDateTime // kotlinx.datetime/TimeZone.toLocalDateTime|toLocalDateTime@kotlin.time.Instant(){}[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/OverloadMarker = ...): kotlin.time/Instant // kotlinx.datetime/TimeZone.toInstant|toInstant@kotlinx.datetime.LocalDateTime(kotlinx.datetime.OverloadMarker){}[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] @@ -817,7 +823,40 @@ final val kotlinx.datetime/isoDayNumber // kotlinx.datetime/isoDayNumber|@kotlin 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/Clock).kotlinx.datetime/asTimeSource(): kotlin.time/TimeSource.WithComparableMarks // kotlinx.datetime/asTimeSource|asTimeSource@kotlin.time.Clock(){}[0] +final fun (kotlin.time/Clock).kotlinx.datetime/toDeprecatedClock(): kotlinx.datetime/Clock // kotlinx.datetime/toDeprecatedClock|toDeprecatedClock@kotlin.time.Clock(){}[0] +final fun (kotlin.time/Clock).kotlinx.datetime/todayAt(kotlinx.datetime/TimeZone): kotlinx.datetime/LocalDate // kotlinx.datetime/todayAt|todayAt@kotlin.time.Clock(kotlinx.datetime.TimeZone){}[0] +final fun (kotlin.time/Clock).kotlinx.datetime/todayIn(kotlinx.datetime/TimeZone): kotlinx.datetime/LocalDate // kotlinx.datetime/todayIn|todayIn@kotlin.time.Clock(kotlinx.datetime.TimeZone){}[0] final fun (kotlin.time/Duration).kotlinx.datetime/toDateTimePeriod(): kotlinx.datetime/DateTimePeriod // kotlinx.datetime/toDateTimePeriod|toDateTimePeriod@kotlin.time.Duration(){}[0] +final fun (kotlin.time/Instant).kotlinx.datetime/daysUntil(kotlin.time/Instant, kotlinx.datetime/TimeZone): kotlin/Int // kotlinx.datetime/daysUntil|daysUntil@kotlin.time.Instant(kotlin.time.Instant;kotlinx.datetime.TimeZone){}[0] +final fun (kotlin.time/Instant).kotlinx.datetime/format(kotlinx.datetime.format/DateTimeFormat, kotlinx.datetime/UtcOffset = ...): kotlin/String // kotlinx.datetime/format|format@kotlin.time.Instant(kotlinx.datetime.format.DateTimeFormat;kotlinx.datetime.UtcOffset){}[0] +final fun (kotlin.time/Instant).kotlinx.datetime/minus(kotlin.time/Instant, kotlinx.datetime/DateTimeUnit, kotlinx.datetime/TimeZone): kotlin/Long // kotlinx.datetime/minus|minus@kotlin.time.Instant(kotlin.time.Instant;kotlinx.datetime.DateTimeUnit;kotlinx.datetime.TimeZone){}[0] +final fun (kotlin.time/Instant).kotlinx.datetime/minus(kotlin.time/Instant, kotlinx.datetime/DateTimeUnit.TimeBased): kotlin/Long // kotlinx.datetime/minus|minus@kotlin.time.Instant(kotlin.time.Instant;kotlinx.datetime.DateTimeUnit.TimeBased){}[0] +final fun (kotlin.time/Instant).kotlinx.datetime/minus(kotlin.time/Instant, kotlinx.datetime/TimeZone): kotlinx.datetime/DateTimePeriod // kotlinx.datetime/minus|minus@kotlin.time.Instant(kotlin.time.Instant;kotlinx.datetime.TimeZone){}[0] +final fun (kotlin.time/Instant).kotlinx.datetime/minus(kotlin/Int, kotlinx.datetime/DateTimeUnit, kotlinx.datetime/TimeZone): kotlin.time/Instant // kotlinx.datetime/minus|minus@kotlin.time.Instant(kotlin.Int;kotlinx.datetime.DateTimeUnit;kotlinx.datetime.TimeZone){}[0] +final fun (kotlin.time/Instant).kotlinx.datetime/minus(kotlin/Int, kotlinx.datetime/DateTimeUnit.TimeBased): kotlin.time/Instant // kotlinx.datetime/minus|minus@kotlin.time.Instant(kotlin.Int;kotlinx.datetime.DateTimeUnit.TimeBased){}[0] +final fun (kotlin.time/Instant).kotlinx.datetime/minus(kotlin/Long, kotlinx.datetime/DateTimeUnit, kotlinx.datetime/TimeZone): kotlin.time/Instant // kotlinx.datetime/minus|minus@kotlin.time.Instant(kotlin.Long;kotlinx.datetime.DateTimeUnit;kotlinx.datetime.TimeZone){}[0] +final fun (kotlin.time/Instant).kotlinx.datetime/minus(kotlin/Long, kotlinx.datetime/DateTimeUnit.TimeBased): kotlin.time/Instant // kotlinx.datetime/minus|minus@kotlin.time.Instant(kotlin.Long;kotlinx.datetime.DateTimeUnit.TimeBased){}[0] +final fun (kotlin.time/Instant).kotlinx.datetime/minus(kotlinx.datetime/DateTimePeriod, kotlinx.datetime/TimeZone): kotlin.time/Instant // kotlinx.datetime/minus|minus@kotlin.time.Instant(kotlinx.datetime.DateTimePeriod;kotlinx.datetime.TimeZone){}[0] +final fun (kotlin.time/Instant).kotlinx.datetime/minus(kotlinx.datetime/DateTimeUnit, kotlinx.datetime/TimeZone): kotlin.time/Instant // kotlinx.datetime/minus|minus@kotlin.time.Instant(kotlinx.datetime.DateTimeUnit;kotlinx.datetime.TimeZone){}[0] +final fun (kotlin.time/Instant).kotlinx.datetime/minus(kotlinx.datetime/DateTimeUnit.TimeBased): kotlin.time/Instant // kotlinx.datetime/minus|minus@kotlin.time.Instant(kotlinx.datetime.DateTimeUnit.TimeBased){}[0] +final fun (kotlin.time/Instant).kotlinx.datetime/monthsUntil(kotlin.time/Instant, kotlinx.datetime/TimeZone): kotlin/Int // kotlinx.datetime/monthsUntil|monthsUntil@kotlin.time.Instant(kotlin.time.Instant;kotlinx.datetime.TimeZone){}[0] +final fun (kotlin.time/Instant).kotlinx.datetime/offsetIn(kotlinx.datetime/TimeZone): kotlinx.datetime/UtcOffset // kotlinx.datetime/offsetIn|offsetIn@kotlin.time.Instant(kotlinx.datetime.TimeZone){}[0] +final fun (kotlin.time/Instant).kotlinx.datetime/periodUntil(kotlin.time/Instant, kotlinx.datetime/TimeZone): kotlinx.datetime/DateTimePeriod // kotlinx.datetime/periodUntil|periodUntil@kotlin.time.Instant(kotlin.time.Instant;kotlinx.datetime.TimeZone){}[0] +final fun (kotlin.time/Instant).kotlinx.datetime/plus(kotlin/Int, kotlinx.datetime/DateTimeUnit, kotlinx.datetime/TimeZone): kotlin.time/Instant // kotlinx.datetime/plus|plus@kotlin.time.Instant(kotlin.Int;kotlinx.datetime.DateTimeUnit;kotlinx.datetime.TimeZone){}[0] +final fun (kotlin.time/Instant).kotlinx.datetime/plus(kotlin/Int, kotlinx.datetime/DateTimeUnit.TimeBased): kotlin.time/Instant // kotlinx.datetime/plus|plus@kotlin.time.Instant(kotlin.Int;kotlinx.datetime.DateTimeUnit.TimeBased){}[0] +final fun (kotlin.time/Instant).kotlinx.datetime/plus(kotlin/Long, kotlinx.datetime/DateTimeUnit, kotlinx.datetime/TimeZone): kotlin.time/Instant // kotlinx.datetime/plus|plus@kotlin.time.Instant(kotlin.Long;kotlinx.datetime.DateTimeUnit;kotlinx.datetime.TimeZone){}[0] +final fun (kotlin.time/Instant).kotlinx.datetime/plus(kotlin/Long, kotlinx.datetime/DateTimeUnit.TimeBased): kotlin.time/Instant // kotlinx.datetime/plus|plus@kotlin.time.Instant(kotlin.Long;kotlinx.datetime.DateTimeUnit.TimeBased){}[0] +final fun (kotlin.time/Instant).kotlinx.datetime/plus(kotlinx.datetime/DateTimePeriod, kotlinx.datetime/TimeZone): kotlin.time/Instant // kotlinx.datetime/plus|plus@kotlin.time.Instant(kotlinx.datetime.DateTimePeriod;kotlinx.datetime.TimeZone){}[0] +final fun (kotlin.time/Instant).kotlinx.datetime/plus(kotlinx.datetime/DateTimeUnit, kotlinx.datetime/TimeZone): kotlin.time/Instant // kotlinx.datetime/plus|plus@kotlin.time.Instant(kotlinx.datetime.DateTimeUnit;kotlinx.datetime.TimeZone){}[0] +final fun (kotlin.time/Instant).kotlinx.datetime/plus(kotlinx.datetime/DateTimeUnit.TimeBased): kotlin.time/Instant // kotlinx.datetime/plus|plus@kotlin.time.Instant(kotlinx.datetime.DateTimeUnit.TimeBased){}[0] +final fun (kotlin.time/Instant).kotlinx.datetime/toDeprecatedInstant(): kotlinx.datetime/Instant // kotlinx.datetime/toDeprecatedInstant|toDeprecatedInstant@kotlin.time.Instant(){}[0] +final fun (kotlin.time/Instant).kotlinx.datetime/toLocalDateTime(kotlinx.datetime/TimeZone): kotlinx.datetime/LocalDateTime // kotlinx.datetime/toLocalDateTime|toLocalDateTime@kotlin.time.Instant(kotlinx.datetime.TimeZone){}[0] +final fun (kotlin.time/Instant).kotlinx.datetime/until(kotlin.time/Instant, kotlinx.datetime/DateTimeUnit, kotlinx.datetime/TimeZone): kotlin/Long // kotlinx.datetime/until|until@kotlin.time.Instant(kotlin.time.Instant;kotlinx.datetime.DateTimeUnit;kotlinx.datetime.TimeZone){}[0] +final fun (kotlin.time/Instant).kotlinx.datetime/until(kotlin.time/Instant, kotlinx.datetime/DateTimeUnit.TimeBased): kotlin/Long // kotlinx.datetime/until|until@kotlin.time.Instant(kotlin.time.Instant;kotlinx.datetime.DateTimeUnit.TimeBased){}[0] +final fun (kotlin.time/Instant).kotlinx.datetime/yearsUntil(kotlin.time/Instant, kotlinx.datetime/TimeZone): kotlin/Int // kotlinx.datetime/yearsUntil|yearsUntil@kotlin.time.Instant(kotlin.time.Instant;kotlinx.datetime.TimeZone){}[0] +final fun (kotlin.time/Instant.Companion).kotlinx.datetime/parse(kotlin/CharSequence, kotlinx.datetime.format/DateTimeFormat): kotlin.time/Instant // kotlinx.datetime/parse|parse@kotlin.time.Instant.Companion(kotlin.CharSequence;kotlinx.datetime.format.DateTimeFormat){}[0] +final fun (kotlin.time/TimeSource).kotlinx.datetime/asClock(kotlin.time/Instant): kotlin.time/Clock // kotlinx.datetime/asClock|asClock@kotlin.time.TimeSource(kotlin.time.Instant){}[0] final fun (kotlin.time/TimeSource).kotlinx.datetime/asClock(kotlinx.datetime/Instant): kotlinx.datetime/Clock // kotlinx.datetime/asClock|asClock@kotlin.time.TimeSource(kotlinx.datetime.Instant){}[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] @@ -830,6 +869,7 @@ final fun (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/OverloadMarker = ...): kotlin.time/Instant // kotlinx.datetime/toInstant|toInstant@kotlinx.datetime.LocalDateTime(kotlinx.datetime.TimeZone;kotlinx.datetime.OverloadMarker){}[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/OverloadMarker = ...): kotlin.time/Instant // kotlinx.datetime/toInstant|toInstant@kotlinx.datetime.LocalDateTime(kotlinx.datetime.UtcOffset;kotlinx.datetime.OverloadMarker){}[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, kotlin/Int, kotlin/Int, kotlin/Unit = ...): kotlinx.datetime/LocalDateTime // kotlinx.datetime/atDate|atDate@kotlinx.datetime.LocalTime(kotlin.Int;kotlin.Int;kotlin.Int;kotlin.Unit){}[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(kotlin/Int, kotlinx.datetime/Month, kotlin/Int, kotlin/Unit = ...): kotlinx.datetime/LocalDateTime // kotlinx.datetime/atDate|atDate@kotlinx.datetime.LocalTime(kotlin.Int;kotlinx.datetime.Month;kotlin.Int;kotlin.Unit){}[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(kotlin.time/Instant): kotlinx.datetime/UtcOffset // kotlinx.datetime/offsetAt|offsetAt@kotlinx.datetime.TimeZone(kotlin.time.Instant){}[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] @@ -902,6 +948,9 @@ final fun kotlinx.datetime/Month(kotlin/Int): kotlinx.datetime/Month // kotlinx. 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 (kotlin.time/Instant).kotlinx.datetime/toNSDate(): platform.Foundation/NSDate // kotlinx.datetime/toNSDate|toNSDate@kotlin.time.Instant(){}[0] + // Targets: [apple] final fun (kotlinx.datetime/Instant).kotlinx.datetime/toNSDate(): platform.Foundation/NSDate // kotlinx.datetime/toNSDate|toNSDate@kotlinx.datetime.Instant(){}[0] @@ -917,6 +966,9 @@ final fun (kotlinx.datetime/TimeZone).kotlinx.datetime/toNSTimeZone(): platform. // 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/OverloadMarker = ...): kotlin.time/Instant // kotlinx.datetime/toKotlinInstant|toKotlinInstant@platform.Foundation.NSDate(kotlinx.datetime.OverloadMarker){}[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 64697efd2..f2bf2e101 100644 --- a/core/build.gradle.kts +++ b/core/build.gradle.kts @@ -222,6 +222,10 @@ kotlin { } } } + + compilerOptions { + optIn.add("kotlin.time.ExperimentalTime") + } } tasks { diff --git a/core/common/src/Clock.kt b/core/common/src/Clock.kt index 8e7eb10b3..24cd7a737 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 kotlin.time.Clock +import kotlin.time.Instant +import kotlin.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]. @@ -108,7 +63,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..9f34ca528 --- /dev/null +++ b/core/common/src/DeprecatedClock.kt @@ -0,0 +1,137 @@ +/* + * 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") +@file:JvmMultifileClass +@file:JvmName("ClockKt") +package kotlinx.datetime + +import kotlin.jvm.JvmMultifileClass +import kotlin.jvm.JvmName +import kotlin.time.ExperimentalTime +import kotlin.time.TimeMark +import kotlin.time.TimeSource + +/** + * Creates a [kotlin.time.Clock] (the standard library version of `Clock`) delegating to `this`. + */ +public fun Clock.toStdlibClock(): kotlin.time.Clock = + (this as? DateTimeClock)?.clock ?: StdlibClock(this) + +/** + * Creates a [kotlinx.datetime.Clock] delegating to the version of `Clock` from the standard library. + */ +public fun kotlin.time.Clock.toDeprecatedClock(): Clock = + (this as? StdlibClock)?.clock ?: DateTimeClock(this) + +private class DateTimeClock(val clock: kotlin.time.Clock): kotlinx.datetime.Clock { + override fun now(): Instant = clock.now().toDeprecatedInstant() +} + +private class StdlibClock(val clock: Clock): kotlin.time.Clock { + override fun now(): kotlin.time.Instant = clock.now().toStdlibInstant() +} + +/** + * The `kotlinx-datetime` version of [kotlin.time.Clock]. + * + * Using this interface is discouraged in favor of the standard library version, [kotlin.time.Clock]. + * Initially, `Instant` and `Clock` were introduced in `kotlinx-datetime`, + * but it turned out they were useful even in contexts where no datetime processing was needed. + * As a result, starting from 2.1.20, Kotlin's standard library includes its own `Instant` and `Clock` classes. + * + * `kotlinx.datetime.Clock` is still available for compatibility reasons in the compatibility artifact of + * `kotlinx-datetime`, but it is deprecated. + * In the normal release artifact, it is replaced by `kotlin.time.Clock`. + * See [https://github.com/Kotlin/kotlinx-datetime?tab=readme-ov-file#deprecation-of-instant] for more information. + */ +@Deprecated( + "Use kotlin.time.Clock instead", + ReplaceWith("kotlin.time.Clock", "kotlin.time.Clock"), + level = DeprecationLevel.WARNING +) +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 = kotlin.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 + */ +@Deprecated("kotlin.datetime.Clock is superseded by kotlin.time.Clock", + level = DeprecationLevel.WARNING, + replaceWith = ReplaceWith("this.toStdlibClock().todayIn(timeZone)") +) +public fun Clock.todayIn(timeZone: TimeZone): LocalDate = + toStdlibClock().todayIn(timeZone) + +/** + * 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 +@Deprecated("kotlin.datetime.Clock is superseded by kotlin.time.Clock", + level = DeprecationLevel.WARNING, + replaceWith = ReplaceWith("this.toStdlibClock().asTimeSource()") +) +public fun Clock.asTimeSource(): TimeSource.WithComparableMarks = toStdlibClock().asTimeSource() + +@Deprecated("Use Clock.todayIn instead", ReplaceWith("this.toStdlibClock().todayIn(timeZone)"), DeprecationLevel.WARNING) +public fun Clock.todayAt(timeZone: TimeZone): LocalDate = todayIn(timeZone) + +@Deprecated( + "kotlin.datetime.Clock is superseded by kotlin.time.Clock", + ReplaceWith("this.asClock(origin.toStdlibInstant()).toDeprecatedClock()"), + level = DeprecationLevel.WARNING +) +public fun TimeSource.asClock(origin: kotlinx.datetime.Instant): kotlinx.datetime.Clock = object : Clock { + private val startMark: TimeMark = markNow() + override fun now() = origin + startMark.elapsedNow() +} \ No newline at end of file diff --git a/core/common/src/DeprecatedInstant.kt b/core/common/src/DeprecatedInstant.kt new file mode 100644 index 000000000..f20a9a945 --- /dev/null +++ b/core/common/src/DeprecatedInstant.kt @@ -0,0 +1,814 @@ +/* + * 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") +@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.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.TimeSource + +/** + * Creates a [kotlin.time.Instant] (the standard library version of `Instant`) identical to `this`. + */ +public fun Instant.toStdlibInstant(): kotlin.time.Instant = + kotlin.time.Instant.fromEpochSeconds(epochSeconds, nanosecondsOfSecond) + +/** + * Creates a [kotlinx.datetime.Instant] identical to the version of `Instant` from the standard library. + */ +public fun kotlin.time.Instant.toDeprecatedInstant(): Instant = + Instant.fromEpochSeconds(epochSeconds, nanosecondsOfSecond) + +/** + * The `kotlinx-datetime` version of [kotlin.time.Instant]. + * + * Using this class is discouraged in favor of the standard library version, [kotlin.time.Instant]. + * Initially, `Instant` and `Clock` were introduced in `kotlinx-datetime`, + * but it turned out they were useful even in contexts where no datetime processing was needed. + * As a result, starting from 2.1.20, Kotlin's standard library includes its own `Instant` and `Clock` classes. + * + * `kotlinx.datetime.Instant` is still available for compatibility reasons in the compatibility artifact of + * `kotlinx-datetime`, but it is deprecated. + * In the normal release artifact, it is replaced by `kotlin.time.Instant`. + * See [https://github.com/Kotlin/kotlinx-datetime?tab=readme-ov-file#deprecation-of-instant] for more information. + */ +@Deprecated( + "Use kotlin.time.Instant instead", + ReplaceWith("kotlin.time.Instant", "kotlin.time.Instant"), + level = DeprecationLevel.WARNING +) +@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 + */ + @Deprecated("kotlinx.datetime.Instant is superseded by kotlin.time.Instant", + level = DeprecationLevel.WARNING, + replaceWith = ReplaceWith("this.toStdlibInstant().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 + */ + @Deprecated("kotlinx.datetime.Instant is superseded by kotlin.time.Instant", + level = DeprecationLevel.WARNING, + replaceWith = ReplaceWith("this.toStdlibInstant().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 + */ + @Deprecated("kotlinx.datetime.Instant is superseded by kotlin.time.Instant", + level = DeprecationLevel.WARNING, + replaceWith = ReplaceWith("this.toStdlibInstant().nanosecondsOfSecond") + ) + 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 + */ + @Deprecated("kotlinx.datetime.Instant is superseded by kotlin.time.Instant", + level = DeprecationLevel.WARNING, + replaceWith = ReplaceWith("(this.toStdlibInstant() + duration).toDeprecatedInstant()") + ) + 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 + */ + @Deprecated("kotlinx.datetime.Instant is superseded by kotlin.time.Instant", + level = DeprecationLevel.WARNING, + replaceWith = ReplaceWith("(this.toStdlibInstant() - duration).toDeprecatedInstant()") + ) + 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 + */ + @Deprecated("kotlinx.datetime.Instant is superseded by kotlin.time.Instant", + level = DeprecationLevel.WARNING, + replaceWith = ReplaceWith("this.toStdlibInstant() - other.toStdlibInstant()") + ) + 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 + */ + @Deprecated("kotlinx.datetime.Instant is superseded by kotlin.time.Instant", + level = DeprecationLevel.WARNING, + replaceWith = ReplaceWith("this.toStdlibInstant().compareTo(other.toStdlibInstant())") + ) + 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 + */ + @Suppress("POTENTIALLY_NON_REPORTED_ANNOTATION") + @Deprecated("kotlinx.datetime.Instant is superseded by kotlin.time.Instant", + level = DeprecationLevel.WARNING, + replaceWith = ReplaceWith("this.toStdlibInstant().toString()") + ) + public override fun toString(): String + + + public companion object { + @Deprecated("Use Clock.System.now() instead", ReplaceWith("Clock.System.now()", "kotlin.time.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 + */ +@Deprecated("kotlinx.datetime.Instant is superseded by kotlin.time.Instant", + level = DeprecationLevel.WARNING, + replaceWith = ReplaceWith("this.toStdlibInstant().isDistantPast", "kotlin.time.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 + */ +@Deprecated("kotlinx.datetime.Instant is superseded by kotlin.time.Instant", + level = DeprecationLevel.WARNING, + replaceWith = ReplaceWith("this.toStdlibInstant().isDistantPast", "kotlin.time.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 + */ +@Deprecated("kotlinx.datetime.Instant is superseded by kotlin.time.Instant", + level = DeprecationLevel.WARNING, + replaceWith = ReplaceWith("this.toStdlibInstant().plus(period, timeZone).toDeprecatedInstant()") +) +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 + */ +@Deprecated("kotlinx.datetime.Instant is superseded by kotlin.time.Instant", + level = DeprecationLevel.WARNING, + replaceWith = ReplaceWith("this.toStdlibInstant().minus(period, timeZone).toDeprecatedInstant()") +) +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 + */ +@Deprecated("kotlinx.datetime.Instant is superseded by kotlin.time.Instant", + level = DeprecationLevel.WARNING, + replaceWith = ReplaceWith("this.toStdlibInstant().periodUntil(other.toStdlibInstant(), timeZone)") +) +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 + */ +@Deprecated("kotlinx.datetime.Instant is superseded by kotlin.time.Instant", + level = DeprecationLevel.WARNING, + replaceWith = ReplaceWith("this.toStdlibInstant().until(other.toStdlibInstant(), unit, timeZone)") +) +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 + */ +@Deprecated("kotlinx.datetime.Instant is superseded by kotlin.time.Instant", + level = DeprecationLevel.WARNING, + replaceWith = ReplaceWith("this.toStdlibInstant().until(other.toStdlibInstant(), unit)") +) +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 + */ +@Deprecated("kotlinx.datetime.Instant is superseded by kotlin.time.Instant", + level = DeprecationLevel.WARNING, + replaceWith = ReplaceWith("this.toStdlibInstant().daysUntil(other.toStdlibInstant(), timeZone)") +) +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 + */ +@Deprecated("kotlinx.datetime.Instant is superseded by kotlin.time.Instant", + level = DeprecationLevel.WARNING, + replaceWith = ReplaceWith("this.toStdlibInstant().monthsUntil(other.toStdlibInstant(), timeZone)") +) +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 + */ +@Deprecated("kotlinx.datetime.Instant is superseded by kotlin.time.Instant", + level = DeprecationLevel.WARNING, + replaceWith = ReplaceWith("this.toStdlibInstant().yearsUntil(other.toStdlibInstant(), timeZone)") +) +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 + */ +@Deprecated("kotlinx.datetime.Instant is superseded by kotlin.time.Instant", + level = DeprecationLevel.WARNING, + replaceWith = ReplaceWith("this.toStdlibInstant().minus(other.toStdlibInstant(), timeZone)") +) +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("kotlinx.datetime.Instant is superseded by kotlin.time.Instant", + level = DeprecationLevel.WARNING, + replaceWith = ReplaceWith("this.toStdlibInstant().plus(1, unit, timeZone).toDeprecatedInstant()") +) +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("kotlinx.datetime.Instant is superseded by kotlin.time.Instant", + level = DeprecationLevel.WARNING, + replaceWith = ReplaceWith("this.toStdlibInstant().minus(1, unit, timeZone).toDeprecatedInstant()") +) +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("kotlinx.datetime.Instant is superseded by kotlin.time.Instant", + level = DeprecationLevel.WARNING, + replaceWith = ReplaceWith("this.toStdlibInstant().plus(1, unit).toDeprecatedInstant()") +) +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("kotlinx.datetime.Instant is superseded by kotlin.time.Instant", + level = DeprecationLevel.WARNING, + replaceWith = ReplaceWith("this.toStdlibInstant().minus(1, unit).toDeprecatedInstant()") +) +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 + */ +@Deprecated("kotlinx.datetime.Instant is superseded by kotlin.time.Instant", + level = DeprecationLevel.WARNING, + replaceWith = ReplaceWith("this.toStdlibInstant().plus(value, unit, timeZone).toDeprecatedInstant()") +) +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 + */ +@Deprecated("kotlinx.datetime.Instant is superseded by kotlin.time.Instant", + level = DeprecationLevel.WARNING, + replaceWith = ReplaceWith("this.toStdlibInstant().minus(value, unit, timeZone).toDeprecatedInstant()") +) +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 + */ +@Deprecated("kotlinx.datetime.Instant is superseded by kotlin.time.Instant", + level = DeprecationLevel.WARNING, + replaceWith = ReplaceWith("this.toStdlibInstant().plus(value, unit).toDeprecatedInstant()") +) +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 + */ +@Deprecated("kotlinx.datetime.Instant is superseded by kotlin.time.Instant", + level = DeprecationLevel.WARNING, + replaceWith = ReplaceWith("this.toStdlibInstant().minus(value, unit).toDeprecatedInstant()") +) +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 + */ +@Deprecated("kotlinx.datetime.Instant is superseded by kotlin.time.Instant", + level = DeprecationLevel.WARNING, + replaceWith = ReplaceWith("this.toStdlibInstant().plus(value, unit, timeZone).toDeprecatedInstant()") +) +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 + */ +@Deprecated("kotlinx.datetime.Instant is superseded by kotlin.time.Instant", + level = DeprecationLevel.WARNING, + replaceWith = ReplaceWith("this.toStdlibInstant().minus(value, unit, timeZone).toDeprecatedInstant()") +) +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 + */ +@Deprecated("kotlinx.datetime.Instant is superseded by kotlin.time.Instant", + level = DeprecationLevel.WARNING, + replaceWith = ReplaceWith("this.toStdlibInstant().plus(value, unit).toDeprecatedInstant()") +) +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 + */ +@Deprecated("kotlinx.datetime.Instant is superseded by kotlin.time.Instant", + level = DeprecationLevel.WARNING, + replaceWith = ReplaceWith("this.toStdlibInstant().minus(value, unit).toDeprecatedInstant()") +) +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 + */ +@Deprecated("kotlinx.datetime.Instant is superseded by kotlin.time.Instant", + level = DeprecationLevel.WARNING, + replaceWith = ReplaceWith("this.toStdlibInstant().minus(other.toStdlibInstant(), unit, timeZone)") +) +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 + */ +@Deprecated("kotlinx.datetime.Instant is superseded by kotlin.time.Instant", + level = DeprecationLevel.WARNING, + replaceWith = ReplaceWith("this.toStdlibInstant().minus(other.toStdlibInstant(), unit)") +) +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 + */ +@Deprecated("kotlinx.datetime.Instant is superseded by kotlin.time.Instant", + level = DeprecationLevel.WARNING, + replaceWith = ReplaceWith("this.toStdlibInstant().format(format, offset)") +) +public fun Instant.format(format: DateTimeFormat, offset: UtcOffset = UtcOffset.ZERO): String { + val instant = this + return format.format { setDateTimeOffset(instant.toStdlibInstant(), offset) } +} + +/** + * An instance of this class can not be obtained, and it should not be used. + * + * The purpose of this class is to allow defining functions that return `kotlin.time.Instant`, + * but still keep the functions returning `kotlinx.datetime.Instant` for binary compatibility. + * Kotlin does not allow two functions with the same name and parameter types but different return types, + * so we need to use a trick to achieve this. + * By introducing a fictional parameter of this type, we can pretend that the function has a different signature, + * even though it can only be called exactly the same way as the function without this parameter used to be. + * There is no ambiguity, as the old functions are deprecated and hidden and can not actually be called. + * + * @suppress this class is not meant to be used, so the documentation is only here for the curious reader. + */ +@Deprecated( + "It is meaningless to try to pass an OverloadMarker to a function directly. " + + "All functions accepting it have its instance as a default value.", + level = DeprecationLevel.ERROR +) +public class OverloadMarker private constructor() { + internal companion object { + @Suppress("DEPRECATION_ERROR") + internal val INSTANCE = OverloadMarker() + } +} diff --git a/core/common/src/Instant.kt b/core/common/src/Instant.kt index 3deedd946..f55211d04 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 kotlin.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 23767380d..8a98b4f56 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 kotlin.time.Instant /** * A time zone, provides the conversion between [Instant] and [LocalDateTime] values @@ -126,6 +127,24 @@ public expect open class TimeZone { */ public fun Instant.toLocalDateTime(): LocalDateTime + /** + * Return the civil datetime value that this instant has in the time zone provided as an implicit receiver. + * + * Note that while this conversion is unambiguous, the inverse ([LocalDateTime.toInstant]) + * is not necessarily so. + * + * @see LocalDateTime.toInstant + * @see Instant.offsetIn + * @throws DateTimeArithmeticException if this value is too large to fit in [LocalDateTime]. + * @sample kotlinx.datetime.test.samples.TimeZoneSamples.toLocalDateTimeWithTwoReceivers + */ + @Suppress("DEPRECATION") + @Deprecated("kotlinx.datetime.Instant is superseded by kotlin.time.Instant", + level = DeprecationLevel.WARNING, + replaceWith = ReplaceWith("this.toStdlibInstant().toLocalDateTime()") + ) + public 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 +160,13 @@ public expect open class TimeZone { * @see Instant.toLocalDateTime * @sample kotlinx.datetime.test.samples.TimeZoneSamples.toInstantWithTwoReceivers */ - public fun LocalDateTime.toInstant(): Instant + @Suppress("DEPRECATION_ERROR") + public fun LocalDateTime.toInstant(youShallNotPass: OverloadMarker = OverloadMarker.INSTANCE): Instant + + @PublishedApi + @Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE", "DEPRECATION") + @kotlin.internal.LowPriorityInOverloadResolution + internal fun LocalDateTime.toInstant(): kotlinx.datetime.Instant } /** @@ -195,6 +220,14 @@ public typealias ZoneOffset = FixedOffsetTimeZone */ public expect fun TimeZone.offsetAt(instant: Instant): UtcOffset +@Suppress("DEPRECATION") +@Deprecated("kotlinx.datetime.Instant is superseded by kotlin.time.Instant", + level = DeprecationLevel.WARNING, + replaceWith = ReplaceWith("this.offsetAt(instant.toStdlibInstant())") +) +public fun TimeZone.offsetAt(instant: kotlinx.datetime.Instant): UtcOffset = + offsetAt(instant.toStdlibInstant()) + /** * Returns a civil datetime value that this instant has in the specified [timeZone]. * @@ -208,6 +241,14 @@ public expect fun TimeZone.offsetAt(instant: Instant): UtcOffset */ public expect fun Instant.toLocalDateTime(timeZone: TimeZone): LocalDateTime +@Suppress("DEPRECATION") +@Deprecated("kotlinx.datetime.Instant is superseded by kotlin.time.Instant", + level = DeprecationLevel.WARNING, + replaceWith = ReplaceWith("this.toStdlibInstant().toLocalDateTime(timeZone)") +) +public fun kotlinx.datetime.Instant.toLocalDateTime(timeZone: TimeZone): LocalDateTime = + toStdlibInstant().toLocalDateTime(timeZone) + /** * Returns a civil datetime value that this instant has in the specified [UTC offset][offset]. * @@ -221,6 +262,14 @@ public expect fun Instant.toLocalDateTime(timeZone: TimeZone): LocalDateTime */ internal expect fun Instant.toLocalDateTime(offset: UtcOffset): LocalDateTime +@Suppress("DEPRECATION") +@Deprecated("kotlinx.datetime.Instant is superseded by kotlin.time.Instant", + level = DeprecationLevel.WARNING, + replaceWith = ReplaceWith("this.toStdlibInstant().toLocalDateTime(offset)") +) +public fun kotlinx.datetime.Instant.toLocalDateTime(offset: UtcOffset): LocalDateTime = + toStdlibInstant().toLocalDateTime(offset) + /** * Finds the offset from UTC the specified [timeZone] has at this instant of physical time. * @@ -235,6 +284,14 @@ internal expect fun Instant.toLocalDateTime(offset: UtcOffset): LocalDateTime public fun Instant.offsetIn(timeZone: TimeZone): UtcOffset = timeZone.offsetAt(this) +@Suppress("DEPRECATION") +@Deprecated("kotlinx.datetime.Instant is superseded by kotlin.time.Instant", + level = DeprecationLevel.WARNING, + replaceWith = ReplaceWith("this.toStdlibInstant().offsetIn(timeZone)") +) +public fun kotlinx.datetime.Instant.offsetIn(timeZone: TimeZone): UtcOffset = + timeZone.offsetAt(toStdlibInstant()) + /** * Returns an instant that corresponds to this civil datetime value in the specified [timeZone]. * @@ -250,7 +307,14 @@ 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 +@Suppress("DEPRECATION_ERROR") +public expect fun LocalDateTime.toInstant(timeZone: TimeZone, youShallNotPass: OverloadMarker = OverloadMarker.INSTANCE): Instant + +@PublishedApi +@Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE", "DEPRECATION") +@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 +322,14 @@ 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 +@Suppress("DEPRECATION_ERROR") +public expect fun LocalDateTime.toInstant(offset: UtcOffset, youShallNotPass: OverloadMarker = OverloadMarker.INSTANCE): Instant + +@PublishedApi +@Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE", "DEPRECATION") +@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 +344,11 @@ public expect fun LocalDateTime.toInstant(offset: UtcOffset): Instant * * @sample kotlinx.datetime.test.samples.TimeZoneSamples.atStartOfDayIn */ -public expect fun LocalDate.atStartOfDayIn(timeZone: TimeZone): Instant +@Suppress("DEPRECATION_ERROR") +public expect fun LocalDate.atStartOfDayIn(timeZone: TimeZone, youShallNotPass: OverloadMarker = OverloadMarker.INSTANCE): Instant + +@PublishedApi +@Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE", "DEPRECATION") +@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 67e430673..f79c91ff8 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 kotlin.time.Instant /** * A collection of datetime fields used specifically for parsing and formatting. @@ -244,6 +245,15 @@ public class DateTimeComponents internal constructor(internal val contents: Date year = year!! + ((instant.epochSeconds / SECONDS_PER_10000_YEARS) * 10000).toInt() } + @Suppress("DEPRECATION") + @Deprecated("kotlinx.datetime.Instant is superseded by kotlin.time.Instant", + level = DeprecationLevel.WARNING, + replaceWith = ReplaceWith("this.setDateTimeOffset(instant.toStdlibInstant(), utcOffset)") + ) + public fun setDateTimeOffset(instant: kotlinx.datetime.Instant, utcOffset: UtcOffset) { + setDateTimeOffset(instant.toStdlibInstant(), utcOffset) + } + /** * Writes the contents of the specified [localDateTime] and [utcOffset] to this [DateTimeComponents]. * @@ -481,7 +491,8 @@ public class DateTimeComponents internal constructor(internal val contents: Date * with one another. * @sample kotlinx.datetime.test.samples.format.DateTimeComponentsSamples.toInstantUsingOffset */ - public fun toInstantUsingOffset(): Instant { + @Suppress("DEPRECATION_ERROR") + public fun toInstantUsingOffset(youShallNotPass: OverloadMarker = OverloadMarker.INSTANCE): Instant { val offset = toUtcOffset() val time = toLocalTime() val truncatedDate = contents.date.copy() @@ -499,10 +510,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") + @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 86% rename from core/common/src/serializers/InstantSerializers.kt rename to core/common/src/serializers/DeprecatedInstantSerializers.kt index c64bdf475..094a9d829 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") package kotlinx.datetime.serializers import kotlinx.datetime.Instant @@ -18,6 +19,11 @@ import kotlinx.serialization.encoding.* * @see Instant.toString * @see Instant.parse */ +@Deprecated( + "kotlinx.datetime.Instant is superseded by kotlin.time.Instant, " + + "whose serializers are provided by kotlinx.serialization.", + level = DeprecationLevel.WARNING, +) public object InstantIso8601Serializer : KSerializer { override val descriptor: SerialDescriptor = @@ -37,6 +43,11 @@ public object InstantIso8601Serializer : KSerializer { * * JSON example: `{"epochSeconds":1607505416,"nanosecondsOfSecond":124000}` */ +@Deprecated( + "kotlinx.datetime.Instant is superseded by kotlin.time.Instant, " + + "whose serializers are provided by kotlinx.serialization.", + level = DeprecationLevel.WARNING, +) public object InstantComponentSerializer : KSerializer { override val descriptor: SerialDescriptor = diff --git a/core/common/test/ClockTimeSourceTest.kt b/core/common/test/ClockTimeSourceTest.kt index 7c7ad0e6c..3db2fce99 100644 --- a/core/common/test/ClockTimeSourceTest.kt +++ b/core/common/test/ClockTimeSourceTest.kt @@ -7,12 +7,12 @@ package kotlinx.datetime.test import kotlinx.datetime.* import kotlin.test.* -import kotlin.time.Duration -import kotlin.time.ExperimentalTime +import kotlin.time.* import kotlin.time.Duration.Companion.days import kotlin.time.Duration.Companion.nanoseconds import kotlin.time.Duration.Companion.seconds -import kotlin.time.TestTimeSource +import kotlin.time.Clock +import kotlin.time.Instant @OptIn(ExperimentalTime::class) @Suppress("DEPRECATION") @@ -47,7 +47,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..85a453ee5 --- /dev/null +++ b/core/common/test/DeprecatedClockTimeSourceTest.kt @@ -0,0 +1,88 @@ +/* + * 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") +package kotlinx.datetime.test + +import kotlinx.datetime.* +import kotlin.test.* +import kotlin.time.ExperimentalTime +import kotlin.time.Duration +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..b8523ae26 --- /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") +package kotlinx.datetime.test + +import kotlinx.datetime.* +import kotlinx.datetime.format.* +import kotlinx.datetime.internal.* +import kotlin.random.* +import kotlin.test.* +import kotlin.time.Duration +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 63289a1eb..082c2f30e 100644 --- a/core/common/test/InstantTest.kt +++ b/core/common/test/InstantTest.kt @@ -10,12 +10,16 @@ import kotlinx.datetime.format.* import kotlinx.datetime.internal.* import kotlin.random.* import kotlin.test.* -import kotlin.time.Duration +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 +import kotlin.time.Clock +import kotlin.time.Instant +import kotlin.time.isDistantFuture +import kotlin.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 f6ded203e..60f4f841b 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 kotlin.time.Clock import kotlin.random.* import kotlin.test.* diff --git a/core/common/test/LocalDateTimeTest.kt b/core/common/test/LocalDateTimeTest.kt index 7d04b9134..07aed3d80 100644 --- a/core/common/test/LocalDateTimeTest.kt +++ b/core/common/test/LocalDateTimeTest.kt @@ -6,9 +6,10 @@ package kotlinx.datetime.test import kotlinx.datetime.* -import kotlinx.datetime.Clock +import kotlin.time.Clock +import kotlin.time.Instant import kotlin.test.* -import kotlin.time.Duration +import kotlin.time.* import kotlin.time.Duration.Companion.hours import kotlin.time.Duration.Companion.days @@ -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 2c84fa63b..880afc3f5 100644 --- a/core/common/test/ReadmeTest.kt +++ b/core/common/test/ReadmeTest.kt @@ -8,7 +8,9 @@ package kotlinx.datetime.test import kotlinx.datetime.* import kotlinx.datetime.format.* import kotlin.test.* -import kotlin.time.Duration +import kotlin.time.* +import kotlin.time.Clock +import kotlin.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 24c6d9e87..027e25e6a 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 kotlin.time.Clock +import kotlin.time.Instant class TimeZoneTest { diff --git a/core/common/test/format/DateTimeComponentsFormatTest.kt b/core/common/test/format/DateTimeComponentsFormatTest.kt index 86d2c3a81..79a085691 100644 --- a/core/common/test/format/DateTimeComponentsFormatTest.kt +++ b/core/common/test/format/DateTimeComponentsFormatTest.kt @@ -10,6 +10,7 @@ import kotlinx.datetime.format.* import kotlin.reflect.KMutableProperty1 import kotlin.reflect.KProperty import kotlin.test.* +import kotlin.time.Instant class DateTimeComponentsFormatTest { @Test diff --git a/core/common/test/format/DateTimeComponentsTest.kt b/core/common/test/format/DateTimeComponentsTest.kt index 8575c601b..3c9e3859b 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 kotlin.time.Clock class DateTimeComponentsTest { @Test diff --git a/core/common/test/samples/ClockSamples.kt b/core/common/test/samples/ClockSamples.kt index 70683efa5..278fa9e0f 100644 --- a/core/common/test/samples/ClockSamples.kt +++ b/core/common/test/samples/ClockSamples.kt @@ -9,6 +9,8 @@ import kotlinx.datetime.* import kotlin.test.* import kotlin.time.Duration.Companion.seconds import kotlin.time.TestTimeSource +import kotlin.time.Clock +import kotlin.time.Instant class ClockSamples { @Test diff --git a/core/common/test/samples/DayOfWeekSamples.kt b/core/common/test/samples/DayOfWeekSamples.kt index c8393ae98..4c491fcc1 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 kotlin.time.Clock class DayOfWeekSamples { diff --git a/core/common/test/samples/InstantSamples.kt b/core/common/test/samples/InstantSamples.kt index 8fec354b7..cbb574354 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 kotlin.time.Clock +import kotlin.time.Instant +import kotlin.time.isDistantFuture +import kotlin.time.isDistantPast class InstantSamples { diff --git a/core/common/test/samples/MonthSamples.kt b/core/common/test/samples/MonthSamples.kt index 70128845a..a2ab2edd1 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 kotlin.time.Clock class MonthSamples { diff --git a/core/common/test/samples/TimeZoneSamples.kt b/core/common/test/samples/TimeZoneSamples.kt index 71ef13fb6..a777f4d98 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 kotlin.time.Instant +import kotlin.time.Clock class TimeZoneSamples { diff --git a/core/common/test/samples/format/DateTimeComponentsSamples.kt b/core/common/test/samples/format/DateTimeComponentsSamples.kt index fbfd5d19c..3c2939e0c 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 kotlin.time.Instant class DateTimeComponentsSamples { diff --git a/core/commonJs/src/internal/Platform.kt b/core/commonJs/src/internal/Platform.kt index c5ba347b7..940777826 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 kotlin.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..db316b779 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 kotlin.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..30d73cce9 --- /dev/null +++ b/core/commonKotlin/src/DeprecatedInstant.kt @@ -0,0 +1,355 @@ +/* + * 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") +@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("kotlin.time.Instant", "kotlin.time.Instant"), + level = DeprecationLevel.WARNING +) +@Serializable(with = InstantIso8601Serializer::class) +public actual class Instant internal constructor( + @Deprecated("kotlinx.datetime.Instant is superseded by kotlin.time.Instant", + level = DeprecationLevel.WARNING, + replaceWith = ReplaceWith("this.toStdlibInstant().epochSeconds") + ) + public actual val epochSeconds: Long, + @Deprecated("kotlinx.datetime.Instant is superseded by kotlin.time.Instant", + level = DeprecationLevel.WARNING, + replaceWith = ReplaceWith("this.toStdlibInstant().nanosecondsOfSecond") + ) + public actual val nanosecondsOfSecond: Int +) : Comparable { + + init { + require(epochSeconds in MIN_SECOND..MAX_SECOND) { "Instant exceeds minimum or maximum instant" } + } + + @Deprecated("kotlinx.datetime.Instant is superseded by kotlin.time.Instant", + level = DeprecationLevel.WARNING, + replaceWith = ReplaceWith("this.toStdlibInstant().nanosecondsOfSecond") + ) + // 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) + } + + @Deprecated("kotlinx.datetime.Instant is superseded by kotlin.time.Instant", + level = DeprecationLevel.WARNING, + replaceWith = ReplaceWith("(this.toStdlibInstant() + duration).toDeprecatedInstant()") + ) + 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 + } + } + + @Deprecated("kotlinx.datetime.Instant is superseded by kotlin.time.Instant", + level = DeprecationLevel.WARNING, + replaceWith = ReplaceWith("(this.toStdlibInstant() - duration).toDeprecatedInstant()") + ) + public actual operator fun minus(duration: Duration): Instant = plus(-duration) + + @Deprecated("kotlinx.datetime.Instant is superseded by kotlin.time.Instant", + level = DeprecationLevel.WARNING, + replaceWith = ReplaceWith("this.toStdlibInstant() - other.toStdlibInstant()") + ) + 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 + + @Deprecated("kotlinx.datetime.Instant is superseded by kotlin.time.Instant", + level = DeprecationLevel.WARNING, + replaceWith = ReplaceWith("this.toStdlibInstant().compareTo(other.toStdlibInstant())") + ) + 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 + + @Suppress("POTENTIALLY_NON_REPORTED_ANNOTATION") + @Deprecated("kotlinx.datetime.Instant is superseded by kotlin.time.Instant", + level = DeprecationLevel.WARNING, + replaceWith = ReplaceWith("this.toStdlibInstant().toString()") + ) + // 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()", "kotlin.time.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.toStdlibInstant()) + return ZonedDateTime(toStdlibInstant().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) +} + +@Deprecated("kotlinx.datetime.Instant is superseded by kotlin.time.Instant", + level = DeprecationLevel.WARNING, + replaceWith = ReplaceWith("this.toStdlibInstant().plus(period, timeZone).toDeprecatedInstant()") +) +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("kotlinx.datetime.Instant is superseded by kotlin.time.Instant", + level = DeprecationLevel.WARNING, + replaceWith = ReplaceWith("this.toStdlibInstant().plus(1, unit, timeZone).toDeprecatedInstant()") +) +public actual fun Instant.plus(unit: DateTimeUnit, timeZone: TimeZone): Instant = + plus(1L, unit, timeZone) +@Deprecated("kotlinx.datetime.Instant is superseded by kotlin.time.Instant", + level = DeprecationLevel.WARNING, + replaceWith = ReplaceWith("this.toStdlibInstant().plus(value, unit, timeZone).toDeprecatedInstant()") +) +public actual fun Instant.plus(value: Int, unit: DateTimeUnit, timeZone: TimeZone): Instant = + plus(value.toLong(), unit, timeZone) +@Deprecated("kotlinx.datetime.Instant is superseded by kotlin.time.Instant", + level = DeprecationLevel.WARNING, + replaceWith = ReplaceWith("this.toStdlibInstant().minus(value, unit, timeZone).toDeprecatedInstant()") +) +public actual fun Instant.minus(value: Int, unit: DateTimeUnit, timeZone: TimeZone): Instant = + plus(-value.toLong(), unit, timeZone) +@Deprecated("kotlinx.datetime.Instant is superseded by kotlin.time.Instant", + level = DeprecationLevel.WARNING, + replaceWith = ReplaceWith("this.toStdlibInstant().plus(value, unit, timeZone).toDeprecatedInstant()") +) +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) +} + +@Deprecated("kotlinx.datetime.Instant is superseded by kotlin.time.Instant", + level = DeprecationLevel.WARNING, + replaceWith = ReplaceWith("this.toStdlibInstant().plus(value, unit).toDeprecatedInstant()") +) +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 + } + +@Deprecated("kotlinx.datetime.Instant is superseded by kotlin.time.Instant", + level = DeprecationLevel.WARNING, + replaceWith = ReplaceWith("this.toStdlibInstant().periodUntil(other.toStdlibInstant(), timeZone)") +) +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) +} + +@Deprecated("kotlinx.datetime.Instant is superseded by kotlin.time.Instant", + level = DeprecationLevel.WARNING, + replaceWith = ReplaceWith("this.toStdlibInstant().until(other.toStdlibInstant(), unit, timeZone)") +) +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 b4cff744c..bcb78979b 100644 --- a/core/commonKotlin/src/Instant.kt +++ b/core/commonKotlin/src/Instant.kt @@ -8,152 +8,11 @@ package kotlinx.datetime -import kotlinx.datetime.format.* +import kotlin.time.Instant import kotlinx.datetime.internal.* -import kotlinx.datetime.serializers.InstantIso8601Serializer -import kotlinx.serialization.Serializable -import kotlin.time.* 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: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 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 fun Instant.toZonedDateTimeFailing(zone: TimeZone): ZonedDateTime = try { toZonedDateTime(zone) } catch (e: IllegalArgumentException) { @@ -181,7 +40,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) @@ -212,12 +78,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 { @@ -243,27 +109,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..6fb495cac 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 kotlin.time.Instant @Serializable(with = TimeZoneSerializer::class) public actual open class TimeZone internal constructor() { @@ -80,7 +81,24 @@ 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) + + @Suppress("DEPRECATION_ERROR") + public actual fun LocalDateTime.toInstant(youShallNotPass: OverloadMarker): Instant = + localDateTimeToInstant(this) + + @Suppress("DEPRECATION") + @Deprecated("kotlinx.datetime.Instant is superseded by kotlin.time.Instant", + level = DeprecationLevel.WARNING, + replaceWith = ReplaceWith("this.toStdlibInstant().toLocalDateTime()") + ) + public actual fun kotlinx.datetime.Instant.toLocalDateTime(): LocalDateTime = + toStdlibInstant().toLocalDateTime() + + @PublishedApi + @Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE", "DEPRECATION") + @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 +165,16 @@ internal fun Instant.toLocalDateTimeImpl(offset: UtcOffset): LocalDateTime { return LocalDateTime(date, time) } -public actual fun LocalDateTime.toInstant(timeZone: TimeZone): Instant = +@Suppress("DEPRECATION_ERROR") +public actual fun LocalDateTime.toInstant(timeZone: TimeZone, youShallNotPass: OverloadMarker): Instant = timeZone.localDateTimeToInstant(this) -public actual fun LocalDateTime.toInstant(offset: UtcOffset): Instant = - Instant(this.toEpochSecond(offset), this.nanosecond) +@Suppress("DEPRECATION_ERROR") +public actual fun LocalDateTime.toInstant(offset: UtcOffset, youShallNotPass: OverloadMarker): Instant = + Instant.fromEpochSeconds(this.toEpochSecond(offset), this.nanosecond) -public actual fun LocalDate.atStartOfDayIn(timeZone: TimeZone): Instant = +@Suppress("DEPRECATION_ERROR") +public actual fun LocalDate.atStartOfDayIn(timeZone: TimeZone, youShallNotPass: OverloadMarker): 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..1847d8ca4 100644 --- a/core/commonKotlin/src/ZonedDateTime.kt +++ b/core/commonKotlin/src/ZonedDateTime.kt @@ -8,6 +8,8 @@ package kotlinx.datetime +import kotlin.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 287dc9e5f..09b47e28e 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 kotlin.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..c769e919d 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 kotlin.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..335402b29 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 kotlin.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..aa8332b4d 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 kotlin.time.Instant internal class TimeZoneRules( /** 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..516a0fa21 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 kotlin.time.Instant class TimeZoneRulesTest { @Test diff --git a/core/darwin/src/Converters.kt b/core/darwin/src/Converters.kt index cf785dead..afe31a370 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 kotlin.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 = toStdlibInstant().toNSDate() + /** * Converts the [NSDate] to the corresponding [Instant]. * @@ -33,13 +38,20 @@ 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 { +@Suppress("DEPRECATION_ERROR") +public fun NSDate.toKotlinInstant(youShallNotPass: OverloadMarker = OverloadMarker.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") +@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..0ad290fa2 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 kotlin.time.Clock +import kotlin.time.Instant class ConvertersTest { diff --git a/core/js/src/Converters.kt b/core/js/src/DeprecatedConverters.kt similarity index 95% rename from core/js/src/Converters.kt rename to core/js/src/DeprecatedConverters.kt index a4a8c9df9..7ac3e9fbd 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") package kotlinx.datetime import kotlin.js.* diff --git a/core/js/test/JsConverterTest.kt b/core/js/test/DeprecatedJsConverterTest.kt similarity index 93% rename from core/js/test/JsConverterTest.kt rename to core/js/test/DeprecatedJsConverterTest.kt index de5eeb6f3..3e34d673c 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") 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 688b0f84a..029f4cb3a 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") +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") +@kotlin.internal.LowPriorityInOverloadResolution +internal fun java.time.Instant.toKotlinInstant(): Instant = Instant(this) /** diff --git a/core/jvm/src/DeprecatedInstant.kt b/core/jvm/src/DeprecatedInstant.kt new file mode 100644 index 000000000..8929d4055 --- /dev/null +++ b/core/jvm/src/DeprecatedInstant.kt @@ -0,0 +1,253 @@ +/* + * 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") +@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("kotlin.time.Instant", "kotlin.time.Instant"), + level = DeprecationLevel.WARNING +) +@Serializable(with = InstantIso8601Serializer::class) +public actual class Instant internal constructor(internal val value: java.time.Instant) : Comparable { + @Deprecated("kotlinx.datetime.Instant is superseded by kotlin.time.Instant", + level = DeprecationLevel.WARNING, + replaceWith = ReplaceWith("this.toStdlibInstant().epochSeconds") + ) + public actual val epochSeconds: Long + get() = value.epochSecond + @Deprecated("kotlinx.datetime.Instant is superseded by kotlin.time.Instant", + level = DeprecationLevel.WARNING, + replaceWith = ReplaceWith("this.toStdlibInstant().nanosecondsOfSecond") + ) + public actual val nanosecondsOfSecond: Int + get() = value.nano + + @Deprecated("kotlinx.datetime.Instant is superseded by kotlin.time.Instant", + level = DeprecationLevel.WARNING, + replaceWith = ReplaceWith("this.toStdlibInstant().nanosecondsOfSecond") + ) + 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 + } + + @Deprecated("kotlinx.datetime.Instant is superseded by kotlin.time.Instant", + level = DeprecationLevel.WARNING, + replaceWith = ReplaceWith("(this.toStdlibInstant() + duration).toDeprecatedInstant()") + ) + 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 + } + } + + @Deprecated("kotlinx.datetime.Instant is superseded by kotlin.time.Instant", + level = DeprecationLevel.WARNING, + replaceWith = ReplaceWith("(this.toStdlibInstant() - duration).toDeprecatedInstant()") + ) + public actual operator fun minus(duration: Duration): Instant = plus(-duration) + + @Deprecated("kotlinx.datetime.Instant is superseded by kotlin.time.Instant", + level = DeprecationLevel.WARNING, + replaceWith = ReplaceWith("this.toStdlibInstant() - other.toStdlibInstant()") + ) + 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 + + @Deprecated("kotlinx.datetime.Instant is superseded by kotlin.time.Instant", + level = DeprecationLevel.WARNING, + replaceWith = ReplaceWith("this.toStdlibInstant().compareTo(other.toStdlibInstant())") + ) + 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() + + @Suppress("POTENTIALLY_NON_REPORTED_ANNOTATION") + @Deprecated("kotlinx.datetime.Instant is superseded by kotlin.time.Instant", + level = DeprecationLevel.WARNING, + replaceWith = ReplaceWith("this.toStdlibInstant().toString()") + ) + actual override fun toString(): String = value.toString() + + public actual companion object { + @Deprecated("Use Clock.System.now() instead", ReplaceWith("Clock.System.now()", "kotlin.time.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) +} + +@Deprecated("kotlinx.datetime.Instant is superseded by kotlin.time.Instant", + level = DeprecationLevel.WARNING, + replaceWith = ReplaceWith("this.toStdlibInstant().plus(period, timeZone).toDeprecatedInstant()") +) +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("kotlinx.datetime.Instant is superseded by kotlin.time.Instant", + level = DeprecationLevel.WARNING, + replaceWith = ReplaceWith("this.toStdlibInstant().plus(1, unit, timeZone).toDeprecatedInstant()") +) +public actual fun Instant.plus(unit: DateTimeUnit, timeZone: TimeZone): Instant = + plus(1L, unit, timeZone) + +@Deprecated("kotlinx.datetime.Instant is superseded by kotlin.time.Instant", + level = DeprecationLevel.WARNING, + replaceWith = ReplaceWith("this.toStdlibInstant().plus(value, unit, timeZone).toDeprecatedInstant()") +) +public actual fun Instant.plus(value: Int, unit: DateTimeUnit, timeZone: TimeZone): Instant = + plus(value.toLong(), unit, timeZone) + +@Deprecated("kotlinx.datetime.Instant is superseded by kotlin.time.Instant", + level = DeprecationLevel.WARNING, + replaceWith = ReplaceWith("this.toStdlibInstant().minus(value, unit, timeZone).toDeprecatedInstant()") +) +public actual fun Instant.minus(value: Int, unit: DateTimeUnit, timeZone: TimeZone): Instant = + plus(-value.toLong(), unit, timeZone) + +@Deprecated("kotlinx.datetime.Instant is superseded by kotlin.time.Instant", + level = DeprecationLevel.WARNING, + replaceWith = ReplaceWith("this.toStdlibInstant().plus(value, unit, timeZone).toDeprecatedInstant()") +) +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) + } + +@Deprecated("kotlinx.datetime.Instant is superseded by kotlin.time.Instant", + level = DeprecationLevel.WARNING, + replaceWith = ReplaceWith("this.toStdlibInstant().plus(value, unit).toDeprecatedInstant()") +) +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 + } + +@Deprecated("kotlinx.datetime.Instant is superseded by kotlin.time.Instant", + level = DeprecationLevel.WARNING, + replaceWith = ReplaceWith("this.toStdlibInstant().periodUntil(other.toStdlibInstant(), timeZone)") +) +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) +} + +@Deprecated("kotlinx.datetime.Instant is superseded by kotlin.time.Instant", + level = DeprecationLevel.WARNING, + replaceWith = ReplaceWith("this.toStdlibInstant().until(other.toStdlibInstant(), unit, timeZone)") +) +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 78f82ed8b..0ec2f6fc9 100644 --- a/core/jvm/src/Instant.kt +++ b/core/jvm/src/Instant.kt @@ -2,108 +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 kotlin.time.Instant +import kotlin.time.toJavaInstant +import kotlin.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) } @@ -116,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) } @@ -137,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) @@ -151,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 { @@ -180,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..c9f68cdba 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 kotlin.time.Instant +import kotlin.time.toJavaInstant +import kotlin.time.toKotlinInstant @Serializable(with = TimeZoneSerializer::class) public actual open class TimeZone internal constructor(internal val zoneId: ZoneId) { @@ -21,7 +24,23 @@ 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) + + @Suppress("DEPRECATION_ERROR") + public actual fun LocalDateTime.toInstant(youShallNotPass: OverloadMarker): Instant = toInstant(this@TimeZone) + + @Suppress("DEPRECATION") + @Deprecated("kotlinx.datetime.Instant is superseded by kotlin.time.Instant", + level = DeprecationLevel.WARNING, + replaceWith = ReplaceWith("this.toStdlibInstant().toLocalDateTime()") + ) + public actual fun kotlinx.datetime.Instant.toLocalDateTime(): LocalDateTime = + toStdlibInstant().toLocalDateTime() + + @PublishedApi + @Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE", "DEPRECATION") + @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 +93,29 @@ 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) +@Suppress("DEPRECATION_ERROR") +public actual fun LocalDateTime.toInstant(timeZone: TimeZone, youShallNotPass: OverloadMarker): Instant = + this.value.atZone(timeZone.zoneId).toInstant().toKotlinInstant() -public actual fun LocalDateTime.toInstant(offset: UtcOffset): Instant = - this.value.toInstant(offset.zoneOffset).let(::Instant) +@Suppress("DEPRECATION_ERROR") +public actual fun LocalDateTime.toInstant(offset: UtcOffset, youShallNotPass: OverloadMarker): Instant = + this.value.toInstant(offset.zoneOffset).toKotlinInstant() -public actual fun LocalDate.atStartOfDayIn(timeZone: TimeZone): Instant = - this.value.atStartOfDay(timeZone.zoneId).toInstant().let(::Instant) +@Suppress("DEPRECATION_ERROR") +public actual fun LocalDate.atStartOfDayIn(timeZone: TimeZone, youShallNotPass: OverloadMarker): Instant = + this.value.atStartOfDay(timeZone.zoneId).toInstant().toKotlinInstant() diff --git a/core/jvm/test/ConvertersTest.kt b/core/jvm/test/ConvertersTest.kt index 06b7e2239..7a4c6149d 100644 --- a/core/jvm/test/ConvertersTest.kt +++ b/core/jvm/test/ConvertersTest.kt @@ -2,6 +2,7 @@ * 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. */ +@file:Suppress("DEPRECATION") package kotlinx.datetime.test import kotlinx.datetime.* @@ -14,19 +15,21 @@ import java.time.LocalDate as JTLocalDate import java.time.Period as JTPeriod import java.time.ZoneId import java.time.ZoneOffset as JTZoneOffset +import kotlinx.datetime.Instant as kxdtInstant class ConvertersTest { @Test fun instant() { + fun test(seconds: Long, nanosecond: Int) { - val ktInstant = Instant.fromEpochSeconds(seconds, nanosecond.toLong()) + val ktInstant = kxdtInstant.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(ktInstant, jtInstant.toString().let(kxdtInstant::parse)) assertEquals(jtInstant, ktInstant.toString().let(JTInstant::parse)) } @@ -172,7 +175,7 @@ class ConvertersTest { @Test fun zoneOffset() { fun test(offsetString: String) { - val ktUtcOffset = TimeZone.of(offsetString).offsetAt(Instant.fromEpochMilliseconds(0)) + val ktUtcOffset = TimeZone.of(offsetString).offsetAt(kotlin.time.Instant.fromEpochMilliseconds(0)) val ktZoneOffset = ktUtcOffset.asTimeZone() val jtZoneOffset = JTZoneOffset.of(offsetString) diff --git a/core/jvm/test/InstantParsing.kt b/core/jvm/test/InstantParsing.kt index 4c29b81e9..04d4205dc 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 kotlin.time.Instant +import kotlin.time.toJavaInstant 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..f50942db6 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 kotlin.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 6352494f8..12d4c7964 100644 --- a/core/windows/test/TimeZoneRulesCompleteTest.kt +++ b/core/windows/test/TimeZoneRulesCompleteTest.kt @@ -14,6 +14,8 @@ import platform.windows.* import kotlin.test.* import kotlin.time.Duration.Companion.hours import kotlin.time.Duration.Companion.milliseconds +import kotlin.time.Instant +import kotlin.time.Clock class TimeZoneRulesCompleteTest { diff --git a/js-without-timezones/common/test/TimezonesWithoutDatabaseTest.kt b/js-without-timezones/common/test/TimezonesWithoutDatabaseTest.kt index 153b505cd..c496881f5 100644 --- a/js-without-timezones/common/test/TimezonesWithoutDatabaseTest.kt +++ b/js-without-timezones/common/test/TimezonesWithoutDatabaseTest.kt @@ -7,7 +7,10 @@ package kotlinx.datetime.test import kotlinx.datetime.* import kotlin.test.* +import kotlin.time.Instant +import kotlin.time.Clock +@OptIn(kotlin.time.ExperimentalTime::class) class TimezonesWithoutDatabaseTest { @Test fun system() { 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..daaa054e2 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") 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..e792e442c 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") 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 +}