默认情况下mysql
的time_zone
为SYSTEM
,也就是mysql
的时区和服务器的时区是一样的。一般服务器都是东八区。在使用mysql
时,客户端所在的时区一般也是东八区。所以从mysql
查询timestamp
类型数据时一切正常。
现在如果我们的条件是这样的:
- 使用的
jdbc url
长这样jdbc:mysql://localhost:3306/table_name
- 客户端的时区为零时区
mysql
的时区是东八区
上面几个条件如果满足了,这时候你从mysql
查找timestamp
的数据,拿到的timestamp
值和数据库中的值是一样的,没有考虑时区的问题,所以事实上是多了8
小时。
通过debug
,发现jdbc
的ConnectionImpl
类中configureTimezone()
配置time zone
时有几个条件
String canonicalTimezone = getServerTimezone();
if ((getUseTimezone() || !getUseLegacyDatetimeCode()) && 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 SQLError.createSQLException(iae.getMessage(), SQLError.SQL_STATE_GENERAL_ERROR, getExceptionInterceptor());
}
}
}
if (canonicalTimezone != null && canonicalTimezone.length() > 0) {
this.serverTimezoneTZ = TimeZone.getTimeZone(canonicalTimezone);
...
getServerTimezone()
方法拿到的值默认为null
,getUseTimezone()
和getUseLegacyDatetimeCode()
方法得到的值分别是false
和true
,这样就导致了connector
不会使用数据库的time zone
。所以我们从数据库内读取timestamp
数据,不做任何时区转化。
如果想要开启时区转换,需要将useTimezone
设置为true
, 即jdbc:mysql://localhost:3306/table_name?useTimezone=true
我们可以看mysql
文档对useTimezone
描述
useTimezone
Convert time/date types between client and server time zones (true/false, defaults to ‘false’)? This is part of the legacy date-time code, thus the property has an effect only when “useLegacyDatetimeCode=true.”
Default: false
将useTimezone
设置为true
后,还有一点很重要,如果mysql
的time_zone
为SYSTEM
,连接数据库是会报错的,需要给定一个明确的时区。我们可以把数据库的time_zone
设置为+08:00
,这样timestamp
的数据就会根据时区进行转换。 如果不想修改数据库的time_zone
,可以在客户端进行设置,指定time_zone
,jdbc:mysql://localhost:3306/table_name?useTimezone=true&serverTimezone=GMT%2B8
,GMT%2B8
表示东八区。