Skip to content

Commit 9bebcf4

Browse files
committed
Support kotlin.time.Instant in FormattedInstantSerializer
The DateTimeFormat-based FormattedInstantSerializer would be introduced in this release for `kotlinx.datetime.Instant` if that wasn't going to be removed. Now, instead, we support the new, `kotlin.time.Instant` in this serializer.
1 parent 165ade3 commit 9bebcf4

File tree

6 files changed

+105
-76
lines changed

6 files changed

+105
-76
lines changed

core/api/kotlinx-datetime.api

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1110,10 +1110,10 @@ public final class kotlinx/datetime/serializers/FixedOffsetTimeZoneSerializer :
11101110
public abstract class kotlinx/datetime/serializers/FormattedInstantSerializer : kotlinx/serialization/KSerializer {
11111111
public fun <init> (Ljava/lang/String;Lkotlinx/datetime/format/DateTimeFormat;)V
11121112
public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object;
1113-
public fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lkotlinx/datetime/Instant;
1113+
public fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lkotlin/time/Instant;
11141114
public fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor;
11151115
public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V
1116-
public fun serialize (Lkotlinx/serialization/encoding/Encoder;Lkotlinx/datetime/Instant;)V
1116+
public fun serialize (Lkotlinx/serialization/encoding/Encoder;Lkotlin/time/Instant;)V
11171117
public fun toString ()Ljava/lang/String;
11181118
}
11191119

core/api/kotlinx-datetime.klib.api

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -139,14 +139,14 @@ sealed interface kotlinx.datetime.format/DateTimeFormatBuilder { // kotlinx.date
139139
}
140140
}
141141

