Я использовал новый объект Date () для заполнения поля в базе данных MySQL, но фактическое значение, хранящееся в этом поле, находится в моем местном часовом поясе.
Как я могу настроить MySQL для хранения его в часовом поясе UTC / GMT?
Думаю, поможет настройка строки подключения, но не знаю как. В строке подключения есть много свойств, таких как useTimezone, serverTimzone, useGmtMillisForDatetimes, useLegacyDatetimeCode, ...




Что ж, если мы говорим об использовании PreparedStatement, есть форма setDate, где вы можете передать Calendar, установленный для определенного часового пояса.
Например, если у вас есть PreparedStatement с именем stmt, Date с именем date и предполагается, что дата является вторым параметром:
stmt.setDate(2, date, Calendar.getInstance(TimeZone.getTimeZone("GMT")));
Самое приятное, что даже если GMT является недопустимым именем часового пояса, он все равно возвращает часовой пояс GMT из-за поведения getTimeZone по умолчанию.
Я всегда предпочитаю использовать UTC вместо GMT.
По умолчанию объект Calendar полностью игнорируется, если вы не используете параметр подключения useLegacyDatetimeCode = false
Короткий ответ:
при получении / установке дат или отметок времени с помощью JDBC всегда используйте параметр Calendar, установленный на UTC:
resultset.getTimestamp ("my_date", Calendar.getInstance (TimeZone.getTimeZone ("UTC")));
Длинный ответ таков:
Имея дело с датами и часовыми поясами в любой базе данных и с любым клиентским кодом, я обычно рекомендую следующую политику:
Настройте свою базу данных на использование часового пояса UTC, вместо использования местного часового пояса сервера (если, конечно, это не UTC).
Как это сделать, зависит от вашего сервера базы данных. Инструкции для MySQL можно найти здесь: http://dev.mysql.com/doc/refman/5.0/en/time-zone-support.html. В основном вам нужно записать это в my.cnf: default-time-zone = utc
Таким образом, вы можете разместить свои серверы баз данных где угодно, легко изменить местоположение вашего хостинга и, в более общем плане, без двусмысленности манипулировать датами на своих серверах.
Внутри кода приложения всегда используйте даты в формате UTC, за исключением случаев, когда даты отображаются для ваших пользователей.
Отметка времени myDate = resultSet.getTimestamp ("my_date", Calendar.getInstance (TimeZone.getTimeZone ("UTC")));
Синхронизируйте свои серверы или полагайтесь только на время сервера базы данных
Если у вас есть веб-сервер на одном сервере (или нескольких), а сервер базы данных - на каком-то другом, я настоятельно рекомендую вам синхронизировать их часы с 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
очень хорошие моменты! Ошибка. Мне нужно поднять еще несколько вопросов. Вы не рассматривали возможность установки часового пояса сеанса при каждом подключении к db, при этом вам не нужно менять часовой пояс, в котором запущен ваш MySQL. У меня много проблем при сохранении 'new java.util.Date' в db. Быстрое решение - настроить мою JVM также на запуск в UTC (-Duser.timezone = UTC). Эти баллы стоит добавить в свой пост!
Согласно документации MySQL по системной переменной часовой пояс, имя переменной my.cnf - default_time_zone (подчеркивания, а не дефисы, показанные в вашем сообщении).
Дата 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, а не в часовом поясе моей локальной рабочей станции.
Вы используете Hibernate или аналогичный для интерфейса с базой данных?