2

timestamp without time zone我在 PostgreSQL 9.1 数据库的列中有这个日期:

2012-11-17 13:00:00

它是 UTC 格式的,我已经通过选择它作为 UNIX 时间戳 ( EXTRACT epoch) 进行了验证。

int epoch = 1353157200; // from database
Date date = new Date((long)epoch * 1000);
System.out.println(date.toGMTString()); // output 17 Nov 2012 13:00:00 GMT

然而,当我使用 JPA/Hibernate 读取这个日期时,事情就出错了。这是我的映射:

@Column(nullable=true,updatable=true,name="startDate")
@Temporal(TemporalType.TIMESTAMP)
private Date start;

然而Date,我得到的是:

17 Nov 2012 12:00:00 GMT

为什么会发生这种情况,更重要的是,我该如何阻止它?

请注意,我只想普遍地存储时间点(就像java.util.Date那样),我不太关心时区,除了我显然不希望它们破坏我的数据。

正如您可能推断的那样,连接到数据库的客户端应用程序位于 UTC+1(荷兰)。

此外,列类型的选择timestamp without time zone是由 Hibernate 在自动生成模式时做出的。应该是这样timestamp with time zone吗?

4

3 回答 3

4

如果数据库不提供时区信息,则 JDBC 驱动程序应将其视为位于 JVM 的本地时区(请参阅PreparedStatement.setDate(int, Date)):

使用运行应用程序的虚拟机的默认时区将指定参数设置为给定的 java.sql.Date 值。

Javadoc 和 JDBC 规范没有明确说明ResultSetetc,但为了保持一致,大多数驱动程序也会将该规则应用于从数据库中检索的日期。如果您想显式控制所使用的时区,您将需要使用各种set/getDate/Time/Timestamp方法来接受Calendar正确时区中的对象。

一些驱动程序还提供了一个连接属性,允许您在与数据库之间进行转换时指定要使用的时区。

于 2012-11-17T14:44:15.407 回答
1

有几种解决方案可以解决这些问题:

1) 最简单的方法是在 JDBC 连接字符串中设置时区——只要数据库支持。

对于 MySQL,您可以使用useGmtMillisForDatetimes=true强制在数据库中使用 UTC。据我所知,Postgres 不支持这样的选项,但我可能错了,因为我不使用 Postgres。

2)在您的Java客户端程序中设置默认时区

TimeZone.setDefault(TimeZone.getTimeZone("UTC"));

缺点:您还可以更改您不想要的时区,例如,如果您的程序有 UI 部分。

3) 仅对 Hibernate 使用具有特殊 getter 的映射:在您的映射文件中

<property name="myTimeWithTzConversion" type="timestamp" access="property">
  <column name="..." />
</property>

并且在您的程序中,您get/setMyTimeWithTzConversion()只能通过休眠访问成员变量Timestamp myTime,并且在这个 getter/setter 中,您可以进行时区转换。

我们最终决定使用 3)(这需要更多的编程工作),因为我们不必更改现有的数据库,我们的数据库是 UTC+1(禁止 JDBC 连接字符串解决方案)并且没有干扰现有的 UI。

于 2012-11-19T07:52:47.870 回答
1

我发现更改列类型以timestamp with time zone解决问题。然后,我还必须将所有其他时间戳列转换为该列。

由此我得出结论,Hibernate 不像timestamp without time zoneUTC 那样读取列,而是像本地时区那样读取列。如果有办法将它们解释为 UTC,请告诉我。

于 2012-11-17T13:32:24.387 回答