问题是我从DAO的加载函数中得到了错误的日期。
观察到的日期存储在PostgreSQL数据库中(来自User表的出生日期列),等于1982-03-28。它有时被认为是1982-03-27。我从更有经验的用户那里得到了一些提示,他们说这在某种程度上与时区问题有关。
错误的值来自java.util.Date类的实例。我通过Calendar类的对象获取它们。当我给这个小家伙打电话时:
calendar.getTimeZone().getDisplayName()
它返回
Central European Time
BirthDate列的类型为:
不带时区的出生日期时间戳
postgresql.conf文件中设置的时区变量的值等于Poland。
加载方法的内部实现如下所示:
public T load(ID aPrimaryKey, boolean aLock)
{
if (aPrimaryKey == null) {
throw new IllegalArgumentException("Parameter aPrimaryKey can't be null!");
}
preLoad(aPrimaryKey);
T entity; if (aLock) {
entity = _session.load(getEntityClass(), aPrimaryKey, LockMode.UPGRADE);
} else
entity = _session.load(getEntityClass(), aPrimaryKey);
postLoad(entity);
return entity;
}
我的数据库版本是
PostgreSQL 9.4.4 on x86_64-unknown-linux-gnu,由gcc编译(Debian4.9.2-10)4.9.2,64位
我使用
hibernate-core-3.6.0.Final.jar
我的实体类看起来是这样的(还有几个字段):
import javax.persistence.Column;
@javax.persistence.Entity
@javax.persistence.Table(name="User")
@javax.persistence.SequenceGenerator(name="userId", sequenceName="user_id_seq", allocationSize=1)
public class User {
@javax.persistence.Id
@Column(name="Id", nullable=false)
@javax.persistence.GeneratedValue(strategy=javax.persistence.GenerationType.AUTO, generator="userId")
private long id;
@Column(name="Name")
private String firstName;
public User(String firstName, java.util.Date birthDate, java.util.Date registrationDate)
{
this.firstName = firstName;
this.birthDate = birthDate;
this.registrationDate = registrationDate;
}
@Column(name="BirthDate")
@javax.persistence.Temporal(javax.persistence.TemporalType.DATE)
private java.util.Date birthDate;
@Column(name="RegistrationDate", nullable=false)
@javax.persistence.Temporal(javax.persistence.TemporalType.TIMESTAMP)
private java.util.Date registrationDate;
public String toString()
{
return firstName + " should clean himself...";
}
public User() {}
}
我将birthdate
字段的类型更改为TIMESTAMPTZ
,然后一切看起来都很好。但是如何在不改变列的数据类型的情况下解决这个问题呢?你有什么建议吗?
我认为问题可能是您的系统设置为CET
,但波兰目前处于CEST
(Pg采用夏令时)。
你可以做几件事:
-
在每次会话开始时使用
SET TIME ZONE timezone;
,以确保它与您的客户端相匹配。 -
在选择与客户端匹配的值或函数
timezone(zone, timestamp)
时使用AT TIME ZONE
。 -
选择时将值强制转换为
timestamptz
,因此TZ信息将被保留(实际上对此不确定)。
但实际上,总是使用timestamptz
来存储时间要好得多。
要从注释中回答您的问题:您可以将timestamp/tz
值强制转换为date
类型(birthdate::date
)。date
没有时间信息,也不依赖于时区,因此您将始终按原样获得值。
这实际上可能更有意义-您可能确实希望在其原始时区(即用户输入的时区)中查看出生日期,而不是转换为当前TZ。