DATETIME и TIMESTAMP
MySQL имеет два типа данных с датой и временем: DATETIME и TIMESTAMP. В официальной документации есть хорошее объяснение и примеры их различий: https://dev.mysql.com/doc/refman/8.0/en/datetime.html
Короче говоря, «MySQL преобразует TIMESTAMP
значения из текущего часового пояса в UTC для хранения и обратно из UTC в текущий часовой пояс для извлечения. (Этого не происходит с другими типами, такими как DATETIME
.) »
Другими словами, информация о часовом поясе теряется в столбцах DATETIME. Это может не быть большой проблемой, если ваши варианты использования в отношении часового пояса однородны, однако, если ваши пользователи находятся в разных часовых поясах, TIMESTAMP более эффективен в использовании.
Давайте посмотрим на несколько примеров:
mysql> CREATE TABLE time_test (timezone VARCHAR(32), time_datetime DATETIME, time_timestamp TIMESTAMP); Query OK, 0 rows affected (0.02 sec) mysql> SET time_zone='America/Los_Angeles'; Query OK, 0 rows affected (0.00 sec) mysql> INSERT INTO time_test VALUES (@@SESSION.time_zone, '2020-03-12 11:00:00', '2020-03-12 11:00:00'); Query OK, 1 row affected (0.01 sec) mysql> SELECT * FROM time_test; +---------------------+---------------------+---------------------+ | timezone | time_datetime | time_timestamp | +---------------------+---------------------+---------------------+ | America/Los_Angeles | 2020-03-12 11:00:00 | 2020-03-12 11:00:00 | +---------------------+---------------------+---------------------+ 1 row in set (0.00 sec)
В приведенном выше примере
- Часовой пояс сеанса: America / Los_Angeles
- Попытка вставить время 11:00:00 в столбцы DATETIME и TIMESTAMP.
- Результат выглядит отлично в текущей сессии, где часовой пояс America / Los_Angeles.
Теперь давайте изменим часовой пояс сеанса.
mysql> SET time_zone='America/New_York'; Query OK, 0 rows affected (0.01 sec) mysql> SELECT * FROM time_test; +---------------------+---------------------+---------------------+ | timezone | time_datetime | time_timestamp | +---------------------+---------------------+---------------------+ | America/Los_Angeles | 2020-03-12 11:00:00 | 2020-03-12 14:00:00 | +---------------------+---------------------+---------------------+ 1 row in set (0.00 sec)
- Часовой пояс сеанса: America / New_York
- Получить данные. Столбец DATETIME по-прежнему показывает то же время 11:00:00, однако столбец TIMESTAMP преобразует время в America / New_York, что составляет 14:00:00.
Теперь давайте вставим другое время в часовой пояс America / New_York:
mysql> INSERT INTO time_test VALUES (@@SESSION.time_zone, '2020-03-12 11:00:00', '2020-03-12 11:00:00'); Query OK, 1 row affected (0.01 sec) mysql> SELECT * FROM time_test; +---------------------+---------------------+---------------------+ | timezone | time_datetime | time_timestamp | +---------------------+---------------------+---------------------+ | America/Los_Angeles | 2020-03-12 11:00:00 | 2020-03-12 14:00:00 | | America/New_York | 2020-03-12 11:00:00 | 2020-03-12 11:00:00 | +---------------------+---------------------+---------------------+ 2 rows in set (0.00 sec)
Давайте переключим часовой пояс сеанса на UTC:
mysql> SET time_zone='UTC'; Query OK, 0 rows affected (0.00 sec) mysql> SELECT * FROM time_test; +---------------------+---------------------+---------------------+ | timezone | time_datetime | time_timestamp | +---------------------+---------------------+---------------------+ | America/Los_Angeles | 2020-03-12 11:00:00 | 2020-03-12 18:00:00 | | America/New_York | 2020-03-12 11:00:00 | 2020-03-12 15:00:00 | +---------------------+---------------------+---------------------+ 2 rows in set (0.00 sec)
Как и ожидалось, столбец DATETIME показывает последовательные 11:00:00, но столбец TIMESTAMP показывает время, преобразованное из America / Los_Angeles и America / New_York в UTC, соответственно (это упражнение выполняется в режиме летнего времени, поэтому разница во времени между UTC и America / Los_Angeles +07: 00, а America / New_York +04: 00).
CONVERT_TZ
MySQL предоставляет встроенную функцию CONVERT_TZ, которая помогает преобразовывать метку времени из одного часового пояса в другой. Он автоматически заботится о переходе на летнее время. Для использования этой функции в вашем экземпляре MySQL должны быть заполнены таблицы часовых поясов во внутренней базе данных mysql. Инструкцию по загрузке таблиц часовых поясов можно найти по адресу https://dev.mysql.com/doc/refman/8.0/en/mysql-tzinfo-to-sql.html.
CONVERT_TZ работает как с DATETIME, так и с TIMESTAMP. Пример,
mysql> SET time_zone='UTC'; Query OK, 0 rows affected (0.00 sec) mysql> SELECT timezone, CONVERT_TZ(time_datetime, timezone, @@SESSION.time_zone) as time_datetime, time_timestamp FROM time_test; +---------------------+---------------------+---------------------+ | timezone | time_datetime | time_timestamp | +---------------------+---------------------+---------------------+ | America/Los_Angeles | 2020-03-12 18:00:00 | 2020-03-12 18:00:00 | | America/New_York | 2020-03-12 15:00:00 | 2020-03-12 15:00:00 | +---------------------+---------------------+---------------------+ 2 rows in set (0.00 sec)
Теперь оба выходных столбца последовательно конвертируются в UTC.
В чем же тогда прецедент применения CONVERT_TZ к значениям TIMESTAMP, если выходные данные TIMESTAMP автоматически преобразуются в часовой пояс сеанса?
Представьте, что соединения с базой данных являются общими, где вы не можете безопасно изменить часовой пояс сеанса, и вам необходимо указать время в другом часовом поясе. Например,
mysql> SET time_zone='UTC'; Query OK, 0 rows affected (0.00 sec) mysql> SELECT timezone, CONVERT_TZ(time_datetime, timezone, @@SESSION.time_zone) as time_datetime, CONVERT_TZ(time_timestamp, @@SESSION.time_zone, 'America/New_York') as time_timestamp FROM time_test; +---------------------+---------------------+---------------------+ | timezone | time_datetime | time_timestamp | +---------------------+---------------------+---------------------+ | America/Los_Angeles | 2020-03-12 18:00:00 | 2020-03-12 14:00:00 | | America/New_York | 2020-03-12 15:00:00 | 2020-03-12 11:00:00 | +---------------------+---------------------+---------------------+ 2 rows in set (0.00 sec) mysql> SET time_zone='Asia/Shanghai'; Query OK, 0 rows affected (0.00 sec) mysql> SELECT timezone, CONVERT_TZ(time_datetime, timezone, @@SESSION.time_zone) as time_datetime, CONVERT_TZ(time_timestamp, @@SESSION.time_zone, 'America/New_York') as time_timestamp FROM time_test; +---------------------+---------------------+---------------------+ | timezone | time_datetime | time_timestamp | +---------------------+---------------------+---------------------+ | America/Los_Angeles | 2020-03-13 02:00:00 | 2020-03-12 14:00:00 | | America/New_York | 2020-03-12 23:00:00 | 2020-03-12 11:00:00 | +---------------------+---------------------+---------------------+ 2 rows in set (0.00 sec)
Обратите внимание, что когда вы CONVERT_TZ a TIMESTAMP, исходный часовой пояс является часовым поясом сеанса.
Пожалуйста, прочтите другие уроки из моей серии статей о часовом поясе в MySQL: