Skip to content

Commit e636c1a

Browse files
committed
Describe the process we ended up with
1 parent 3417029 commit e636c1a

File tree

1 file changed

+82
-4
lines changed

1 file changed

+82
-4
lines changed

proposals/stdlib/instant.md

Lines changed: 82 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1311,6 +1311,27 @@ fail with a `ClassNotFoundException` until it is rewritten to use
13111311
This renders most libraries relying on `kotlinx.datetime.Instant`
13121312
(even internally) completely useless overnight.
13131313

1314+
#### Process 0.8: break binary compatibility, preserve source compatibility
1315+
1316+
* Standard Library publishes its own `kotlin.time.Instant` and `Clock`.
1317+
* `kotlinx-datetime` removes the implementations of `kotlinx.datetime.Instant`
1318+
and `Clock` and depends on the new release of the Standard Library,
1319+
changing the API entries that used to work on `kotlinx.datetime.Instant`
1320+
to instead use `kotlin.time.Instant`.
1321+
* `kotlinx-datetime` introduces the typealiases
1322+
`kotlinx.datetime.Instant = kotlinx.time.Instant` and
1323+
`kotlinx.datetime.Clock = kotlinx.time.Clock` and immediately deprecates them
1324+
with a warning.
1325+
1326+
##### Analysis
1327+
1328+
All code that used to use `kotlinx-datetime`'s `Clock` and `Instant` will
1329+
compile.
1330+
However, all third-party libraries using `kotlinx.datetime.Instant` (even
1331+
internally, without exposing the class in the public API) will become
1332+
unusable with the new release, because the class `kotlinx.datetime.Instant` will
1333+
no longer actually exist.
1334+
13141335
#### Process 1: pure library solutions against breakage
13151336

13161337
* Standard Library publishes its own `kotlin.time.Instant` and `Clock`.
@@ -1326,8 +1347,7 @@ This renders most libraries relying on `kotlinx.datetime.Instant`
13261347
instead. The proposed suggestions involve the new converters.
13271348
Exception: `Clock.asTimeSource` does not get the new overload.
13281349
- For every function that returns but doesn't accept an `Instant`,
1329-
hides it and adds a new function, one that returns `kotlin.time.Instant`,
1330-
but with different platform names.
1350+
hides it and adds a new function, one that returns `kotlin.time.Instant`.
13311351
- Deprecates the `Instant` serializers with a warning.
13321352
- A new version of `kotlinx-datetime` is published with this.
13331353
* One major version of `kotlinx-datetime` later:
@@ -1364,8 +1384,11 @@ fun LocalDateTime.toInstant(): kotlinx.datetime.Instant
13641384

13651385
fun kotlin.time.Instant.toLocalDateTime(): LocalDateTime
13661386

1367-
@JsName("temporary_toInstant") // and other PlatformName annotations
1368-
fun LocalDateTime.toInstant(): kotlin.time.Instant
1387+
fun LocalDateTime.toInstant(
1388+
// a trick to avoid Kotlin's limitation on having two functions that are
1389+
// only different in their return types
1390+
overloadMarker: OverloadMarker = OverloadMarker.HIDDEN_INSTANCE
1391+
): kotlin.time.Instant
13691392

13701393
// 0.X+2.0
13711394
@Deprecated(level = DeprecationLevel.ERROR)
@@ -1475,3 +1498,58 @@ All code that used to work will continue to:
14751498
* All existing references to `kotlinx.datetime.Instant` will keep functioning,
14761499
as there will be a class with all the same methods as the currently existing
14771500
one.
1501+
1502+
#### Process 1.8: publish process 1 and process 0.8 as separate artifacts.
1503+
1504+
This is the process we reached as the compromise between the other options.
1505+
1506+
* Standard Library publishes its own `kotlin.time.Instant` and `Clock`.
1507+
* `kotlinx-datetime` publishes two separate releases:
1508+
- `0.7.0` follows process 0.8 almost fully.
1509+
In the optimistic scenarios where the user of
1510+
the `kotlinx-datetime` library doesn't depend on any third-party libraries
1511+
that also use `kotlinx-datetime`, everything keeps working.
1512+
- `0.7.0-0.6.x-compat` follows process 1.
1513+
If the user project depends on third-party libraries using `kotlinx-datetime`
1514+
`0.6.x`, which get broken by process 0.8, the user project can depend on
1515+
`0.7.0-0.6.x-compat` instead. Then, some code in the user project may have
1516+
to be changed, but this is less problematic than the third-party code over
1517+
which the user project has no control being broken.
1518+
`0.7.0-0.6.x-compat` is larger than `0.7.0` according to Gradle's algorithm
1519+
(<https://docs.gradle.org/current/userguide/dependency_versions.html#sec:version-ordering>),
1520+
and in Maven projects, one needs to specify the version in full anyway,
1521+
so having a dependency that itself transitively depends on `0.7.0` will not
1522+
prevent the compatibility artifact from being chosen.
1523+
* For the next minor releases (and depending on the impact, maybe even some
1524+
major releases), in addition to `0.A.B`, `0.A.B-0.6.x-compat` will be
1525+
published with the same set of changes.
1526+
1527+
This is the approach that we ended up with after considering
1528+
the potential impact.
1529+
1530+
`0.7.0` will not fully follow the 0.8 process, as we need it to be interoperable
1531+
with `0.7.0-0.6.x-compat`: if one of a project's dependencies is compiled against
1532+
`kotlinx-datetime` `0.7.0`, but the project itself needs to use
1533+
`0.7.0-0.6.x-compat`, the library using `0.7.0` must work.
1534+
This means that the set of APIs provided in `0.7.0-0.6.x-compat` must be strict
1535+
superset of the ones provided in `0.7.0`.
1536+
The problem arises when it comes to functions returning an `Instant`:
1537+
1538+
```kotlin
1539+
// necessary in 0.7.0-0.6.x-compat for binary compatibility
1540+
@Deprecated(level = DeprecationLevel.HIDDEN)
1541+
fun LocalDateTime.toInstant(): kotlinx.datetime.Instant
1542+
1543+
fun LocalDateTime.toInstant(
1544+
// this parameter is necessary in 0.7.0-0.6.x-compat
1545+
overloadMarker: OverloadMarker = OverloadMarker.HIDDEN_INSTANCE
1546+
): kotlin.time.Instant
1547+
```
1548+
1549+
`0.7.0` can not simply introduce
1550+
`fun LocalDateTime.toInstant(): kotlin.time.Instant`, because
1551+
`0.7.0-0.6.x-compat` has to be compatible with it and also can not contain this
1552+
function due to Kotlin's limitations. Because of that, the `overloadMarker`
1553+
workaround, even though it's completely unnecessary when transitioning from
1554+
`0.6.x` to `0.7.0` in the optimistic scenario, also needs to be introduced in
1555+
`0.7.0`.

0 commit comments

Comments
 (0)