The JDBC规范 http://download.oracle.com/otndocs/jcp/jdbc-4_1-mrel-spec/index.html没有定义有关时区的任何详细信息。尽管如此,我们大多数人都知道必须处理 JDBC 时区差异的痛苦;看看所有的StackOverflow 问题 https://stackoverflow.com/search?q=jdbc+time+zone!
最终,日期/时间数据库类型的时区处理归结为数据库服务器、JDBC 驱动程序以及两者之间的所有内容。您甚至会受到 JDBC 驱动程序错误的影响; PostgreSQL 修复了一个8.3版本的bug http://jdbc.postgresql.org/changes.html#N10600 where
传递 Calendar 对象的 Statement.getTime、.getDate 和 .getTimestamp 方法正在以错误的方向旋转时区。
当您使用创建新日期时new Date(0)
(假设您正在使用 Oracle JavaSEjava.sql.Date http://docs.oracle.com/javase/7/docs/api/java/sql/Date.html,您的日期已创建
使用给定的毫秒时间值。如果给定的毫秒值包含时间信息,则驱动程序会将时间组件设置为与零 GMT 相对应的默认时区(运行应用程序的 Java 虚拟机的时区)中的时间。
So, new Date(0)
应该使用 GMT。
你打电话时ResultSet.getDate(int) http://docs.oracle.com/javase/7/docs/api/java/sql/ResultSet.html#getDate%28int%29,您正在执行 JDBC 实现。 JDBC 规范没有规定 JDBC 实现应如何处理时区详细信息;所以你只能受实施的摆布。看看Oracle 11goracle.sql.DATE http://docs.oracle.com/cd/E18283_01/appdev.112/e13995/oracle/sql/DATE.htmlJavaDoc,Oracle DB 似乎没有存储时区信息,因此它执行自己的转换以将日期转换为java.sql.Date
。我没有使用 Oracle DB 的经验,但我猜测 JDBC 实现正在使用服务器和本地 JVM 的时区设置来进行转换oracle.sql.DATE
to java.sql.Date
.
您提到多种 RDBMS 实现都可以正确处理时区,但 SQLite 除外。让我们看看如何H2 http://www.h2database.com/ and SQLite http://www.sqlite.org/当您将日期值发送到 JDBC 驱动程序以及从 JDBC 驱动程序获取日期值时,该功能会起作用。
H2 JDBC 驱动程序PrepStmt.setDate(int, Date) https://code.google.com/p/h2database/source/browse/trunk/h2/src/main/org/h2/jdbc/JdbcPreparedStatement.java#359 uses ValueDate.get(Date) https://code.google.com/p/h2database/source/browse/trunk/h2/src/main/org/h2/value/ValueDate.java#56,这称为DateTimeUtils.dateValueFromDate(long) https://code.google.com/p/h2database/source/browse/trunk/h2/src/main/org/h2/util/DateTimeUtils.java#732它进行时区转换。
使用这个SQLite JDBC 驱动程序 https://code.google.com/p/sqlite-jdbc/, PrepStmt.setDate(int, Date) https://code.google.com/p/sqlite-jdbc/source/browse/src/main/java/org/sqlite/PrepStmt.java#283 calls PrepStmt.setObject(int, Object) https://code.google.com/p/sqlite-jdbc/source/browse/src/main/java/org/sqlite/PrepStmt.java#221并且不进行任何时区转换。
H2 JDBC 驱动程序JdbcResultSet.getDate(int) https://code.google.com/p/h2database/source/browse/trunk/h2/src/main/org/h2/jdbc/JdbcResultSet.java#344回报get(columnIndex).getDate()
. get(int) https://code.google.com/p/h2database/source/browse/trunk/h2/src/main/org/h2/jdbc/JdbcResultSet.java#2950返回一个H2Value https://code.google.com/p/h2database/source/browse/trunk/h2/src/main/org/h2/value/Value.java对于指定的列。由于列类型是DATE
, H2 使用ValueDate https://code.google.com/p/h2database/source/browse/trunk/h2/src/main/org/h2/value/ValueDate.java. ValueDate.getDate() https://code.google.com/p/h2database/source/browse/trunk/h2/src/main/org/h2/value/ValueDate.java#80 calls DateTimeUtils.convertDateValueToDate(long) https://code.google.com/p/h2database/source/browse/trunk/h2/src/main/org/h2/util/DateTimeUtils.java#628,最终创建一个java.sql.Date
时区转换后。
使用这个SQLite JDBC 驱动程序 https://code.google.com/p/sqlite-jdbc/, the RS.getDate(int) https://code.google.com/p/sqlite-jdbc/source/browse/src/main/java/org/sqlite/RS.java#265代码更简单;它只是返回一个java.sql.Date
使用long
存储在数据库中的日期值。
因此,我们看到 H2 JDBC 驱动程序在处理日期时区转换方面很聪明,而 SQLite JDBC 驱动程序则不然(并不是说这个决定不聪明,它可能很适合 SQLite 设计决策)。如果您追查您提到的其他 RDBMS JDBC 驱动程序的源代码,您可能会发现大多数驱动程序都以与 H2 类似的方式接近日期和时区。
尽管 JDBC 规范没有详细说明时区处理,但 RDBMS 和 JDBC 实现设计者考虑时区并正确处理它是很有意义的;特别是如果他们希望自己的产品在全球舞台上畅销。这些设计师非常聪明,即使在没有具体规范的情况下,他们中的大多数人都能做到这一点,我对此并不感到惊讶。
我找到了这个 Microsoft SQL Server 博客,在 SQL Server 2008 中使用时区数据 https://blogs.msdn.com/b/sqlprogrammability/archive/2008/03/18/using-time-zone-data-in-sql-server-2008.aspx,这解释了时区如何使事情变得复杂:
时区是一个复杂的领域,每个应用程序都需要解决如何处理时区数据的问题,以使程序对用户更加友好。
不幸的是,当前时区名称和值没有国际标准权威。每个系统都需要使用自己选择的系统,在出现国际标准之前,尝试让 SQL Server 提供一个系统是不可行的,并且最终会导致比它解决的问题更多的问题。