前言

话说工作十多年,mysql 还真没用几年。起初是外企银行,无法直接接触到 DB;后来一直从事架构方面,也多是解决问题为主。

这次搭建海外机房,围绕时区大家做了一番讨论。不说最终的结果是什么,期间有同事认为 DB 返回的是 UTC 时间。

这里简单做个验证,顺便看下时区的问题到底是如何处理。

环境

openjdk version “1.8.0_242”

mysql-connector-Java “8.0.20”

mysql “5.7” 时区 TZ=Europe/London

本地时区 GMT+8

创建个简单的库test及表user, 表结构如下: CREATE TABLE `user` (

`name` varchar(50) NOT NULL,

`birth_date` timestamp NULL DEFAULT CURRENT_TIMESTAMP

) ENGINE=InnoDB DEFAULT CHARSET=latin1

插入一条测试数据: mysql> insert into `user`

-> values ('Tom', time('2020-05-15 08:00:00'));

Query OK, 1 row affected (0.01 sec)

mysql> select * from user;

+------+---------------------+

| name | birth_date |

+------+---------------------+

| Tom | 2020-05-14 08:00:00 |

+------+---------------------+

1 row in set (0.00 sec)

测试代码: Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test?useSSL=false", "root", "root");

Statement stmt = conn.createStatement();

stmt.execute("select * from user where name = 'Tom'");

ResultSet rs = stmt.getResultSet();

while (rs.next()) {

Timestamp timestamp = rs.getTimestamp("birth_date");

System.out.println(timestamp.toLocalDateTime().toString());

}

执行结果:

2020-05-14T15:00

分析

程序的执行过程同时用 wireshark 抓了包。可以看到一次查询,做了这么多次的交互(包含了会话初始化)。这里可以看到 #177 的交互返回查询的结果:Tom 2020-05-14 08:00:00,与 DB 中的数据相符。可见,返回的并不是 UTC 时间。

在 TCP 抓包结果中 #155 的查询语句: /* mysql-connector-java-8.0.20 (Revision: afc0a13cd3c5a0bf57eaa809ee0ee6df1fd5ac9b) */

SELECT @@session.auto_increment_increment AS auto_increment_increment,

@@character_set_client AS character_set_client,

@@character_set_connection AS character_set_connection,

@@character_set_results AS character_set_results,

@@character_set_server AS character_set_server,

@@collation_server AS collation_server,

@@collation_connection AS collation_connection,

@@init_connect AS init_connect,

@@interactive_timeout AS interactive_timeout,

@@license AS license,

@@lower_case_table_names AS lower_case_table_names,

@@max_allowed_packet AS max_allowed_packet,

@@net_write_timeout AS net_write_timeout,

@@performance_schema AS performance_schema,

@@query_cache_size AS query_cache_size,

@@query_cache_type AS query_cache_type,

@@sql_mode AS sql_mode,

@@system_time_zone AS system_time_zone,

@@time_zone AS time_zone,

@@transaction_isolation AS transaction_isolation,

@@wait_timeout AS wait_timeout;

服务端返回的 time_zone 为 BST。与本地时区的转换,由 mysql 的 connector 自动完成。

进阶

时区自动转换

实现源码:

ResultSetImpl源码 this.defaultTimestampValueFactory = new SqlTimestampValueFactory(pset, null, this.session.getServerSession().getServerTimeZone());@Overridepublic Timestamp getTimestamp(int columnIndex) throws SQLException {

checkRowPos();

checkColumnBounds(columnIndex); return this.thisRow.getValue(columnIndex - 1, this.defaultTimestampValueFactory);

}

如何确认服务端时区?

使用会话中的服务端时区进行服务端时区。会话初始化时会进行时区的确认,比如前面获取的到BST。确认时区的逻辑在NativeProtocol#configureTimezone()中: public void configureTimezone() {

#从mysql的响应获取 time_zone 和 system_time_zone 的设置

String configuredTimeZoneOnServer = this.serverSession.getServerVariable("time_zone");

if ("SYSTEM".equalsIgnoreCase(configuredTimeZoneOnServer)) {

configuredTimeZoneOnServer = this.serverSession.getServerVariable("system_time_zone");

}

#从 jdbc url 参数 serverTimezone 获取时区

String canonicalTimezone = getPropertySet().getStringProperty(PropertyKey.serverTimezone).getValue();

if (configuredTimeZoneOnServer != null) {

//如果 jdbc url 中未通过 serverTimezone 指定时区。则从TimeZoneMapping.properties中获取mysql 回传的时区缩写对应的标准时区,比如此处的 BST => Europe/London

//会出现无法映射的情况,不如 CEST 无法映射到 => Europe/Berlin,可以指定自定义的 Properties 文件进行映射

// 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());

}

}

}

//如果 jdbc url 中通过 serverTimezone 指定了时区,则优先使用该时区

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());

}

}

}

关于 serverTimezone 的官方说明

Override detection/mapping of time zone. Used when time zone from server doesn't map to Java time zone

修改一下 jdbc url,通过serverTimezone指定时区为 GMT+8:jdbc:mysql://localhost:3306/test?serverTimezone=GMT%2B8&useSSL=false

再次执行代码:

2020-05-14T08:00

总结

