Skip to content

Commit

Permalink
Separate ISO and default serializers
Browse files Browse the repository at this point in the history
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
  • Loading branch information
dkhalanskyjb committed Jul 25, 2024
1 parent faedbcc commit aec4a6c
Show file tree
Hide file tree
Showing 29 changed files with 233 additions and 111 deletions.
6 changes: 3 additions & 3 deletions core/common/src/Instant.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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.*

Expand Down Expand Up @@ -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<Instant> {

/**
Expand Down
3 changes: 2 additions & 1 deletion core/common/src/LocalDate.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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.
*
Expand All @@ -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<LocalDate> {
public companion object {
/**
Expand Down
6 changes: 3 additions & 3 deletions core/common/src/LocalDateTime.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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

/**
Expand Down Expand Up @@ -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.
*
Expand All @@ -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<LocalDateTime> {
public companion object {

Expand Down
6 changes: 3 additions & 3 deletions core/common/src/LocalTime.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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


Expand Down Expand Up @@ -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.
*
Expand All @@ -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<LocalTime> {
public companion object {

Expand Down
6 changes: 4 additions & 2 deletions core/common/src/UtcOffset.kt
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
package kotlinx.datetime

import kotlinx.datetime.format.*
import kotlinx.datetime.serializers.UtcOffsetSerializer
import kotlinx.datetime.serializers.*
import kotlinx.serialization.Serializable

/**
Expand Down Expand Up @@ -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 {
/**
Expand Down
26 changes: 22 additions & 4 deletions core/common/src/serializers/InstantSerializers.kt
Original file line number Diff line number Diff line change
Expand Up @@ -17,19 +17,18 @@ 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<Instant> {

override val descriptor: SerialDescriptor =
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))
}

}
Expand Down Expand Up @@ -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<Instant> {

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())
}

}
38 changes: 23 additions & 15 deletions core/common/src/serializers/LocalDateSerializers.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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<LocalDate> {

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<LocalDate>
by LocalDate.Formats.ISO.asKSerializer("kotlinx.datetime.LocalDate")

/**
* A serializer for [LocalDate] that represents a value as its components.
Expand Down Expand Up @@ -117,3 +105,23 @@ internal fun <T> DateTimeFormat<T>.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<LocalDate> {

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())
}

}
38 changes: 23 additions & 15 deletions core/common/src/serializers/LocalDateTimeSerializers.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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<LocalDateTime> {

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<LocalDateTime>
by LocalDateTime.Formats.ISO.asKSerializer("kotlinx.datetime.LocalDateTime")

/**
* A serializer for [LocalDateTime] that represents a value as its components.
Expand Down Expand Up @@ -130,3 +118,23 @@ public object LocalDateTimeComponentSerializer: KSerializer<LocalDateTime> {
public abstract class FormattedLocalDateTimeSerializer(
name: String, format: DateTimeFormat<LocalDateTime>
) : KSerializer<LocalDateTime> 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<LocalDateTime> {

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())
}

}
39 changes: 25 additions & 14 deletions core/common/src/serializers/LocalTimeSerializers.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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<LocalTime> {

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<LocalTime>
by LocalTime.Formats.ISO.asKSerializer("kotlinx.datetime.LocalTime")

/**
* A serializer for [LocalTime] that represents a value as its components.
Expand Down Expand Up @@ -109,3 +98,25 @@ public object LocalTimeComponentSerializer : KSerializer<LocalTime> {
public abstract class FormattedLocalTimeSerializer(
name: String, format: DateTimeFormat<LocalTime>
) : KSerializer<LocalTime> 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<LocalTime> {

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())
}
}
12 changes: 10 additions & 2 deletions core/common/src/serializers/TimeZoneSerializers.kt
Original file line number Diff line number Diff line change
Expand Up @@ -57,9 +57,17 @@ public object FixedOffsetTimeZoneSerializer: KSerializer<FixedOffsetTimeZone> {
*
* JSON example: `"+02:00"`
*
* @see UtcOffset.parse
* @see UtcOffset.toString
* @see UtcOffset.Formats.ISO
*/
public object UtcOffsetIso8601Serializer : KSerializer<UtcOffset>
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<UtcOffset> {

override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("kotlinx.datetime.UtcOffset", PrimitiveKind.STRING)
Expand Down
4 changes: 2 additions & 2 deletions core/commonJs/src/Instant.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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<Instant> {

public actual val epochSeconds: Long
Expand Down
4 changes: 2 additions & 2 deletions core/commonJs/src/LocalDate.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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<LocalDate> {
public actual companion object {

Expand Down
4 changes: 2 additions & 2 deletions core/commonJs/src/LocalDateTime.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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<LocalDateTime> {

public actual constructor(year: Int, monthNumber: Int, dayOfMonth: Int, hour: Int, minute: Int, second: Int, nanosecond: Int) :
Expand Down
4 changes: 2 additions & 2 deletions core/commonJs/src/LocalTime.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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<LocalTime> {

Expand Down
Loading

0 comments on commit aec4a6c

Please sign in to comment.