[转帖]mysql 里 CST 时区的坑
mysql 里 CST 时区的坑
一、问题简述
mysql 里 CST 时区是个非常坑的概念,因为在 mysql 里CST既表示中国也表示美国的时区。但是在Jdk代码里,CST 这个字符串被理解为Central Standard Time (USA)(GMT-6),这就是造成坑的原因。
解决办法
mysql 的 time_zone配置 不要用SYSTEM,因为用了就是跟随 system_time_zone 的值,而 system_time_zone 读取自数据库所在宿主机的操作系统,常常是 CST 的值。解决办法是将 time_zone 改成例如 +08:00 以免造成误解。(肯定改mysql啦,你改得了jdk源码吗?)。修改方法见后文
二、查看、修改时区
1、怎么查看mysql是什么时区?
选一个即可
方法1
命令: 连上mysql后命令行执行
show variables like '%time_zone%'(Navicat连上后执行也行)结果:
system_time_zone CST
time_zone SYSTEM看time_zone就行了,SYSTEM表示其值跟随system_time_zone。
而system_time_zone的CST值,是表示中国呢,还是美国,是不清楚的,你直接再执行
select now()跟你手机的时间对比一下就清楚了。为什么会system_time_zone和time_zone两个? 要看哪个? 不急后面会解释
方法2
命令:连上mysql后命令行执行 (Navicat连上后执行也行)
// 分两次执行下面2条命令
select @@system_time_zone;
select @@time_zone;结果:
select @@system_time_zone;
+--------------------+
| @@system_time_zone |
+--------------------+
| CST |
+--------------------+
1 row in set (4.81 sec) mysql> select @@time_zone;
+-------------+
| @@time_zone |
+-------------+
| SYSTEM |
+-------------+
1 row in set (0.04 sec)方法3
// 查出全局时区和会话时区
select @@GLOBAL.time_zone,@@SESSION.time_zone;
为什么mysql有system_time_zone和time_zone两个
见 https://dev.mysql.com/doc/refman/8.0/en/time-zone-support.html
简单说就是system_time_zone就是mysql服务(即mysqld)启动的时候读取数据库所在宿主机的时区,固定下来的值,这个值之后不再改变(除非重启mysql服务)。time_zone的值未SYSTEM,意思是它的时区跟system_time_zone一样(注意system_time_zone的值固定下来后,数据库宿主机的时区再改变,time_zone的值都是不变的,因为它是跟随system_time_zone变量的,不是实时跟随操作系统的)
存在 “为什么linux时区是对的,但是mysql的时区是错的” 的疑惑,因为启动mysql服务时如果linux时区是错的,把mysql的时区也带错了,只是后来你发现linux时区错了于是改对了,但是你忘记启动mysql的时候linux时区是错的,而且你忘了是后来你才把linux的时区改正确的,所以你有这个的疑惑。我好像也有过这样的疑惑
2、如何修改mysql的时区
- 永久修改,改配置文件,重启mysql服务也有效
修改 my.cnf 文件,在 [mysqld] 节下增加 default-time-zone = '+08:00'
没有这个文件的话,见后文,让你的mysql启动的时候读取配置文件。
临时修改,重启mysql服务后丢失
选择下面之一执行即可。
- 修改会话级别定的,关闭会话后就会失效,也仅仅影响当前会话窗口:
set time_zone='+08:00'; - 全局修改,重启mysql服务后丢失,对所有会话生效:`set global time_zone=’+08:00’;
- 修改会话级别定的,关闭会话后就会失效,也仅仅影响当前会话窗口:
三、这个bug 是怎么产生的
下面基于mysql-connector-java-8.0.19 进行分析,使用 com.mysql.cj.jdbc.Driver
在获取java.sql.Connection的过程会确定时区,调用如下方法
com.mysql.cj.protocol.a.NativeProtocol#configureTimezone
/**
* Configures the client's timezone if required.
*
* @throws CJException
* if the timezone the server is configured to use can't be
* mapped to a Java timezone.
*/
public void configureTimezone() {
String configuredTimeZoneOnServer = this.serverSession.getServerVariable("time_zone");
if ("SYSTEM".equalsIgnoreCase(configuredTimeZoneOnServer)) {
configuredTimeZoneOnServer = this.serverSession.getServerVariable("system_time_zone");
}
String canonicalTimezone = getPropertySet().getStringProperty(PropertyKey.serverTimezone).getValue();
if (configuredTimeZoneOnServer != null) {
// user can override this with driver properties, so don't detect if that's the case
if (canonicalTimezone == null || StringUtils.isEmptyOrWhitespaceOnly(canonicalTimezone)) {
try {
canonicalTimezone = TimeUtil.getCanonicalTimezone(configuredTimeZoneOnServer, getExceptionInterceptor());
} catch (IllegalArgumentException iae) {
throw ExceptionFactory.createException(WrongArgumentException.class, iae.getMessage(), getExceptionInterceptor());
}
}
}
if (canonicalTimezone != null && canonicalTimezone.length() > 0) {
this.serverSession.setServerTimeZone(TimeZone.getTimeZone(canonicalTimezone));
//
// The Calendar class has the behavior of mapping unknown timezones to 'GMT' instead of throwing an exception, so we must check for this...
//
if (!canonicalTimezone.equalsIgnoreCase("GMT") && this.serverSession.getServerTimeZone().getID().equals("GMT")) {
throw ExceptionFactory.createException(WrongArgumentException.class, Messages.getString("Connection.9", new Object[] { canonicalTimezone }),
getExceptionInterceptor());
}
}
this.serverSession.setDefaultTimeZone(this.serverSession.getServerTimeZone());
}
大概逻辑如下:
从mysql的time_zone读取值
如果值是SYSTEM则使用system_time_zone的值
如果jdbc url配置了时区则使用url里的,如
jdbc:mysql://localhost:3306/test?useSSL=true&serverTimezone=Asia/Shanghai获取的是字符串配置定的
如果获取到CST,则因为在java里 TimeZone("CST")表示美国中部时间,与mysql数据库用 CST 表示中国时区的含义就不同了,bug就是这么产生的。
四、其他问题
1、CST究竟是美国还是中国
其实CST总共有4个含义
- China Standard Time:中国标准时间
- Central Standard Time (USA) :美国中部时间(中部就是在国土的中间的部分,例如芝加哥)
- Central Standard Time (Australia):澳大利亚中部时间
- Cuba Standard Time:古巴标准时间
就是因为这个含义不清,所以会有坑,体现在Java里就是:
// 不建议这么创建 TimeZone,这里的CST指的是美国中部时间,不是中国,从
TimeZone tz = TimeZone.getTimeZone("CST");
System.out.println(tz);// 可以看到偏移量是offset=-21600000,-21600000微秒=-6小时,所以实锤这里的CST指美国
// 建议创建 TimeZone 用 ZoneId,因为ZoneId 不允许 CST、JST 这种简称,能提前预防进坑,如下
// ZoneId zoneId = ZoneId.of("CST");// 抛异常
ZoneId zoneId = ZoneId.of("GMT+8");// 明确指定,是OK的,或者 "区域/城市" 的写法如 Asia/Shanghai
TimeZone tz1 = TimeZone.getTimeZone(zoneId);
体现在Mysql里:
你根本不知道mysql里的CST是中国还是美国,查出来都展示一样

(关于system_time_zone和time_zone后续有解释)
2、你说CST除了表示中国,也表示美国定的时区,拿得出证据吗?
是的!!! 动手做实验给你看 !!!
使用show variables like '%time_zone%'; 查看mysql的时区,目前是北京时间,查出
Variable_name Value
system_time_zone CST
time_zone SYSTEM
当把操作系统(mysql的宿主机器的操作系统)的时区改成东京时区,并重启mysql服务后(必须重启服务),显示
Variable_name Value
system_time_zone JST
time_zone SYSTEM
最后把操作系统时区改成美国中部时间(如芝加哥),并重启mysql服务后,显示
Variable_name Value
system_time_zone CST
time_zone SYSTEM
可以看到非常坑,无论是中国还是美国,都显示CST,给人很大的误解
五、其他
1、mybatis/jdbc 是怎么将日期存入到数据库的,又是怎么查出来的
(这个实在有点复杂,要考虑mysql所在操作系统的时区,也考虑跑你的mysql程序的web服务器的操作系统的时区。加又没时间继续研究了,缓一缓)
2、再次啰嗦补充
mysql里的 system_time_zone 和 time_zone是什么关系
system_time_zone 是mysql服务启动时读取宿主操作系统的时区,当时读取到什么值就固定什么,是那个时刻的快照。这个值只有在被重新指定或重启mysql服务的时候才可能变化。
time_zone就是表示所有连接到此mysql的客户端(client)的时区,不管你是什么形式的客户端,是java通过jdbc连接的程序,还是Navicat这种图形化的,还是命令行的,都叫客户端。这个时区是可以被覆盖的,可以被jdbcUrl中的时区覆盖。
在jdbc的URL里配置了时区会怎么样?
会使用这个时区,在jdbcUrl里指定的时区优先级最高,会覆盖其他。例如jdbc:mysql://localhost:3306/test?useSSL=true&serverTimezone=Asia/Shanghaitime_zone的SYSTEM是什么意思,是不是指 “操作系统” 的时区?
time_zone的SYSTEM,是指值跟随 system_time_zone 的值,不是说跟随操作系统的值,操作系统的时区改了,如果 system_time_zone 不改,则 time_zone 也是不会改的(亲自实验过了!!)
[转帖]mysql 里 CST 时区的坑的更多相关文章
- 【转贴】一次 JDBC 与 MySQL 因 “CST” 时区协商误解导致时间差了 14 或 13 小时的排错经历
原文:https://juejin.im/post/5902e087da2f60005df05c3d ------------------------------------------------- ...
- 与 MySQL 因“CST” 时区协商误解导致时间差了13 小时
CST 时区名为 CST 的时区是一个很混乱的时区,有四种含义: 美国中部时间 Central Standard Time (USA) UTC-05:00 / UTC-06:00 澳大利亚中部时间 C ...
- CST时区,MYSQL与JAVA-WEB服务器时间相差13个小时的问题
最近倒腾了一台阿里云主机,打算在上面装点自己的应用.使用docker安装了安装mysql后,发现数据库的存储的时间与java-web应用的时间差8个小时,初步怀疑是docker容器时区的问题.经过一系 ...
- ISO日期格式标准,浏览器到服务器到mysql中的时区
时区简单理解 https://zh.wikipedia.org/wiki/%E6%97%B6%E5%8C%BA 上面的链接是时区的wiki说明,下面说说我记住的部分: GMT时区是格林威治标准时间,我 ...
- 安装Xampp-配置appche,mysql运行环境遇到的坑(转)
用php编写的web应用程序,需运行在php的web容器中,其中apache server是一个针对php web容器,它是apache下的开源项目.通常要运行一个web程序,我们还需要安装数据库软件 ...
- 修改mysql的时间/时区
# 背景 往db中insert数据发现时间不对,因为是新DB,所以猜测是mysql设置不对 # 解决方法 方法一:通过mysql命令行模式下动态修改 show variables like " ...
- 安装Xampp-配置appche,mysql运行环境遇到的坑
用php编写的web应用程序,需运行在php的web容器中,其中apache server是一个针对php web容器,它是apache下的开源项目.通常要运行一个web程序,我们还需要安装数据库软件 ...
- MySql 里的IFNULL、NULLIF和ISNULL用法
MySql 里的IFNULL.NULLIF和ISNULL用法 mysql中isnull,ifnull,nullif的用法如下: isnull(expr) 的用法: 如expr 为null,那么isnu ...
- MySQL里的wait_timeout
如果你没有修改过MySQL的配置,缺省情况下,wait_timeout的初始值是28800. wait_timeout过大有弊端,其体现就是MySQL里大量的SLEEP进程无法及时释放,拖累系统性能, ...
- linux系统修改CST时区
================================================= [root@tzyyserveryg ~]# date -R Fri, 28 Nov 2014 08 ...
随机推荐
- javaScript正则截取自定义标签-javascript-zheng-ze-jie-qu-zi-ding-yi-biao-qian
title: javaScript正则截取自定义标签 date: 2021-12-29 17:31:48.448 updated: 2021-12-29 17:31:48.448 url: https ...
- MySQL|mysql-索引
1.索引是什么 1.1索引简介 索引是表的目录,是数据库中专门用于帮助用户快速查询数据的一种数据结构.类似于字典中的目录,查找字典内容时可以根据目录查找到数据的存放位置,以及快速定位查询数据.对于索引 ...
- 听说生鲜领军企业k8s集群都上云了,鱼会飞了?
在这个中秋都和国庆在一起的双节里,我们的小明还在辛辛苦苦的找工作,听说他经历了一段"难忘"的面试. 小明面对着面试官的"层层拷问",游刃有余的化解了这些难题. ...
- JavaFx之Ikonli图标库大全(十五)
JavaFx之Ikonli图标库大全(十五) Ikonli给java提供了大量的图标库, 官网:https://kordamp.org/ikonli/ Ikonli 提供了可以在 Java 应用程序中 ...
- Java面试必考:什么是字节码?采用字节码的好处?
Java面试必考:什么是字节码?采用字节码的好处? 于哥你好,最近在java面试中被问答到什么是字节码?采用字节码的好处是什么?瞬间懵了,如果你连这个都不知道,我保证你面试GG! 首先说下Java的优 ...
- 在距离distribution 证书过期一个月(或被手动revoke了)的时候会受到apple的邮件
编辑 虽然distribution过期(或者被手动revoke)了,如果你的开发者账号是company(公司)类型或个人类型的,只要你的每年99$的开发者membership没有过期,就不会对已 ...
- Sublime Text 16进制显示
大文件推荐使用 UltraEdit 工具 Sublime Text 16进制显示(可以直接显示不同数据类型转换后的结果,不用在线工具,转二进制了) 安装 HexViewer 插件 1. Ctrl + ...
- MVCC多版本并发控制和幻读问题的解决
首先我们先介绍一下锁的分类,再进入今天的正题. 一.锁分类: 1.从性能上分:乐观锁.悲观锁.乐观锁(用版本号对比或CAS机制)适用于读比较多的场景,悲观锁适用于写比较多的场景.如果在写比较多的场景使 ...
- 浅谈locust 性能压测使用
1. 基本介绍 Locust是一个开源的负载测试工具,用于模拟大量用户并发访问一个系统或服务,以评估其性能和稳定性.编写语言为Python,可通过Python来自定义构建性能压测场景脚本.Locust ...
- AI 视频云 VS 窄带高清,谁是视频时代的宠儿
随着网络技术的逐渐改善,各类视频消息成为媒体传播的主要选择手段.但其实支撑着视频传播的并不单单是网络技术,还有视频转码与压缩技术.这类技术下分很多,比如曾经被频繁提到的 H.265,比如时下热门的窄带 ...