到此这篇关于关于Java中mysql时区问题的文章就介绍到这了,更多相关Java中mysql时区问题内容请搜索聚米学院以前的文章或继续浏览下面的相关文章希望大家以后多多支持聚米学院!

java代码中设置mysql时区_关于Java中的mysql时区问题详解相关推荐

  1. java 代码里设置环境变量_如何在一个java程序里设置环境变量

    展开全部 环境变量是一个具有特定名字的对象,它包含了一个或者多个应用程序所将使用到的信息.例如path,当要62616964757a686964616fe4b893e5b19e313333613064 ...

  2. html中设置负边距的意义,css负边距之详解

    自从1998年CSS2作为推荐以来,表格的使用渐渐退去,成为历史.正因为此,从那以后CSS布局成为了优雅代码的代名词. 对于所有设计师使用过的CSS概念,负边距作为最少讨论到的定位方式要记上一功.这就 ...

  3. 上位机和MYSQL连接_上位机下位机串口通信设计详解

    串行接口是一种可以将接受来自CPU的并行数据字符转换为连续的串行数据流发送出去,同时可将接受的串行数据流转换为并行的数据字符供给CPU的器件.一般完成这种功能的电路,我们称为串行接口电路. 串口通信结 ...

  4. java 微信企业号上传文件_微信企业号上传下载多媒体文件接口详解演示-java

    讲完这篇博客,微信企业号的接口就说完了,下载了我源码的童鞋都知道,里面的备注很详细,但凡看过几遍就都会自己开发了,我说的这些接口至此我已经全部开发完了,剩下的就是你们自己写功能了,都是轻而易举的事情了 ...

  5. java登录注册功能怎么实现_使用Java代码实现登录注册功能

    实现思路:分别创建5个包和5个类{cn.f.pojo(User.java):cn.f.Dao(UserDao.java):cn.f.Dao.impl(UserDaoImpl.java): cn.f.g ...

  6. java爬虫中split的使用_关于java中split的使用

    之前在http://shukuiyan.iteye.com/blog/507915文中已经叙述过这个问题,但是最近一次笔试中居然有碰到了这个知识点,而且还做错了,囧!学艺不精啊.题目大概是这样的: J ...

  7. java前端长连接框架_Java中Spring Boot+Socket实现与html页面的长连接实例详解

    Spring Boot+Socket实现与html页面的长连接,客户端给服务器端发消息,服务器给客户端轮询发送消息,附案例源码 功能介绍 客户端给所有在线用户发送消息客户端给指定在线用户发送消息服务器 ...

  8. java包间通信,诊断Java代码: 消除包间的耦合关联[Java编程]

    赞助商链接 本文"诊断Java代码: 消除包间的耦合关联[Java编程]"是由七道奇为您精心收集,来源于网络转载,文章版权归文章作者所有,本站不对其观点以及内容做任何评价,请读者自 ...

  9. java中多线程之CAS(compareAndSet),Unsafe类大白话详解.

    java中多线程之CAS(compareAndSet),Unsafe类大白话详解 什么是CAS CAS原理 Unsafe类:     什么是CAS 比较并交换 在学习CAS之前,我们先了解一下JMM. ...

  10. R语言ggplot2可视化:jupyter中设置全局图像大小、jupyter中自定义单个ggplot2图像结果的大小

    R语言ggplot2可视化:jupyter中设置全局图像大小.jupyter中自定义单个ggplot2图像结果的大小 目录

最新文章

  1. HDU - 3078 Network 倍增LCA
  2. 2.2. php://stdin php://stdout
  3. ASP.NET Core 中文文档 第二章 指南(4.3)添加 View
  4. JAVA_OA(十四)番外:JAVAWEB防止表单重复提交的方法整合(包括集群部署)
  5. 揭秘阿里秒级百万TPS平台架构实现
  6. 重庆高职高专计算机排名,重庆十大大专排名(含分数线2021年参考)-重庆最好的全日制专科学校...
  7. 【美文保存】nosql数据库对比以及如何巧妙利用redis来提高效率?
  8. 如何深入学习python_菜鸟如何学好python
  9. android JNI(转)
  10. ext4 关闭延迟分配
  11. python工资高还是java-深圳python工资高还是java
  12. 清空上传控件HtmlInputFile的方法
  13. 【机器学习】数据处理详解
  14. 【数据库作业10】用SQL语句来表示关系代数中的表达式
  15. 提升网络营销策略的方法
  16. 对数 java_Java对数函数及Java对数运算
  17. 秋冬饮品研发没思路?带你看新品5大趋势!
  18. XILINX DSP Slice功能特点
  19. 51.com 对于6个月未登录的用户,相册进行清空处理
  20. 攻略:邮件搬家同一个域名操作步骤,设置邮箱搬家功能的方法

热门文章

  1. 618京东预售一般便宜多少?跟直接买有啥区别?
  2. Java类中各元素的初始化顺序
  3. Linux 进程间通信编程
  4. 麻省理工教授良心总结,Python的学习方法 学习笔记教程都在这里 ,一学就会
  5. can‘t resolve symble R
  6. 11.网络协议-HTTP及HTTPS协议
  7. Shell编程——极简教程
  8. vs2008破解90天限制
  9. S60真无线楼氏动铁耳机 强烈种草!
  10. V版S7 edge 升级Android8.0教程