Сериализация java.sql. Дата через Джексона при учете летнего времени - PullRequest
0 голосов
/ 04 марта 2019

Я использую java.sql.Date для хранения поля даты в одном из объектов моего домена.Это поле сопоставлено с колонкой MySQL DATE.Когда я пытаюсь сериализовать это поле в JSON через Джексона, Джексон, похоже, не учитывает переход на летнее время.

Вот как выглядит поле в моем доменном объекте:

@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd", timezone = "EST")
private Date date;

Как видите, Джексон получил указание интерпретировать часовой пояс как EST.Моя база данных MySQL также использует часовой пояс EST:

SHOW VARIABLES LIKE '%zone';
Output:
system_time_zone | EST
time_zone        | SYSTEM

Моя проблема в том, что для дат между началом и окончанием перехода на летнее время Джексон возвращает на один день меньше даты, сохраненной в базе данных.Я предполагаю, что это потому, что Джексон не рассматривает переход на летнее время.

Я пришел к такому выводу, пытаясь изменить формат даты с yyyy-MM-dd на yyyy-MM-dd HH:mm:ss в поле Java.Я заметил, что сервер вернул что-то вроде 2017-03-13 00:00:00, но Джексон сериализовал его в 2017-03-12 23:00:00 (на час меньше, то есть на сколько времени EST влияет летнее время).

Есть ли способ преодолетьЭта проблема?Кроме того, java.sql.Date является правильным типом для использования, учитывая, что у него нет аналога по времени?Я подумывал об использовании java.time.LocalDate вместо этого, но еще не выяснил, будет ли он успешным.

Заранее спасибо!

1 Ответ

0 голосов
/ 04 марта 2019

tl; др

  • Никогда не используйте java.sql.Date или java.util.Date.Используйте только java.time классы.
  • Для значения только на дату используйте LocalDate.Вы увидите только стабильные значения без влияния перехода на летнее время (DST).

Пример:

LocalDate.parse( "2019-01-23" )

java.sql.Date означает , а не дату

Я не совсем уверен, в чем проблема, но я подозреваю, что это связано с использованием вами ужасного класса java.sql.Date, и, следовательно, спорным.

java.sql.Date class делает вид, что представляет значение только для даты, без времени суток и без часового пояса.Однако из-за некоторых невообразимо плохих дизайнерских решений этот класс расширяется java.util.Date.java.util.Date класс имеет время суток и находится в UTC.Еще более запутанно то, что java.util.Date имеет часовой пояс, скрытый в исходном коде без методов получения и установки, поэтому, похоже, его еще нет, что влияет на поведение таких методов, как equals.Таким образом, java.sql.Date, несмотря на название и цель хранения только даты, на самом деле имеет время суток, установленное в UTC.Так что java.sql.Date делает несколько трюков в настройке времени суток, что, вероятно, является проблемой, с которой вы сталкиваетесь.Эти устаревшие классы даты и времени представляют собой большую дымящуюся кучу… овсянки.Никогда не используйте их.

Чтобы процитировать JavaDoc для класса java.sql.Date:

Тонкая оболочка вокруг миллисекундного значения, позволяющая JDBC идентифицироватьэто как значение SQL DATE.Значение в миллисекундах представляет количество миллисекунд, прошедших с 1 января 1970 года 00: 00: 00.000 GMT.

Чтобы соответствовать определению SQL DATE, значения в миллисекундах переносятся экземпляром java.sql.Date.должно быть «нормализовано» путем установки часов, минут, секунд и миллисекунд на ноль в конкретном часовом поясе, с которым связан экземпляр.

Кстати, java.sql.Timestamp такой же плохой беспорядок.Он также неуклюже наследует от java.util.Date, добавляя секунду доли секунды для наносекунд.Также избегайте этого класса, теперь его заменяют на java.time.Instant или java.time.OffsetDateTime.Аналогично, java.sql.Time заменяется на java.time.LocalTime.

java.time.LocalDate - это по-настоящему дата

С внедрением JSR 310 и JDBC 4.2 вы можете использовать современные ведущие java.time классы.К настоящему времени Джексон, возможно, был обновлен для использования java.time .Если нет, см. этот вопрос для ссылок на модули типа данных для обработки java.time в Джексоне.

