MySQL JDBC驱动程序5.1.33 - 时区问题

469

一些背景:

我有一个运行在Tomcat 7上的Java 1.6 web应用程序。数据库是MySQL 5.5。之前,我使用Mysql JDBC驱动程序5.1.23连接到数据库。一切正常。最近我升级到了Mysql JDBC驱动程序5.1.33。升级后,当启动应用程序时,Tomcat会抛出以下错误。

WARNING: Unexpected exception resolving reference
java.sql.SQLException: The server timezone value 'UTC' is unrecognized or represents
  more than one timezone. You must configure either the server or JDBC driver (via
  the serverTimezone configuration property) to use a more specifc timezone value if
  you want to utilize timezone support.

为什么会发生这种情况?


1
你的JDBC URL是什么样子的? - David Levesque
35个回答

831

显然,为了让MySQL JDBC驱动程序的版本5.1.33能够与UTC时区配合使用,必须在连接字符串中明确指定serverTimezone

jdbc:mysql://localhost/db?useUnicode=true&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=UTC

5
根据文档,当使用useLegacyDatetimeCode=false时,useJDBCCompliantTimezoneShift无效,因此在那里不需要它。 - matof
29
这解决了我的错误。另外需要注意,在 persistence.xml 文件中使用 & 转义符代替 & :<property name="javax.persistence.jdbc.url" value="jdbc:mysql://localhost/test?useUnicode=true&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=UTC"/> - pdem
5
不正确。useLegacyDatetimeCode=false 的目的是不必指定 serverTimezone,以便客户端可以纠正时区差异。这是 MySQL 客户端版本中的一个错误。 - Aníbal
5
除了 GMT 之外,这个解决方案会破坏时区。我认为下面的被低估的方案才是正确的解决方案。 - DuncanSungWKim
1
解决方案适用于8.0.17版本。这是在全新的MySQL安装中发生的。难以相信这个错误在这么多年后仍未被修复。 - Tilman Hausherr
显示剩余13条评论

147

我通过配置MySQL解决了这个问题。

SET GLOBAL time_zone = '+3:00';


8
如果你使用的是莫斯科标准时间(MSK),时差为+3,那么你可以将以下内容作为数据库地址:jdbc:mysql://localhost/db?useUnicode=true&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=Europe/Moscow。看起来mysql-connector不理解短时区名称。 - babay
4
当夏令时更改时间时,你会怎么做? - isapir
4
使用 MySQL 8.0 版本,您可以调用“set persist time_zone = '+00:00';”将其持久地设置为 UTC,无需编辑 my.cnf 或重新启动服务器。请参阅 https://mysqlserverteam.com/mysql-8-0-persisting-configuration-variables/。 - ccleve
如果您是通过唯一的手动 SQL 查询进行设置的,则在数据库重新启动后,此设置将恢复为原始值。 - CBA110
请注意,在+3的位置修改您本地时区字符串:SET GLOBAL time_zone = '+3:00'; - Pravin Bansal

82

