Java 为什么我的时间戳在时区中移动?

Java 为什么我的时间戳在时区中移动?,java,hibernate,postgresql,jpa,timestamp,Java,Hibernate,Postgresql,Jpa,Timestamp,我在PostgreSQL 9.1数据库中的时间戳(无时区)列中有此日期: 2012-11-17 13:00:00 它应该是UTC格式的,我已经选择它作为UNIX时间戳(EXTRACT epoch)进行了验证 然而,当我使用JPA/Hibernate阅读这个日期时,事情就出了问题。这是我的映射: @Column(nullable=true,updatable=true,name="startDate") @Temporal(TemporalType.TIMESTAMP) private Date

我在PostgreSQL 9.1数据库中的
时间戳(无时区)列中有此日期:

2012-11-17 13:00:00

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

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

@Column(nullable=true,updatable=true,name="startDate")
@Temporal(TemporalType.TIMESTAMP)
private Date start;
然而,我得到的
日期是:

2012年11月17日格林尼治标准时间12:00:00

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

请注意,我只想普遍地存储时间点(正如
java.util.Date
所做的那样),我对时区毫不关心,只是我显然不希望它们破坏我的数据

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


此外,Hibernate在自动生成模式时选择了不带时区的列类型
timestamp
。是否应该改为带时区的时间戳?

我发现将列类型更改为带时区的时间戳可以解决问题。我还必须将所有其他时间戳列转换为该列


由此我得出结论,Hibernate不像UTC那样读取没有时区的
时间戳
列,而是像本地时区一样读取。如果有办法将其解释为UTC,请让我知道。

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

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

Javadoc和JDBC规范没有明确说明任何关于
ResultSet
等的内容,但为了保持一致,大多数驱动程序也会将该规则应用于从数据库检索的日期。如果希望对所使用的时区进行明确控制,则需要使用各种
set/getDate/Time/Timestamp
方法,这些方法也接受正确时区中的
Calendar
对象


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

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

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()
,只允许hibernate访问成员变量
Timestamp myTime
,在这个getter/setter中,您可以进行时区转换


我们最终决定使用3(这是一项稍微多一些的编程工作),因为在那里我们不必更改现有的数据库,我们的数据库是UTC+1(这禁止使用JDBC连接字符串解决方案),并且不会干扰现有的UI

我认为将
无时区时间戳
读取为本地时区是Java/JDBC的事情。根据当前时区解释
无时区时间戳
是普遍正确的方法!没什么特别的。可能有助于理解。@ErwinBrandstetter不,没有时区的时间戳是UTC,因为数据库没有“当前”时区,因为它是数据存储,不是暂时的。
<property name="myTimeWithTzConversion" type="timestamp" access="property">
  <column name="..." />
</property>