我对 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.DATE
到 java.sql 的转换.日期
.
您提到多个 RDBMS 实现可以正确处理时区,但 SQLite 除外。让我们看看H2和 SQLite当您向 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/