在阅读了几篇关于这个主题的帖子、测试了不同的配置并基于this mysql bug thread的一些见解后,我所理解的是:

  • 服务器时区对于将存储在数据库中的日期转换为应用程序服务器的时区非常重要。还有其他影响,但这是最明显的一个。
  • GMT x UTC 时区系统。 GMT 是在19世纪末提出的,可以在标准时间和夏令时之间进行切换。这个属性可能会导致数据库服务器切换到夏令时,而应用程序没有注意到(也许还有其他复杂情况,但我没有进一步研究)。UTC 不随时间变化(它始终在0°经线上的平均太阳时间左右1秒内)。
  • serverTimeZone 定义是在 mysql jdbc 连接器版本 5.1 之前引入的。直到版本 8,它可以被忽略,使用 useLegacyDatetimeCode=true,与 useJDBCCompliantTimezoneShift=true 结合使用,可以使应用程序在每次连接时获取数据库时区。在此模式下,GMT 时区(如“英国夏令时”)将被转换为内部 java/JDBC 格式。新的时区可以在 .properties 文件中定义,例如 this one
  • 从 jdbc 驱动程序版本 8 开始,自动时间匹配(useJDBCCompliantTimezoneShift)和遗留时间格式(useLegacyDatetimeCode)被删除(请参阅 mysql jdbc 连接器更改日志)。因此,设置这两个参数没有任何效果,因为它们完全被忽略了(新的默认值是 useLegacyDateTimeCode=false)。
  • 以这种方式设置 serverTimezone 如果任何一个时区(应用程序/数据库服务器)不是 'UTC+xx' 或 'GMT+xx' 格式,则变得强制性
  • 将服务器时间设置为 UTC(例如使用 jdbc:mysql://localhost:3306/myschema?serverTimezone=UTC),即使您的应用程序/数据库服务器不在此时区,也不会产生影响。重要的是应用程序连接字符串 + 数据库与相同的时区同步。换句话说,仅仅设置 serverTimezone=UTC 并在数据库服务器上使用不同的时区将会导致从数据库中提取的任何日期发生偏移
  • 可以通过在 my.ini 或 my.cnf 文件(分别适用于 Windows / Linux)中添加行 default-time-zone = '+00:00' 来将 MySQL 默认时区设置为 UTC+0(详见 this StackOverflow post)。
  • 在 AWS(Amazon Web Services)上配置的数据库自动分配 UTC+0 默认值(请参阅 AWS 帮助页面)。

2
好的回答,谢谢。各个要点都很有用。我采纳了将 default-time-zone = '+00:00' 放入 homebrew /usr/local/etc/my.cnf 文件的建议。不过似乎等号周围的空格很重要,所以你可能需要编辑一下这个要点来包含它们。 - Mark Edington

55
如果您正在使用Maven,您可以在pom.xml中设置另一个MySQL连接器版本(我遇到了同样的错误,所以我将其从6.0.2更改为5.1.39):
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>5.1.39</version>
</dependency>

如其他回答所述,此问题已在6.0.3或更高版本中得到修复,因此您可以使用更新的版本:

<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>6.0.3</version>
</dependency>

Maven会在你保存pom.xml文件后自动重新构建你的项目。


2
对于那些下载了mysql-connector-java/6的人 -> 只需下载例如mysql-connector-java/5.1.20,它应该可以工作。谢谢! - Combine
6
应避免降级。而且,这个问题在6.0.6版本中仍未被解决。最好使用上述解决方案。 - phil294
我即使使用最新的jar包[mysql-connector-java-6.0.5.jar:6.0.5]仍然遇到相同的错误。 - user2478236
24
我即使在8.0.12版本中也遇到了这个问题。 - Robert Niestroj
16
8.0.13 给出了相同的错误。然而,5.1.47 对我来说可行。 - localhost
这种情况发生在我身上。一个旧项目被设置为版本8.0.19。出于一时兴起,我尝试了8.0.26(截至我撰写此文时的最新版本),然后所有东西都开始正常工作了。 - Marvo

42

连接字符串应该设置为这样:

jdbc:mysql://localhost/db?useUnicode=true&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=UTC

如果您是在xml文件(例如persistence.xmlstandalone-full.xml等)中定义连接,请使用&amp;代替&或使用CDATA块。


1
这不正确。useLegacyDatetimeCode=false 的重点不在于必须指定 serverTimezone,而是客户端会自动纠正时区差异。 - Aníbal
这对我来说可行,使用phpStorm 2019.1.4连接到MySQL 5.7。 - moult86
感谢提醒在XML文件中使用&amp;,救了我的一天! - Bikramjit Singh

38

我只需在 application.properties 上添加 serverTimeZone=UTC,它就对我生效了。
spring.datasource.url=jdbc:mysql://localhost/db?serverTimezone=UTC


1
尝试从带有EDT时区的MacOS连接到MySQL时,出现了异常。这个解决方案对我很有效。只需在JDBC URL的末尾添加*?serverTimezone=UTC*即可。 - RafiAlhamd
我正在使用Spring Boot 2.3.1和MySQL Connector 8.0.21,这对我很有效。 - user3533413

34

我解决了将以下连接字符串放在URL中的问题

jdbc:mysql://localhost:3306/db?useUnicode=true&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=UTC

1
这不正确。useLegacyDatetimeCode=false 的目的是不必指定 serverTimezone,因此客户端会纠正时区差异。 - Aníbal

29
这是mysql-connector-java版本5.1.33到5.1.37中的一个bug。我在这里报告了它:http://bugs.mysql.com/bug.php?id=79343 编辑: 这已经从mysql-connector-java 5.1.39中得到了修正。
这是TimeUtil类中loadTimeZoneMappings方法的一个错别字,导致NPE定位/com/mysql/jdbc/TimeZoneMapping.properties文件。如果您查看代码,该文件应该位于TimeUtil类加载器内部,而不是TimeZone:
TimeUtil.class.getResourceAsStream(TIME_ZONE_MAPPINGS_RESOURCE);

参数useLegacyDatetimeCode允许在使用日期时自动纠正客户端和服务器时区之间的差异,因此它可以帮助您精确地不必在每个部分中指定时区。尽管使用serverTimeZone参数是一种解决方法,在补丁发布之前,您可以尝试自行更正代码,就像我所做的那样。

  • 如果这是一个独立应用程序,您可以尝试简单地向代码中添加已更正的com/mysql/jdbc/TimeUtil类,并注意jar加载顺序。这可能会有所帮助:https://owenou.com/2010/07/20/patching-with-class-shadowing-and-maven.html

  • 如果这是一个Web应用程序,则更简单的解决方案是创建您自己的mysql-connector-java-5.1.37-patched.jar,直接将.class替换为原始jar中的文件。


甜的,谢谢你报告这个。 很高兴有人能够找到这个错误。 你知道修复会在什么时候发布吗? - bluecollarcoder
已在mysql-connector-java 5.1.39中进行了修正。 - Aníbal
4
@Gili 在发布6.0.6版本时,这个问题还没有得到解决。 - Imme22009
6
这个问题在8.0.11中仍然存在。 - John Little
3
@JohnLittle,我在8.0.15版本也遇到了这个问题,但它不再是由于一个bug导致的。时区已正确加载,但CET和CEST(这些时区令我困扰)既没有包含在TimeZone.getAvailableIDs()中,也没有在TimeZoneMapping.properties中,所以这个解决方案在这里并不能起作用。解决方案可能是设置像serverTimezone=Europe/Berlin这样的内容。 - JPT
显示剩余5条评论

27
  1. I added in mysql config file in section [mysqld]

    default_time_zone='+03:00'
    
  2. And restart mysql server:

    sudo service mysql restart
    

我的UTC时区在+03:00.

Ubuntu 16.04系统中配置文件的路径:

/etc/mysql/mysql.conf.d/mysqld.cnf

警告:如果您所在的时区有夏令时和冬令时。如果更改时间,则必须在配置中更改UTC。每年两次(通常)或使用sudo设置crontab。

我的URL JDBC连接:

"jdbc:mysql://localhost/java"

1
在几乎所有生产用例中,不得不重新启动Mysql基本上是行不通的。当涉及到复制时,这个问题变得更加严重。 - bluecollarcoder
@bluecollarcoder 需要在 [mysqld] 部分中添加 add。如果没有该部分,请添加 [mysqld] 部分。例如,我的配置 https://pastebin.com/j4F7t2KS - Fortran
1
我将Linux服务器的/etc/localtime从/usr/share/zoneinfo/US/Pacific更新为/usr/share/zoneinfo/America/Los_Angeles,然后重新启动了mysql服务,这解决了我的问题。 - vinnyjames
在我的情况下,使用提供的语法重新启动时出现错误,正确的语法应该是:default-time-zone='+03:00',根据这个答案。同样适用于DBeaver。 - wscourge
不合适要求每个开发人员在公司中更改他们的MySQL配置 :) - pheromix

18

以上程序将生成时区错误。

在您的数据库名称之后,您需要添加:?useTimezone=true&serverTimezone=UTC。一旦完成,您的代码将正常工作。

祝您好运:)


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接