我在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
那样),我不关心时区,除了我显然不希望它们破坏我的数据。< / p>
正如您所推断的那样,连接到数据库的客户端应用程序是UTC + 1(荷兰)。
此外,列类型timestamp without time zone
的选择是由Hibernate在自动生成模式时做出的。那可能是timestamp with time zone
吗?
答案 0 :(得分:4)
如果数据库没有提供时区信息,那么JDBC驱动程序应将其视为JVM的本地时区(请参阅PreparedStatement.setDate(int, Date)):
使用运行应用程序的虚拟机的默认时区将指定参数设置为给定的java.sql.Date值。
Javadoc和JDBC规范没有明确说明有关ResultSet
等的任何内容,但为了保持一致,大多数驱动程序也会将该规则应用于从数据库中检索的日期。如果要显式控制所使用的时区,则需要使用在正确的时区内同时接受set/getDate/Time/Timestamp
对象的各种Calendar
方法。
某些驱动程序还提供了一个连接属性,允许您指定转换为数据库或从数据库转换时使用的时区。
答案 1 :(得分:1)
我发现将列类型更改为timestamp with time zone
可以解决问题。我必须将所有其他时间戳列转换为该列。
由此我得出结论,Hibernate不会像UTC那样读取timestamp without time zone
列,而是像本地时区一样。如果有办法让它解释为UTC,请告诉我。
答案 2 :(得分: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>
并且在您的程序中,只有hibernate访问成员变量get/setMyTimeWithTzConversion()
才能访问Timestamp myTime
,并且在此getter / setter中执行时区转换。
我们最终决定3)(这是一个更多的编程工作),因为我们没有必要更改现有数据库,我们的数据库是UTC + 1(禁止JDBC连接字符串解决方案)和那没有干扰现有的UI。