6.6.1 Сохранение мгновений времени
Обзор
Мгновение времени — это конкретный момент на временной шкале. Мгновение времени считается сохранённым, когда оно всегда ссылается на ту же точку во времени, когда его значение сохраняется в базе данных или извлекается из неё, независимо от часовых поясов сервера базы данных и клиентов.
TIMESTAMP — единственный тип данных MySQL, предназначенный для хранения мгновений. Для сохранения мгновений времени сервер применяет преобразования часовых поясов к входящим или исходящим значениям времени при необходимости. Входящие значения преобразуются сервером из в координированное универсальное время (UTC) для хранения, а исходящие значения преобразуются из UTC в часовой пояс сеанса. Начиная с MySQL 8.0.19, вы также можете указать смещение часового пояса при хранении TIMESTAMP значений (см. для получения подробностей), в этом случае значения TIMESTAMP преобразуются в UTC из указанного смещения вместо часового пояса сеанса. Однако, после хранения информация об исходном смещении больше не сохраняется.
Ситуация менее проста с типом данных DATETIME: он не представляет мгновение, и при отсутствии смещения часового пояса нет преобразования часового пояса для DATETIME значений, поэтому они хранятся и извлекаются как есть. Однако при указании смещения часового пояса входное значение преобразуется в часовой пояс сеанса перед хранением; в результате, при извлечении в другом сеансе с другим смещением часового пояса, отличным от указанного, значение DATETIME отличается от исходного входного значения.
Поскольку типы данных MySQL, отличные от TIMESTAMP (и обертки Java для этих других типов данных MySQL), не представляют истинных мгновений времени; смешивание типов данных даты и времени, представляющих мгновения, и не представляющих мгновения, при хранении и извлечении значений может привести к неожиданным результатам. Например:
При хранении
java.sql.Timestamp, например, в столбцеDATETIME, вы можете не получить то же значение мгновения при извлечении его в клиент, который находится в другом часовом поясе, чем клиент, в котором было сохранено значение.При хранении, например,
java.time.LocalDateTimeв столбцеTIMESTAMP, вы можете не сохранить правильное значение, основанное на UTC, потому что часовой пояс значения фактически не определён.
Поэтому не передавайте типы даты и времени мгновений (java.util.Calendar, java.util.Date, java.time.OffsetDateTime, java.sql.Timestamp) в типы даты и времени, не являющиеся мгновениями (например, java.sql.DATE, java.time.LocalDate, java.time.LocalTime, java.time.OffsetTime) или наоборот, при работе с сервером.
Остальная часть раздела описывает, как сохранить мгновения времени при работе с Connector/J.
Сохранение мгновений с Connector/J
Ситуация: Предположим, что приложение работает на определённом сервере приложений и подключается к серверу MySQL с помощью Connector/J. В сеансе подключения происходят определённые события, для которых генерируются отметки времени, и отметки времени событий связаны с часовым поясом JVM сервера приложений. Эти отметки времени должны быть сохранены на сервере MySQL, и должны быть извлечены из него позже.
Задача: Значения мгновений времени должны быть сохранены при сохранении или извлечении их с сервера с помощью Connector/J. Поскольку сервер MySQL всегда неявно предполагает, что значение мгновения времени относится к часовому поясу сеанса подключения (который задаётся переменной сеанса ), когда оно сохраняется или извлекается с сервера, значение мгновения времени сохраняется правильно только в следующих ситуациях:
Когда Connector/J работает в том же часовом поясе, что и сервер MySQL (т. е. часовой пояс сеанса сервера такой же, как и часовой пояс JVM), мгновения времени сохраняются естественным образом, и преобразование часовых поясов не требуется. Обратите внимание, что в этом случае мгновения времени сохраняются только в том случае, если сервер и JVM продолжают работать всегда в одном и том же часовом поясе в будущем.
-
Когда Connector/J работает в другом часовом поясе, чем сервер MySQL (т. е. часовой пояс JVM отличается от часового пояса сеанса сервера), Connector/.J выполняет одно из следующих действий:
Запрашивает значение часового пояса сеанса с сервера и преобразует отметки времени событий между часовым поясом сеанса и часовым поясом JVM.
Изменяет часовой пояс сеанса сервера на часовой пояс JVM, после чего преобразование часовых поясов не потребуется.
Изменяет часовой пояс сеанса сервера на желаемый часовой пояс, указанный пользователем, а затем преобразует отметки времени между часовым поясом JVM и указанным пользователем часовым поясом.
Мы идентифицируем вышеуказанные решения для сохранения мгновений времени как Решение 1, 2a, 2b и 2c. Для достижения этих решений были введены следующие свойства подключения в Connector/J с версии 8.0.23:
-
preserveInstants={true|false}: Попытка сохранить значения мгновений времени путём корректировки отметки времени.-
Когда оно
false, преобразования не выполняются; отметка времени отправляется на сервер как есть для хранения, и сохраняется её визуальное представление, а не фактическое мгновение времени. При извлечении её с сервера Connector/J, с ней могут быть связаны разные часовые пояса, так как извлечение может происходить в различных часовых поясах JVM. Например: Например:Часовые пояса: UTC для JVM, UTC+1 для сеанса сервера
Исходная отметка времени от клиента (в UTC):
2020-01-01 01:00:00Отметка времени, отправленная на сервер Connector/J:
2020-01-01 01:00:00(без преобразования)Значения отметки времени, хранящиеся внутри сервера:
2020-01-01 00:00:00 UTC(после внутреннего преобразования2020-01-01 00:00:00 UTC+1в UTC)Значение отметки времени, извлечённое позже в сеанс сервера (в UTC+1):
2020-01-01 01:00:00(после внутреннего преобразования2020-01-01 00:00:00из UTC в UTC+1)Значения отметки времени, сконструированные Connector/J в другом часовом поясе JVM (скажем, в UTC+3):
2020-01-01 01:00:00Примечание: мгновение времени не сохраняется
-
Когда оно
true, Connector/J пытается сохранить мгновения времени, выполняя преобразования, определяемые свойствами подключенияconnectionTimeZoneиforceConnectionTimeZoneToSession.При сохранении значения преобразование выполняется только в том случае, если целевой тип данных, явный или по умолчанию, является
TIMESTAMP. При извлечении значения преобразование выполняется только в том случае, если у исходного столбца естьTIMESTAMP,DATETIMEили тип символьных данных, а целевой класс является сохраняющим мгновения, таким какjava.sql.Timestampилиjava.time.OffsetDateTime.
-
-
connectionTimeZone={LOCAL|SERVER|: Указывает, как часовой пояс сеанса сервера (относительно которого отметки времени сохраняются на сервере) должен определяться Connector/J. Принимает одно из следующих значений:user-defined-time-zone}LOCAL: Connector/J предполагает, что часовой пояс сеанса сервера либо (a) совпадает с часовым поясом JVM для Connector/J, либо (b) должен быть установлен таким же, как часовой пояс JVM для Connector/J. Connector/J рассматривает ситуацию как (a) или (b) в зависимости от значения свойства подключенияforceConnectionTimeZoneToSession.SERVER: Connector/J должен запросить часовой пояс сеанса с сервера вместо того, чтобы делать какие-либо предположения о нём. Если часовой пояс сеанса фактически отличается от часового пояса JVM Connector/J и
preserveInstants=true, Connector/J выполняет преобразование часовых поясов между часовым поясом сеанса и часовым поясом JVM.user-defined-time-zone: Connector/J предполагает, что часовой пояс сеанса сервера либо (a) совпадает с определённым пользователем часовым поясом, либо (b) должен быть установлен как определённый пользователем часовой пояс. Connector/J принимает ситуацию как (a) или (b) в зависимости от значения свойства подключенияforceConnectionTimeZoneToSession.
ПримечаниеДля Connector/J 8.0.23 и более поздних версий,
serverTimezoneявляется псевдонимом дляconnectionTimeZone. Для Connector/J 8.0.22 и более ранних версий,serverTimezoneиспользовался для переопределения настройки часового пояса сеанса на сервере. forceConnectionTimeZoneToSession={true|false}: Управляет тем, устанавливается ли переменная сеанса в значение, указанное вconnectionTimeZone.
Теперь давайте рассмотрим значения свойств подключения, которые следует использовать для достижения описанных выше решений по сохранению мгновений времени:
-
Решение 1: Используйте либо preserveInstants=false, либо connectionTimeZone=LOCAL& forceConnectionTimeZoneToSession=false. Поскольку можно предположить, что часовой пояс сессии сервера совпадает с часовым поясом JVM Connector/J, запрос часового пояса сессии сервера не выполняется, и преобразование часовых поясов не происходит. Например:
Часовые пояса: UTC+1 как для JVM, так и для сессии сервера
Исходная метка времени с клиента (в UTC+1):
2020-01-01 01:00:00Метка времени, отправленная на сервер Connector/J:
2020-01-01 01:00:00(преобразование не требуется)Значения меток времени, хранящиеся внутри сервера:
2020-01-01 00:00:00 UTC(после внутреннего преобразования из UTC+1 в UTC)Значение метки времени, полученное позже в сессии сервера в UTC+1, к которой подключен Connector/J:
2020-01-01 01:00:00(после внутреннего преобразования из UTC в UTC+1)Значение метки времени, сгенерированное Connector/J в том же часовом поясе JVM, что и ранее (UTC+1), и возвращенное приложению:
2020-01-01 01:00:00Комментарий: Мгновение времени сохраняется без преобразования.
ПримечаниеЭтот параметр соответствует поведению по умолчанию Connector/J 5.1
-
Решение 2a: Используйте preserveInstants=true&connectionTimeZone=SERVER . Connector/J запрашивает значение часового пояса сессии с сервера и преобразует метки времени событий между часовым поясом сессии и часовым поясом JVM. Например:
Часовые пояса: UTC+2 для JVM, UTC+1 для сессии сервера
Исходная метка времени с клиента (в UTC+2):
2020-01-01 02:00:00Метка времени, отправленная на сервер Connector/J:
2020-01-01 01:00:00(после преобразования из UTC+2 в UTC+1)Значение метки времени, хранящееся внутри сервера:
2020-01-01 00:00:00 UTC(после внутреннего преобразования из UTC+1 в UTC)Значение метки времени, полученное позже в сессии сервера в UTC+1:
2020-01-01 01:00:00(после внутреннего преобразования из UTC в UTC+1)Значение метки времени, сгенерированное Connector/J в том же часовом поясе JVM, что и ранее (UTC+2), и возвращенное приложению:
2020-01-01 02:00:00(после преобразования из UTC+1 в UTC+2)Значения меток времени, сгенерированные Connector/J в другом часовом поясе JVM (например, UTC+3) и возвращенные приложению:
2020-01-01 03:00:00(после преобразования из UTC+1 в UTC+3)Комментарий: Мгновение времени сохраняется.
ПримечанияЭтот параметр соответствует поведению по умолчанию Connector/J 8.0.22 и более ранних версий и поведению Connector/J 5.1 с
useLegacyDatetimeCode=false.
-
Решение 2b: Используйте connectionTimeZone=LOCAL& forceConnectionTimeZoneToSession=true. Connector/J изменяет часовой пояс сессии сервера на часовой пояс JVM, после чего преобразования часовых поясов при хранении или получении меток времени не требуются. Например:
Часовые пояса: UTC+1 для JVM, UTC+2 для сессии сервера изначально, но теперь изменён на UTC+1 Connector/J
Исходная метка времени с клиента (в UTC+1):
2020-01-01 01:00:00Метка времени, отправленная на сервер Connector/J:
2020-01-01 01:00:00(без преобразования)Значения меток времени, хранящиеся внутри сервера:
2020-01-01 00:00:00(после внутреннего преобразования из UTC+1 в UTC)Значения меток времени, полученные позже в сессии сервера (в UTC+1, как установлено Connector/J):
2020-01-01 01:00:00(после внутреннего преобразования из UTC в UTC+1)Значение метки времени, сгенерированное Connector/J в том же часовом поясе JVM, что и ранее (UTC+1):
2020-01-01 01:00:00(без преобразования)Значения меток времени, полученные позже в сессии сервера (часовой пояс изменен, например, на UTC+3, Connector/J):
2020-01-01 03:00:00(после внутреннего преобразования из UTC в UTC+3)Значение метки времени, сгенерированное Connector/J в часовом поясе JVM UTC+3:
2020-01-01 03:00:00(без преобразования)Комментарий: Мгновение времени сохраняется без преобразования Connector/J, так как часовой пояс сессии изменяется Connector/J на значение его JVM.
Предупреждения-
Изменение часового пояса сессии влияет на результаты функций MySQL, таких как
NOW(),CURTIME()илиCURDATE()— если вы не хотите, чтобы эти функции влияли, не используйте этот параметр.Если вы используете этот параметр для разных клиентов в разных часовых поясах, клиенты будут изменять часовые пояса своих подключений на разные значения; если вы хотите сохранить одинаковое представление визуального значения даты и времени для одного мгновения времени для всех клиентов и в всех их сессиях, сохраните значения в колонку
DATETIMEвместоTIMESTAMPи используйте для них не-мгновенные Java классы, например,java.time.LocalDateTime.
-
Решение 2c: Используйте preserveInstants=true&connectionTimeZone=
user-defined-time-zone& forceConnectionTimeZoneToSession=true. Connector/J изменяет часовой пояс сессии сервера на определенный пользователем часовой пояс и преобразует метки времени между заданным пользователем часовым поясом и часовым поясом JVM. Типичный случай использования этого параметра — когда значение часового пояса сессии на сервере не распознаётся Connector/J (например,CSTилиCEST). Например:Часовые пояса: UTC+2 для JVM,
CETдля сессии сервера изначально, но теперь изменён на определенный пользователемEurope/BerlinConnector/JИсходная метка времени с клиента (в UTC+2):
2020-01-01 02:00:00Метка времени, отправленная на сервер Connector/J:
2020-01-01 01:00:00(после преобразования между часовым поясом JVM (UTC+2) и заданным пользователем часовым поясом (Europe/Berlin=UTC+1))Значения меток времени, хранящиеся внутри сервера:
2020-01-01 00:00:00(после внутреннего преобразования из UTC+1 в UTC)Значение метки времени, полученное в сессии сервера (часовой пояс изменён на
Europe/Berlin(=UTC+1) Connector/J):2020-01-01 01:00:00(после внутреннего преобразования из UTC в UTC+1)Значение метки времени, сгенерированное Connector/J в том же часовом поясе JVM, что и ранее (UTC+2), и возвращенное приложению:
2020-01-01 02:00:00(после преобразования между заданным пользователем часовым поясом (UTC+1) и часовым поясом JVM (UTC+2)).Комментарий: Мгновение времени сохраняется с преобразованием и с изменением часового пояса сессии Connector/J в соответствии с заданным пользователем значением.
В качестве альтернативы этому решению пользователь может захотеть выполнить то же преобразование меток времени между часовым поясом JVM и заданным пользователем часовым поясом, как описано выше, не изменяя фактически недопустимое значение часового пояса на сервере. Для этого используйте
preserveInstants=true&connectionTimeZone=user-defined-time-zone& forceConnectionTimeZoneToSession=false. Это достигает того же результата — сохранения мгновения времени.ПредупрежденияСм. предупреждения выше для решения 2b.
© 2025 Oracle
Licensed under the GPLv2 License.