From aec4a6cada3bb7d00114dabfb1e7677c8d9ed4d6 Mon Sep 17 00:00:00 2001 From: Dmitry Khalanskiy Date: Mon, 13 May 2024 15:47:12 +0200 Subject: [PATCH] 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 --- core/common/src/Instant.kt | 6 +-- core/common/src/LocalDate.kt | 3 +- core/common/src/LocalDateTime.kt | 6 +-- core/common/src/LocalTime.kt | 6 +-- core/common/src/UtcOffset.kt | 6 ++- .../src/serializers/InstantSerializers.kt | 26 ++++++++-- .../src/serializers/LocalDateSerializers.kt | 38 ++++++++------ .../serializers/LocalDateTimeSerializers.kt | 38 ++++++++------ .../src/serializers/LocalTimeSerializers.kt | 39 +++++++++----- .../src/serializers/TimeZoneSerializers.kt | 12 ++++- core/commonJs/src/Instant.kt | 4 +- core/commonJs/src/LocalDate.kt | 4 +- core/commonJs/src/LocalDateTime.kt | 4 +- core/commonJs/src/LocalTime.kt | 4 +- core/commonJs/src/UtcOffset.kt | 3 +- core/jvm/src/Instant.kt | 4 +- core/jvm/src/LocalDate.kt | 4 +- core/jvm/src/LocalDateTime.kt | 4 +- core/jvm/src/LocalTime.kt | 4 +- core/jvm/src/UtcOffsetJvm.kt | 3 +- core/native/src/Instant.kt | 4 +- core/native/src/LocalDate.kt | 4 +- core/native/src/LocalDateTime.kt | 2 +- core/native/src/LocalTime.kt | 4 +- core/native/src/UtcOffset.kt | 3 +- .../common/test/InstantSerializationTest.kt | 26 ++++++++-- .../test/LocalDateTimeSerializationTest.kt | 15 +++++- .../common/test/LocalTimeSerializationTest.kt | 16 +++++- .../common/test/UtcOffsetSerializationTest.kt | 52 +++++++++++++------ 29 files changed, 233 insertions(+), 111 deletions(-) diff --git a/core/common/src/Instant.kt b/core/common/src/Instant.kt index 36fd40d28..7437bd1cf 100644 --- a/core/common/src/Instant.kt +++ b/core/common/src/Instant.kt @@ -7,8 +7,7 @@ package kotlinx.datetime import kotlinx.datetime.format.* import kotlinx.datetime.internal.* -import kotlinx.datetime.serializers.InstantIso8601Serializer -import kotlinx.datetime.serializers.InstantComponentSerializer +import kotlinx.datetime.serializers.* import kotlinx.serialization.Serializable import kotlin.time.* @@ -189,12 +188,13 @@ import kotlin.time.* * ``` * * Additionally, there are several `kotlinx-serialization` serializers for [Instant]: + * - The default serializer, delegating to [toString] and [parse]. * - [InstantIso8601Serializer] for the ISO 8601 extended format. * - [InstantComponentSerializer] for an object with components. * * @see LocalDateTime for a user-visible representation of moments in time in an unspecified time zone. */ -@Serializable(with = InstantIso8601Serializer::class) +@Serializable(with = InstantSerializer::class) public expect class Instant : Comparable { /** diff --git a/core/common/src/LocalDate.kt b/core/common/src/LocalDate.kt index 0146d6f95..eb80dc01a 100644 --- a/core/common/src/LocalDate.kt +++ b/core/common/src/LocalDate.kt @@ -56,6 +56,7 @@ import kotlinx.serialization.Serializable * See sample 4. * * Additionally, there are several `kotlinx-serialization` serializers for [LocalDate]: + * - The default serializer, delegating to [toString] and [parse]. * - [LocalDateIso8601Serializer] for the ISO 8601 extended format. * - [LocalDateComponentSerializer] for an object with components. * @@ -64,7 +65,7 @@ import kotlinx.serialization.Serializable * @sample kotlinx.datetime.test.samples.LocalDateSamples.simpleParsingAndFormatting * @sample kotlinx.datetime.test.samples.LocalDateSamples.customFormat */ -@Serializable(with = LocalDateIso8601Serializer::class) +@Serializable(with = LocalDateSerializer::class) public expect class LocalDate : Comparable { public companion object { /** diff --git a/core/common/src/LocalDateTime.kt b/core/common/src/LocalDateTime.kt index db4533b8c..09691d3bc 100644 --- a/core/common/src/LocalDateTime.kt +++ b/core/common/src/LocalDateTime.kt @@ -6,8 +6,7 @@ package kotlinx.datetime import kotlinx.datetime.format.* -import kotlinx.datetime.serializers.LocalDateTimeIso8601Serializer -import kotlinx.datetime.serializers.LocalDateTimeComponentSerializer +import kotlinx.datetime.serializers.* import kotlinx.serialization.Serializable /** @@ -94,6 +93,7 @@ import kotlinx.serialization.Serializable * See sample 4. * * Additionally, there are several `kotlinx-serialization` serializers for [LocalDateTime]: + * - The default serializer, delegating to [toString] and [parse]. * - [LocalDateTimeIso8601Serializer] for the ISO 8601 extended format. * - [LocalDateTimeComponentSerializer] for an object with components. * @@ -105,7 +105,7 @@ import kotlinx.serialization.Serializable * @sample kotlinx.datetime.test.samples.LocalDateTimeSamples.simpleParsingAndFormatting * @sample kotlinx.datetime.test.samples.LocalDateTimeSamples.customFormat */ -@Serializable(with = LocalDateTimeIso8601Serializer::class) +@Serializable(with = LocalDateTimeSerializer::class) public expect class LocalDateTime : Comparable { public companion object { diff --git a/core/common/src/LocalTime.kt b/core/common/src/LocalTime.kt index 695a1090e..f27d64011 100644 --- a/core/common/src/LocalTime.kt +++ b/core/common/src/LocalTime.kt @@ -7,8 +7,7 @@ package kotlinx.datetime import kotlinx.datetime.LocalDate.Companion.parse import kotlinx.datetime.format.* -import kotlinx.datetime.serializers.LocalTimeIso8601Serializer -import kotlinx.datetime.serializers.LocalTimeComponentSerializer +import kotlinx.datetime.serializers.* import kotlinx.serialization.Serializable @@ -68,6 +67,7 @@ import kotlinx.serialization.Serializable * See sample 4. * * Additionally, there are several `kotlinx-serialization` serializers for [LocalTime]: + * - The default serializer, delegating to [toString] and [parse]. * - [LocalTimeIso8601Serializer] for the ISO 8601 extended format, * - [LocalTimeComponentSerializer] for an object with components. * @@ -76,7 +76,7 @@ import kotlinx.serialization.Serializable * @sample kotlinx.datetime.test.samples.LocalTimeSamples.simpleParsingAndFormatting * @sample kotlinx.datetime.test.samples.LocalTimeSamples.customFormat */ -@Serializable(LocalTimeIso8601Serializer::class) +@Serializable(LocalTimeSerializer::class) public expect class LocalTime : Comparable { public companion object { diff --git a/core/common/src/UtcOffset.kt b/core/common/src/UtcOffset.kt index 97c5e4103..8a5cc7696 100644 --- a/core/common/src/UtcOffset.kt +++ b/core/common/src/UtcOffset.kt @@ -6,7 +6,7 @@ package kotlinx.datetime import kotlinx.datetime.format.* -import kotlinx.datetime.serializers.UtcOffsetSerializer +import kotlinx.datetime.serializers.* import kotlinx.serialization.Serializable /** @@ -47,12 +47,14 @@ import kotlinx.serialization.Serializable * [parse] and [UtcOffset.format] both support custom formats created with [Format] or defined in [Formats]. * See sample 3. * - * To serialize and deserialize [UtcOffset] values with `kotlinx-serialization`, use the [UtcOffsetSerializer]. + * To serialize and deserialize [UtcOffset] values with `kotlinx-serialization`, use the default serializer, + * or [UtcOffsetIso8601Serializer] for the ISO 8601 format explicitly. * * @sample kotlinx.datetime.test.samples.UtcOffsetSamples.construction * @sample kotlinx.datetime.test.samples.UtcOffsetSamples.simpleParsingAndFormatting * @sample kotlinx.datetime.test.samples.UtcOffsetSamples.customFormat */ +@Suppress("DEPRECATION") @Serializable(with = UtcOffsetSerializer::class) public expect class UtcOffset { /** diff --git a/core/common/src/serializers/InstantSerializers.kt b/core/common/src/serializers/InstantSerializers.kt index 3e5db5a9a..e46a0c449 100644 --- a/core/common/src/serializers/InstantSerializers.kt +++ b/core/common/src/serializers/InstantSerializers.kt @@ -17,8 +17,7 @@ import kotlinx.serialization.encoding.* * * JSON example: `"2020-12-09T09:16:56.000124Z"` * - * @see Instant.toString - * @see Instant.parse + * @see DateTimeComponents.Formats.ISO_DATE_TIME_OFFSET */ public object InstantIso8601Serializer : KSerializer { @@ -26,10 +25,10 @@ public object InstantIso8601Serializer : KSerializer { PrimitiveSerialDescriptor("kotlinx.datetime.Instant", PrimitiveKind.STRING) override fun deserialize(decoder: Decoder): Instant = - Instant.parse(decoder.decodeString()) + Instant.parse(decoder.decodeString(), DateTimeComponents.Formats.ISO_DATE_TIME_OFFSET) override fun serialize(encoder: Encoder, value: Instant) { - encoder.encodeString(value.toString()) + encoder.encodeString(value.format(DateTimeComponents.Formats.ISO_DATE_TIME_OFFSET)) } } @@ -125,3 +124,22 @@ public abstract class FormattedInstantSerializer( @OptIn(ExperimentalSerializationApi::class) override fun toString(): String = descriptor.serialName } + +/** + * A serializer for [Instant] that uses the default [Instant.toString]/[Instant.parse]. + * + * JSON example: `"2020-12-09T09:16:56.000124Z"` + */ +@PublishedApi internal object InstantSerializer : KSerializer { + + override val descriptor: SerialDescriptor = + PrimitiveSerialDescriptor("kotlinx.datetime.Instant", PrimitiveKind.STRING) + + override fun deserialize(decoder: Decoder): Instant = + Instant.parse(decoder.decodeString()) + + override fun serialize(encoder: Encoder, value: Instant) { + encoder.encodeString(value.toString()) + } + +} diff --git a/core/common/src/serializers/LocalDateSerializers.kt b/core/common/src/serializers/LocalDateSerializers.kt index 5932d68ad..aaa0e2bf3 100644 --- a/core/common/src/serializers/LocalDateSerializers.kt +++ b/core/common/src/serializers/LocalDateSerializers.kt @@ -16,22 +16,10 @@ import kotlinx.serialization.encoding.* * * JSON example: `"2020-01-01"` * - * @see LocalDate.parse - * @see LocalDate.toString + * @see LocalDate.Formats.ISO */ -public object LocalDateIso8601Serializer: KSerializer { - - override val descriptor: SerialDescriptor = - PrimitiveSerialDescriptor("kotlinx.datetime.LocalDate", PrimitiveKind.STRING) - - override fun deserialize(decoder: Decoder): LocalDate = - LocalDate.parse(decoder.decodeString()) - - override fun serialize(encoder: Encoder, value: LocalDate) { - encoder.encodeString(value.toString()) - } - -} +public object LocalDateIso8601Serializer : KSerializer +by LocalDate.Formats.ISO.asKSerializer("kotlinx.datetime.LocalDate") /** * A serializer for [LocalDate] that represents a value as its components. @@ -117,3 +105,23 @@ internal fun DateTimeFormat.asKSerializer(serialName: String): KSerialize override fun toString(): String = serialName } + +/** + * A serializer for [LocalDate] that uses the default [LocalDate.toString]/[LocalDate.parse]. + * + * JSON example: `"2020-01-01"` + */ +@PublishedApi +internal object LocalDateSerializer: KSerializer { + + override val descriptor: SerialDescriptor = + PrimitiveSerialDescriptor("kotlinx.datetime.LocalDate", PrimitiveKind.STRING) + + override fun deserialize(decoder: Decoder): LocalDate = + LocalDate.parse(decoder.decodeString()) + + override fun serialize(encoder: Encoder, value: LocalDate) { + encoder.encodeString(value.toString()) + } + +} diff --git a/core/common/src/serializers/LocalDateTimeSerializers.kt b/core/common/src/serializers/LocalDateTimeSerializers.kt index 9cac97906..ddbe38387 100644 --- a/core/common/src/serializers/LocalDateTimeSerializers.kt +++ b/core/common/src/serializers/LocalDateTimeSerializers.kt @@ -16,22 +16,10 @@ import kotlinx.serialization.encoding.* * * JSON example: `"2007-12-31T23:59:01"` * - * @see LocalDateTime.parse - * @see LocalDateTime.toString + * @see LocalDateTime.Formats.ISO */ -public object LocalDateTimeIso8601Serializer: KSerializer { - - override val descriptor: SerialDescriptor = - PrimitiveSerialDescriptor("kotlinx.datetime.LocalDateTime", PrimitiveKind.STRING) - - override fun deserialize(decoder: Decoder): LocalDateTime = - LocalDateTime.parse(decoder.decodeString()) - - override fun serialize(encoder: Encoder, value: LocalDateTime) { - encoder.encodeString(value.toString()) - } - -} +public object LocalDateTimeIso8601Serializer : KSerializer +by LocalDateTime.Formats.ISO.asKSerializer("kotlinx.datetime.LocalDateTime") /** * A serializer for [LocalDateTime] that represents a value as its components. @@ -130,3 +118,23 @@ public object LocalDateTimeComponentSerializer: KSerializer { public abstract class FormattedLocalDateTimeSerializer( name: String, format: DateTimeFormat ) : KSerializer by format.asKSerializer("kotlinx.datetime.LocalDateTime serializer $name") + +/** + * A serializer for [LocalDateTime] that uses the default [LocalDateTime.toString]/[LocalDateTime.parse]. + * + * JSON example: `"2007-12-31T23:59:01"` + */ +@PublishedApi +internal object LocalDateTimeSerializer: KSerializer { + + override val descriptor: SerialDescriptor = + PrimitiveSerialDescriptor("kotlinx.datetime.LocalDateTime", PrimitiveKind.STRING) + + override fun deserialize(decoder: Decoder): LocalDateTime = + LocalDateTime.parse(decoder.decodeString()) + + override fun serialize(encoder: Encoder, value: LocalDateTime) { + encoder.encodeString(value.toString()) + } + +} diff --git a/core/common/src/serializers/LocalTimeSerializers.kt b/core/common/src/serializers/LocalTimeSerializers.kt index 10a3750e2..0b86050b3 100644 --- a/core/common/src/serializers/LocalTimeSerializers.kt +++ b/core/common/src/serializers/LocalTimeSerializers.kt @@ -16,21 +16,10 @@ import kotlinx.serialization.encoding.* * * JSON example: `"12:01:03.999"` * - * @see LocalDate.parse - * @see LocalDate.toString + * @see LocalTime.Formats.ISO */ -public object LocalTimeIso8601Serializer : KSerializer { - - override val descriptor: SerialDescriptor = - PrimitiveSerialDescriptor("kotlinx.datetime.LocalTime", PrimitiveKind.STRING) - - override fun deserialize(decoder: Decoder): LocalTime = - LocalTime.parse(decoder.decodeString()) - - override fun serialize(encoder: Encoder, value: LocalTime) { - encoder.encodeString(value.toString()) - } -} +public object LocalTimeIso8601Serializer : KSerializer +by LocalTime.Formats.ISO.asKSerializer("kotlinx.datetime.LocalTime") /** * A serializer for [LocalTime] that represents a value as its components. @@ -109,3 +98,25 @@ public object LocalTimeComponentSerializer : KSerializer { public abstract class FormattedLocalTimeSerializer( name: String, format: DateTimeFormat ) : KSerializer by format.asKSerializer("kotlinx.datetime.LocalTime serializer $name") + +/** + * A serializer for [LocalTime] that uses the ISO 8601 representation. + * + * JSON example: `"12:01:03.999"` + * + * @see LocalDate.parse + * @see LocalDate.toString + */ +@PublishedApi +internal object LocalTimeSerializer : KSerializer { + + override val descriptor: SerialDescriptor = + PrimitiveSerialDescriptor("kotlinx.datetime.LocalTime", PrimitiveKind.STRING) + + override fun deserialize(decoder: Decoder): LocalTime = + LocalTime.parse(decoder.decodeString()) + + override fun serialize(encoder: Encoder, value: LocalTime) { + encoder.encodeString(value.toString()) + } +} diff --git a/core/common/src/serializers/TimeZoneSerializers.kt b/core/common/src/serializers/TimeZoneSerializers.kt index b0d5c5071..f9281809a 100644 --- a/core/common/src/serializers/TimeZoneSerializers.kt +++ b/core/common/src/serializers/TimeZoneSerializers.kt @@ -57,9 +57,17 @@ public object FixedOffsetTimeZoneSerializer: KSerializer { * * JSON example: `"+02:00"` * - * @see UtcOffset.parse - * @see UtcOffset.toString + * @see UtcOffset.Formats.ISO */ +public object UtcOffsetIso8601Serializer : KSerializer +by UtcOffset.Formats.ISO.asKSerializer("kotlinx.datetime.UtcOffset") + +/** + * A serializer for [UtcOffset] that uses the default [UtcOffset.toString]/[UtcOffset.parse]. + * + * JSON example: `"+02:00"` + */ +@Deprecated("Use UtcOffset.serializer() instead", ReplaceWith("UtcOffset.serializer()")) public object UtcOffsetSerializer: KSerializer { override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("kotlinx.datetime.UtcOffset", PrimitiveKind.STRING) diff --git a/core/commonJs/src/Instant.kt b/core/commonJs/src/Instant.kt index af7cc868a..45c6c2f8b 100644 --- a/core/commonJs/src/Instant.kt +++ b/core/commonJs/src/Instant.kt @@ -13,13 +13,13 @@ import kotlinx.datetime.internal.JSJoda.ChronoUnit as jtChronoUnit import kotlinx.datetime.internal.JSJoda.ZonedDateTime as jtZonedDateTime import kotlinx.datetime.internal.safeAdd import kotlinx.datetime.internal.* -import kotlinx.datetime.serializers.InstantIso8601Serializer +import kotlinx.datetime.serializers.* import kotlinx.serialization.Serializable import kotlin.time.* import kotlin.time.Duration.Companion.nanoseconds import kotlin.time.Duration.Companion.seconds -@Serializable(with = InstantIso8601Serializer::class) +@Serializable(with = InstantSerializer::class) public actual class Instant internal constructor(internal val value: jtInstant) : Comparable { public actual val epochSeconds: Long diff --git a/core/commonJs/src/LocalDate.kt b/core/commonJs/src/LocalDate.kt index a650665d7..2f6285cbd 100644 --- a/core/commonJs/src/LocalDate.kt +++ b/core/commonJs/src/LocalDate.kt @@ -6,12 +6,12 @@ package kotlinx.datetime import kotlinx.datetime.format.* -import kotlinx.datetime.serializers.LocalDateIso8601Serializer +import kotlinx.datetime.serializers.* import kotlinx.serialization.Serializable import kotlinx.datetime.internal.JSJoda.LocalDate as jtLocalDate import kotlinx.datetime.internal.JSJoda.ChronoUnit as jtChronoUnit -@Serializable(with = LocalDateIso8601Serializer::class) +@Serializable(with = LocalDateSerializer::class) public actual class LocalDate internal constructor(internal val value: jtLocalDate) : Comparable { public actual companion object { diff --git a/core/commonJs/src/LocalDateTime.kt b/core/commonJs/src/LocalDateTime.kt index c85e772a4..59dfaaabe 100644 --- a/core/commonJs/src/LocalDateTime.kt +++ b/core/commonJs/src/LocalDateTime.kt @@ -7,11 +7,11 @@ package kotlinx.datetime import kotlinx.datetime.format.* import kotlinx.datetime.format.ISO_DATETIME import kotlinx.datetime.format.LocalDateTimeFormat -import kotlinx.datetime.serializers.LocalDateTimeIso8601Serializer +import kotlinx.datetime.serializers.* import kotlinx.serialization.Serializable import kotlinx.datetime.internal.JSJoda.LocalDateTime as jtLocalDateTime -@Serializable(with = LocalDateTimeIso8601Serializer::class) +@Serializable(with = LocalDateTimeSerializer::class) public actual class LocalDateTime internal constructor(internal val value: jtLocalDateTime) : Comparable { public actual constructor(year: Int, monthNumber: Int, dayOfMonth: Int, hour: Int, minute: Int, second: Int, nanosecond: Int) : diff --git a/core/commonJs/src/LocalTime.kt b/core/commonJs/src/LocalTime.kt index 674e5cb69..72aab2236 100644 --- a/core/commonJs/src/LocalTime.kt +++ b/core/commonJs/src/LocalTime.kt @@ -8,11 +8,11 @@ import kotlinx.datetime.format.* import kotlinx.datetime.format.ISO_TIME import kotlinx.datetime.format.LocalTimeFormat import kotlinx.datetime.internal.* -import kotlinx.datetime.serializers.LocalTimeIso8601Serializer +import kotlinx.datetime.serializers.* import kotlinx.serialization.Serializable import kotlinx.datetime.internal.JSJoda.LocalTime as jtLocalTime -@Serializable(LocalTimeIso8601Serializer::class) +@Serializable(LocalTimeSerializer::class) public actual class LocalTime internal constructor(internal val value: jtLocalTime) : Comparable { diff --git a/core/commonJs/src/UtcOffset.kt b/core/commonJs/src/UtcOffset.kt index 65dcf8651..8f0ed74eb 100644 --- a/core/commonJs/src/UtcOffset.kt +++ b/core/commonJs/src/UtcOffset.kt @@ -11,9 +11,10 @@ import kotlinx.datetime.internal.JSJoda.DateTimeFormatterBuilder as jtDateTimeFo import kotlinx.datetime.internal.JSJoda.DateTimeFormatter as jtDateTimeFormatter import kotlinx.datetime.internal.JSJoda.ResolverStyle as jtResolverStyle import kotlinx.datetime.format.* -import kotlinx.datetime.serializers.UtcOffsetSerializer +import kotlinx.datetime.serializers.* import kotlinx.serialization.Serializable +@Suppress("DEPRECATION") @Serializable(with = UtcOffsetSerializer::class) public actual class UtcOffset internal constructor(internal val zoneOffset: jtZoneOffset) { public actual val totalSeconds: Int get() = zoneOffset.totalSeconds() diff --git a/core/jvm/src/Instant.kt b/core/jvm/src/Instant.kt index ab3474647..b77832066 100644 --- a/core/jvm/src/Instant.kt +++ b/core/jvm/src/Instant.kt @@ -9,7 +9,7 @@ package kotlinx.datetime import kotlinx.datetime.format.* import kotlinx.datetime.internal.safeMultiply import kotlinx.datetime.internal.* -import kotlinx.datetime.serializers.InstantIso8601Serializer +import kotlinx.datetime.serializers.* import kotlinx.serialization.Serializable import java.time.DateTimeException import java.time.temporal.* @@ -19,7 +19,7 @@ import kotlin.time.Duration.Companion.seconds import java.time.Instant as jtInstant import java.time.Clock as jtClock -@Serializable(with = InstantIso8601Serializer::class) +@Serializable(with = InstantSerializer::class) public actual class Instant internal constructor(internal val value: jtInstant) : Comparable { public actual val epochSeconds: Long diff --git a/core/jvm/src/LocalDate.kt b/core/jvm/src/LocalDate.kt index fe3b9ae1a..c0d2be849 100644 --- a/core/jvm/src/LocalDate.kt +++ b/core/jvm/src/LocalDate.kt @@ -9,14 +9,14 @@ import kotlinx.datetime.format.* import kotlinx.datetime.internal.safeAdd import kotlinx.datetime.internal.safeMultiply import kotlinx.datetime.internal.* -import kotlinx.datetime.serializers.LocalDateIso8601Serializer +import kotlinx.datetime.serializers.* import kotlinx.serialization.Serializable import java.time.DateTimeException import java.time.format.DateTimeParseException import java.time.temporal.ChronoUnit import java.time.LocalDate as jtLocalDate -@Serializable(with = LocalDateIso8601Serializer::class) +@Serializable(with = LocalDateSerializer::class) public actual class LocalDate internal constructor(internal val value: jtLocalDate) : Comparable { public actual companion object { public actual fun parse(input: CharSequence, format: DateTimeFormat): LocalDate = diff --git a/core/jvm/src/LocalDateTime.kt b/core/jvm/src/LocalDateTime.kt index 7dc28cdb1..54f2b3de0 100644 --- a/core/jvm/src/LocalDateTime.kt +++ b/core/jvm/src/LocalDateTime.kt @@ -6,7 +6,7 @@ package kotlinx.datetime import kotlinx.datetime.format.* -import kotlinx.datetime.serializers.LocalDateTimeIso8601Serializer +import kotlinx.datetime.serializers.* import kotlinx.serialization.Serializable import java.time.DateTimeException import java.time.format.DateTimeParseException @@ -15,7 +15,7 @@ import java.time.LocalDateTime as jtLocalDateTime public actual typealias Month = java.time.Month public actual typealias DayOfWeek = java.time.DayOfWeek -@Serializable(with = LocalDateTimeIso8601Serializer::class) +@Serializable(with = LocalDateTimeSerializer::class) public actual class LocalDateTime internal constructor(internal val value: jtLocalDateTime) : Comparable { public actual constructor(year: Int, monthNumber: Int, dayOfMonth: Int, hour: Int, minute: Int, second: Int, nanosecond: Int) : diff --git a/core/jvm/src/LocalTime.kt b/core/jvm/src/LocalTime.kt index 71052570f..fe67736d8 100644 --- a/core/jvm/src/LocalTime.kt +++ b/core/jvm/src/LocalTime.kt @@ -8,13 +8,13 @@ package kotlinx.datetime import kotlinx.datetime.format.* import kotlinx.datetime.internal.* -import kotlinx.datetime.serializers.LocalTimeIso8601Serializer +import kotlinx.datetime.serializers.* import kotlinx.serialization.Serializable import java.time.DateTimeException import java.time.format.DateTimeParseException import java.time.LocalTime as jtLocalTime -@Serializable(with = LocalTimeIso8601Serializer::class) +@Serializable(with = LocalTimeSerializer::class) public actual class LocalTime internal constructor(internal val value: jtLocalTime) : Comparable { diff --git a/core/jvm/src/UtcOffsetJvm.kt b/core/jvm/src/UtcOffsetJvm.kt index 129857d74..aa02495cb 100644 --- a/core/jvm/src/UtcOffsetJvm.kt +++ b/core/jvm/src/UtcOffsetJvm.kt @@ -6,13 +6,14 @@ package kotlinx.datetime import kotlinx.datetime.format.* -import kotlinx.datetime.serializers.UtcOffsetSerializer +import kotlinx.datetime.serializers.* import kotlinx.serialization.Serializable import java.time.DateTimeException import java.time.ZoneOffset import java.time.format.DateTimeFormatterBuilder import java.time.format.* +@Suppress("DEPRECATION") @Serializable(with = UtcOffsetSerializer::class) public actual class UtcOffset(internal val zoneOffset: ZoneOffset) { public actual val totalSeconds: Int get() = zoneOffset.totalSeconds diff --git a/core/native/src/Instant.kt b/core/native/src/Instant.kt index 70e0017be..96185694d 100644 --- a/core/native/src/Instant.kt +++ b/core/native/src/Instant.kt @@ -10,7 +10,7 @@ package kotlinx.datetime import kotlinx.datetime.format.* import kotlinx.datetime.internal.* -import kotlinx.datetime.serializers.InstantIso8601Serializer +import kotlinx.datetime.serializers.* import kotlinx.serialization.Serializable import kotlin.time.* import kotlin.time.Duration.Companion.nanoseconds @@ -38,7 +38,7 @@ private const val MAX_SECOND = 31494816403199L // +1000000-12-31T23:59:59 private fun isValidInstantSecond(second: Long) = second >= MIN_SECOND && second <= MAX_SECOND -@Serializable(with = InstantIso8601Serializer::class) +@Serializable(with = InstantSerializer::class) public actual class Instant internal constructor(public actual val epochSeconds: Long, public actual val nanosecondsOfSecond: Int) : Comparable { init { diff --git a/core/native/src/LocalDate.kt b/core/native/src/LocalDate.kt index 14ee3a173..714bbebf6 100644 --- a/core/native/src/LocalDate.kt +++ b/core/native/src/LocalDate.kt @@ -12,7 +12,7 @@ import kotlinx.datetime.format.* import kotlinx.datetime.internal.* import kotlinx.datetime.internal.safeAdd import kotlinx.datetime.internal.safeMultiply -import kotlinx.datetime.serializers.LocalDateIso8601Serializer +import kotlinx.datetime.serializers.* import kotlinx.serialization.Serializable import kotlin.math.* @@ -22,7 +22,7 @@ internal const val YEAR_MAX = 999_999 private fun isValidYear(year: Int): Boolean = year >= YEAR_MIN && year <= YEAR_MAX -@Serializable(with = LocalDateIso8601Serializer::class) +@Serializable(with = LocalDateSerializer::class) public actual class LocalDate actual constructor(public actual val year: Int, public actual val monthNumber: Int, public actual val dayOfMonth: Int) : Comparable { init { diff --git a/core/native/src/LocalDateTime.kt b/core/native/src/LocalDateTime.kt index 33187d4a5..3b58e4701 100644 --- a/core/native/src/LocalDateTime.kt +++ b/core/native/src/LocalDateTime.kt @@ -13,7 +13,7 @@ import kotlinx.datetime.internal.* import kotlinx.datetime.serializers.* import kotlinx.serialization.* -@Serializable(with = LocalDateTimeIso8601Serializer::class) +@Serializable(with = LocalDateTimeSerializer::class) public actual class LocalDateTime public actual constructor(public actual val date: LocalDate, public actual val time: LocalTime) : Comparable { public actual companion object { diff --git a/core/native/src/LocalTime.kt b/core/native/src/LocalTime.kt index 28ebd78df..42f5c0177 100644 --- a/core/native/src/LocalTime.kt +++ b/core/native/src/LocalTime.kt @@ -10,10 +10,10 @@ package kotlinx.datetime import kotlinx.datetime.internal.* import kotlinx.datetime.format.* -import kotlinx.datetime.serializers.LocalTimeIso8601Serializer +import kotlinx.datetime.serializers.* import kotlinx.serialization.Serializable -@Serializable(LocalTimeIso8601Serializer::class) +@Serializable(LocalTimeSerializer::class) public actual class LocalTime actual constructor( public actual val hour: Int, public actual val minute: Int, diff --git a/core/native/src/UtcOffset.kt b/core/native/src/UtcOffset.kt index abe8c64da..6213360bb 100644 --- a/core/native/src/UtcOffset.kt +++ b/core/native/src/UtcOffset.kt @@ -7,10 +7,11 @@ package kotlinx.datetime import kotlinx.datetime.internal.* import kotlinx.datetime.format.* -import kotlinx.datetime.serializers.UtcOffsetSerializer +import kotlinx.datetime.serializers.* import kotlinx.serialization.Serializable import kotlin.math.abs +@Suppress("DEPRECATION") @Serializable(with = UtcOffsetSerializer::class) public actual class UtcOffset private constructor(public actual val totalSeconds: Int) { diff --git a/serialization/common/test/InstantSerializationTest.kt b/serialization/common/test/InstantSerializationTest.kt index 6c848bbd4..6d83dc0b1 100644 --- a/serialization/common/test/InstantSerializationTest.kt +++ b/serialization/common/test/InstantSerializationTest.kt @@ -15,10 +15,26 @@ class InstantSerializationTest { private fun iso8601Serialization(serializer: KSerializer) { for ((instant, json) in listOf( - Pair(Instant.fromEpochSeconds(1607505416, 124000), - "\"2020-12-09T09:16:56.000124Z\""), - Pair(Instant.fromEpochSeconds(-1607505416, -124000), - "\"1919-01-23T14:43:03.999876Z\""), + Pair(Instant.fromEpochSeconds(1607505416, 120000), + "\"2020-12-09T09:16:56.00012Z\""), + Pair(Instant.fromEpochSeconds(-1607505416, -120000), + "\"1919-01-23T14:43:03.99988Z\""), + Pair(Instant.fromEpochSeconds(987654321, 123456789), + "\"2001-04-19T04:25:21.123456789Z\""), + Pair(Instant.fromEpochSeconds(987654321, 0), + "\"2001-04-19T04:25:21Z\""), + )) { + assertEquals(json, Json.encodeToString(serializer, instant)) + assertEquals(instant, Json.decodeFromString(serializer, json)) + } + } + + private fun defaultSerialization(serializer: KSerializer) { + for ((instant, json) in listOf( + Pair(Instant.fromEpochSeconds(1607505416, 120000), + "\"2020-12-09T09:16:56.000120Z\""), + Pair(Instant.fromEpochSeconds(-1607505416, -120000), + "\"1919-01-23T14:43:03.999880Z\""), Pair(Instant.fromEpochSeconds(987654321, 123456789), "\"2001-04-19T04:25:21.123456789Z\""), Pair(Instant.fromEpochSeconds(987654321, 0), @@ -68,7 +84,7 @@ class InstantSerializationTest { fun testDefaultSerializers() { // should be the same as the ISO 8601 assertKSerializerName("kotlinx.datetime.Instant", Json.serializersModule.serializer()) - iso8601Serialization(Json.serializersModule.serializer()) + defaultSerialization(Json.serializersModule.serializer()) } object Rfc1123InstantSerializer : FormattedInstantSerializer("RFC_1123", DateTimeComponents.Formats.RFC_1123) diff --git a/serialization/common/test/LocalDateTimeSerializationTest.kt b/serialization/common/test/LocalDateTimeSerializationTest.kt index 0520d3f06..63649a341 100644 --- a/serialization/common/test/LocalDateTimeSerializationTest.kt +++ b/serialization/common/test/LocalDateTimeSerializationTest.kt @@ -15,6 +15,19 @@ import kotlin.test.* class LocalDateTimeSerializationTest { private fun iso8601Serialization(serializer: KSerializer) { + for ((localDateTime, json) in listOf( + Pair(LocalDateTime(2008, 7, 5, 2, 1), "\"2008-07-05T02:01:00\""), + Pair(LocalDateTime(2007, 12, 31, 23, 59, 1), "\"2007-12-31T23:59:01\""), + Pair(LocalDateTime(999, 12, 31, 23, 59, 59, 990000000), "\"0999-12-31T23:59:59.99\""), + Pair(LocalDateTime(-1, 1, 2, 23, 59, 59, 999990000), "\"-0001-01-02T23:59:59.99999\""), + Pair(LocalDateTime(-2008, 1, 2, 23, 59, 59, 999999990), "\"-2008-01-02T23:59:59.99999999\""), + )) { + assertEquals(json, Json.encodeToString(serializer, localDateTime)) + assertEquals(localDateTime, Json.decodeFromString(serializer, json)) + } + } + + private fun defaultSerialization(serializer: KSerializer) { for ((localDateTime, json) in listOf( Pair(LocalDateTime(2008, 7, 5, 2, 1), "\"2008-07-05T02:01\""), Pair(LocalDateTime(2007, 12, 31, 23, 59, 1), "\"2007-12-31T23:59:01\""), @@ -84,7 +97,7 @@ class LocalDateTimeSerializationTest { fun testDefaultSerializers() { // should be the same as the ISO 8601 assertKSerializerName("kotlinx.datetime.LocalDateTime", Json.serializersModule.serializer()) - iso8601Serialization(Json.serializersModule.serializer()) + defaultSerialization(Json.serializersModule.serializer()) } object PythonDateTimeSerializer : FormattedLocalDateTimeSerializer("PythonDateTime", LocalDateTime.Format { diff --git a/serialization/common/test/LocalTimeSerializationTest.kt b/serialization/common/test/LocalTimeSerializationTest.kt index f1af3b276..3bd6e09c4 100644 --- a/serialization/common/test/LocalTimeSerializationTest.kt +++ b/serialization/common/test/LocalTimeSerializationTest.kt @@ -15,6 +15,19 @@ import kotlin.test.* class LocalTimeSerializationTest { private fun iso8601Serialization(serializer: KSerializer) { + for ((localTime, json) in listOf( + Pair(LocalTime(2, 1), "\"02:01:00\""), + Pair(LocalTime(23, 59, 1), "\"23:59:01\""), + Pair(LocalTime(23, 59, 59, 990000000), "\"23:59:59.99\""), + Pair(LocalTime(23, 59, 59, 999990000), "\"23:59:59.99999\""), + Pair(LocalTime(23, 59, 59, 999999990), "\"23:59:59.99999999\""), + )) { + assertEquals(json, Json.encodeToString(serializer, localTime)) + assertEquals(localTime, Json.decodeFromString(serializer, json)) + } + } + + private fun defaultSerialization(serializer: KSerializer) { for ((localTime, json) in listOf( Pair(LocalTime(2, 1), "\"02:01\""), Pair(LocalTime(23, 59, 1), "\"23:59:01\""), @@ -72,9 +85,8 @@ class LocalTimeSerializationTest { @Test fun testDefaultSerializers() { - // should be the same as the ISO 8601 assertKSerializerName("kotlinx.datetime.LocalTime", Json.serializersModule.serializer()) - iso8601Serialization(Json.serializersModule.serializer()) + defaultSerialization(Json.serializersModule.serializer()) } object FixedWidthTimeSerializer : FormattedLocalTimeSerializer("FixedWidth", LocalTime.Format { diff --git a/serialization/common/test/UtcOffsetSerializationTest.kt b/serialization/common/test/UtcOffsetSerializationTest.kt index 687c62b9a..32304f13c 100644 --- a/serialization/common/test/UtcOffsetSerializationTest.kt +++ b/serialization/common/test/UtcOffsetSerializationTest.kt @@ -14,28 +14,50 @@ import kotlin.test.* class UtcOffsetSerializationTest { - private fun testSerializationAsPrimitive(serializer: KSerializer) { - val offset2h = UtcOffset(hours = 2) - assertEquals("\"+02:00\"", Json.encodeToString(serializer, offset2h)) - assertEquals(offset2h, Json.decodeFromString(serializer, "\"+02:00\"")) - assertEquals(offset2h, Json.decodeFromString(serializer, "\"+02:00:00\"")) - - assertFailsWith { - Json.decodeFromString(serializer, "\"UTC+02:00\"") // not an offset + private fun iso8601Serialization(serializer: KSerializer) { + // the default form is obtainable and parsable + for ((offset, json) in listOf( + Pair(UtcOffset(hours = 0), "\"Z\""), + Pair(UtcOffset(hours = 1), "\"+01:00\""), + Pair(UtcOffset(hours = 1, minutes = 30), "\"+01:30\""), + Pair(UtcOffset(hours = 1, minutes = 30, seconds = 59), "\"+01:30:59\""), + )) { + assertEquals(json, Json.encodeToString(serializer, offset)) + assertEquals(offset, Json.decodeFromString(serializer, json)) + } + // alternative forms are also parsable + for ((offset, json) in listOf( + Pair(UtcOffset(hours = 0), "\"+00:00\""), + Pair(UtcOffset(hours = 0), "\"z\""), + )) { + assertEquals(offset, Json.decodeFromString(serializer, json)) + } + // some strings aren't parsable + for (json in listOf( + "\"+3\"", + "\"+03\"", + "\"+03:0\"", + "\"UTC+02:00\"", + )) { + assertFailsWith { + Json.decodeFromString(serializer, json) + } } } @Test - fun defaultSerializer() { - assertKSerializerName("kotlinx.datetime.UtcOffset", Json.serializersModule.serializer()) - testSerializationAsPrimitive(Json.serializersModule.serializer()) + fun testIso8601Serialization() { + assertKSerializerName("kotlinx.datetime.UtcOffset", UtcOffsetIso8601Serializer) + iso8601Serialization(UtcOffsetIso8601Serializer) } @Test - fun stringPrimitiveSerializer() { - assertKSerializerName("kotlinx.datetime.UtcOffset", UtcOffsetSerializer) - testSerializationAsPrimitive(UtcOffsetSerializer) - testSerializationAsPrimitive(UtcOffset.serializer()) + fun testDefaultSerializers() { + // should be the same as the ISO 8601 + assertKSerializerName("kotlinx.datetime.UtcOffset", Json.serializersModule.serializer()) + iso8601Serialization(Json.serializersModule.serializer()) + assertKSerializerName("kotlinx.datetime.UtcOffset", UtcOffset.serializer()) + iso8601Serialization(UtcOffset.serializer()) } object FourDigitOffsetSerializer : FormattedUtcOffsetSerializer("FOUR_DIGITS", UtcOffset.Formats.FOUR_DIGITS)