Похоже, что ваши входные строки в стандарте Формат ISO 8601 , ГГГГ-ММ-ДД.Классы java.time по умолчанию используют стандартные форматы при разборе / генерации строк, представляющих значения даты и времени.Для использования только по дате LocalDate класс, который действительно является датой без времени суток и без зоны / смещения.Вы увидите стабильные значения даты без изменений: Летнее время (DST) .

Разбор.

LocalDate ld = LocalDate.parse( "2019-01-23" ) ;

Store.

myPreparedStatement.setObject( … , ld ) ;

Получить.

LocalDate ld = myResultSet.getObject( … , LocalDate.class ) ;

О LocalDate

Класс LocalDate представляет значение только для даты без времени суток и без часовой пояс или смещение от UTC .

Часовой пояс имеет решающее значение при определении даты.В любой момент времени дата меняется по всему земному шару в зависимости от зоны.Например, через несколько минут после полуночи в Париж Франция - это новый день, в то время как «вчера» в Монреаль Квебек .

Если часовой пояс не указан,JVM неявно применяет свой текущий часовой пояс по умолчанию.Это значение по умолчанию может измениться в любой момент во время выполнения (!), Поэтому ваши результаты могут отличаться.Лучше явно указать желаемый / ожидаемый часовой пояс в качестве аргумента.Если критично, подтвердите зону с вашим пользователем.

Spвведите правильное имя часового пояса в формате Continent/Region, например America/Montreal, Africa/Casablanca или Pacific/Auckland.Никогда не используйте 2-4-буквенное сокращение, такое как EST или IST, поскольку они не истинные часовые пояса, не стандартизированы и даже не уникальны (!).

ZoneId z = ZoneId.of( "America/Montreal" ) ;  
LocalDate today = LocalDate.now( z ) ;

Если вы хотите использовать текущий часовой пояс JVM по умолчанию, запросите его и передайте в качестве аргумента.Если опущен, код становится неоднозначным для чтения, поскольку мы точно не знаем, намеревались ли вы использовать значение по умолчанию или если вы, как и многие другие программисты, не знали о проблеме.

ZoneId z = ZoneId.systemDefault() ;  // Get JVM’s current default time zone.

Или укажитесвидание.Вы можете установить месяц по номеру, с нормальным номером 1-12 для января-декабря.

LocalDate ld = LocalDate.of( 1986 , 2 , 23 ) ;  // Years use sane direct numbering (1986 means year 1986). Months use sane numbering, 1-12 for January-December.

Или, лучше, использовать предварительно определенные объекты перечисления Month, по одному на каждый месяц года.Совет: используйте эти Month объекты по всей вашей кодовой базе, а не просто целое число, чтобы сделать ваш код более самодокументируемым, обеспечить допустимые значения и обеспечить type-safety .То же самое для Year & YearMonth.

LocalDate ld = LocalDate.of( 1986 , Month.FEBRUARY , 23 ) ;

О java.time

Инфраструктура java.time встроена в Java 8 и более поздние версии.Эти классы вытесняют проблемные старые устаревшие классы даты и времени, такие как java.util.Date, Calendar и & SimpleDateFormat.

Чтобы узнать больше, см. Oracle Tutorial .И поиск переполнения стека для многих примеров и объяснений.Спецификация: JSR 310 .

Проект Joda-Time , теперь в режиме обслуживания , рекомендует перейти на java.time классы.

Вы можете обмениваться java.time объектами напрямую с вашей базой данных.Используйте драйвер JDBC , совместимый с JDBC 4.2 или более поздней версии.Нет необходимости в строках, нет необходимости в java.sql.* классах.

Где получить классы java.time?

  • Java SE 8 , Java SE 9 , Java SE 10, Java SE 11 и более поздние версии - часть стандартного Java API с связанной реализацией.
    • Java 9 добавляет некоторые незначительные функции и исправления.
  • Java SE 6 и JavaSE 7
    • Большинство функций java.time перенесены в Java 6 & 7 в ThreeTen-Backport .
  • Android
    • Более поздние версии пакетов Android для реализации java.time классы.
    • Для более ранних версий Android (<26) проект <a href="https://github.com/JakeWharton/ThreeTenABP" rel="nofollow noreferrer"> ThreeTenABP адаптируется ThreeTen-Backport (упомянуто выше).См. Как использовать ThreeTenABP… .

ThreeTen-Extra Проект расширяет java.time дополнительными классами.Этот проект является полигоном для возможных будущих дополнений к java.time.Вы можете найти здесь несколько полезных классов, таких как Interval, YearWeek, YearQuarter и more .

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...