Java与MySQL时间戳传递/存储/协调问题--userLegacyDatetimeCode--userTimezone--serverTimezone
00. 基本问题
0.0 版本: 驱动5.1.47和8.0.17
0.1 mysql驱动5.1有userlegacydatetimecode和usertimezone两个参数, 8.0没有
0.2 java与mysql间传递时间戳的时候, 传递的是年月日时分秒, 没有时区
0.3 mysql传递回来的是: mysql读取到底层存储的时间戳, 按照当前连接(mysql侧)的时区转为年月日时分秒
0.4 但是, 两个系统时区可能会不同, userlegacydatetimecode和usertimezone就是用来协调时区的
01. mysql驱动5.1
1.1 数据库连接在建立时, 会创建一个calendar对象保存在连接中, 其中保存了连接创建时的时区, 即下文的"连接时区". 见connectionimpl#705
1.2 如果配置了servertimezone,则会将其保存到连接中, 即下文的"配置时区". 见connectionimpl#1978
1.3 userlegacydatetimecod=true&usertimezone=false, 这是默认情况
1.3.1 此时对应java和mysql时区相同
1.3.2 java接收到mysql传递来的年月日时分秒, 加上"连接时区"创建时间戳java.sql.timestamp, 见resultsetimpl#5877和timeutil#369
1.4 userlegacydatetimecod=true&usertimezone=true&servertimezone=gmt%2b6
1.4.0 usertimezone=true, 必须在userlegacydatetimecod=true时才有效
1.4.1 此时对应二者时区不同
1.4.2 与3.2相同, 先将年月日时分秒+"连接时区", 创建时间戳
1.4.3 再进行时区调整, 调整为"配置时区". 见resultsetimpl#5877和timeutil#160
1.5 userlegacydatetimecod=false&servertimezone=gmt%2b6
1.5.1 此时对应二者时区不同
1.5.2 将年月日时分秒+"配置时区"创建时间戳. 见resultsetimpl#5874
1.5.3 这也是8.0的处理方式
02. mysql驱动8.0
2.1 8.0没有userlegacydatetimecode和usertimezone两个参数
2.2 一定要配置servertimezone为mysql运行的时区. 连接建立时会将这个时区存储到连接中. 见nativeprotocol#2147#2158
2.3 将年月日时分秒+"配置时区"构造时间戳. 见sqltimestampvaluefactory#100. 这里的cal就是在#68根据"配置时区"创建的
03. 代码跟踪中的一些关键点
版本5.1
连接初始化的过程
- connectionimpl#1978 "配置时区"
- connectionimpl#705 将当前时区保存到了数据库连接中
读取的过程
- mybatis的各个typehandler
- bytearrayrow#63 拿到字节数组
- resultsetrow#705 将字节数组转为字符串
- resultsetimpl#5729 将字符串分离为年月日时分秒
- resultsetimpl#5873 对应上文01.5.2
- resultsetimpl#5877 对应上文01.4.2和01.4.3
- resultsetimpl#5317 如果java要返回的是string, 则会在这里将时间戳转为jvm当前时区下的年月日时分秒
版本8.0
- nativeprotocol#2147#2158 保存"配置时区"
- bytearrayrow#89 拿到数据库返回的字节, 大致相当于 01.5.1的bytearrayrow#63
- mysqltextvaluedecoder#338 解析字节数组, 拿到年月日时分秒并封装为internaltimestamp
- sqltimestampvaluefactory#100 也就是上文02.3
- stringvaluefactory#94 如果java要返回的是string, 就直接将internaltimestamp转为字符串, 不考虑当前系统时区了, 与5.1的第7条有区别
- abstractresultsetrow#78
- propertykey jdbc url的property的key枚举类, https://dev.mysql.com/doc/connector-j/8.0/en/connector-j-reference-configuration-properties.html
04. the server time zone value '???dz?ʱ?' is unrecognized or represents more than one time zone 异常是怎么回事?
在三种情况下会抛出
上文01.4/01.5/02.2情况下未配置servertimezone是都会抛出
因为, nativeprotocol#2130或者connectionimpl#1960拿到数据库的system_time_zone是乱码, 也就是select @@system_time_zone 的值
所以要配置servertimezone为数据库运行的时区
05. 问题
时间戳传递为什么不是一个数字形式的秒/毫秒呢, 而是一个没有时区的年月日时分秒呢? 还得协调时区, 多复杂呢?