Подтвердить что ты не робот

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

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

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

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

4b9b3361

Ответ 1

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

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

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

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

Долгий ответ таков:

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

  • Настройте базу данных на использование часового пояса 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 часа ночи)!
  • Внутри вашего кода приложения всегда используйте даты UTC, за исключением случаев, когда вы показываете даты своим пользователям.

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

    Timestamp myDate = resultSet.getTimestamp( "my_date", Calendar.getInstance(TimeZone.getTimeZone( "UTC" )));

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

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

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

    prepareStatement = connection.prepareStatement( "UPDATE my_table SET my_time =? ГДЕ [...]" );
    java.util.Date now = new java.util.Date();//местное время!:-(
    prepareStatement.setTimestamp(1, новая отметка времени (now.getTime()))

    int result = prepareStatement.execute();

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

    prepareStatement = connection.prepareStatement( "UPDATE my_table SET my_time = NOW() WHERE [...]" );
    int result = prepareStatement.execute();

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

Ответ 2

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. Это возвращает одно и то же значение независимо от того, какой календарь вы его передаете.

Ответ 3

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

Код Buggy:

    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 Driver. Вызов setTimestamp listerally просто игнорирует параметр Calendar.

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

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

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

Ответ 4

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

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

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

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

Ответ 5

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

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