Я произвел рефакторинг некоторого устаревшего кода в системе Spring Boot (2.1.2) и перешел с классов java.util.Date
на java.time
(jsr310).Система ожидает даты в форматированной строке ISO8601, в то время как некоторые являются полными временными метками с информацией о времени (например, "2019-01-29T15:29:34+01:00"
), в то время как другие являются только датами со смещением (например, "2019-01-29+01:00"
).Вот DTO (как класс данных Kotlin):
data class Dto(
// ...
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ssXXX")
@JsonProperty("processingTimestamp")
val processingTimestamp: OffsetDateTime,
// ...
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-ddXXX")
@JsonProperty("orderDate")
val orderDate: OffsetDateTime,
// ...
)
Хотя Джексон отлично десериализует processingTimestamp
, он терпит неудачу с orderDate
:
Caused by: java.time.DateTimeException: Unable to obtain OffsetDateTime from TemporalAccessor: {OffsetSeconds=32400},ISO resolved to 2018-10-23 of type java.time.format.Parsed
at java.time.OffsetDateTime.from(OffsetDateTime.java:370) ~[na:1.8.0_152]
at com.fasterxml.jackson.datatype.jsr310.deser.InstantDeserializer.deserialize(InstantDeserializer.java:207) ~[jackson-datatype-jsr310-2.9.8.jar:2.9.8]
Это имеет смысл для меня,так как OffsetDateTime
не может найти информацию о времени, необходимую для создания момента.Если я перехожу на val orderDate: LocalDate
, Джексон может успешно десериализоваться, но тогда информация о смещении исчезает (которую мне нужно преобразовать в Instant
позже).
Вопрос
Мой текущий обходной путь -используйте OffsetDateTime
в сочетании с пользовательским десериализатором (см. ниже). Но мне интересно, есть ли лучшее решение для этого?
Кроме того, я хотел бы иметь более подходящий тип данных, например OffsetDate , но яне могу найти его в java.time
.
PS
Я спрашивал себя, является ли "2019-01-29 + 01: 00" допустимым для ISO8601.Однако, поскольку я обнаружил, что java.time.DateTimeFormatter.ISO_DATE
может правильно его проанализировать, и я не могу изменить формат отправки данных клиентами, я отложил этот вопрос.
Обходной путь
data class Dto(
// ...
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-ddXXX")
@JsonProperty("catchDate")
@JsonDeserialize(using = OffsetDateDeserializer::class)
val orderDate: OffsetDateTime,
// ...
)
class OffsetDateDeserializer(
private val formatter: DateTimeFormatter = DateTimeFormatter.ISO_DATE
) : JSR310DateTimeDeserializerBase<OffsetDateTime>(OffsetDateTime::class.java, formatter) {
override fun deserialize(parser: JsonParser, context: DeserializationContext): OffsetDateTime? {
if (parser.hasToken(JsonToken.VALUE_STRING)) {
val string = parser.text.trim()
if (string.isEmpty()) {
return null
}
val parsed: TemporalAccessor = formatter.parse(string)
val offset = if(parsed.isSupported(ChronoField.OFFSET_SECONDS)) ZoneOffset.from(parsed) else ZoneOffset.UTC
val localDate = LocalDate.from(parsed)
return OffsetDateTime.of(localDate.atStartOfDay(), offset)
}
throw context.wrongTokenException(parser, _valueClass, parser.currentToken, "date with offset must be contained in string")
}
override fun withDateFormat(otherFormatter: DateTimeFormatter?): JsonDeserializer<OffsetDateTime> = OffsetDateDeserializer(formatter)
}