我在PostgreSQL数据库中存储了两个日期。首先,是网页访问的数据,第二个日期是网页的最后修改日期(这是一个很长的时间)。
我怀疑存储这些值的最佳策略是什么。
我只需要日/月/年和小时:秒,这只适用于统计建议。
所以,有些疑惑:
答案 0 :(得分:70)
任何在Postgresql中存储日期和时间数据的策略都应该依赖于这两点:
Instant
或LocalDateTime
数据类型。我对每个场景的配方:
如果您想要在特定事件(通常是某些创建/修改/删除)发生时记录物理即时(真正的“时间戳”),那么使用方法:
Instant
(Java 8,或Jodatime)。java.sql.Timestamp
TIMESTAMP WITH TIMEZONE
(TIMESTAMPTZ
)(不要让Postgresql特有的数据类型WITH TIMEZONE
/ WITHOUT TIMEZONE
混淆你:它们实际上都没有存储时区)
一些样板代码:以下假设ps
是PreparedStatement
,rs
和ResultSet
而tzUTC
是静态Calendar
对象对应UTC
时区。
public static final Calendar tzUTC = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
将Instant
写入数据库TIMESTAMPTZ
:
Instant instant = ...;
Timestamp ts = instant != null ? new Timestamp(instant.toEpochMilli()) : null;
ps.setTimestamp(col, ts, tzUTC); // column is TIMESTAMPTZ!
从数据库Instant
中读取TIMESTAMPTZ
:
Timestamp ts = rs.getTimestamp(col,tzUTC); // column is TIMESTAMPTZ
Instant inst = ts !=null ? Instant.ofEpochMilli(ts.getTime()) : null;
如果您的PG类型为TIMESTAMPTZ
,则可以安全地工作(在这种情况下,calendarUTC
对该代码没有影响;但始终建议不依赖于默认时区)。
“安全”意味着结果将不依赖于服务器或数据库时区,或时区信息:操作是完全可逆的,无论时区设置发生什么,您都将获得相同的“瞬间”时间“你原来在Java方面。
如果您有一个“民用”本地日期时间(即字段{year-month-day hour:min:sec}
),而不是时间戳(物理时间轴上的瞬间),你应该使用
LocalDateTime
(Java 8,或Jodatime)。java.sql.Timestamp
TIMESTAMP WITHOUT TIMEZONE
(TIMESTAMP
)从数据库LocalDateTime
中读取TIMESTAMP
:
Timestamp ts = rs.getTimestamp(col, tzUTC); //
LocalDateTime localDt = null;
if( ts != null )
localDt = LocalDateTime.ofInstant(Instant.ofEpochMilli(ts.getTime()), ZoneOffset.UTC);
将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规则等等。
补充:如果你想(似乎是一个自然要求)存储完整的日期时间规范(ZonedDatetime
:时间戳和时区,其中隐含地还包括完整的民用日期时间信息 - 加上时区) ,那么你运气不好:Postgresql没有这方面的数据类型(据我所知,其他数据库都没有)。您必须设计自己的存储,可能是在一对字段中:可能是上述两种类型(高度冗余,虽然检索和计算有效),或者其中一种加上时间偏移(您丢失了时区信息,一些计算成为困难,有些不可能),或其中一个加上时区(作为字符串;一些计算可能非常昂贵)。
答案 1 :(得分:3)
它并不漂亮,但这对于我使用Java {8}及更高版本(ZonedDateTime
)中的新java.time框架的Tutorial实例起了作用:
ZonedDateTime receivedTimestamp = some_ts_value;
Timestamp ts = new Timestamp(receivedTimestamp.toInstant().toEpochMilli());
ps.setTimestamp(
1,
ts,
Calendar.getInstance(TimeZone.getTimeZone(receivedTimestamp.getZone()))
);
答案 2 :(得分:2)
在Java应用程序中使用java.util.Date
,在数据库中使用timestamp with time zone
。