@@ -24,6 +24,7 @@ import java.io.InputStream
2424import java.time.DateTimeException
2525import java.time.LocalDate
2626import java.time.LocalDateTime
27+ import java.time.OffsetDateTime
2728import java.time.ZonedDateTime
2829import java.time.format.DateTimeFormatter
2930import java.time.temporal.ChronoField
@@ -37,6 +38,7 @@ fun jsonMapper(): JsonMapper =
3738 SimpleModule ()
3839 .addSerializer(InputStreamSerializer )
3940 .addDeserializer(LocalDateTime ::class .java, LenientLocalDateTimeDeserializer ())
41+ .addDeserializer(OffsetDateTime ::class .java, LenientOffsetDateTimeDeserializer ())
4042 )
4143 .withCoercionConfig(LogicalType .Boolean ) {
4244 it.setCoercion(CoercionInputShape .Integer , CoercionAction .Fail )
@@ -123,6 +125,33 @@ private object InputStreamSerializer : BaseSerializer<InputStream>(InputStream::
123125 }
124126}
125127
128+ /* *
129+ * A deserializer that can deserialize [OffsetDateTime], assuming UTC when a timezone isn't given.
130+ */
131+ private class LenientOffsetDateTimeDeserializer : StdDeserializer <OffsetDateTime >(OffsetDateTime : :class.java) {
132+ override fun logicalType (): LogicalType = LogicalType .DateTime
133+
134+ override fun deserialize (p : JsonParser , context : DeserializationContext ? ): OffsetDateTime {
135+ val exceptions = mutableListOf<Exception >()
136+
137+ try {
138+ return OffsetDateTime .parse(p.text)
139+ } catch (e: DateTimeException ) {
140+ exceptions.add(e)
141+ }
142+
143+ try {
144+ return OffsetDateTime .parse(p.text + ' Z' )
145+ } catch (e: DateTimeException ) {
146+ exceptions.add(e)
147+ }
148+
149+ throw JsonParseException (p, " Cannot parse `OffsetDateTime` from value: ${p.text} " ).apply {
150+ exceptions.forEach { addSuppressed(it) }
151+ }
152+ }
153+ }
154+
126155/* *
127156 * A deserializer that can deserialize [LocalDateTime] from datetimes, dates, and zoned datetimes.
128157 */
0 commit comments