Skip to content

Commit 4b64100

Browse files
authored
Added support for the kotlin.time.Duration class as built-in
1 parent 74072c5 commit 4b64100

File tree

7 files changed

+104
-2
lines changed

7 files changed

+104
-2
lines changed

core/api/kotlinx-serialization-core.api

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,7 @@ public final class kotlinx/serialization/builtins/BuiltinSerializersKt {
175175
public static final fun serializer (Lkotlin/jvm/internal/LongCompanionObject;)Lkotlinx/serialization/KSerializer;
176176
public static final fun serializer (Lkotlin/jvm/internal/ShortCompanionObject;)Lkotlinx/serialization/KSerializer;
177177
public static final fun serializer (Lkotlin/jvm/internal/StringCompanionObject;)Lkotlinx/serialization/KSerializer;
178+
public static final fun serializer (Lkotlin/time/Duration$Companion;)Lkotlinx/serialization/KSerializer;
178179
}
179180

180181
public final class kotlinx/serialization/builtins/LongAsStringSerializer : kotlinx/serialization/KSerializer {
@@ -662,6 +663,15 @@ public final class kotlinx/serialization/internal/DoubleSerializer : kotlinx/ser
662663
public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V
663664
}
664665

666+
public final class kotlinx/serialization/internal/DurationSerializer : kotlinx/serialization/KSerializer {
667+
public static final field INSTANCE Lkotlinx/serialization/internal/DurationSerializer;
668+
public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object;
669+
public fun deserialize-5sfh64U (Lkotlinx/serialization/encoding/Decoder;)J
670+
public fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor;
671+
public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V
672+
public fun serialize-HG0u8IE (Lkotlinx/serialization/encoding/Encoder;J)V
673+
}
674+
665675
public final class kotlinx/serialization/internal/ElementMarker {
666676
public fun <init> (Lkotlinx/serialization/descriptors/SerialDescriptor;Lkotlin/jvm/functions/Function2;)V
667677
public final fun mark (I)V

core/commonMain/src/kotlinx/serialization/builtins/BuiltinSerializers.kt

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import kotlinx.serialization.*
99
import kotlinx.serialization.internal.*
1010
import kotlin.reflect.*
1111
import kotlinx.serialization.descriptors.*
12+
import kotlin.time.Duration
1213

1314
/**
1415
* Returns a nullable serializer for the given serializer of non-null type.
@@ -217,3 +218,11 @@ public fun UByte.Companion.serializer(): KSerializer<UByte> = UByteSerializer
217218
@ExperimentalSerializationApi
218219
@ExperimentalUnsignedTypes
219220
public fun UShort.Companion.serializer(): KSerializer<UShort> = UShortSerializer
221+
222+
/**
223+
* Returns serializer for [Duration].
224+
* It is serialized as a string that represents a duration in the ISO-8601 format.
225+
*
226+
* The result of serialization is similar to calling [Duration.toIsoString], for deserialization is [Duration.parseIsoString].
227+
*/
228+
public fun Duration.Companion.serializer(): KSerializer<Duration> = DurationSerializer
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
/*
2+
* Copyright 2017-2022 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
3+
*/
4+
package kotlinx.serialization.internal
5+
6+
import kotlinx.serialization.KSerializer
7+
import kotlinx.serialization.descriptors.PrimitiveKind
8+
import kotlinx.serialization.descriptors.SerialDescriptor
9+
import kotlinx.serialization.encoding.Decoder
10+
import kotlinx.serialization.encoding.Encoder
11+
import kotlin.time.Duration
12+
13+
14+
@PublishedApi
15+
internal object DurationSerializer : KSerializer<Duration> {
16+
override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("kotlin.time.Duration", PrimitiveKind.STRING)
17+
18+
override fun serialize(encoder: Encoder, value: Duration) {
19+
encoder.encodeString(value.toIsoString())
20+
}
21+
22+
override fun deserialize(decoder: Decoder): Duration {
23+
return Duration.parseIsoString(decoder.decodeString())
24+
}
25+
}

core/commonMain/src/kotlinx/serialization/internal/Primitives.kt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import kotlinx.serialization.descriptors.*
1313
import kotlinx.serialization.encoding.*
1414
import kotlin.native.concurrent.*
1515
import kotlin.reflect.*
16+
import kotlin.time.Duration
1617

1718
@SharedImmutable
1819
private val BUILTIN_SERIALIZERS = mapOf(
@@ -33,7 +34,8 @@ private val BUILTIN_SERIALIZERS = mapOf(
3334
ByteArray::class to ByteArraySerializer(),
3435
Boolean::class to Boolean.serializer(),
3536
BooleanArray::class to BooleanArraySerializer(),
36-
Unit::class to Unit.serializer()
37+
Unit::class to Unit.serializer(),
38+
Duration::class to Duration.serializer()
3739
)
3840

3941
internal class PrimitiveSerialDescriptor(

core/commonTest/src/kotlinx/serialization/BasicTypesSerializationTest.kt

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,13 @@
44

55
package kotlinx.serialization
66

7+
import kotlinx.serialization.builtins.serializer
78
import kotlinx.serialization.descriptors.*
89
import kotlinx.serialization.encoding.*
910
import kotlinx.serialization.encoding.CompositeDecoder.Companion.UNKNOWN_NAME
10-
import kotlinx.serialization.internal.*
1111
import kotlinx.serialization.modules.*
1212
import kotlin.test.*
13+
import kotlin.time.Duration
1314

1415
/*
1516
* Test ensures that type that aggregate all basic (primitive/collection/maps/arrays)
@@ -170,4 +171,24 @@ class BasicTypesSerializationTest {
170171
assertEquals(umbrellaInstance, other)
171172
assertNotSame(umbrellaInstance, other)
172173
}
174+
175+
@Test
176+
fun testEncodeDuration() {
177+
val sb = StringBuilder()
178+
val out = KeyValueOutput(sb)
179+
180+
val duration = Duration.parseIsoString("P4DT12H30M5S")
181+
out.encodeSerializableValue(Duration.serializer(), duration)
182+
183+
assertEquals("\"${duration.toIsoString()}\"", sb.toString())
184+
}
185+
186+
@Test
187+
fun testDecodeDuration() {
188+
val durationString = "P4DT12H30M5S"
189+
val inp = KeyValueInput(Parser(StringReader("\"$durationString\"")))
190+
val other = inp.decodeSerializableValue(Duration.serializer())
191+
assertEquals(Duration.parseIsoString(durationString), other)
192+
}
193+
173194
}

formats/json/commonTest/src/kotlinx/serialization/SerializersLookupTest.kt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import kotlinx.serialization.modules.*
1414
import kotlinx.serialization.test.*
1515
import kotlin.reflect.*
1616
import kotlin.test.*
17+
import kotlin.time.Duration
1718

1819
@Suppress("RemoveExplicitTypeArguments") // This is exactly what's being tested
1920
class SerializersLookupTest : JsonTestBase() {
@@ -243,6 +244,13 @@ class SerializersLookupTest : JsonTestBase() {
243244
}
244245
}
245246

247+
// TODO uncomment when Kotlin 1.7.20 is released
248+
// @Test
249+
// fun testLookupDuration() = noLegacyJs {
250+
// assertNotNull(serializerOrNull(typeOf<Duration>()))
251+
// assertSame(Duration.serializer(), serializer<Duration>())
252+
// }
253+
246254
private inline fun <reified T> assertSerializedWithType(
247255
expected: String,
248256
value: T,
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
/*
2+
* Copyright 2017-2022 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
3+
*/
4+
5+
package kotlinx.serialization.features
6+
7+
import kotlinx.serialization.Serializable
8+
import kotlinx.serialization.json.JsonTestBase
9+
import kotlin.test.Test
10+
import kotlin.time.Duration
11+
import kotlin.time.DurationUnit
12+
import kotlin.time.toDuration
13+
14+
15+
class DurationTest : JsonTestBase() {
16+
// TODO uncomment when Kotlin 1.7.20 is released
17+
// @Serializable
18+
// data class DurationHolder(val duration: Duration)
19+
// @Test
20+
// fun testDuration() {
21+
// assertJsonFormAndRestored(
22+
// DurationHolder.serializer(),
23+
// DurationHolder(1000.toDuration(DurationUnit.SECONDS)),
24+
// """{"duration":"PT16M40S"}"""
25+
// )
26+
// }
27+
}

0 commit comments

Comments
 (0)