Как сохранить java.util.Date в поле метки времени MySQL в часовом поясе UTC / GMT?

Я использовал новый объект Date () для заполнения поля в базе данных MySQL, но фактическое значение, хранящееся в этом поле, находится в моем местном часовом поясе.

Как я могу настроить MySQL для хранения его в часовом поясе UTC / GMT?

Думаю, поможет настройка строки подключения, но не знаю как. В строке подключения есть много свойств, таких как useTimezone, serverTimzone, useGmtMillisForDatetimes, useLegacyDatetimeCode, ...

Вы используете Hibernate или аналогичный для интерфейса с базой данных?

activout.se 22.11.2008 20:08
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
15
1
20 868
5
Перейти к ответу Данный вопрос помечен как решенный

Ответы 5

Что ж, если мы говорим об использовании PreparedStatement, есть форма setDate, где вы можете передать Calendar, установленный для определенного часового пояса.

Например, если у вас есть PreparedStatement с именем stmt, Date с именем date и предполагается, что дата является вторым параметром:

stmt.setDate(2, date, Calendar.getInstance(TimeZone.getTimeZone("GMT")));

Самое приятное, что даже если GMT является недопустимым именем часового пояса, он все равно возвращает часовой пояс GMT из-за поведения getTimeZone по умолчанию.

Я всегда предпочитаю использовать UTC вместо GMT.

rafa.ferreira 20.05.2011 17:55

По умолчанию объект Calendar полностью игнорируется, если вы не используете параметр подключения useLegacyDatetimeCode = false

nogridbag 12.05.2012 02:29
Ответ принят как подходящий

Короткий ответ:

  • добавить "default-time-zone = utc" в my.cnf
  • в коде всегда "думайте" в формате UTC, кроме случаев отображения дат для пользователей.
  • при получении / установке дат или отметок времени с помощью JDBC всегда используйте параметр Calendar, установленный на UTC:

    resultset.getTimestamp ("my_date", Calendar.getInstance (TimeZone.getTimeZone ("UTC")));

  • либо синхронизируйте свои серверы с NTP, либо полагайтесь только на сервер базы данных, чтобы сказать вам, который час.

Длинный ответ таков:

Имея дело с датами и часовыми поясами в любой базе данных и с любым клиентским кодом, я обычно рекомендую следующую политику:

  1. Настройте свою базу данных на использование часового пояса UTC, вместо использования местного часового пояса сервера (если, конечно, это не UTC).

    • Как это сделать, зависит от вашего сервера базы данных. Инструкции для MySQL можно найти здесь: http://dev.mysql.com/doc/refman/5.0/en/time-zone-support.html. В основном вам нужно записать это в my.cnf: default-time-zone = utc

    • Таким образом, вы можете разместить свои серверы баз данных где угодно, легко изменить местоположение вашего хостинга и, в более общем плане, без двусмысленности манипулировать датами на своих серверах.

    • Если вы действительно предпочитаете использовать местный часовой пояс, я рекомендую хотя бы отключить летнее время, потому что наличие неоднозначных дат в вашей базе данных может стать настоящим кошмаром.
      • Например, если вы создаете службу телефонии и используете летнее время на сервере базы данных, тогда вы просите о проблеме: не будет возможности узнать, звонил ли клиент с номера "2008-10-26 02:30 : 00 "на" 2008-10-26 02:35:00 "фактически звонил на 5 минут или на 1 час 5 минут (предположим, что переход на летнее время произошел 26 октября в 3 часа ночи)!
  2. Внутри кода приложения всегда используйте даты в формате UTC, за исключением случаев, когда даты отображаются для ваших пользователей.

    • В Java при чтении из базы данных всегда используйте:

    Отметка времени myDate = resultSet.getTimestamp ("my_date", Calendar.getInstance (TimeZone.getTimeZone ("UTC")));

    • Если вы этого не сделаете, будет считаться, что метка времени находится в вашем местном часовом поясе, а не в формате UTC.
  3. Синхронизируйте свои серверы или полагайтесь только на время сервера базы данных

    • Если у вас есть веб-сервер на одном сервере (или нескольких), а сервер базы данных - на каком-то другом, я настоятельно рекомендую вам синхронизировать их часы с NTP.

    • ИЛИ, полагайтесь только на один сервер, чтобы сказать вам, который час. Обычно лучше всего запрашивать время у сервера базы данных. Другими словами, избегайте такого кода:

    prepareStatement = connection.prepareStatement ("ОБНОВЛЕНИЕ my_table SET my_time =? WHERE [...]");
    java.util.Date сейчас = новый java.util.Date (); // местное время! :-(
    prepareStatement.setTimestamp (1, новая отметка времени (now.getTime ()));
    int результат = подготовленныйStatement.execute ();

    • Вместо этого полагайтесь на время сервера базы данных:

    prepareStatement = connection.prepareStatement ("ОБНОВЛЕНИЕ my_table SET my_time = NOW () WHERE [...]");
    int результат = подготовленныйStatement.execute ();

