@@ -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 )
@@ -165,3 +167,31 @@ private class LenientLocalDateTimeDeserializer :
165167 }
166168 }
167169}
170+
171+ /* *
172+ * A deserializer that can deserialize [OffsetDateTime], assuming UTC when a timezone isn't given.
173+ */
174+ private class LenientOffsetDateTimeDeserializer :
175+ StdDeserializer <OffsetDateTime >(OffsetDateTime ::class .java) {
176+ override fun logicalType (): LogicalType = LogicalType .DateTime
177+
178+ override fun deserialize (p : JsonParser , context : DeserializationContext ? ): OffsetDateTime {
179+ val exceptions = mutableListOf<Exception >()
180+
181+ try {
182+ return OffsetDateTime .parse(p.text)
183+ } catch (e: DateTimeException ) {
184+ exceptions.add(e)
185+ }
186+
187+ try {
188+ return OffsetDateTime .parse(p.text + ' Z' )
189+ } catch (e: DateTimeException ) {
190+ exceptions.add(e)
191+ }
192+
193+ throw JsonParseException (p, " Cannot parse `OffsetDateTime` from value: ${p.text} " ).apply {
194+ exceptions.forEach { addSuppressed(it) }
195+ }
196+ }
197+ }
0 commit comments