java - SQL DATE 与 java.sql.Date 中的时区

标签 java sql date jdbc timezone

我对 SQL DATE 数据类型的行为与 java.sql.Date 的行为有点混淆。以下面的语句为例:

select cast(? as date)           -- in most databases
select cast(? as date) from dual -- in Oracle

让我们用 Java 准备和执行语句

PreparedStatement stmt = connection.prepareStatement(sql);
stmt.setDate(1, new java.sql.Date(0)); // GMT 1970-01-01 00:00:00
ResultSet rs = stmt.executeQuery();
rs.next();

// I live in Zurich, which is CET, not GMT. So the following prints -3600000, 
// which is CET 1970-01-01 00:00:00
// ... or   GMT 1969-12-31 23:00:00
System.out.println(rs.getDate(1).getTime());

换句话说,我绑定(bind)到语句的 GMT 时间戳变成了我返回的 CET 时间戳。时区是在哪一步添加的?为什么?

注意:

  • 我观察到这些数据库中的任何一个都是如此:

    DB2、Derby、H2、HSQLDB、Ingres、MySQL、Oracle、Postgres、SQL Server、Sybase ASE、Sybase SQL Anywhere

  • 我观察到这对于 SQLite 是错误的(它实际上没有真正的 DATE 数据类型)
  • 当使用 java.sql.Timestamp 而不是 java.sql.Date
  • 时,所有这些都无关紧要
  • 这是一个类似的问题,但没有回答这个问题:java.util.Date vs java.sql.Date

最佳答案

JDBC specification没有定义有关时区的任何细节。尽管如此,我们大多数人都知道不得不处理 JDBC 时区差异的痛苦。看看所有StackOverflow questions !

最终,日期/时间数据库类型的时区处理归结为数据库服务器、JDBC 驱动程序以及介于两者之间的所有内容。您甚至会受到 JDBC 驱动程序错误的摆布; PostgreSQL 修复了 bug in version 8.3在哪里

Statement.getTime, .getDate, and .getTimestamp methods which are passed a Calendar object were rotating the timezone in the wrong direction.

当您使用 new Date(0) 创建新日期时(假设您使用的是 Oracle JavaSE java.sql.Date,您的日期已创建

using the given milliseconds time value. If the given milliseconds value contains time information, the driver will set the time components to the time in the default time zone (the time zone of the Java virtual machine running the application) that corresponds to zero GMT.

所以,new Date(0) 应该使用 GMT。

当您调用 ResultSet.getDate(int) 时,您正在执行 JDBC 实现。 JDBC 规范没有规定 JDBC 实现应该如何处理时区细节。所以你在实现的摆布。看 Oracle 11g oracle.sql.DATE JavaDoc,Oracle DB 似乎不存储时区信息,因此它执行自己的转换以将日期转换为 java.sql.Date。我没有使用 Oracle DB 的经验,但我猜想 JDBC 实现是使用服务器和本地 JVM 的时区设置来进行从 oracle.sql.DATEjava.sql 的转换.日期.


您提到多个 RDBMS 实现可以正确处理时区,但 SQLite 除外。让我们看看H2SQLite当您向 JDBC 驱动程序发送日期值以及从 JDBC 驱动程序获取日期值时工作。

H2 JDBC 驱动程序 PrepStmt.setDate(int, Date)使用 ValueDate.get(Date) , 调用 DateTimeUtils.dateValueFromDate(long)进行时区转换。

使用这个SQLite JDBC driver , PrepStmt.setDate(int, Date)来电PrepStmt.setObject(int, Object)并且不做任何时区转换。

H2 JDBC 驱动程序 JdbcResultSet.getDate(int)返回 get(columnIndex).getDate()get(int)返回 H2 Value对于指定的列。由于列类型为 DATE,因此 H2 使用 ValueDate . ValueDate.getDate()来电DateTimeUtils.convertDateValueToDate(long) ,最终在时区转换后创建一个 java.sql.Date

使用这个SQLite JDBC driver , RS.getDate(int)代码要简单得多;它只是使用存储在数据库中的 long 日期值返回一个 java.sql.Date

所以我们看到 H2 JDBC 驱动程序在处理带有日期的时区转换方面很聪明,而 SQLite JDBC 驱动程序则不是(并不是说这个决定不聪明,它可能很适合 SQLite 设计决策)。如果您追查您提到的其他 RDBMS JDBC 驱动程序的来源,您可能会发现大多数都以与 H2 类似的方式接近日期和时区。

虽然 JDBC 规范没有详细说明时区处理,但 RDBMS 和 JDBC 实现设计者将时区考虑在内并妥善处理是很有意义的;特别是如果他们希望他们的产品在全局范围内有市场。这些设计师非常聪明,即使在没有具体规范的情况下,他们中的大多数人都能做到这一点,我并不感到惊讶。


我发现了这个 Microsoft SQL Server 博客,Using time zone data in SQL Server 2008 ,这解释了时区如何使事情复杂化:

timezones are a complex area and each application will need to address how you are going to handle time zone data to make programs more user friendly.

Unfortunately, there is no current international standard authority for timezone names and values. Each system needs to use a system of their own choosing, and until there is an international standard, it is not feasible to try to have SQL Server provide one, and would ultimately cause more problems than it would solve.

关于java - SQL DATE 与 java.sql.Date 中的时区,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9202857/

相关文章:

sql - 针对具有重命名列的表的向后兼容 SQL 查询

mysql - 使用 CONCAT 的 SQL 语法

sql - 哈希联接出现在全文查询中 - SQL Server 2005

Java方法生成通过实例变量传递的随机数

java - JDBC 连接在同事的计算机上运行,​​而不是在我的计算机上

java - 简单日期格式将下午时间错误地解析为上午时间

MySql 日期/时间值问题

php - 在 JS 中默认更改时区

java - maven 无法识别来自 build-helper-maven-plugin 的执行标记

java - android中文件的创建日期