JPA TemporalType. Дата, указывающая неверную дату - PullRequest
9 голосов
/ 14 марта 2011

У меня есть класс, у которого есть поле даты, представляющее дату «с начала» для части данных. Это определяется так:

@Temporal( TemporalType.DATE )
private Date validFrom;

Кажется, все работает нормально, вплоть до того момента, когда я извлекаю дату из базы данных и отображаю ее. Если я выберу дату 18-Sep-2003 во внешнем интерфейсе, то сохраню ее, когда я проверю базу данных, достаточно точно, чтобы это была проведенная дата (база данных MySQL 5.5.9, тип столбца - DATE). Однако, когда я открываю список записей, показанная дата - 17 сентября 2003 года - на один день раньше.

Если я выберу дату в начале или конце года, например, 26 марта 2003 г. или 25 декабря 2003 г., все будет в порядке, поэтому я предполагаю, что это как-то связано с переходом на летнее время, но где закрадывается ошибка? Поскольку база данных, похоже, содержит правильную дату, я предполагаю, что это должно быть, когда JPA преобразует обратно в java.util.Date. Является ли java.util.Date лучшим классом для даты? Я видел несколько примеров, когда люди используют Calendar, но он кажется довольно тяжелым, и я не уверен, насколько хорошо он будет работать с интерфейсом на основе JSF.

Ответы [ 6 ]

24 голосов
/ 04 февраля 2013

Очень жаль, но все ответы пока вообще неверны. Ответ довольно прост, но требует, чтобы мы разделили пять пунктов:

  1. DATE = java.sql.Date, которая является оболочкой для java.util.Date, которая представляет собой количество миллисекунд с начала эпохи в часовом поясе UTC. Таким образом, это имеет год / месяц / дату / часы / минуты / секунды в фиксированном часовом поясе GMT ​​+ 0 (UTC). Однако обратите внимание, что java.sql.Date устанавливает нулевые компоненты времени!
  2. TIMESTAMP = java.sql.TimeStamp, который является оберткой компонента вокруг даты, которая добавляет доли секунды для поддержки стандарта типа SQL DATE. Этот класс / тип не подходит или не нужен для этого вопроса, но вкратце он имеет дату плюс время.
  3. В базе данных хранятся объекты DATE, как определено (с использованием UTC в качестве смещения от Java), но может переводить время, если настроено в базе данных, чтобы быть в другом часовом поясе. По умолчанию большинство баз данных по умолчанию используют часовой пояс локального сервера, что является очень плохой идеей. Дамы, господа ... ВСЕГДА храните объекты DATE в UTC. Читайте дальше ...
  4. Время в JVM и часовом поясе должно быть правильным. Поскольку объект Date использует UTC, рассчитывается ли смещение для вашего времени сервера? Учтите, что при наличии строгой рекомендации установить время сервера на GMT + 0 (UTC).
  5. Наконец, когда мы хотим визуализировать DATE из базы данных (используя JSF или что-либо еще), должен быть настроен на часовой пояс GMT + 0 и, если это делается со стороны сервера, также ... Ваши даты и время ВСЕГДА будут последовательны, ссылочны и все хорошее. Осталось только отрендерить время, и в ЭТОМ пользовательском агенте (например, для веб-приложения) можно было бы использовать для перевода времени GMT + 0 в «локальный» часовой пояс пользователя.

Сводка: используйте UTC (GMT + 0) на сервере, в базе данных, в ваших объектах Java.

DATE и TIMESTAMP отличаются только от перспективы базы данных тем, что TIMESTAMP несет дополнительные доли секунд. Оба используют GMT + 0 (подразумевается). JodaTime является предпочтительной структурой календаря для решения всех этих проблем, но она не устраняет проблемы несоответствия JVM настройкам часового пояса базы данных.

Если при разработке приложений от JVM до БД не используется GMT, из-за перехода на летнее время, настройки часов и все другие региональные игры, в которые играют в мировые часы ... время транзакций и все остальное будет навсегда искажены, нереферентны, противоречивы и т. д.

Еще один хороший связанный ответ о типах данных: java.util.Date vs java.sql.Date

Также обратите внимание, что в Java 8 есть обновления с улучшенной обработкой даты / времени (наконец), но это не исправляет наличие часов сервера, на которых работает JVM, в одном часовом поясе, а база данных - в другом. На этом этапе всегда происходит перевод. По этой причине в каждом крупном (умном) клиенте, с которым я работаю, часовые пояса базы данных и JVM установлены на UTC, даже если их операции в основном происходят в каком-то другом часовом поясе.

5 голосов
/ 14 марта 2011

После долгих экспериментов и поисков я почти уверен, что нашел причину проблемы.Дата хранится в java.util.Date, которая поставляется со всем багажом времени и часового пояса.Может показаться, что JPA читает дату 18 сентября 2003 года из базы данных, а затем заполняет дату следующим образом: «Чт 18 сентября 00:00:00 BST 2003» - обратите внимание, что часовой пояс был установлен на BST, вероятно, потому что он не былявно установлено базой данных.В любом случае, необходимо отформатировать вывод на странице JSF, если вы хотите видеть только такую ​​дату:

<h:outputText value="#{t.validFrom}">
    <f:convertDateTime pattern="dd MMM yyyy"/>
</h:outputText>

Это, однако, предполагает, что часовой пояс - это то, что в данный момент действует на компьютере.,В моем случае часовым поясом в настоящее время является GMT (потому что сейчас зима), поэтому при представлении даты «Чт. 18 сентября 00:00:00 BST 2003» он преобразует его в GMT, вычитая один час, оставляя на дисплее 17 сентября 2003 года.

2 голосов
/ 19 марта 2011

Но использование DATETIME вместо даты приведет к разнице в один час (или больше в зависимости от часового пояса), которую вы можете игнорировать, обрабатывая дату, но не значение времени.Для меня данные, поступающие из базы данных mysql, были правильными, но разница возникла при использовании f: convertDateTime без параметра timeZone, что приводит к тому, что по умолчанию используется GMT!, но я думаю, что это больше не будет работать, когда мы перейдем к CEST ....

1 голос
/ 27 марта 2014

У меня возникла та же проблема, но я использовал JSF 2 в качестве внешнего интерфейса. Если вы используете компоненты JSF, посмотрите на это другое обсуждение стекопотока и увидите, что JSF 2 не воспроизводится по ожидаемым правилам TimeZone. Дизайнеры реализовали это, чтобы всегда использовать GMT. В моей ситуации это привело к тому, что мои даты были отключены на 5 или 6 часов в базе данных, но отображались правильно.

JSF convertDateTime отображает предыдущий день

1 голос
/ 15 марта 2011

У меня была такая же проблема. Не знаю причину, но мой обходной путь был следующий:

В базе данных я изменил тип столбца с DATE на DATETIME.

В классе сущностей я изменил аннотацию @Temporal, но сохранил тип данных Date:

@Temporal(TemporalType.TIMESTAMP)
private Date myDate;
0 голосов
/ 23 января 2018

Была такая же проблема с SQL Server.Проблема была в старом драйвере SQL JDBC.Имел sqljdbc4.jar с апреля 2010 года, который был совместим с SQL 2000 и имел проблему с датами, уходящими в прошлое на один или два дня.Затем обновили до последней версии драйвера или даже до версии 2012 года, и проблема исчезла.

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