diff --git a/core/common/src/TimeZone.kt b/core/common/src/TimeZone.kt index 89c49232c..49fc95ae7 100644 --- a/core/common/src/TimeZone.kt +++ b/core/common/src/TimeZone.kt @@ -33,7 +33,6 @@ import kotlin.time.Instant * * @sample kotlinx.datetime.test.samples.TimeZoneSamples.usage */ -@Serializable(with = TimeZoneSerializer::class) public expect open class TimeZone { /** * Returns the identifier string of the time zone. @@ -163,6 +162,15 @@ public expect open class TimeZone { * @sample kotlinx.datetime.test.samples.TimeZoneSamples.availableZoneIds */ public val availableZoneIds: Set + + /** @suppress */ + @Deprecated( + "Serializing TimeZone is discouraged, " + + "as deserialization can fail depending on the configuration. " + + "Please serialize the id String instead.", + level = DeprecationLevel.WARNING, + ) + public fun serializer(): kotlinx.serialization.KSerializer } /** @@ -235,7 +243,6 @@ public expect open class TimeZone { * * @sample kotlinx.datetime.test.samples.TimeZoneSamples.FixedOffsetTimeZoneSamples.casting */ -@Serializable(with = FixedOffsetTimeZoneSerializer::class) public expect class FixedOffsetTimeZone : TimeZone { /** * Constructs a time zone with the fixed [offset] from UTC. @@ -253,6 +260,18 @@ public expect class FixedOffsetTimeZone : TimeZone { @Deprecated("Use offset.totalSeconds", ReplaceWith("offset.totalSeconds")) public val totalSeconds: Int + + /** @suppress */ + public companion object { + /** @suppress */ + @Deprecated( + "Serializing FixedOffsetTimeZone is discouraged, " + + "as deserialization can fail or return a non-fixed-offset zone depending on the configuration. " + + "Please serialize the id String instead.", + level = DeprecationLevel.WARNING, + ) + public fun serializer(): kotlinx.serialization.KSerializer + } } @Deprecated("Use FixedOffsetTimeZone or UtcOffset instead", ReplaceWith("FixedOffsetTimeZone")) diff --git a/core/common/src/serializers/TimeZoneSerializers.kt b/core/common/src/serializers/TimeZoneSerializers.kt index c4af596f8..610925779 100644 --- a/core/common/src/serializers/TimeZoneSerializers.kt +++ b/core/common/src/serializers/TimeZoneSerializers.kt @@ -1,12 +1,11 @@ /* - * Copyright 2019-2021 JetBrains s.r.o. + * Copyright 2025 JetBrains s.r.o. * Use of this source code is governed by the Apache 2.0 License that can be found in the LICENSE.txt file. */ package kotlinx.datetime.serializers import kotlinx.datetime.* -import kotlinx.datetime.format.DateTimeFormat import kotlinx.serialization.* import kotlinx.serialization.descriptors.* import kotlinx.serialization.encoding.* @@ -16,6 +15,10 @@ import kotlinx.serialization.encoding.* * * JSON example: `"Europe/Berlin"` */ +@Deprecated( + "Serializing TimeZone is discouraged. Please serialize the id String instead.", + level = DeprecationLevel.WARNING, +) public object TimeZoneSerializer: KSerializer { override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("kotlinx.datetime.TimeZone", PrimitiveKind.STRING) @@ -33,6 +36,10 @@ public object TimeZoneSerializer: KSerializer { * * JSON example: `"+02:00"` */ +@Deprecated( + "Serializing FixedOffsetTimeZoneSerializer is discouraged. Please serialize the id String instead.", + level = DeprecationLevel.WARNING, +) public object FixedOffsetTimeZoneSerializer: KSerializer { override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("kotlinx.datetime.FixedOffsetTimeZone", PrimitiveKind.STRING) @@ -51,60 +58,3 @@ public object FixedOffsetTimeZoneSerializer: KSerializer { } } - -/** - * A serializer for [UtcOffset] that uses the extended ISO 8601 representation. - * - * JSON example: `"+02:00"` - * - * @see UtcOffset.Formats.ISO - */ -public object UtcOffsetIso8601Serializer : KSerializer -by UtcOffset.Formats.ISO.asKSerializer("kotlinx.datetime.UtcOffset/ISO") - -/** - * 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) - - override fun deserialize(decoder: Decoder): UtcOffset { - return UtcOffset.parse(decoder.decodeString()) - } - - override fun serialize(encoder: Encoder, value: UtcOffset) { - encoder.encodeString(value.toString()) - } - -} - -/** - * An abstract serializer for [UtcOffset] values that uses - * a custom [DateTimeFormat] to serialize and deserialize the value. - * - * [name] is the name of the serializer. - * The [SerialDescriptor.serialName] of the resulting serializer is `kotlinx.datetime.UtcOffset/serializer/`[name]. - * [SerialDescriptor.serialName] must be unique across all serializers in the same serialization context. - * When defining a serializer in a library, it is recommended to use the fully qualified class name in [name] - * to avoid conflicts with serializers defined by other libraries and client code. - * - * This serializer is abstract and must be subclassed to provide a concrete serializer. - * Example: - * ``` - * // serializes the UTC offset UtcOffset(hours = 2) as the string "+0200" - * object FourDigitOffsetSerializer : FormattedUtcOffsetSerializer( - * "my.package.FOUR_DIGITS", UtcOffset.Formats.FOUR_DIGITS - * ) - * ``` - * - * Note that [UtcOffset] is [kotlinx.serialization.Serializable] by default, - * so it is not necessary to create custom serializers when the format is not important. - * Additionally, [UtcOffsetSerializer] is provided for the ISO 8601 format. - */ -public abstract class FormattedUtcOffsetSerializer( - name: String, format: DateTimeFormat -) : KSerializer by format.asKSerializer("kotlinx.datetime.UtcOffset/serializer/$name") diff --git a/core/common/src/serializers/UtcOffsetSerializers.kt b/core/common/src/serializers/UtcOffsetSerializers.kt new file mode 100644 index 000000000..282aded1c --- /dev/null +++ b/core/common/src/serializers/UtcOffsetSerializers.kt @@ -0,0 +1,69 @@ +/* + * Copyright 2019-2021 JetBrains s.r.o. + * Use of this source code is governed by the Apache 2.0 License that can be found in the LICENSE.txt file. + */ + +package kotlinx.datetime.serializers + +import kotlinx.datetime.* +import kotlinx.datetime.format.DateTimeFormat +import kotlinx.serialization.* +import kotlinx.serialization.descriptors.* +import kotlinx.serialization.encoding.* + +/** + * A serializer for [UtcOffset] that uses the extended ISO 8601 representation. + * + * JSON example: `"+02:00"` + * + * @see UtcOffset.Formats.ISO + */ +public object UtcOffsetIso8601Serializer : KSerializer +by UtcOffset.Formats.ISO.asKSerializer("kotlinx.datetime.UtcOffset/ISO") + +/** + * 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) + + override fun deserialize(decoder: Decoder): UtcOffset { + return UtcOffset.parse(decoder.decodeString()) + } + + override fun serialize(encoder: Encoder, value: UtcOffset) { + encoder.encodeString(value.toString()) + } + +} + +/** + * An abstract serializer for [UtcOffset] values that uses + * a custom [DateTimeFormat] to serialize and deserialize the value. + * + * [name] is the name of the serializer. + * The [SerialDescriptor.serialName] of the resulting serializer is `kotlinx.datetime.UtcOffset/serializer/`[name]. + * [SerialDescriptor.serialName] must be unique across all serializers in the same serialization context. + * When defining a serializer in a library, it is recommended to use the fully qualified class name in [name] + * to avoid conflicts with serializers defined by other libraries and client code. + * + * This serializer is abstract and must be subclassed to provide a concrete serializer. + * Example: + * ``` + * // serializes the UTC offset UtcOffset(hours = 2) as the string "+0200" + * object FourDigitOffsetSerializer : FormattedUtcOffsetSerializer( + * "my.package.FOUR_DIGITS", UtcOffset.Formats.FOUR_DIGITS + * ) + * ``` + * + * Note that [UtcOffset] is [kotlinx.serialization.Serializable] by default, + * so it is not necessary to create custom serializers when the format is not important. + * Additionally, [UtcOffsetSerializer] is provided for the ISO 8601 format. + */ +public abstract class FormattedUtcOffsetSerializer( + name: String, format: DateTimeFormat +) : KSerializer by format.asKSerializer("kotlinx.datetime.UtcOffset/serializer/$name") diff --git a/core/commonKotlin/src/TimeZone.kt b/core/commonKotlin/src/TimeZone.kt index 4a27e9103..853989d27 100644 --- a/core/commonKotlin/src/TimeZone.kt +++ b/core/commonKotlin/src/TimeZone.kt @@ -14,7 +14,6 @@ import kotlinx.datetime.serializers.* import kotlinx.serialization.Serializable import kotlin.time.Instant -@Serializable(with = TimeZoneSerializer::class) public actual open class TimeZone internal constructor() { public actual companion object { @@ -78,6 +77,15 @@ public actual open class TimeZone internal constructor() { public actual val availableZoneIds: Set get() = getAvailableZoneIds() + + @Deprecated( + "Serializing TimeZone is discouraged, " + + "as deserialization can fail depending on the configuration. " + + "Please serialize the id String instead.", + level = DeprecationLevel.WARNING, + ) + @Suppress("DEPRECATION") + public actual fun serializer(): kotlinx.serialization.KSerializer = TimeZoneSerializer } public actual open val id: String @@ -126,7 +134,6 @@ public actual open class TimeZone internal constructor() { actual override fun toString(): String = id } -@Serializable(with = FixedOffsetTimeZoneSerializer::class) public actual class FixedOffsetTimeZone internal constructor(public actual val offset: UtcOffset, override val id: String) : TimeZone() { public actual constructor(offset: UtcOffset) : this(offset, offset.toString()) @@ -144,6 +151,20 @@ public actual class FixedOffsetTimeZone internal constructor(public actual val o override fun instantToLocalDateTime(instant: Instant): LocalDateTime = instant.toLocalDateTime(offset) override fun localDateTimeToInstant(dateTime: LocalDateTime): Instant = dateTime.toInstant(offset) + + /** @suppress */ + public actual companion object { + /** @suppress */ + @Deprecated( + "Serializing FixedOffsetTimeZone is discouraged, " + + "as deserialization can fail or return a non-fixed-offset zone depending on the configuration. " + + "Please serialize the id String instead.", + level = DeprecationLevel.WARNING, + ) + @Suppress("DEPRECATION") + public actual fun serializer(): kotlinx.serialization.KSerializer = + FixedOffsetTimeZoneSerializer + } } diff --git a/core/jvm/src/TimeZoneJvm.kt b/core/jvm/src/TimeZoneJvm.kt index cb1f8dda2..4469a7208 100644 --- a/core/jvm/src/TimeZoneJvm.kt +++ b/core/jvm/src/TimeZoneJvm.kt @@ -9,7 +9,6 @@ package kotlinx.datetime import kotlinx.datetime.serializers.* -import kotlinx.serialization.Serializable import java.time.DateTimeException import java.time.ZoneId import java.time.ZoneOffset as jtZoneOffset @@ -17,7 +16,6 @@ import kotlin.time.Instant import kotlin.time.toJavaInstant import kotlin.time.toKotlinInstant -@Serializable(with = TimeZoneSerializer::class) public actual open class TimeZone internal constructor(internal val zoneId: ZoneId) { public actual val id: String get() = zoneId.id @@ -71,6 +69,15 @@ public actual open class TimeZone internal constructor(internal val zoneId: Zone } public actual val availableZoneIds: Set get() = ZoneId.getAvailableZoneIds() + + @Deprecated( + "Serializing TimeZone is discouraged, " + + "as deserialization can fail depending on the configuration. " + + "Please serialize the id String instead.", + level = DeprecationLevel.WARNING, + ) + @Suppress("DEPRECATION") + public actual fun serializer(): kotlinx.serialization.KSerializer = TimeZoneSerializer } } @@ -83,7 +90,6 @@ private val ZoneId.isFixedOffset: Boolean false // Happens for America/Costa_Rica, Africa/Cairo, Egypt } -@Serializable(with = FixedOffsetTimeZoneSerializer::class) public actual class FixedOffsetTimeZone internal constructor(public actual val offset: UtcOffset, zoneId: ZoneId): TimeZone(zoneId) { @@ -91,6 +97,20 @@ internal constructor(public actual val offset: UtcOffset, zoneId: ZoneId): TimeZ @Deprecated("Use offset.totalSeconds", ReplaceWith("offset.totalSeconds")) public actual val totalSeconds: Int get() = offset.totalSeconds + + /** @suppress */ + public actual companion object { + /** @suppress */ + @Deprecated( + "Serializing FixedOffsetTimeZone is discouraged, " + + "as deserialization can fail or return a non-fixed-offset zone depending on the configuration. " + + "Please serialize the id String instead.", + level = DeprecationLevel.WARNING, + ) + @Suppress("DEPRECATION") + public actual fun serializer(): kotlinx.serialization.KSerializer = + FixedOffsetTimeZoneSerializer + } } public actual fun TimeZone.offsetAt(instant: Instant): UtcOffset = diff --git a/integration-testing/serialization/common/test/TimeZoneSerializationTest.kt b/integration-testing/serialization/common/test/TimeZoneSerializationTest.kt index b34f5633d..ed9835428 100644 --- a/integration-testing/serialization/common/test/TimeZoneSerializationTest.kt +++ b/integration-testing/serialization/common/test/TimeZoneSerializationTest.kt @@ -34,23 +34,15 @@ class TimeZoneSerializationTest { } } + @Suppress("DEPRECATION") @Test fun testZoneOffsetSerialization() { zoneOffsetSerialization(FixedOffsetTimeZoneSerializer) } + @Suppress("DEPRECATION") @Test fun testSerialization() { serialization(TimeZoneSerializer) } - - @Test - fun testDefaultSerializers() { - assertKSerializerName( - "kotlinx.datetime.FixedOffsetTimeZone", Json.serializersModule.serializer() - ) - zoneOffsetSerialization(Json.serializersModule.serializer()) - assertKSerializerName("kotlinx.datetime.TimeZone", Json.serializersModule.serializer()) - serialization(Json.serializersModule.serializer()) - } }