时区问题小总结
前置说明
关于时区问题 , 我具体碰到了三个问题 , 具体如下
- mysql 时区 以及 springboot 配置连接时候指定时区
- centos7 系统的时区
- docker 容器指定时区
之前整理 , 但是一直遗忘 , 最近看到了三篇写的比较好的帖子 , 正好贴出来参考 , 供以后查找解决
mysql时区问题
参考网址:
https://mp.weixin.qq.com/s/cZxU9kR4ZVQ2VI58ofqC3Q
今天来聊一个简单的话题,这是一个小伙伴在微信上问我的,对于初学者我非常能理解这类问题带来的困扰,各种尝试,各种搜索,别人说的头头是道,但是就是解决不了自己的问题,今天我简单从两个方面来和大家聊聊这个问题,如果小伙伴们有其他的解决思路,也可以留言一起分享。
这个问题我们可以从两方面来分析:
- MySQL 本身的问题。
- Java 代码的问题。
1. MySQL 本身问题
MySQL 本身问题,这个其实很好验证,不就是时间么,我们执行如下 SQL 看看 MySQL 上的时间跟我的电脑时间是否是一致的:
select now();
可以看到,MySQL 的这个时间跟我系统的时间其实就差了 8 小时,MySQL 本身的时间都不对,那你将来插入/查询的时间肯定也不对。
这个查询大家注意,要么使用命令行操作,要么使用 Sqlyog、Navicat 或者 Sequel Pro 之类的数据库工具来操作,切勿使用 JDBC 来查询,具体原因一会看完第二小节就明白了。
出现这个问题,多半是 MySQL 的时区不太对,我们重新给其设置一下时区即可。
首先我们通过如下指令来查看一下 MySQL 当前的时区:
show variables like '%time_zone%';
可以看到,MySQL 说它的时区是 SYSTEM,那 SYSTEM 又是啥呢?第一条说了 SYSTEM 是 UTC(协调世界时,又称世界标准时间或世界协调时间)。而我们的北京时间比 UTC 快了 8 小时,即 UTC+8。
所以我们现在要把 MySQL 的时区先给改对,可以通过修改配置文件来实现(/etc/mysql/mysql.conf.d/mysqld.cnf
),如下:
修改完成后,重启 MySQL,再来查看 MySQL 的时区:
可以看到,此时的 MySQL 时区就正常了。
那么此时再执行 select now();
也就不会有问题了:
有的小伙伴可能嫌修改配置文件太麻烦了,那么也可以通过 SQL 来修改时区:
set global time_zone = Asia/Shanghai
注意我们所在的时区是 Asia/Shanghai,小伙伴们不要自由发挥写其他城市。
首先我们要确认 MySQL 没问题。
2. JDBC 连接问题
当确认了 MySQL 没有问题后,如果你的 MySQL 时间还是不对,那么就有可能是 JDBC 连接的问题了。
这里我用大家常见的 JdbcTemplate 来举个例子,其他的数据库框架操作也都是一样的,我这里主要是演示时区问题,数据操作细节问题就不再展示了。
首先我们来准备一个表,如下:
CREATE TABLE `user` (
`id` int NOT NULL AUTO_INCREMENT,
`createTime` datetime DEFAULT NULL,
`updateTime` timestamp NULL DEFAULT NULL,
`username` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
很简单的几个字段,createTime 是 datetime 类型,updateTime 是 Timestamp 类型。
然后向表中添加一条记录:
并且这个数据库的时区是 Asia/Shanghai
接下来我们创建一个 Spring Boot 项目,引入 Web、JDBC API 依赖和 MySQL 驱动,如下:
然后我们来配置一下 MySQL 的连接信息,如下:
spring.datasource.username=root
spring.datasource.password=123
spring.datasource.url=jdbc:mysql:///test01?serverTimezone=UTC
小伙伴们看一下,在数据库连接地址中,我特意设置了时区为 UTC,这个时区比我们目前的时区慢了 8 小时,我们来看看用这样一个错误的时区,操作的结果是什么样子的。
@Autowired
JdbcTemplate jdbcTemplate;
@Test
void contextLoads() {
List<User> list = jdbcTemplate.query("select * from user", new BeanPropertyRowMapper<>(User.class));
System.out.println("list = " + list);
}
大家看到,这个查询结果查到的时间是 21 点,跟 13 点相比快了 8 小时。
为啥呢?
因为我们连接地址中加了 serverTimezone=UTC
参数,这个时候,系统会把从数据库查询到的数据当成是 UTC 时区的,即把 13 点当成 UTC 时区的,但是我自己当前设备又是 Asia/Shanghai
时区,UTC 时区的 13 点转成 Asia/Shanghai
时区之后就是 21 点了。
相同道理,大家也可以自行尝试设置 serverTimezone=Asia/Tokyo
,时区设置为东京,东京比我们早一个小时,东京的 13 点就是我们的 12 点,那么最终查询结果就是 12 点。
从这个案例中我们可以看到,jdbc 连接参数中的时区优先级高于 MySQL 服务器的时区参数,所以这个连接参数大家也要尤其注意。
3. 题外话
有的小伙伴遇到的时区问题则是另外一种,返回 JSON 的时候时间不对。
如果在项目中用了 jackson,并且使用 @JsonFormat
注解来格式化日期,就有可能出现时区问题,如下:
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone = "Asia/Shanghai")
大家看到,这段代码如果没有设置 timezone 属性,那么默认的时区就是 UTC,也会导致最终的时间差了 8 小时。
docker部署项目时区问题
参考网址:
https://mp.weixin.qq.com/s/Fp5TDDOJp1t1-ruJcFgxqQ
docker部署Java项目的时候,默认是采用UTC时间的,所以就会导致查看日志的时候,少了8个小时,解决方法也很简单,构建的时候指定采用Asia/shanghai时间即可
-
以Dockerfile构建镜像的时候
1# RUN代表的构建的时候运行的命令;> 代表将内容写入后面的文件,当今内容就是“Asia/Shanghai" 2RUN echo "Asia/Shanghai" > /etc/timezone
-
运行容器的时候通过挂载的形式写入
1docker run 2--name xxx 3-p xxxx:xxxx 4# -v参数代表将本地的目录挂载到容器里面 通过ls -l /etc/localtime 可以查看一下当前系统的时区 5-v /etc/localtime:/etc/localtime
Centos7修改系统时区timezone
参考网址:
https://blog.csdn.net/zlt995768025/article/details/79765738?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522164977790716781683979333%2522%252C%2522scm%2522%253A%252220140713.130102334…%2522%257D&request_id=164977790716781683979333&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2allsobaiduend~default-1-79765738.142v7control,157v4control&utm_term=centos7%E4%BF%AE%E6%94%B9%E6%97%B6%E9%97%B4%E5%92%8C%E6%97%B6%E5%8C%BA&spm=1018.2226.3001.4187
突然发现Linux本地时间和当前时间不符,正好差八个小时,导致上面的mysql在进行数据处理的时候,时间不符。所以修改一下centos7的时区为Asia/Shanghai
第一步:查询服务器时间
[root@localhost ~]# timedatectl
Local time: Sat 2018-03-31 01:11:46 UTC
Universal time: Sat 2018-03-31 01:11:46 UTC
RTC time: Sat 2018-03-31 01:11:36
Time zone: Universal (UTC, +0000)
NTP enabled: yes
NTP synchronized: yes
RTC in local TZ: no
DST active: n/a
第二步;修改时区为Asia/Shanghai
[root@localhost ~]# timedatectl set-timezone Asia/Shanghai
第三步:查看修改后的结果
[root@localhost ~]# timedatectl
Local time: Sat 2018-03-31 09:13:21 CST
Universal time: Sat 2018-03-31 01:13:21 UTC
RTC time: Sat 2018-03-31 01:13:11
Time zone: Asia/Shanghai (CST, +0800)
NTP enabled: yes
NTP synchronized: yes
RTC in local TZ: no
DST active: n/a
31 01:13:21 UTC
RTC time: Sat 2018-03-31 01:13:11 Time zone: Asia/Shanghai (CST, +0800) NTP enabled: yes
NTP synchronized: yes
RTC in local TZ: no
DST active: n/a