Skip to content

Commit 5e81277

Browse files
committed
Separate ISO and default serializers
Sometimes, `X.Formats.ISO` and `X.parse()`/`X.toString()` behave subtly differently; currently, it's the case for `LocalDateTime` and `LocalTime`. With this change, every entity that supports custom formats obtains a separate default serializer in addition to the ISO 8601 serializer, which now properly delegates to the corresponding `DateTimeFormat`. Fixes #351
1 parent a100ce8 commit 5e81277

28 files changed

+221
-108
lines changed

core/common/src/Instant.kt

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,7 @@ package kotlinx.datetime
77

88
import kotlinx.datetime.format.*
99
import kotlinx.datetime.internal.*
10-
import kotlinx.datetime.serializers.InstantIso8601Serializer
11-
import kotlinx.datetime.serializers.InstantComponentSerializer
10+
import kotlinx.datetime.serializers.*
1211
import kotlinx.serialization.Serializable
1312
import kotlin.time.*
1413

@@ -194,7 +193,7 @@ import kotlin.time.*
194193
*
195194
* @see LocalDateTime for a user-visible representation of moments in time in an unspecified time zone.
196195
*/
197-
@Serializable(with = InstantIso8601Serializer::class)
196+
@Serializable(with = InstantSerializer::class)
198197
public expect class Instant : Comparable<Instant> {
199198

200199
/**

core/common/src/LocalDate.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ import kotlinx.serialization.Serializable
6464
* @sample kotlinx.datetime.test.samples.LocalDateSamples.simpleParsingAndFormatting
6565
* @sample kotlinx.datetime.test.samples.LocalDateSamples.customFormat
6666
*/
67-
@Serializable(with = LocalDateIso8601Serializer::class)
67+
@Serializable(with = LocalDateSerializer::class)
6868
public expect class LocalDate : Comparable<LocalDate> {
6969
public companion object {
7070
/**

core/common/src/LocalDateTime.kt

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,7 @@
66
package kotlinx.datetime
77

88
import kotlinx.datetime.format.*
9-
import kotlinx.datetime.serializers.LocalDateTimeIso8601Serializer
10-
import kotlinx.datetime.serializers.LocalDateTimeComponentSerializer
9+
import kotlinx.datetime.serializers.*
1110
import kotlinx.serialization.Serializable
1211

1312
/**
@@ -105,7 +104,7 @@ import kotlinx.serialization.Serializable
105104
* @sample kotlinx.datetime.test.samples.LocalDateTimeSamples.simpleParsingAndFormatting
106105
* @sample kotlinx.datetime.test.samples.LocalDateTimeSamples.customFormat
107106
*/
108-
@Serializable(with = LocalDateTimeIso8601Serializer::class)
107+
@Serializable(with = LocalDateTimeSerializer::class)
109108
public expect class LocalDateTime : Comparable<LocalDateTime> {
110109
public companion object {
111110

core/common/src/LocalTime.kt

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,7 @@ package kotlinx.datetime
77

88
import kotlinx.datetime.LocalDate.Companion.parse
99
import kotlinx.datetime.format.*
10-
import kotlinx.datetime.serializers.LocalTimeIso8601Serializer
11-
import kotlinx.datetime.serializers.LocalTimeComponentSerializer
10+
import kotlinx.datetime.serializers.*
1211
import kotlinx.serialization.Serializable
1312

1413

@@ -76,7 +75,7 @@ import kotlinx.serialization.Serializable
7675
* @sample kotlinx.datetime.test.samples.LocalTimeSamples.simpleParsingAndFormatting
7776
* @sample kotlinx.datetime.test.samples.LocalTimeSamples.customFormat
7877
*/
79-
@Serializable(LocalTimeIso8601Serializer::class)
78+
@Serializable(LocalTimeSerializer::class)
8079
public expect class LocalTime : Comparable<LocalTime> {
8180
public companion object {
8281

core/common/src/UtcOffset.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
package kotlinx.datetime
77

88
import kotlinx.datetime.format.*
9-
import kotlinx.datetime.serializers.UtcOffsetSerializer
9+
import kotlinx.datetime.serializers.*
1010
import kotlinx.serialization.Serializable
1111

1212
/**
@@ -53,6 +53,7 @@ import kotlinx.serialization.Serializable
5353
* @sample kotlinx.datetime.test.samples.UtcOffsetSamples.simpleParsingAndFormatting
5454
* @sample kotlinx.datetime.test.samples.UtcOffsetSamples.customFormat
5555
*/
56+
@Suppress("DEPRECATION")
5657
@Serializable(with = UtcOffsetSerializer::class)
5758
public expect class UtcOffset {
5859
/**

core/common/src/serializers/InstantSerializers.kt

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
package kotlinx.datetime.serializers
77

88
import kotlinx.datetime.Instant
9+
import kotlinx.datetime.format
10+
import kotlinx.datetime.format.DateTimeComponents
911
import kotlinx.serialization.*
1012
import kotlinx.serialization.descriptors.*
1113
import kotlinx.serialization.encoding.*
@@ -15,19 +17,18 @@ import kotlinx.serialization.encoding.*
1517
*
1618
* JSON example: `"2020-12-09T09:16:56.000124Z"`
1719
*
18-
* @see Instant.toString
19-
* @see Instant.parse
20+
* @see DateTimeComponents.Formats.ISO_DATE_TIME_OFFSET
2021
*/
2122
public object InstantIso8601Serializer : KSerializer<Instant> {
2223

2324
override val descriptor: SerialDescriptor =
2425
PrimitiveSerialDescriptor("kotlinx.datetime.Instant", PrimitiveKind.STRING)
2526

2627
override fun deserialize(decoder: Decoder): Instant =
27-
Instant.parse(decoder.decodeString())
28+
Instant.parse(decoder.decodeString(), DateTimeComponents.Formats.ISO_DATE_TIME_OFFSET)
2829

2930
override fun serialize(encoder: Encoder, value: Instant) {
30-
encoder.encodeString(value.toString())
31+
encoder.encodeString(value.format(DateTimeComponents.Formats.ISO_DATE_TIME_OFFSET))
3132
}
3233

3334
}
@@ -75,3 +76,22 @@ public object InstantComponentSerializer : KSerializer<Instant> {
7576
}
7677

7778
}
79+
80+
/**
81+
* A serializer for [Instant] that uses the default [Instant.toString]/[Instant.parse].
82+
*
83+
* JSON example: `"2020-12-09T09:16:56.000124Z"`
84+
*/
85+
@PublishedApi internal object InstantSerializer : KSerializer<Instant> {
86+
87+
override val descriptor: SerialDescriptor =
88+
PrimitiveSerialDescriptor("kotlinx.datetime.Instant", PrimitiveKind.STRING)
89+
90+
override fun deserialize(decoder: Decoder): Instant =
91+
Instant.parse(decoder.decodeString())
92+
93+
override fun serialize(encoder: Encoder, value: Instant) {
94+
encoder.encodeString(value.toString())
95+
}
96+
97+
}

core/common/src/serializers/LocalDateSerializers.kt

Lines changed: 39 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
package kotlinx.datetime.serializers
77

88
import kotlinx.datetime.LocalDate
9+
import kotlinx.datetime.format.DateTimeFormat
910
import kotlinx.serialization.*
1011
import kotlinx.serialization.descriptors.*
1112
import kotlinx.serialization.encoding.*
@@ -15,22 +16,10 @@ import kotlinx.serialization.encoding.*
1516
*
1617
* JSON example: `"2020-01-01"`
1718
*
18-
* @see LocalDate.parse
19-
* @see LocalDate.toString
19+
* @see LocalDate.Formats.ISO
2020
*/
21-
public object LocalDateIso8601Serializer: KSerializer<LocalDate> {
22-
23-
override val descriptor: SerialDescriptor =
24-
PrimitiveSerialDescriptor("kotlinx.datetime.LocalDate", PrimitiveKind.STRING)
25-
26-
override fun deserialize(decoder: Decoder): LocalDate =
27-
LocalDate.parse(decoder.decodeString())
28-
29-
override fun serialize(encoder: Encoder, value: LocalDate) {
30-
encoder.encodeString(value.toString())
31-
}
32-
33-
}
21+
public object LocalDateIso8601Serializer : KSerializer<LocalDate>
22+
by DateTimeFormatSerializer(LocalDate.Formats.ISO, "kotlinx.datetime.LocalDate")
3423

3524
/**
3625
* A serializer for [LocalDate] that represents a value as its components.
@@ -76,3 +65,38 @@ public object LocalDateComponentSerializer: KSerializer<LocalDate> {
7665
}
7766

7867
}
68+
69+
/**
70+
* A serializer for [LocalDate] that uses the default [LocalDate.toString]/[LocalDate.parse].
71+
*
72+
* JSON example: `"2020-01-01"`
73+
*/
74+
@PublishedApi internal object LocalDateSerializer: KSerializer<LocalDate> {
75+
76+
override val descriptor: SerialDescriptor =
77+
PrimitiveSerialDescriptor("kotlinx.datetime.LocalDate", PrimitiveKind.STRING)
78+
79+
override fun deserialize(decoder: Decoder): LocalDate =
80+
LocalDate.parse(decoder.decodeString())
81+
82+
override fun serialize(encoder: Encoder, value: LocalDate) {
83+
encoder.encodeString(value.toString())
84+
}
85+
86+
}
87+
88+
/**
89+
* A general mechanism of implementing [KSerializer] instances using the given [string formats][DateTimeFormat].
90+
*/
91+
internal class DateTimeFormatSerializer<T>(val format: DateTimeFormat<T>, className: String): KSerializer<T> {
92+
override val descriptor: SerialDescriptor =
93+
PrimitiveSerialDescriptor(className, PrimitiveKind.STRING)
94+
95+
override fun serialize(encoder: Encoder, value: T) {
96+
encoder.encodeString(format.format(value))
97+
}
98+
99+
override fun deserialize(decoder: Decoder): T =
100+
format.parse(decoder.decodeString())
101+
102+
}

core/common/src/serializers/LocalDateTimeSerializers.kt

Lines changed: 22 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -15,22 +15,10 @@ import kotlinx.serialization.encoding.*
1515
*
1616
* JSON example: `"2007-12-31T23:59:01"`
1717
*
18-
* @see LocalDateTime.parse
19-
* @see LocalDateTime.toString
18+
* @see LocalDateTime.Formats.ISO
2019
*/
21-
public object LocalDateTimeIso8601Serializer: KSerializer<LocalDateTime> {
22-
23-
override val descriptor: SerialDescriptor =
24-
PrimitiveSerialDescriptor("kotlinx.datetime.LocalDateTime", PrimitiveKind.STRING)
25-
26-
override fun deserialize(decoder: Decoder): LocalDateTime =
27-
LocalDateTime.parse(decoder.decodeString())
28-
29-
override fun serialize(encoder: Encoder, value: LocalDateTime) {
30-
encoder.encodeString(value.toString())
31-
}
32-
33-
}
20+
public object LocalDateTimeIso8601Serializer : KSerializer<LocalDateTime>
21+
by DateTimeFormatSerializer(LocalDateTime.Formats.ISO, "kotlinx.datetime.LocalDateTime")
3422

3523
/**
3624
* A serializer for [LocalDateTime] that represents a value as its components.
@@ -98,3 +86,22 @@ public object LocalDateTimeComponentSerializer: KSerializer<LocalDateTime> {
9886
}
9987

10088
}
89+
90+
/**
91+
* A serializer for [LocalDateTime] that uses the default [LocalDateTime.toString]/[LocalDateTime.parse].
92+
*
93+
* JSON example: `"2007-12-31T23:59:01"`
94+
*/
95+
public object LocalDateTimeSerializer: KSerializer<LocalDateTime> {
96+
97+
override val descriptor: SerialDescriptor =
98+
PrimitiveSerialDescriptor("kotlinx.datetime.LocalDateTime", PrimitiveKind.STRING)
99+
100+
override fun deserialize(decoder: Decoder): LocalDateTime =
101+
LocalDateTime.parse(decoder.decodeString())
102+
103+
override fun serialize(encoder: Encoder, value: LocalDateTime) {
104+
encoder.encodeString(value.toString())
105+
}
106+
107+
}

core/common/src/serializers/LocalTimeSerializers.kt

Lines changed: 24 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -15,21 +15,10 @@ import kotlinx.serialization.encoding.*
1515
*
1616
* JSON example: `"12:01:03.999"`
1717
*
18-
* @see LocalDate.parse
19-
* @see LocalDate.toString
18+
* @see LocalTime.Formats.ISO
2019
*/
21-
public object LocalTimeIso8601Serializer : KSerializer<LocalTime> {
22-
23-
override val descriptor: SerialDescriptor =
24-
PrimitiveSerialDescriptor("kotlinx.datetime.LocalTime", PrimitiveKind.STRING)
25-
26-
override fun deserialize(decoder: Decoder): LocalTime =
27-
LocalTime.parse(decoder.decodeString())
28-
29-
override fun serialize(encoder: Encoder, value: LocalTime) {
30-
encoder.encodeString(value.toString())
31-
}
32-
}
20+
public object LocalTimeIso8601Serializer : KSerializer<LocalTime>
21+
by DateTimeFormatSerializer(LocalTime.Formats.ISO, "kotlinx.datetime.LocalTime")
3322

3423
/**
3524
* A serializer for [LocalTime] that represents a value as its components.
@@ -81,3 +70,24 @@ public object LocalTimeComponentSerializer : KSerializer<LocalTime> {
8170
}
8271
}
8372
}
73+
74+
/**
75+
* A serializer for [LocalTime] that uses the ISO 8601 representation.
76+
*
77+
* JSON example: `"12:01:03.999"`
78+
*
79+
* @see LocalDate.parse
80+
* @see LocalDate.toString
81+
*/
82+
public object LocalTimeSerializer : KSerializer<LocalTime> {
83+
84+
override val descriptor: SerialDescriptor =
85+
PrimitiveSerialDescriptor("kotlinx.datetime.LocalTime", PrimitiveKind.STRING)
86+
87+
override fun deserialize(decoder: Decoder): LocalTime =
88+
LocalTime.parse(decoder.decodeString())
89+
90+
override fun serialize(encoder: Encoder, value: LocalTime) {
91+
encoder.encodeString(value.toString())
92+
}
93+
}

core/common/src/serializers/TimeZoneSerializers.kt

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,7 @@
55

66
package kotlinx.datetime.serializers
77

8-
import kotlinx.datetime.FixedOffsetTimeZone
9-
import kotlinx.datetime.TimeZone
10-
import kotlinx.datetime.UtcOffset
8+
import kotlinx.datetime.*
119
import kotlinx.serialization.*
1210
import kotlinx.serialization.descriptors.*
1311
import kotlinx.serialization.encoding.*
@@ -54,13 +52,21 @@ public object FixedOffsetTimeZoneSerializer: KSerializer<FixedOffsetTimeZone> {
5452
}
5553

5654
/**
57-
* A serializer for [UtcOffset] that uses the extended ISO 8601 representation.
55+
* A serializer for [UtcOffset] that uses the ISO 8601 representation.
5856
*
5957
* JSON example: `"+02:00"`
6058
*
61-
* @see UtcOffset.parse
62-
* @see UtcOffset.toString
59+
* @see UtcOffset.Formats.ISO
6360
*/
61+
public object UtcOffsetIso8601Serializer : KSerializer<UtcOffset>
62+
by DateTimeFormatSerializer(UtcOffset.Formats.ISO, "kotlinx.datetime.UtcOffset")
63+
64+
/**
65+
* A serializer for [UtcOffset] that uses the default [UtcOffset.toString]/[UtcOffset.parse].
66+
*
67+
* JSON example: `"+02:00"`
68+
*/
69+
@Deprecated("Use UtcOffset.serializer() instead", ReplaceWith("UtcOffset.serializer()"))
6470
public object UtcOffsetSerializer: KSerializer<UtcOffset> {
6571

6672
override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("kotlinx.datetime.UtcOffset", PrimitiveKind.STRING)

core/commonJs/src/Instant.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,13 @@ import kotlinx.datetime.internal.JSJoda.ChronoUnit as jtChronoUnit
1313
import kotlinx.datetime.internal.JSJoda.ZonedDateTime as jtZonedDateTime
1414
import kotlinx.datetime.internal.safeAdd
1515
import kotlinx.datetime.internal.*
16-
import kotlinx.datetime.serializers.InstantIso8601Serializer
16+
import kotlinx.datetime.serializers.*
1717
import kotlinx.serialization.Serializable
1818
import kotlin.time.*
1919
import kotlin.time.Duration.Companion.nanoseconds
2020
import kotlin.time.Duration.Companion.seconds
2121

22-
@Serializable(with = InstantIso8601Serializer::class)
22+
@Serializable(with = InstantSerializer::class)
2323
public actual class Instant internal constructor(internal val value: jtInstant) : Comparable<Instant> {
2424

2525
public actual val epochSeconds: Long

core/commonJs/src/LocalDate.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,12 @@
66
package kotlinx.datetime
77

88
import kotlinx.datetime.format.*
9-
import kotlinx.datetime.serializers.LocalDateIso8601Serializer
9+
import kotlinx.datetime.serializers.*
1010
import kotlinx.serialization.Serializable
1111
import kotlinx.datetime.internal.JSJoda.LocalDate as jtLocalDate
1212
import kotlinx.datetime.internal.JSJoda.ChronoUnit as jtChronoUnit
1313

14-
@Serializable(with = LocalDateIso8601Serializer::class)
14+
@Serializable(with = LocalDateSerializer::class)
1515
public actual class LocalDate internal constructor(internal val value: jtLocalDate) : Comparable<LocalDate> {
1616
public actual companion object {
1717

core/commonJs/src/LocalDateTime.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,11 @@ package kotlinx.datetime
77
import kotlinx.datetime.format.*
88
import kotlinx.datetime.format.ISO_DATETIME
99
import kotlinx.datetime.format.LocalDateTimeFormat
10-
import kotlinx.datetime.serializers.LocalDateTimeIso8601Serializer
10+
import kotlinx.datetime.serializers.*
1111
import kotlinx.serialization.Serializable
1212
import kotlinx.datetime.internal.JSJoda.LocalDateTime as jtLocalDateTime
1313

14-
@Serializable(with = LocalDateTimeIso8601Serializer::class)
14+
@Serializable(with = LocalDateTimeSerializer::class)
1515
public actual class LocalDateTime internal constructor(internal val value: jtLocalDateTime) : Comparable<LocalDateTime> {
1616

1717
public actual constructor(year: Int, monthNumber: Int, dayOfMonth: Int, hour: Int, minute: Int, second: Int, nanosecond: Int) :

0 commit comments

Comments
 (0)