Skip to content

Commit 0f5f48e

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 5dea535 commit 0f5f48e

24 files changed

+223
-102
lines changed

core/common/src/Instant.kt

+3-3
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

@@ -189,12 +188,13 @@ import kotlin.time.*
189188
* ```
190189
*
191190
* Additionally, there are several `kotlinx-serialization` serializers for [Instant]:
191+
* - The default serializer, delegating to [toString] and [parse].
192192
* - [InstantIso8601Serializer] for the ISO 8601 extended format.
193193
* - [InstantComponentSerializer] for an object with components.
194194
*
195195
* @see LocalDateTime for a user-visible representation of moments in time in an unspecified time zone.
196196
*/
197-
@Serializable(with = InstantIso8601Serializer::class)
197+
@Serializable(with = InstantSerializer::class)
198198
public expect class Instant : Comparable<Instant> {
199199

200200
/**

core/common/src/LocalDate.kt

+2-1
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ import kotlin.internal.*
5757
* See sample 4.
5858
*
5959
* Additionally, there are several `kotlinx-serialization` serializers for [LocalDate]:
60+
* - The default serializer, delegating to [toString] and [parse].
6061
* - [LocalDateIso8601Serializer] for the ISO 8601 extended format.
6162
* - [LocalDateComponentSerializer] for an object with components.
6263
*
@@ -65,7 +66,7 @@ import kotlin.internal.*
6566
* @sample kotlinx.datetime.test.samples.LocalDateSamples.simpleParsingAndFormatting
6667
* @sample kotlinx.datetime.test.samples.LocalDateSamples.customFormat
6768
*/
68-
@Serializable(with = LocalDateIso8601Serializer::class)
69+
@Serializable(with = LocalDateSerializer::class)
6970
public expect class LocalDate : Comparable<LocalDate> {
7071
public companion object {
7172
/**

core/common/src/LocalDateTime.kt

+3-3
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,7 @@
88
package kotlinx.datetime
99

1010
import kotlinx.datetime.format.*
11-
import kotlinx.datetime.serializers.LocalDateTimeIso8601Serializer
12-
import kotlinx.datetime.serializers.LocalDateTimeComponentSerializer
11+
import kotlinx.datetime.serializers.*
1312
import kotlinx.serialization.Serializable
1413
import kotlin.internal.*
1514
import kotlin.jvm.JvmMultifileClass
@@ -99,6 +98,7 @@ import kotlin.jvm.JvmName
9998
* See sample 4.
10099
*
101100
* Additionally, there are several `kotlinx-serialization` serializers for [LocalDateTime]:
101+
* - The default serializer, delegating to [toString] and [parse].
102102
* - [LocalDateTimeIso8601Serializer] for the ISO 8601 extended format.
103103
* - [LocalDateTimeComponentSerializer] for an object with components.
104104
*
@@ -110,7 +110,7 @@ import kotlin.jvm.JvmName
110110
* @sample kotlinx.datetime.test.samples.LocalDateTimeSamples.simpleParsingAndFormatting
111111
* @sample kotlinx.datetime.test.samples.LocalDateTimeSamples.customFormat
112112
*/
113-
@Serializable(with = LocalDateTimeIso8601Serializer::class)
113+
@Serializable(with = LocalDateTimeSerializer::class)
114114
public expect class LocalDateTime : Comparable<LocalDateTime> {
115115
public companion object {
116116

core/common/src/LocalTime.kt

+3-3
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,7 @@
88
package kotlinx.datetime
99

1010
import kotlinx.datetime.format.*
11-
import kotlinx.datetime.serializers.LocalTimeIso8601Serializer
12-
import kotlinx.datetime.serializers.LocalTimeComponentSerializer
11+
import kotlinx.datetime.serializers.*
1312
import kotlinx.serialization.Serializable
1413
import kotlin.internal.*
1514
import kotlin.jvm.JvmMultifileClass
@@ -72,6 +71,7 @@ import kotlin.jvm.JvmName
7271
* See sample 4.
7372
*
7473
* Additionally, there are several `kotlinx-serialization` serializers for [LocalTime]:
74+
* - The default serializer, delegating to [toString] and [parse].
7575
* - [LocalTimeIso8601Serializer] for the ISO 8601 extended format,
7676
* - [LocalTimeComponentSerializer] for an object with components.
7777
*
@@ -80,7 +80,7 @@ import kotlin.jvm.JvmName
8080
* @sample kotlinx.datetime.test.samples.LocalTimeSamples.simpleParsingAndFormatting
8181
* @sample kotlinx.datetime.test.samples.LocalTimeSamples.customFormat
8282
*/
83-
@Serializable(LocalTimeIso8601Serializer::class)
83+
@Serializable(LocalTimeSerializer::class)
8484
public expect class LocalTime : Comparable<LocalTime> {
8585
public companion object {
8686

core/common/src/UtcOffset.kt

+4-2
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
/**
@@ -47,12 +47,14 @@ import kotlinx.serialization.Serializable
4747
* [parse] and [UtcOffset.format] both support custom formats created with [Format] or defined in [Formats].
4848
* See sample 3.
4949
*
50-
* To serialize and deserialize [UtcOffset] values with `kotlinx-serialization`, use the [UtcOffsetSerializer].
50+
* To serialize and deserialize [UtcOffset] values with `kotlinx-serialization`, use the default serializer,
51+
* or [UtcOffsetIso8601Serializer] for the ISO 8601 format explicitly.
5152
*
5253
* @sample kotlinx.datetime.test.samples.UtcOffsetSamples.construction
5354
* @sample kotlinx.datetime.test.samples.UtcOffsetSamples.simpleParsingAndFormatting
5455
* @sample kotlinx.datetime.test.samples.UtcOffsetSamples.customFormat
5556
*/
57+
@Suppress("DEPRECATION")
5658
@Serializable(with = UtcOffsetSerializer::class)
5759
public expect class UtcOffset {
5860
/**

core/common/src/serializers/InstantSerializers.kt

+22-4
Original file line numberDiff line numberDiff line change
@@ -17,19 +17,18 @@ import kotlinx.serialization.encoding.*
1717
*
1818
* JSON example: `"2020-12-09T09:16:56.000124Z"`
1919
*
20-
* @see Instant.toString
21-
* @see Instant.parse
20+
* @see DateTimeComponents.Formats.ISO_DATE_TIME_OFFSET
2221
*/
2322
public object InstantIso8601Serializer : KSerializer<Instant> {
2423

2524
override val descriptor: SerialDescriptor =
2625
PrimitiveSerialDescriptor("kotlinx.datetime.Instant", PrimitiveKind.STRING)
2726

2827
override fun deserialize(decoder: Decoder): Instant =
29-
Instant.parse(decoder.decodeString())
28+
Instant.parse(decoder.decodeString(), DateTimeComponents.Formats.ISO_DATE_TIME_OFFSET)
3029

3130
override fun serialize(encoder: Encoder, value: Instant) {
32-
encoder.encodeString(value.toString())
31+
encoder.encodeString(value.format(DateTimeComponents.Formats.ISO_DATE_TIME_OFFSET))
3332
}
3433

3534
}
@@ -125,3 +124,22 @@ public abstract class FormattedInstantSerializer(
125124
@OptIn(ExperimentalSerializationApi::class)
126125
override fun toString(): String = descriptor.serialName
127126
}
127+
128+
/**
129+
* A serializer for [Instant] that uses the default [Instant.toString]/[Instant.parse].
130+
*
131+
* JSON example: `"2020-12-09T09:16:56.000124Z"`
132+
*/
133+
@PublishedApi internal object InstantSerializer : KSerializer<Instant> {
134+
135+
override val descriptor: SerialDescriptor =
136+
PrimitiveSerialDescriptor("kotlinx.datetime.Instant", PrimitiveKind.STRING)
137+
138+
override fun deserialize(decoder: Decoder): Instant =
139+
Instant.parse(decoder.decodeString())
140+
141+
override fun serialize(encoder: Encoder, value: Instant) {
142+
encoder.encodeString(value.toString())
143+
}
144+
145+
}

core/common/src/serializers/LocalDateSerializers.kt

+23-15
Original file line numberDiff line numberDiff line change
@@ -16,22 +16,10 @@ import kotlinx.serialization.encoding.*
1616
*
1717
* JSON example: `"2020-01-01"`
1818
*
19-
* @see LocalDate.parse
20-
* @see LocalDate.toString
19+
* @see LocalDate.Formats.ISO
2120
*/
22-
public object LocalDateIso8601Serializer: KSerializer<LocalDate> {
23-
24-
override val descriptor: SerialDescriptor =
25-
PrimitiveSerialDescriptor("kotlinx.datetime.LocalDate", PrimitiveKind.STRING)
26-
27-
override fun deserialize(decoder: Decoder): LocalDate =
28-
LocalDate.parse(decoder.decodeString())
29-
30-
override fun serialize(encoder: Encoder, value: LocalDate) {
31-
encoder.encodeString(value.toString())
32-
}
33-
34-
}
21+
public object LocalDateIso8601Serializer : KSerializer<LocalDate>
22+
by LocalDate.Formats.ISO.asKSerializer("kotlinx.datetime.LocalDate")
3523

3624
/**
3725
* A serializer for [LocalDate] that represents a value as its components.
@@ -117,3 +105,23 @@ internal fun <T> DateTimeFormat<T>.asKSerializer(serialName: String): KSerialize
117105

118106
override fun toString(): String = serialName
119107
}
108+
109+
/**
110+
* A serializer for [LocalDate] that uses the default [LocalDate.toString]/[LocalDate.parse].
111+
*
112+
* JSON example: `"2020-01-01"`
113+
*/
114+
@PublishedApi
115+
internal object LocalDateSerializer: KSerializer<LocalDate> {
116+
117+
override val descriptor: SerialDescriptor =
118+
PrimitiveSerialDescriptor("kotlinx.datetime.LocalDate", PrimitiveKind.STRING)
119+
120+
override fun deserialize(decoder: Decoder): LocalDate =
121+
LocalDate.parse(decoder.decodeString())
122+
123+
override fun serialize(encoder: Encoder, value: LocalDate) {
124+
encoder.encodeString(value.toString())
125+
}
126+
127+
}

core/common/src/serializers/LocalDateTimeSerializers.kt

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

3624
/**
3725
* A serializer for [LocalDateTime] that represents a value as its components.
@@ -130,3 +118,23 @@ public object LocalDateTimeComponentSerializer: KSerializer<LocalDateTime> {
130118
public abstract class FormattedLocalDateTimeSerializer(
131119
name: String, format: DateTimeFormat<LocalDateTime>
132120
) : KSerializer<LocalDateTime> by format.asKSerializer("kotlinx.datetime.LocalDateTime serializer $name")
121+
122+
/**
123+
* A serializer for [LocalDateTime] that uses the default [LocalDateTime.toString]/[LocalDateTime.parse].
124+
*
125+
* JSON example: `"2007-12-31T23:59:01"`
126+
*/
127+
@PublishedApi
128+
internal object LocalDateTimeSerializer: KSerializer<LocalDateTime> {
129+
130+
override val descriptor: SerialDescriptor =
131+
PrimitiveSerialDescriptor("kotlinx.datetime.LocalDateTime", PrimitiveKind.STRING)
132+
133+
override fun deserialize(decoder: Decoder): LocalDateTime =
134+
LocalDateTime.parse(decoder.decodeString())
135+
136+
override fun serialize(encoder: Encoder, value: LocalDateTime) {
137+
encoder.encodeString(value.toString())
138+
}
139+
140+
}

core/common/src/serializers/LocalTimeSerializers.kt

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

3524
/**
3625
* A serializer for [LocalTime] that represents a value as its components.
@@ -109,3 +98,25 @@ public object LocalTimeComponentSerializer : KSerializer<LocalTime> {
10998
public abstract class FormattedLocalTimeSerializer(
11099
name: String, format: DateTimeFormat<LocalTime>
111100
) : KSerializer<LocalTime> by format.asKSerializer("kotlinx.datetime.LocalTime serializer $name")
101+
102+
/**
103+
* A serializer for [LocalTime] that uses the ISO 8601 representation.
104+
*
105+
* JSON example: `"12:01:03.999"`
106+
*
107+
* @see LocalDate.parse
108+
* @see LocalDate.toString
109+
*/
110+
@PublishedApi
111+
internal object LocalTimeSerializer : KSerializer<LocalTime> {
112+
113+
override val descriptor: SerialDescriptor =
114+
PrimitiveSerialDescriptor("kotlinx.datetime.LocalTime", PrimitiveKind.STRING)
115+
116+
override fun deserialize(decoder: Decoder): LocalTime =
117+
LocalTime.parse(decoder.decodeString())
118+
119+
override fun serialize(encoder: Encoder, value: LocalTime) {
120+
encoder.encodeString(value.toString())
121+
}
122+
}

core/common/src/serializers/TimeZoneSerializers.kt

+10-2
Original file line numberDiff line numberDiff line change
@@ -57,9 +57,17 @@ public object FixedOffsetTimeZoneSerializer: KSerializer<FixedOffsetTimeZone> {
5757
*
5858
* JSON example: `"+02:00"`
5959
*
60-
* @see UtcOffset.parse
61-
* @see UtcOffset.toString
60+
* @see UtcOffset.Formats.ISO
6261
*/
62+
public object UtcOffsetIso8601Serializer : KSerializer<UtcOffset>
63+
by UtcOffset.Formats.ISO.asKSerializer("kotlinx.datetime.UtcOffset")
64+
65+
/**
66+
* A serializer for [UtcOffset] that uses the default [UtcOffset.toString]/[UtcOffset.parse].
67+
*
68+
* JSON example: `"+02:00"`
69+
*/
70+
@Deprecated("Use UtcOffset.serializer() instead", ReplaceWith("UtcOffset.serializer()"))
6371
public object UtcOffsetSerializer: KSerializer<UtcOffset> {
6472

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

core/commonKotlin/src/Instant.kt

+2-2
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ package kotlinx.datetime
1010

1111
import kotlinx.datetime.format.*
1212
import kotlinx.datetime.internal.*
13-
import kotlinx.datetime.serializers.InstantIso8601Serializer
13+
import kotlinx.datetime.serializers.*
1414
import kotlinx.serialization.Serializable
1515
import kotlin.time.*
1616
import kotlin.time.Duration.Companion.nanoseconds
@@ -26,7 +26,7 @@ private const val MIN_SECOND = -31557014167219200L // -1000000000-01-01T00:00:00
2626
*/
2727
private const val MAX_SECOND = 31556889864403199L // +1000000000-12-31T23:59:59
2828

29-
@Serializable(with = InstantIso8601Serializer::class)
29+
@Serializable(with = InstantSerializer::class)
3030
public actual class Instant internal constructor(public actual val epochSeconds: Long, public actual val nanosecondsOfSecond: Int) : Comparable<Instant> {
3131

3232
init {

core/commonKotlin/src/LocalDate.kt

+2-2
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import kotlinx.datetime.format.*
1212
import kotlinx.datetime.internal.*
1313
import kotlinx.datetime.internal.safeAdd
1414
import kotlinx.datetime.internal.safeMultiply
15-
import kotlinx.datetime.serializers.LocalDateIso8601Serializer
15+
import kotlinx.datetime.serializers.*
1616
import kotlinx.serialization.Serializable
1717
import kotlin.math.*
1818

@@ -22,7 +22,7 @@ internal const val YEAR_MAX = 999_999_999
2222
private fun isValidYear(year: Int): Boolean =
2323
year >= YEAR_MIN && year <= YEAR_MAX
2424

25-
@Serializable(with = LocalDateIso8601Serializer::class)
25+
@Serializable(with = LocalDateSerializer::class)
2626
public actual class LocalDate actual constructor(public actual val year: Int, month: Int, public actual val day: Int) : Comparable<LocalDate> {
2727

2828
private val _month: Int = month

core/commonKotlin/src/LocalDateTime.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import kotlinx.datetime.internal.*
1313
import kotlinx.datetime.serializers.*
1414
import kotlinx.serialization.*
1515

16-
@Serializable(with = LocalDateTimeIso8601Serializer::class)
16+
@Serializable(with = LocalDateTimeSerializer::class)
1717
public actual class LocalDateTime
1818
public actual constructor(public actual val date: LocalDate, public actual val time: LocalTime) : Comparable<LocalDateTime> {
1919
public actual companion object {

core/commonKotlin/src/LocalTime.kt

+2-2
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,10 @@ package kotlinx.datetime
1010

1111
import kotlinx.datetime.internal.*
1212
import kotlinx.datetime.format.*
13-
import kotlinx.datetime.serializers.LocalTimeIso8601Serializer
13+
import kotlinx.datetime.serializers.*
1414
import kotlinx.serialization.Serializable
1515

16-
@Serializable(LocalTimeIso8601Serializer::class)
16+
@Serializable(LocalTimeSerializer::class)
1717
public actual class LocalTime actual constructor(
1818
public actual val hour: Int,
1919
public actual val minute: Int,

0 commit comments

Comments
 (0)