Дата часового пояса с весны загрузки и Джексона
Я разрабатываю весеннее загрузочное приложение, которое обрабатывает даты. Когда я отправляю объект назначения, который имеет startDateTime
и endDateTime
(Оба типа java.util.Date
) Я отправляю такой формат:
{
"lastName": "Jhon",
"firstName": "Doe",
"email": "jhon.doe@gmail.com",
"description": "MyDescription",
"startDateTime": "2017-10-09T22:43:07.109+0300",
"endDateTime": "2017-10-09T21:40:07.109+0300",
}
Когда данные сохраняются в базе данных, они находятся в правильном часовом поясе, когда я пытаюсь получить свои данные обратно, они кажутся правильными, когда я отлаживаю, однако, после того, как Джексон их сериализует, у меня есть вывод с этими значениями:
"startDateTime": "2017-10-09T19:43:07.109+0000",
"endDateTime": "2017-10-09T18:40:07.109+0000",
Как я могу настроить Джексона для использования часового пояса, который поставляется с данными из моего хранилища?
------Обновить---------
Я попробовал ответ с OffsetDateTime
но вывод довольно странный:
"startDateTime": {
"offset": {
"totalSeconds": 7200,
"id": "+02:00",
"rules": {
"fixedOffset": true,
"transitionRules": [],
"transitions": []
}
},
"month": "OCTOBER",
"year": 2017,
"hour": 21,
"minute": 49,
"nano": 654000000,
"second": 15,
"dayOfMonth": 9,
"dayOfWeek": "MONDAY",
"dayOfYear": 282,
"monthValue": 10
}
Я хотел бы иметь что-то вроде:
2017-10-09T22: 43: 07,109+0300
2 ответа
java.util.Date
не имеет никакой информации о часовом поясе. После десериализации String
к Date
, смещение +0300
теряется: дата сохраняет только значение метки времени, и она не может знать, из какого исходного часового пояса она пришла.
Если выход всегда должен быть в +03:00
смещение, вы можете установить его непосредственно в соответствующих полях, используя com.fasterxml.jackson.annotation.JsonFormat
аннотация:
@JsonFormat(timezone = "GMT+03:00")
private Date startDateTime;
@JsonFormat(timezone = "GMT+03:00")
private Date endDateTime;
При этом поля даты всегда будут сериализованы в +03:00
смещение:
{
"startDateTime":"2017-10-09T22:43:07.109+0300",
"endDateTime":"2017-10-09T21:40:07.109+0300"
}
Если входы могут быть в любом другом смещении (не только +03:00
) и вы хотите сохранить его, java.util.Date
не идеальный тип. Одной из альтернатив является использование модулей Jackson Java 8, если вы используете Java >= 8.
Для Java 6 и 7 есть ThreeTen Backport и соответствующий модуль Джексона - хотя я не тестировал, но код мог бы быть похожим, так как ThreeTen Backport содержит те же классы и методы, отличается только пакет - (в Java 8 это java.time
а в ThreeTen Backport есть org.threeten.bp
)
Чтобы сохранить дату, время и смещение, лучшей альтернативой является OffsetDateTime
учебный класс. Так что вам просто нужно изменить тип полей и установить соответствующий формат:
@JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss.SSSXX")
private OffsetDateTime startDateTime;
@JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss.SSSXX")
private OffsetDateTime endDateTime;
В сопоставителе объектов вы также должны зарегистрировать JavaTimeModule
и отключить ADJUST_DATES_TO_CONTEXT_TIME_ZONE
особенность, поэтому смещения сохраняются (поведение по умолчанию заключается в преобразовании в часовой пояс контекста Джексона, который может не совпадать с используемым во входах - при отключении этого смещение сохраняется).
Вы можете использовать JacksonConfigurator
(как объяснено в этом ответе) и сделайте эти конфигурации:
ObjectMapper om = new ObjectMapper();
om.registerModule(new JavaTimeModule());
om.configure(DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE, false);
Этой конфигурации обычно достаточно, но вы также можете установить SerializationFeature.WRITE_DATES_AS_TIMESTAMPS
в false
на всякий случай.
Если вам все еще нужно работать с java.util.Date
, вы можете использовать API для преобразования из / в него. В Java 8 есть новый Date.from
метод:
// convert to java.util.Date
public Date getStartAsJavaUtilDate() {
return Date.from(startDateTime.toInstant());
}
И в ThreeTen Backport есть org.threeten.bp.DateTimeUtils
учебный класс:
// convert to java.util.Date
DateTimeUtils.toDate(startDateTime.toInstant());
Чтобы преобразовать Date
вернуться к OffsetDateTime
Впрочем, это сложнее. Date
У объекта нет информации о часовом поясе, поэтому он не может знать исходное смещение. Один из вариантов - сохранить исходное смещение в отдельной переменной:
// keep the original offset
ZoneOffset startDateOffset = startDateTime.getOffset();
Затем вы можете преобразовать Date
в Instant
, а затем преобразовать его в исходное смещение:
// convert java.util.Date to original offset (Java 8)
startDateTime = date.toInstant().atOffset(startDateOffset);
// ThreeTen Backport
startDateTime = DateTimeUtils.toInstant(date).atOffset(startDateOffset);
Если вы используете чванство с весенней загрузкой, и ваша дата всегда сериализуется так долго. И SerializationFeature.WRITE_DATES_AS_TIMESTAMPS и spring.jackson.serialization.write -ates-as-timestamps=false не помогают ниже, решение сработало для меня. Добавьте его в свой класс с аннотацией @SpringBootApplication:
Проблема: значение SerializationFeature.WRITE_DATES_AS_TIMESTAMPS не читается из файла конфигурации Spring, который необходимо установить в false, чтобы скрыть длинное значение во время сериализации.
@Autowired
private RequestMappingHandlerAdapter handlerAdapter;
@EventListener
public void handleContextRefresh(ContextRefreshedEvent event) {
handlerAdapter
.getMessageConverters()
.stream()
.forEach(c -> {
if (c instanceof MappingJackson2HttpMessageConverter) {
MappingJackson2HttpMessageConverter jsonMessageConverter = (MappingJackson2HttpMessageConverter) c;
ObjectMapper objectMapper = jsonMessageConverter.getObjectMapper();
objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
}
});
}