在 IMO 中,任何在 PostgreSQL 中存储日期和时间数据的策略都应该依赖于这两点:
- 你的解决方案应该never取决于服务器或客户端时区设置。
- 目前,PostgreSQL(与大多数数据库一样)没有数据类型来存储full带时区的日期和时间。因此,您需要在Instant or a LocalDateTime数据类型。
我的食谱如下。
如果你想记录身体instant当特定事件发生时,(一个真实的“时间戳" ,通常是一些创建/修改/删除事件),然后使用:
- Java: Instant(Java 8 或 Jodatime)。
- JDBC:
java.sql.Timestamp
- PostgreSQL:TIMESTAMP WITH TIMEZONE (
TIMESTAMPTZ
)
(不要让 PostgreSQL 特有的数据类型WITH TIMEZONE
/WITHOUT TIMEZONE
让您感到困惑:它们实际上都没有存储时区)
一些样板代码:以下假设ps
is a PreparedStatement
, rs
a ResultSet
and tzUTC
是一个静态的Calendar
对象对应于UTC
时区。
public static final Calendar tzUTC = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
Write Instant
到数据库TIMESTAMPTZ
:
Instant instant = ...;
Timestamp ts = instant != null ? Timestamp.from(instant) : null;
ps.setTimestamp(col, ts, tzUTC); // column is TIMESTAMPTZ!
Read Instant
来自数据库TIMESTAMPTZ
:
Timestamp ts = rs.getTimestamp(col,tzUTC); // column is TIMESTAMPTZ
Instant inst = ts !=null ? ts.toInstant() : null;
如果您的 PG 类型是,这可以安全地工作TIMESTAMPTZ
(在这种情况下,calendarUTC
对该代码没有影响;但始终建议不要依赖默认时区)。
“安全”意味着结果将不依赖于服务器或数据库时区或时区信息:该操作是完全可逆的,无论时区设置发生什么,您总是会得到与 Java 端最初相同的“即时时间”。
如果您正在处理的不是时间戳(物理时间线上的瞬间)“民用”本地日期时间(即字段的集合{year-month-day hour:min:sec(:msecs)}
),你会使用:
- Java: LocalDateTime(Java 8 或 Jodatime)。
- JDBC:
java.sql.Timestamp
- PostgreSQL:TIMESTAMP WITHOUT TIMEZONE (
TIMESTAMP
)
Read LocalDateTime
来自数据库TIMESTAMP
:
Timestamp ts = rs.getTimestamp(col, tzUTC); //
LocalDateTime localDt = null;
if( ts != null )
localDt = LocalDateTime.ofInstant(Instant.ofEpochMilli(ts.getTime()), ZoneOffset.UTC);
Write LocalDateTime
到数据库TIMESTAMP
:
Timestamp ts = null;
if( localDt != null)
ts = new Timestamp(localDt.toInstant(ZoneOffset.UTC).toEpochMilli()), tzUTC);
ps.setTimestamp(colNum,ts, tzUTC);
再次强调,这个策略是安全的,你可以安然入睡:如果你储存了2011-10-30 23:59:30
,您将始终检索这些精确字段(小时 = 23,分钟 = 59 ...等),无论如何 - 即使明天您的 Postgresql 服务器(或客户端)的时区发生变化,或者您的 JVM 或操作系统时区,或者如果您所在的国家/地区修改了 DST 规则等。
添加:如果您想要(这似乎是一个自然的要求)存储完整的日期时间规范(aZonedDatetime:时间戳和时区,其中隐含地还包括完整的民用日期时间信息 - 加上时区)...那么我有坏消息给你:PostgreSQL没有这样的数据类型(据我所知,其他数据库也没有) 。您必须设计自己的存储,也许在一对字段中:可以是上述两种类型(高度冗余,但对于检索和计算来说很有效),或者其中一个加上时间偏移量(您丢失了时区信息,一些计算变成困难,有些不可能),或者其中之一加上时区(作为字符串;某些计算可能非常昂贵)。