欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

Java与MySQL时间戳传递/存储/协调问题--userLegacyDatetimeCode--userTimezone--serverTimezone

程序员文章站 2023-11-09 21:16:46
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传递回来 ......

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

连接初始化的过程

  1. connectionimpl#1978 "配置时区"
  2. connectionimpl#705 将当前时区保存到了数据库连接中

读取的过程

  1. mybatis的各个typehandler
  2. bytearrayrow#63 拿到字节数组
  3. resultsetrow#705 将字节数组转为字符串
  4. resultsetimpl#5729 将字符串分离为年月日时分秒
  5. resultsetimpl#5873 对应上文01.5.2
  6. resultsetimpl#5877 对应上文01.4.2和01.4.3
  7. resultsetimpl#5317 如果java要返回的是string, 则会在这里将时间戳转为jvm当前时区下的年月日时分秒

版本8.0

  1. nativeprotocol#2147#2158 保存"配置时区"
  2. bytearrayrow#89 拿到数据库返回的字节, 大致相当于 01.5.1的bytearrayrow#63
  3. mysqltextvaluedecoder#338 解析字节数组, 拿到年月日时分秒并封装为internaltimestamp
  4. sqltimestampvaluefactory#100 也就是上文02.3
  5. stringvaluefactory#94 如果java要返回的是string, 就直接将internaltimestamp转为字符串, 不考虑当前系统时区了, 与5.1的第7条有区别
  6. abstractresultsetrow#78
  7. 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. 问题

时间戳传递为什么不是一个数字形式的秒/毫秒呢, 而是一个没有时区的年月日时分秒呢? 还得协调时区, 多复杂呢?