Надеюсь это поможет! :-)

Два дополнения: 1. Вы должны использовать default-time-zone = GMT (с учетом регистра), потому что MySQL недостаточно умен, чтобы самостоятельно отображать «utc» в «GMT», а Java распознает только последнее. 2. Вы должны передать драйвер useLegacyDatetimeCode = false, чтобы исправить эту ошибку: bugs.mysql.com/bug.php?id=15604

Gili 16.12.2008 07:51

очень хорошие моменты! Ошибка. Мне нужно поднять еще несколько вопросов. Вы не рассматривали возможность установки часового пояса сеанса при каждом подключении к db, при этом вам не нужно менять часовой пояс, в котором запущен ваш MySQL. У меня много проблем при сохранении 'new java.util.Date' в db. Быстрое решение - настроить мою JVM также на запуск в UTC (-Duser.timezone = UTC). Эти баллы стоит добавить в свой пост!

rafa.ferreira 20.05.2011 17:29

Согласно документации MySQL по системной переменной часовой пояс, имя переменной my.cnf - default_time_zone (подчеркивания, а не дефисы, показанные в вашем сообщении).

Scott D. Strader 02.07.2012 01:19

Дата Java не зависит от часового пояса. Он ВСЕГДА представляет дату в GMD (UTC) в миллисекундах от эпохи.

ЕДИНСТВЕННОЕ время, когда актуален часовой пояс, - это когда вы генерируете дату в виде строки или анализируете строку данных в объект даты.

MiniQuark дал несколько хороших ответов для баз данных в целом, но есть некоторые особенности MySql, которые следует учитывать ...

Configure your database to use UTC timezone

На самом деле этого недостаточно, чтобы решить проблему. Если вы передадите java.util.Date в MySql, как запрашивал OP, драйвер MySql выполнит изменить значение, чтобы оно выглядело как то же местное время в часовом поясе базы данных.

Пример: ваша база данных настроена на UTC. Ваше приложение - EST. Вы передаете объект java.util.Date на 5:00 (EST). База данных преобразует его в 5:00 UTC и сохранит. Потрясающий.

Вам нужно будет настроить время, прежде чем передавать данные, чтобы «отменить» эту автоматическую настройку. Что-то типа...

long originalTime = originalDate.getTime();
Date newDate = new Date(originalTime - TimeZone.getDefault().getOffset(originalTime));
ps.setDate(1, newDate);

Для обратного чтения данных требуется аналогичное преобразование.

long dbTime = rs.getTimestamp(1).getTime();
Date originalDate = new Date(dbTime + TimeZone.getDefault().getOffset(dbTime));

Вот еще одна забавная причуда ...

In Java, when reading from the database, always use: Timestamp myDate = resultSet.getTimestamp("my_date", Calendar.getInstance(TimeZone.getTimeZone("UTC")));

MySql фактически игнорирует этот параметр Calendar. Это возвращает одно и то же значение независимо от того, какой календарь вы его передали.

У меня была такая же проблема, и мне потребовался почти день, чтобы ее разыскать. Я храню столбцы DateTime в MySQL. Экземпляр RDS, работающий в облаке Amazon, по умолчанию правильно настроен на временную метку UTC.

Код ошибки:

    String startTime = "2013-02-01T04:00:00.000Z";
    DateTime dt = ISODateTimeFormat.dateTimeParser().parseDateTime(startTime);            

    PreparedStatement stmt = connection.prepareStatement(insertStatementTemplate);

    Timestamp ts = new Timestamp(dt.getMillis());
    stmt.setTimestamp(1, ts, Calendar.getInstance(TimeZone.getTimeZone("UTC"))); 

В приведенном выше коде вызов ".setTimestamp" НЕ принимает дату как дату в формате UTC!

После нескольких часов расследования выяснилось, что это известная ошибка в драйвере Java / MySQL. Вызов setTimestamp листерально просто игнорирует параметр Calendar.

Чтобы исправить это, добавьте «useLegacyDatetimeCode = false» в URI вашей базы данных.

private final static String DatabaseName =
  "jdbc:mysql://foo/?useLegacyDatetimeCode=false";

Как только я это сделал, дата, хранящаяся в базе данных MySQL, была в правильной форме UTC, а не в часовом поясе моей локальной рабочей станции.

Другие вопросы по теме