【问题标题】:How to change MySQL timezone in a database connection using Java?如何使用 Java 更改数据库连接中的 MySQL 时区?
【发布时间】:2011-11-28 04:32:48
【问题描述】:

MySQL 使用时区“GMT+8”运行,而 Tomcat 使用“GMT”运行。当我将 datetime 保存到我的数据库时,一切似乎都正常,但是当我检查数据库中的 datetime 值时,我看到了“GMT”值。

另外,当我尝试从数据库中获取值时,值发生了变化,似乎数据库中的值被视为“GMT+8”,因此 Java 将值更改为“GMT”。

我已经这样设置了连接 U​​RL:

useTimezone=true&serverTimezone=GMT

但它不起作用。

【问题讨论】:

  • 您只得到“错误”的时区,还是值本身错误?
  • 我将配置更改为 useGmtMillisForDatetimes=true&serverTimezone=GMT。似乎这是我想要的行为。但仍然感到困惑。

标签: java mysql jdbc timezone


【解决方案1】:

useTimezone 是一种较旧的解决方法。 MySQL 团队最近重写了 setTimestamp/getTimestamp 代码,但只有在您设置连接参数 useLegacyDatetimeCode=false 并且您使用的是最新版本的 mysql JDBC 连接器时才会启用它。比如:

String url =
 "jdbc:mysql://localhost/mydb?useLegacyDatetimeCode=false

如果你下载 mysql-connector 源代码并查看 setTimestamp,很容易看出发生了什么:

如果使用旧日期时间码 = false,则调用 newSetTimestampInternal(...)。然后,如果传递给 newSetTimestampInternal 的 Calendar 为 NULL,则您的日期对象将在数据库的时区中格式化:

this.tsdf = new SimpleDateFormat("''yyyy-MM-dd HH:mm:ss", Locale.US);
this.tsdf.setTimeZone(this.connection.getServerTimezoneTZ());
timestampString = this.tsdf.format(x);

Calendar 为 null 非常重要 - 所以请确保您正在使用:

setTimestamp(int,Timestamp).

... NOT setTimestamp(int,Timestamp,Calendar).

现在应该很清楚这是如何工作的。如果您使用 java.util.Calendar 构建日期:2011 年 1 月 5 日凌晨 3:00 在 America/Los_Angeles(或您想要的任何时区)并调用 setTimestamp(1, myDate),那么它将获取您的日期,使用 SimpleDateFormat以数据库时区对其进行格式化。因此,如果您的数据库位于 America/New_York,它将构造要插入的字符串 '2011-01-05 6:00:00'(因为 NY 比 LA 早 3 小时)。

要检索日期,请使用 getTimestamp(int)(不带日历)。它将再次使用数据库时区来构建日期。

注意:网络服务器时区现在完全无关紧要了!如果您不将 useLegacyDatetimecode 设置为 false,则网络服务器时区用于格式化 - 增加了很多混乱。


注意:

我可能会抱怨 MySQL 的服务器时区不明确。例如,如果您的数据库设置为使用 EST,Java 中可能有几个可能的 EST 时区,因此您可以通过准确告诉 mysql-connector 数据库时区是什么来澄清这一点:

String url =
 "jdbc:mysql://localhost/mydb?useLegacyDatetimeCode=false&serverTimezone=America/New_York";

只有在它抱怨时你才需要这样做。

【讨论】:

  • 感谢您对 useLegacyDatetimeCode 的建议!注意:这也解决了我在使用 Joda Time 时遇到的问题(通过 usertypes hibernate 插件)。
  • 因此,将 serverTimezone 和 useLegacyDatetimeCode 添加到我的连接字符串中,我可以发送一个 Timestmp 并正确记录在 DateTime 列中,因为它是 UTC 时间。我可以在我的 MySQL 控制台上看到它。当我通过 JDBC 执行 SELECT 并尝试获取数据时,它仍然会在 JDBC 层中自动转换它。我已经尝试过 getObject() 和 geTimestmp(没有和有 UTC 日历对象),它一直在本地时间返回。
  • 原来这是我解决生产问题所需的设置。在本地我们无法重现它。但是在生产中的驱动程序中尝试这个可以消除我们在处理 10-30 秒后看到的停顿。
  • @djsumdog - 时区信息未存储在 Timestamp 类中。因此,当您将日期对象打印到控制台时,它将在 JVM 时区中格式化。
  • 试试String url = "jdbc:mysql://localhost?tz=useLegacyDatetimeCode=false&serverTimezone=America/New_York";。对于我的应用程序,此示例链接有效:url: jdbc:mysql://localhost:3306/trip_planner?tz=useLegacyDatetimeCode=false&serverTimezone=Europe/Berlin
【解决方案2】:

JDBC使用了所谓的“连接URL”,所以可以用“%2B”转义“+”,即

useTimezone=true&serverTimezone=GMT%2B8

【讨论】:

    【解决方案3】:

    对于 Squirrel SQL Client (http://squirrel-sql.sourceforge.net/) 版本 4 等应用程序,您可以将“驱动程序属性”下的“serverTimezone”设置为 GMT+1(时区示例“欧洲/维也纳”)。

    【讨论】:

    • 谢谢,成功了!我还想指出 IST 没有改变任何东西,而 GMT+1 做到了。
    • 谢谢!我从来没有估计我的答案会有任何相关性...... :-)
    【解决方案4】:

    有没有办法从 MySQL 获取支持的 timeZone 列表? ex - serverTimezone=America/New_York。这可以解决很多这样的问题。我相信每次您都需要从应用程序中指定正确的时区,而不考虑 DB TimeZone。

    【讨论】:

    • 您可以使用 TimeZone.getAvailableIDs() 方法。基本上返回一个字符串数组。您可以打印出来并查看可用时区列表。
    猜你喜欢
    • 2013-03-06
    • 1970-01-01
    • 2011-07-27
    • 2014-08-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-10-03
    相关资源
    最近更新 更多