Как сохранить java.util.Date в поле отметки времени MySQL в часовом поясе UTC / GMT? - PullRequest
15 голосов
/ 21 ноября 2008

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

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

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

Ответы [ 5 ]

13 голосов
/ 22 ноября 2008

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

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

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

    Timestamp 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 now = new java.util.Date (); // местное время! : - (
    prepareStatement.setTimestamp (1, новая отметка времени (now.getTime ()));
    int result = prepareStatement.execute ();

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

    prepareStatement = connection.prepareStatement ("ОБНОВИТЬ my_table SET my_time = NOW () WHERE [...]");
    int result = readyStatement.execute ();

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

2 голосов
/ 21 марта 2013

У меня была такая же проблема, и мне потребовался почти день, чтобы выследить. Я храню столбцы 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, а не в часовом поясе моей локальной рабочей станции.

2 голосов
/ 15 ноября 2012

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

Настройка базы данных для использования часового пояса UTC

Этого на самом деле будет недостаточно для решения проблемы. Если вы передадите 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));

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

В Java при чтении из базы данных всегда используйте: Timestamp myDate = resultSet.getTimestamp ("my_date", Calendar.getInstance (TimeZone.getTimeZone ("UTC")));

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

1 голос
/ 22 ноября 2008

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

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

1 голос
/ 21 ноября 2008

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

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

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

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

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