null

Что если LocalDate сохраняется в БД c отставанием в один день?

Достаточно часто при работе с датами и хранением их в базе данных возникают различные проблемы. Сегодня рассмотрим одну из них. А именно, случай, когда при сохранении сущности, которая содержит LocalDate, данные в Java' и БД отличаются на один день.

 

Опишу ситуацию при которой возникла проблема.

Есть веб-приложение Spring + ReactJS. Данные хранятся в mysql, а живёт это всё на Linux. В определенный момент, замечаешь, что выбранная дата в интерфейсе после сохранения на сервере и обновления UI, показывает выбранное значение, но из него вычтен один день. На самом деле это часто возникающая бага.

Посмотрев  в networkManager'е браузера какие данные идут на сервер и возвращаются с него, убедился, что проблема не на стороне фронтенда. Данные на сервер отправляются в формате json строкой вида "2017-02-10". А возвращаются после обновления в таком виде "2017-02-09".

Далее я направился к Spring'у. Посмотрел, что endpoint, принимает DTO класс, в котором есть поле типа LocalDate прямиком из 8ой java'ы. Добавив вывод в лог, узнали что в endpoint приходит правильное значение  "2017-02-10". 

 

 

И что тогда? Тогда проблема лежит где-то в сохранении данных в БД. Немного размышлений приводят к тому, что проблемы в разных timezone'ах.  Давайте подключимся к БД и проверим, какая таймзона стоит там.

mysql> SELECT @@global.time_zone, @@session.time_zone;
+--------------------+---------------------+
| @@global.time_zone | @@session.time_zone |
+--------------------+---------------------+
| SYSTEM             | SYSTEM              |
+--------------------+---------------------+

Таймзону БД берёт из системы, давайте посмотрим, что установлено в системе.

cat /etc/timezone
Europe/Moscow

Для большей уверенности попробуем сравнить время в системе и БД.

date
Чт дек 28 19:40:26 MSK 2017
mysql> select now();
+---------------------+
| now()               |
+---------------------+
| 2017-12-28 19:40:49 |
+---------------------+

Время совпадает. Следовательно, java по default'у должна брать такое же системное время, какое и в mysql? Но в чём тогда проблема?

 

Предлагаю сразу перейти к ответу:

serverTimezone

Override detection/mapping of time zone. Used when time zone from server doesn't map to Java time zone

Что это такое? Это документация по JDBC в mysql. Можно почитать это здесь. Что в неё такого интересного? А то, что, при задании jdbc.url в него также передаются различные параметры. ServerTimezone - это как раз параметр, который опредялет с какой таймзоной будет посылаться время из java'ы в mysql. Следовательно, БД получив дату в с таймозоной не свопадающей с ней, приводит её к своей таймзоне. Где это можно посмотреть?

В application.properties ищем следующую строку

spring.datasource.url = jdbc:mysql://${DB_ADDRESS}:3306/${DB_NAME}?serverTimezone=UTC

Как видно, в нашем примере проблема в том, что задана таймзона UTC, а в БД используется Europe/Moscow.  Чтобы решить эту проблему, нужно сменить параметр serverTimezone.

serverTimezone=Europe/Moscow

Если вы пользуетесь конфигурацией datasource через сервер приложений, то datasource.url задаётся в админке сервера приложений, или же в его конфигах.

После изменения параметра, всё стало работать корректно. На этой ноте я вас покину.