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 May 13, 2024
1 parent a100ce8 commit 5e81277
Show file tree
Hide file tree
Showing 28 changed files with 221 additions and 108 deletions.
5 changes: 2 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 @@ -194,7 +193,7 @@ import kotlin.time.*
*
* @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
2 changes: 1 addition & 1 deletion core/common/src/LocalDate.kt
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,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
5 changes: 2 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 @@ -105,7 +104,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
5 changes: 2 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 @@ -76,7 +75,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
3 changes: 2 additions & 1 deletion 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 @@ -53,6 +53,7 @@ import kotlinx.serialization.Serializable
* @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
28 changes: 24 additions & 4 deletions core/common/src/serializers/InstantSerializers.kt
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
package kotlinx.datetime.serializers

import kotlinx.datetime.Instant
import kotlinx.datetime.format
import kotlinx.datetime.format.DateTimeComponents
import kotlinx.serialization.*
import kotlinx.serialization.descriptors.*
import kotlinx.serialization.encoding.*
Expand All @@ -15,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 @@ -75,3 +76,22 @@ public object InstantComponentSerializer : KSerializer<Instant> {
}

}

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

}
54 changes: 39 additions & 15 deletions core/common/src/serializers/LocalDateSerializers.kt
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
package kotlinx.datetime.serializers

import kotlinx.datetime.LocalDate
import kotlinx.datetime.format.DateTimeFormat
import kotlinx.serialization.*
import kotlinx.serialization.descriptors.*
import kotlinx.serialization.encoding.*
Expand All @@ -15,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 DateTimeFormatSerializer(LocalDate.Formats.ISO, "kotlinx.datetime.LocalDate")

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

}

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

}

/**
* A general mechanism of implementing [KSerializer] instances using the given [string formats][DateTimeFormat].
*/
internal class DateTimeFormatSerializer<T>(val format: DateTimeFormat<T>, className: String): KSerializer<T> {
override val descriptor: SerialDescriptor =
PrimitiveSerialDescriptor(className, PrimitiveKind.STRING)

override fun serialize(encoder: Encoder, value: T) {
encoder.encodeString(format.format(value))
}

override fun deserialize(decoder: Decoder): T =
format.parse(decoder.decodeString())

}
37 changes: 22 additions & 15 deletions core/common/src/serializers/LocalDateTimeSerializers.kt
Original file line number Diff line number Diff line change
Expand Up @@ -15,22 +15,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 DateTimeFormatSerializer(LocalDateTime.Formats.ISO, "kotlinx.datetime.LocalDateTime")

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

}

/**
* A serializer for [LocalDateTime] that uses the default [LocalDateTime.toString]/[LocalDateTime.parse].
*
* JSON example: `"2007-12-31T23:59:01"`
*/
public 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())
}

}
38 changes: 24 additions & 14 deletions core/common/src/serializers/LocalTimeSerializers.kt
Original file line number Diff line number Diff line change
Expand Up @@ -15,21 +15,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 DateTimeFormatSerializer(LocalTime.Formats.ISO, "kotlinx.datetime.LocalTime")

/**
* A serializer for [LocalTime] that represents a value as its components.
Expand Down Expand Up @@ -81,3 +70,24 @@ public object LocalTimeComponentSerializer : KSerializer<LocalTime> {
}
}
}

/**
* A serializer for [LocalTime] that uses the ISO 8601 representation.
*
* JSON example: `"12:01:03.999"`
*
* @see LocalDate.parse
* @see LocalDate.toString
*/
public 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())
}
}
18 changes: 12 additions & 6 deletions core/common/src/serializers/TimeZoneSerializers.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,7 @@

package kotlinx.datetime.serializers

import kotlinx.datetime.FixedOffsetTimeZone
import kotlinx.datetime.TimeZone
import kotlinx.datetime.UtcOffset
import kotlinx.datetime.*
import kotlinx.serialization.*
import kotlinx.serialization.descriptors.*
import kotlinx.serialization.encoding.*
Expand Down Expand Up @@ -54,13 +52,21 @@ public object FixedOffsetTimeZoneSerializer: KSerializer<FixedOffsetTimeZone> {
}

/**
* A serializer for [UtcOffset] that uses the extended ISO 8601 representation.
* A serializer for [UtcOffset] that uses the ISO 8601 representation.
*
* JSON example: `"+02:00"`
*
* @see UtcOffset.parse
* @see UtcOffset.toString
* @see UtcOffset.Formats.ISO
*/
public object UtcOffsetIso8601Serializer : KSerializer<UtcOffset>
by DateTimeFormatSerializer(UtcOffset.Formats.ISO, "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
Loading

0 comments on commit 5e81277

Please sign in to comment.