在将Java Date写入SQL TIMESTAMP列之前,JDBC是否将日期从JVM时区转换为数据库会话时区?

Before writing a Java Date to an SQL TIMESTAMP column, does JDBC translate the date from the JVM time zone to the database session time zone?

在将Java Date写入SQL TIMESTAMP列之前,JDBC是否将日期从Java虚拟机时区转换为数据库会话的日期?

例如,假设Java虚拟机时区为UTC且数据库会话时区为UTC-5。 如果Java程序试图通过将2000-01-01 00:00:00传递给PreparedStatement#setTimestamp(int, Timestamp)来存储2000-01-01 00:00:00,那么根据JDBC标准,数据库是否会存储TIMESTAMP '2000-01-01 00:00:00'TIMESTAMP '1999-12-31 19:00:00'


不,JDBC只是客户端如何访问数据库的API。对于时间戳存储,这必须由编写符合JDBC API标准的数据库驱动程序的组织所依赖。

这是MySQL的PreparedStatement实现的实现。他们似乎把Java的JVM时区带到MySQL Timezone(检查setTimestampInternal()方法)。


现在我的要求是它应该以GMT / UTC存储值,而不管JVM的时区。有没有办法在运行时设置时区,然后在完成JDBC后取消设置时区?

编辑:
好的,我找到了解决这个问题的方法。做了以下

1
2
3
4
5
6
7
8
9
10
11
TimeZone default = TimeZone.getDefault();
try
{
  TimeZone.setDefault(TimeZone.getTimeZone("UTC"));

  //Do stuff with JDBC
}
finally
{
  TimeZone.setDefault(default);
}


您可以使用重载的setTimestamp setter接受Calendar实例来指定时区

示例(如果您使用的是Joda日期时间):

1
2
org.joda.time.DateTime sendDateUTC = new DateTime( DateTimeZone.UTC ).withMillis( millis );
statement.setTimestamp (1, sendDateUTC, sendDateUTC.toGregorianCalendar() );

根据javaDoc:
使用给定的Calendar对象将指定参数设置为给定的java.sql.Timestamp值。驱动程序使用Calendar对象构造SQL TIMESTAMP值,然后驱动程序将其发送到数据库。使用Calendar对象,驱动程序可以计算考虑自定义时区的时间戳。如果未指定Calendar对象,则驱动程序将使用默认时区,即运行应用程序的虚拟机的时区。

1
2
void setTimestamp(int parameterIndex, java.sql.Timestamp x, Calendar cal)
    throws SQLException;

规范很傻。 java.util.Date存储GMT参考帧中纪元的毫秒数。 Java.sql.Timestamp是同一参考帧中的Date加纳秒。所有未弃用的getter和setter都使用GMT参考框架。对于任何类型的健全性,存储时间戳的默认时区应为GMT。

在多层应用程序中,前端,驱动程序和数据库服务器都可以位于不同的时区。有些层可能同时位于不同的时区;例如,如果您在整个大陆进行互联网负载平衡,或者您有一个移动应用程序连接到中央服务器。云操作环境与您不知道JDBC驱动程序将在何处运行的情况大致相同,也不会保证永远不会发生变化。

我知道在这些环境中实现一致性的唯一方法是仅使用接受日历的参数setter和ResultSet getter,并确保访问数据的每个应用程序都使用某些日历,最好是GMT或UTC。