Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 21 additions & 2 deletions core/common/src/TimeZone.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -163,6 +162,15 @@ public expect open class TimeZone {
* @sample kotlinx.datetime.test.samples.TimeZoneSamples.availableZoneIds
*/
public val availableZoneIds: Set<String>

/** @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<TimeZone>
}

/**
Expand Down Expand Up @@ -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.
Expand All @@ -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<FixedOffsetTimeZone>
}
}

@Deprecated("Use FixedOffsetTimeZone or UtcOffset instead", ReplaceWith("FixedOffsetTimeZone"))
Expand Down
68 changes: 9 additions & 59 deletions core/common/src/serializers/TimeZoneSerializers.kt
Original file line number Diff line number Diff line change
@@ -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.*
Expand All @@ -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<TimeZone> {

override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("kotlinx.datetime.TimeZone", PrimitiveKind.STRING)
Expand All @@ -33,6 +36,10 @@ public object TimeZoneSerializer: KSerializer<TimeZone> {
*
* JSON example: `"+02:00"`
*/
@Deprecated(
"Serializing FixedOffsetTimeZoneSerializer is discouraged. Please serialize the id String instead.",
level = DeprecationLevel.WARNING,
)
public object FixedOffsetTimeZoneSerializer: KSerializer<FixedOffsetTimeZone> {

override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("kotlinx.datetime.FixedOffsetTimeZone", PrimitiveKind.STRING)
Expand All @@ -51,60 +58,3 @@ public object FixedOffsetTimeZoneSerializer: KSerializer<FixedOffsetTimeZone> {
}

}

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

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<UtcOffset>
) : KSerializer<UtcOffset> by format.asKSerializer("kotlinx.datetime.UtcOffset/serializer/$name")
69 changes: 69 additions & 0 deletions core/common/src/serializers/UtcOffsetSerializers.kt
Original file line number Diff line number Diff line change
@@ -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<UtcOffset>
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<UtcOffset> {

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<UtcOffset>
) : KSerializer<UtcOffset> by format.asKSerializer("kotlinx.datetime.UtcOffset/serializer/$name")
25 changes: 23 additions & 2 deletions core/commonKotlin/src/TimeZone.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -78,6 +77,15 @@ public actual open class TimeZone internal constructor() {

public actual val availableZoneIds: Set<String>
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<TimeZone> = TimeZoneSerializer
}

public actual open val id: String
Expand Down Expand Up @@ -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())
Expand All @@ -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<FixedOffsetTimeZone> =
FixedOffsetTimeZoneSerializer
}
}


Expand Down
26 changes: 23 additions & 3 deletions core/jvm/src/TimeZoneJvm.kt
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,13 @@
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
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

Expand Down Expand Up @@ -71,6 +69,15 @@ public actual open class TimeZone internal constructor(internal val zoneId: Zone
}

public actual val availableZoneIds: Set<String> 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<TimeZone> = TimeZoneSerializer
}
}

Expand All @@ -83,14 +90,27 @@ 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) {

public actual constructor(offset: UtcOffset) : this(offset, offset.zoneOffset)

@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<FixedOffsetTimeZone> =
FixedOffsetTimeZoneSerializer
}
}

public actual fun TimeZone.offsetAt(instant: Instant): UtcOffset =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,23 +34,15 @@ class TimeZoneSerializationTest {
}
}

@Suppress("DEPRECATION")
@Test
fun testZoneOffsetSerialization() {
zoneOffsetSerialization(FixedOffsetTimeZoneSerializer)
}

@Suppress("DEPRECATION")
@Test
fun testSerialization() {
serialization(TimeZoneSerializer)
}

@Test
fun testDefaultSerializers() {
assertKSerializerName<FixedOffsetTimeZone>(
"kotlinx.datetime.FixedOffsetTimeZone", Json.serializersModule.serializer()
)
zoneOffsetSerialization(Json.serializersModule.serializer())
assertKSerializerName<TimeZone>("kotlinx.datetime.TimeZone", Json.serializersModule.serializer())
serialization(Json.serializersModule.serializer())
}
}