142-
abstract class kotlinx.datetime.serializers/FormattedInstantSerializer : kotlinx.serialization/KSerializer<kotlinx.datetime/Instant> { // kotlinx.datetime.serializers/FormattedInstantSerializer|null[0]
142+
abstract class kotlinx.datetime.serializers/FormattedInstantSerializer : kotlinx.serialization/KSerializer<kotlin.time/Instant> { // kotlinx.datetime.serializers/FormattedInstantSerializer|null[0]
143143
constructor <init>(kotlin/String, kotlinx.datetime.format/DateTimeFormat<kotlinx.datetime.format/DateTimeComponents>) // kotlinx.datetime.serializers/FormattedInstantSerializer.<init>|<init>(kotlin.String;kotlinx.datetime.format.DateTimeFormat<kotlinx.datetime.format.DateTimeComponents>){}[0]
144144

145145
open val descriptor // kotlinx.datetime.serializers/FormattedInstantSerializer.descriptor|{}descriptor[0]
146146
open fun <get-descriptor>(): kotlinx.serialization.descriptors/SerialDescriptor // kotlinx.datetime.serializers/FormattedInstantSerializer.descriptor.<get-descriptor>|<get-descriptor>(){}[0]
147147

148-
open fun deserialize(kotlinx.serialization.encoding/Decoder): kotlinx.datetime/Instant // kotlinx.datetime.serializers/FormattedInstantSerializer.deserialize|deserialize(kotlinx.serialization.encoding.Decoder){}[0]
149-
open fun serialize(kotlinx.serialization.encoding/Encoder, kotlinx.datetime/Instant) // kotlinx.datetime.serializers/FormattedInstantSerializer.serialize|serialize(kotlinx.serialization.encoding.Encoder;kotlinx.datetime.Instant){}[0]
148+
open fun deserialize(kotlinx.serialization.encoding/Decoder): kotlin.time/Instant // kotlinx.datetime.serializers/FormattedInstantSerializer.deserialize|deserialize(kotlinx.serialization.encoding.Decoder){}[0]
149+
open fun serialize(kotlinx.serialization.encoding/Encoder, kotlin.time/Instant) // kotlinx.datetime.serializers/FormattedInstantSerializer.serialize|serialize(kotlinx.serialization.encoding.Encoder;kotlin.time.Instant){}[0]
150150
open fun toString(): kotlin/String // kotlinx.datetime.serializers/FormattedInstantSerializer.toString|toString(){}[0]
151151
}
152152

core/common/src/serializers/DeprecatedInstantSerializers.kt

Lines changed: 0 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ package kotlinx.datetime.serializers
88

99
import kotlinx.datetime.*
1010
import kotlinx.datetime.format.DateTimeComponents
11-
import kotlinx.datetime.format.DateTimeFormat
1211
import kotlinx.serialization.*
1312
import kotlinx.serialization.descriptors.*
1413
import kotlinx.serialization.encoding.*
@@ -88,53 +87,6 @@ public object InstantComponentSerializer : KSerializer<Instant> {
8887

8988
}
9089

91-
/**
92-
* An abstract serializer for [Instant] values that uses
93-
* a custom [DateTimeFormat] for serializing to and deserializing.
94-
*
95-
* [format] should be a format that includes enough components to unambiguously define a date, a time, and a UTC offset.
96-
* See [Instant.parse] for details of how deserialization is performed.
97-
*
98-
* [name] is the name of the serializer.
99-
* The [SerialDescriptor.serialName] of the resulting serializer is `kotlinx.datetime.Instant/serializer/`[name].
100-
* [SerialDescriptor.serialName] must be unique across all serializers in the same serialization context.
101-
* When defining a serializer in a library, it is recommended to use the fully qualified class name in [name]
102-
* to avoid conflicts with serializers defined by other libraries and client code.
103-
*
104-
* When serializing, the [Instant] value is formatted as a string using the specified [format]
105-
* in the [ZERO][UtcOffset.ZERO] UTC offset.
106-
*
107-
* This serializer is abstract and must be subclassed to provide a concrete serializer.
108-
* Example:
109-
* ```
110-
* // serializes LocalDateTime(2008, 6, 30, 11, 5, 30).toInstant(TimeZone.UTC)
111-
* // as the string "Mon, 30 Jun 2008 11:05:30 GMT"
112-
* object Rfc1123InstantSerializer : FormattedInstantSerializer(
113-
* "my.package.RFC1123", DateTimeComponents.Formats.RFC_1123
114-
* )
115-
* ```
116-
*
117-
* Note that [Instant] is [kotlinx.serialization.Serializable] by default,
118-
* so it is not necessary to create custom serializers when the format is not important.
119-
* Additionally, [InstantIso8601Serializer] is provided for the ISO 8601 format.
120-
*/
121-
public abstract class FormattedInstantSerializer(
122-
name: String,
123-
private val format: DateTimeFormat<DateTimeComponents>,
124-
) : KSerializer<Instant> {
125-
override val descriptor: SerialDescriptor =
126-
PrimitiveSerialDescriptor("kotlinx.datetime.Instant/serializer/$name", PrimitiveKind.STRING)
127-
128-
override fun deserialize(decoder: Decoder): Instant =
129-
Instant.parse(decoder.decodeString(), format)
130-
131-
override fun serialize(encoder: Encoder, value: Instant) {
132-
encoder.encodeString(value.format(format))
133-
}
134-
135-
@OptIn(ExperimentalSerializationApi::class)
136-
override fun toString(): String = descriptor.serialName
137-
}
13890

13991
/**
14092
* A serializer for [Instant] that uses the default [Instant.toString]/[Instant.parse].
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
/*
2+
* Copyright 2019-2025 JetBrains s.r.o. and contributors.
3+
* Use of this source code is governed by the Apache 2.0 License that can be found in the LICENSE.txt file.
4+
*/
5+
6+
package kotlinx.datetime.serializers
7+
8+
import kotlinx.datetime.format
9+
import kotlinx.datetime.format.DateTimeComponents
10+
import kotlinx.datetime.format.DateTimeFormat
11+
import kotlinx.datetime.parse
12+
import kotlinx.serialization.*
13+
import kotlinx.serialization.descriptors.*
14+
import kotlinx.serialization.encoding.Decoder
15+
import kotlinx.serialization.encoding.Encoder
16+
import kotlin.time.Instant
17+
18+
/**
19+
* An abstract serializer for [Instant] values that uses
20+
* a custom [DateTimeFormat] for serializing to and deserializing.
21+
*
22+
* [format] should be a format that includes enough components to unambiguously define a date, a time, and a UTC offset.
23+
* See [Instant.parse] for details of how deserialization is performed.
24+
*
25+
* [name] is the name of the serializer.
26+
* The [SerialDescriptor.serialName] of the resulting serializer is `kotlinx.datetime.Instant/serializer/`[name].
27+
* [SerialDescriptor.serialName] must be unique across all serializers in the same serialization context.
28+
* When defining a serializer in a library, it is recommended to use the fully qualified class name in [name]
29+
* to avoid conflicts with serializers defined by other libraries and client code.
30+
*
31+
* When serializing, the [Instant] value is formatted as a string using the specified [format]
32+
* in the [ZERO][UtcOffset.ZERO] UTC offset.
33+
*
34+
* This serializer is abstract and must be subclassed to provide a concrete serializer.
35+
* Example:
36+
* ```
37+
* // serializes LocalDateTime(2008, 6, 30, 11, 5, 30).toInstant(TimeZone.UTC)
38+
* // as the string "Mon, 30 Jun 2008 11:05:30 GMT"
39+
* object Rfc1123InstantSerializer : FormattedInstantSerializer(
40+
* "my.package.RFC1123", DateTimeComponents.Formats.RFC_1123
41+
* )
42+
* ```
43+
*
44+
* Note that `kotlinx.serialization` already provides [kotlinx.serialization.Serializable] support for [Instant],
45+
* so [Instant] values can be serialized and deserialized using the default serializer.
46+
*/
47+
public abstract class FormattedInstantSerializer(
48+
name: String,
49+
private val format: DateTimeFormat<DateTimeComponents>,
50+
) : KSerializer<Instant> {
51+
override val descriptor: SerialDescriptor =
52+
PrimitiveSerialDescriptor("kotlin.time.Instant/serializer/$name", PrimitiveKind.STRING)
53+
54+
override fun deserialize(decoder: Decoder): Instant =
55+
Instant.parse(decoder.decodeString(), format)
56+
57+
override fun serialize(encoder: Encoder, value: Instant) {
58+
encoder.encodeString(value.format(format))
59+
}
60+
61+
@OptIn(ExperimentalSerializationApi::class)
62+
override fun toString(): String = descriptor.serialName
63+
}

integration-testing/serialization/common/test/DeprecatedInstantSerializationTest.kt

Lines changed: 0 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
package kotlinx.datetime.serialization.test
88

99
import kotlinx.datetime.*
10-
import kotlinx.datetime.format.DateTimeComponents
1110
import kotlinx.datetime.serializers.*
1211
import kotlinx.serialization.*
1312
import kotlinx.serialization.json.*
@@ -88,26 +87,4 @@ class DeprecatedInstantSerializationTest {
8887
assertKSerializerName<Instant>("kotlinx.datetime.Instant", Json.serializersModule.serializer())
8988
defaultSerialization(Json.serializersModule.serializer())
9089
}
91-
92-
object Rfc1123InstantSerializer : FormattedInstantSerializer("RFC_1123", DateTimeComponents.Formats.RFC_1123)
93-
94-
@Test
95-
fun testCustomSerializer() {
96-
assertKSerializerName("kotlinx.datetime.Instant/serializer/RFC_1123", Rfc1123InstantSerializer)
97-
for ((instant, json) in listOf(
98-
Pair(Instant.fromEpochSeconds(1607505416),
99-
"\"Wed, 9 Dec 2020 09:16:56 GMT\""),
100-
Pair(Instant.fromEpochSeconds(-1607505416),
101-
"\"Thu, 23 Jan 1919 14:43:04 GMT\""),
102-
Pair(Instant.fromEpochSeconds(987654321),
103-
"\"Thu, 19 Apr 2001 04:25:21 GMT\""),
104-
)) {
105-
assertEquals(json, Json.encodeToString(Rfc1123InstantSerializer, instant))
106-
assertEquals(instant, Json.decodeFromString(Rfc1123InstantSerializer, json))
107-
}
108-
assertEquals("\"Thu, 19 Apr 2001 04:25:21 GMT\"",
109-
Json.encodeToString(Rfc1123InstantSerializer, Instant.fromEpochSeconds(987654321, 123456789)))
110-
assertEquals(Instant.fromEpochSeconds(987654321),
111-
Json.decodeFromString(Rfc1123InstantSerializer, "\"Thu, 19 Apr 2001 08:25:21 +0400\""))
112-
}
11390
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
/*
2+
* Copyright 2019-2025 JetBrains s.r.o. and contributors.
3+
* Use of this source code is governed by the Apache 2.0 License that can be found in the LICENSE.txt file.
4+
*/
5+
6+
package kotlinx.datetime.serialization.test
7+
8+
import kotlinx.datetime.format.DateTimeComponents
9+
import kotlinx.datetime.serializers.*
10+
import kotlinx.serialization.json.*
11+
import kotlin.test.*
12+
import kotlin.time.*
13+
14+
@OptIn(ExperimentalTime::class)
15+
class InstantSerializationTest {
16+
object Rfc1123InstantSerializer : FormattedInstantSerializer("RFC_1123", DateTimeComponents.Formats.RFC_1123)
17+
18+
@Test
19+
fun testCustomSerializer() {
20+
assertKSerializerName("kotlin.time.Instant/serializer/RFC_1123", Rfc1123InstantSerializer)
21+
for ((instant, json) in listOf(
22+
Pair(Instant.fromEpochSeconds(1607505416),
23+
"\"Wed, 9 Dec 2020 09:16:56 GMT\""),
24+
Pair(Instant.fromEpochSeconds(-1607505416),
25+
"\"Thu, 23 Jan 1919 14:43:04 GMT\""),
26+
Pair(Instant.fromEpochSeconds(987654321),
27+
"\"Thu, 19 Apr 2001 04:25:21 GMT\""),
28+
)) {
29+
assertEquals(json, Json.encodeToString(Rfc1123InstantSerializer, instant))
30+
assertEquals(instant, Json.decodeFromString(Rfc1123InstantSerializer, json))
31+
}
32+
assertEquals("\"Thu, 19 Apr 2001 04:25:21 GMT\"",
33+
Json.encodeToString(Rfc1123InstantSerializer, Instant.fromEpochSeconds(987654321, 123456789)))
34+
assertEquals(Instant.fromEpochSeconds(987654321),
35+
Json.decodeFromString(Rfc1123InstantSerializer, "\"Thu, 19 Apr 2001 08:25:21 +0400\""))
36+
}
37+
}

0 commit comments

Comments
 (0)