引
今日一位开发同时问我,为什么我在建表的时候总是会报warning。看了一下建表的语句,类似于
create table aa
(x int n,
timestart timestamp default '1970-01-01 00:00:02' not null ,
timestop ....
);
在执行的时候一直报错ERROR 1067 (42000): Invalid default value for timestart,这个时间明明在timestamp的范围里面,为什么就会报错呢?其实这个涉及到timestamp的一个时间转换的问题
Mysql中和时区相关的variable
在Mysql中和时区相关的variable主要有两个一个是system_time_zone一个是time_zone
mh01@3306>show variables like '%time%z%';
+------------------+--------+
| Variable_name | Value |
+------------------+--------+
| system_time_zone | CST |
| time_zone | -08:00 |
+------------------+--------+
2 rows in set (0.00 sec)
其中这两个参数分别对应了Mysql维护的两种时区,一个是系统时区一个是当前时区。
系统时区
当Mysql启动的时候,Mysql回去主动的读取操作系统少女革命的时区,然后把从操作系统中获取的时区写入到system_time_zone这个变量中,很显然这个变量跟随操作系统的,是一个只读的变量
当前时区
当前时区由time_zone这个变量控制,会话的时区也就是通过time_zone这个事情去定义的,也就意味这这个time_zone可以在会话级别配置。默认情况下这个参数的值为SYSTEM,也就是跟随系统的时区,那么Mysql如何知道系统的时区呢?就是通过上面的一个只读变量system_time_zone去获取。有一点需要注意的是,当Mysql的时区被设置为system的时候,所有需要做时区计算的函数都会调用系统库做当前时区的计算。 如果要修改当前时区可以在启动的时候添加--default-time-zone = timezone或者在my.cnf文件中添加default-time-zone = timezone,也可以通过set global的方式进行设置。
timestamp和时区的关系
回到上面的问题,为什么1970-01-01 00:00:00秒明明在timestamp允许的时间范围内,却不能被插入呢?因为timestamp数据类型在存储数据的时候使用的是UTC时间,也就是说当系统时间设置为东八区的时候,插入的1970-01-01 00:00:03实际上会被转换为1969-12-31 18:00:03(UTC时间=当前时间-时区)。可以看下面的实验:
当前时区位于+08:00
mh01@3306>select current_time();
+----------------+
| current_time() |
+----------------+
| 16:30:53 |
+----------------+
1 row in set (0.01 sec)
mh01@3306>show variables like '%time%z%';
+------------------+--------+
| Variable_name | Value |
+------------------+--------+
| system_time_zone | CST |
| time_zone | SYSTEM |
+------------------+--------+
2 rows in set (0.00 sec)
mh01@3306>create table time_test (id int primary key ,my_date timestamp);
Query OK, 0 rows affected (0.02 sec)
mh01@3306>insert into time_test values (1,'2019-11-12 12:00:00');
mh01@3306>select * from time_test;
+----+---------------------+
| id | my_date |
+----+---------------------+
| 1 | 2019-11-12 12:00:00 |
+----+---------------------+
1 row in set (0.00 sec)
插入数据之后显示为正常的12点,但是在存储的时候需要转换为UTC时间也就是4:00,当我修改系统时区为-08的时候,再次查询就不会是12点了
mh01@3306>set time_zone='-08:00';
Query OK, 0 rows affected (0.00 sec)
mh01@3306>
mh01@3306>select * from time_test;
+----+---------------------+
| id | my_date |
+----+---------------------+
| 1 | 2019-11-11 20:00:00 |
+----+---------------------+
1 row in set (0.00 sec)
因为当前时间是-8的时区,存储的是UTC时间也就是04点,那么在显示的时候就会做一次转换 时间=UTC+时区也就是1970-01-01 04:00:03 - 8小时得到2019-11-11 20:00:00
所以,如果在遇到数据库迁移的时候,需要注意主机的tz配置,可以通过直接cp文件的方式修改主机的时区配置
cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime