简体   繁体   English

MySQL客户端与服务器时区

[英]MySQL client vs server time zone

I'm running into an issue where MySQL stores different date time values than the client passes. 我遇到的问题是MySQL存储的日期时间值与客户端传递的不同。 The server runs in UTC and the client in a different time zone. 服务器以UTC运行,客户端在不同的时区运行。 Somehow MySQL seems to convert date time values between the client and server time zone even though the SQL types DATE , TIME and TIMESTAMP all have no time zone. 不管怎样,MySQL似乎在客户端和服务器时区之间转换日期时间值,即使SQL类型DATETIMETIMESTAMP都没有时区。 No other database I tested so far has this behaviour. 到目前为止,我测试的其他数据库都没有这种行为

The following code can be used to reproduce the issue. 以下代码可用于重现该问题。 When the server runs in UTC the code only works when the client also runs in UTC. 当服务器以UTC运行时,代码仅在客户端也以UTC运行时才有效。

try (Connection connection = this.dataSource.getConnection();
     PreparedStatement preparedStatement = connection.prepareStatement(
             "SELECT ? = DATE '1988-12-25', ? = TIME '15:09:02', ? = TIMESTAMP '1980-01-01 23:03:20'")) {
  preparedStatement.setDate(1, java.sql.Date.valueOf("1988-12-25"));
  preparedStatement.setTime(2, java.sql.Time.valueOf("15:09:02"));
  preparedStatement.setTimestamp(3, java.sql.Timestamp.valueOf("1980-01-01 23:03:20"));
  try (ResultSet resultSet = preparedStatement.executeQuery()) {
    while (resultSet.next()) {
      System.out.println(resultSet.getBoolean(1));
      System.out.println(resultSet.getBoolean(2));
      System.out.println(resultSet.getBoolean(3));
    }
  }

}

I'm using 我正在使用

  • MySQL 5.7.14 MySQL 5.7.14
  • mysql-connector-java 6.0.5 mysql-connector-java 6.0.5
  • Oracle Java 1.8.0_131 Oracle Java 1.8.0_131

My JDBC URL is just jdbc:mysql://host:port/database 我的JDBC URL只是jdbc:mysql://host:port/database

edit 编辑

My reasoning why time zones should not play a role here and no time zone conversion should happen is two fold. 我的理由为什么时区不应该在这里发挥作用,并且不应该发生时区转换是双重的。 Firstly on the SQL level TIMESTAMP is an alias for TIMESTAMP WITHOUT TIME ZONE which strongly implies that unlike TIMESTAMP WITH TIME ZONE it values have no time zone. 首先在SQL级别上, TIMESTAMPTIMESTAMP WITHOUT TIME ZONE的别名,强烈暗示与TIMESTAMP WITH TIME ZONE它的值没有时区。 In other words values are not instants in time but rather in local date time values. 换句话说,值不是时间上的瞬间,而是本地日期时间值。

Secondly that java.sql.Timestamp is in JVM time zone is merely artefact of being a subclass of java.util.Date . 其次java.sql.Timestamp在JVM时区只是作为java.util.Date的子类的人工制品。 (I am aware that java.util.Date has no time zone). (我知道java.util.Date没有时区)。 The Javadoc of java.sql.Timestamp of makes it quite clear that relationship is only for implementation purposes. java.sql.Timestamp的Javadoc非常清楚,这种关系仅用于实现目的。

I feel both of these assertions are confirmed by the fact that in Java SE 8 / JDBC 4.2 java.sql.Timestamp is mapped to java.time.LocalDateTime and not java.time.ZonedDateTime or java.time.OffsetDateTime . 我觉得这两个断言都得到了以下事实的证实:在Java SE 8 / JDBC 4.2中, java.sql.Timestamp被映射到java.time.LocalDateTime而不是java.time.ZonedDateTimejava.time.OffsetDateTime

edit 2 编辑2

I do not understand why TIMESTAMP values are subject to time zone conversion. 我不明白为什么TIMESTAMP值受时区转换的影响。 Unlike TIMESTAMP WITH TIME ZOONE These are "local" values and do not have an associated time zone and should therefore have no time zone conversion applied to them. TIMESTAMP WITH TIME ZOONE不同这些是“本地”值,并且没有关联的时区,因此不应对它们应用时区转换。

I agree with the comment that timezone handling under MySQL JDBC can be confusing, but in this case ... 我同意评论MySQL JDBC下的时区处理可能令人困惑,但在这种情况下......

MySQL stores different date time values than the client passes MySQL存储的日期时间值与客户端传递的不同

... is not quite correct. ......不太正确。 It interprets and/or displays the string representation of the same datetime value in the context of the server timezone . 在服务器时区的上下文中解释和/或显示相同日期时间值的字符串表示形式

First you need to understand that java.sql.Timestamp#valueOf creates a Timestamp value in the local timezone under which the Java Virtual Machine is running. 首先,您需要了解java.sql.Timestamp#valueOf在运行Java虚拟机的本地时区中创建Timestamp值。 So, for my machine which is on "Mountain Time" in Canada (UTC-7 for Standard Time): 因此,对于我在加拿大的“山地时间”(标准时间为UTC-7)的机器:

System.out.printf("Local (client) timezone is %s%n", TimeZone.getDefault().getID());

java.sql.Timestamp tStamp = java.sql.Timestamp.valueOf("1980-01-01 23:03:20");

SimpleDateFormat sdfLocal = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss z");
SimpleDateFormat sdfUTC = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss z");
sdfUTC.setCalendar(Calendar.getInstance(TimeZone.getTimeZone("UTC")));

System.out.println("tStamp = java.sql.Timestamp.valueOf(\"1980-01-01 23:03:20\")");
System.out.printf("                        ... which is %s%n", sdfLocal.format(tStamp));
System.out.printf("                        ... which is %s%n", sdfUTC.format(tStamp));

prints 版画

Local (client) timezone is America/Edmonton
tStamp = java.sql.Timestamp.valueOf("1980-01-01 23:03:20")
                        ... which is 1980-01-01 23:03:20 MST
                        ... which is 1980-01-02 06:03:20 UTC

Now, when we pass that Timestamp value to a PreparedStatement and send it off to a MySQL server that is using the UTC timezone, the server interprets and displays string literal representations of that MySQL TIMESTAMP as UTC: 现在,当我们将Timestamp值传递给PreparedStatement并将其发送到使用UTC时区的MySQL服务器时,服务器会解释并显示该MySQL TIMESTAMP的字符串文字表示形式为UTC:

String connUrl = "jdbc:mysql://localhost/mydb";
try (Connection conn = DriverManager.getConnection(connUrl, myUid, myPwd)) {
    String sql = "SELECT CAST((TIMESTAMP ?) AS CHAR) AS foo";
    try (PreparedStatement ps = conn.prepareStatement(sql)) {
        ps.setTimestamp(1, tStamp);
        try (ResultSet rs = ps.executeQuery()) {
            rs.next();
            System.out.printf("String representation of TIMESTAMP value at server: %s%n", 
                    rs.getString(1));
        }
    }
}

producing 生产

String representation of TIMESTAMP value at server: 1980-01-02 06:03:20

If the MySQL server had been running under Toronto time (UTC-5 for Standard Time) the output would have been ... 如果MySQL服务器在多伦多时间(标准时间为UTC-5)下运行,则输出将是......

String representation of TIMESTAMP value at server: 1980-01-02 01:03:20

... not because the MySQL TIMESTAMP value is different, but because the string representation of that value in the context of the MySQL server timezone is different. ...不是因为MySQL TIMESTAMP值不同,而是因为MySQL服务器时区上下文中该值的字符串表示形式不同。

I guess it is like Gord Thompson and spencer7593 stated, that the timezone of the server and that of the connection-string of the client differ and a conversion between timezones has to be made. 我想这就像Gord Thompson和spencer7593所述,服务器的时区和客户端的连接字符串的时区不同,必须在时区之间进行转换。

I also learned something while reading your edits, as in that the java.sql.Timestamp contains no timezone. 我在阅读您的编辑时也学到了一些东西,因为java.sql.Timestamp不包含时区。

As spencer7593 stated: "The server is interpreting the literal in the context of the server timezone". 正如spencer7593所说:“服务器正在解释服务器时区上下文中的文字”。

My two approaches here would be: 我的两种方法是:

  1. Connect to server (UTC) with your timezone in the connection-string(eg &serverTimezone=Europe/Zurich). 使用连接字符串中的时区连接到服务器(UTC)(例如&serverTimezone = Europe / Zurich)。

  2. Alternatively pass an instance of an Calendar with your local timezone (Calendar.getInstance()) to PreparedStatement.setTimestamp . 或者,将具有本地时区(Calendar.getInstance())的Calendar实例传递给PreparedStatement.setTimestamp

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM