使用Java在PostgreSQL中存储时间的最推荐方法是什么?java.time

2022-08-31 12:36:15

我在PostgreSQL数据库中存储了两个日期。首先,是网页的访问数据,第二个日期是网页最后一次修改的日期(这是一个长)。

我有些怀疑存储这些价值观的最佳策略是什么。

我只需要日/月/年和小时:秒,这只适用于统计建议。

所以,一些疑问:

  • 最好存储一样长,并在恢复信息时进行转换,还是以上述数据格式存储?
  • 最好在软件上还是在数据库中插入时设置访问日期?
  • 在Java中,如何处理日期的最佳类?

答案 1

IMO,任何在PostgreSQL中存储日期和时间数据的策略都应该依赖于以下两点:

  • 您的解决方案永远不应依赖于服务器或客户端时区设置。
  • 目前,PostgreSQL(与大多数数据库一样)没有数据类型来存储带有时区的完整日期和时间。因此,您需要在即时数据类型或LocalDateTime数据类型之间做出决定。

我的食谱如下。


如果要记录特定事件发生时的物理时刻(真正的“时间戳”,通常是一些创建/修改/删除事件),请使用:

(不要让PostgreSQL特殊的数据类型/混淆你:它们实际上都没有存储时区)WITH TIMEZONEWITHOUT TIMEZONE

一些样板代码:以下假设是 一个 、 a 并且是对应于时区的静态对象。psPreparedStatementrsResultSettzUTCCalendarUTC

public static final Calendar tzUTC = Calendar.getInstance(TimeZone.getTimeZone("UTC"));  

写入数据库 :InstantTIMESTAMPTZ

Instant instant = ...;
Timestamp ts = instant != null ? Timestamp.from(instant) : null;
ps.setTimestamp(col, ts, tzUTC);   // column is TIMESTAMPTZ!

从数据库读取 :InstantTIMESTAMPTZ

Timestamp ts = rs.getTimestamp(col,tzUTC); // column is TIMESTAMPTZ
Instant inst = ts !=null ? ts.toInstant() : null;

如果您的PG类型是安全的(在这种情况下,在该代码中没有效果;但始终建议不依赖于默认值时区)。“安全”意味着结果将不依赖于服务器或数据库时区或时区信息:该操作是完全可逆的,无论时区设置发生什么,您都将始终获得与Java端相同的“时间瞬间”。TIMESTAMPTZcalendarUTC


如果处理的不是时间戳(物理时间轴上的某个时刻),而是“民事”本地日期时间(即字段集),则可以使用:{year-month-day hour:min:sec(:msecs)}

从数据库读取 :LocalDateTimeTIMESTAMP

Timestamp ts = rs.getTimestamp(col, tzUTC); //
LocalDateTime localDt = null;
if( ts != null ) 
    localDt =  LocalDateTime.ofInstant(Instant.ofEpochMilli(ts.getTime()), ZoneOffset.UTC);

写入数据库 :LocalDateTimeTIMESTAMP

  Timestamp ts = null;
  if( localDt != null)    
      ts = new Timestamp(localDt.toInstant(ZoneOffset.UTC).toEpochMilli()), tzUTC);
  ps.setTimestamp(colNum,ts, tzUTC); 

同样,这种策略是安全的,您可以安然入睡:如果您存储,您将始终检索这些精确字段(小时= 23,分钟= 59等),无论发生什么 - 即使明天您的Postgresql服务器(或客户端)的时区发生变化,或者您的JVM或操作系统时区,或者您的国家/地区修改其DST规则等。2011-10-30 23:59:30


添加:如果你想(这似乎是一个自然的要求)存储完整的日期时间规范(ZonedDatetime:时间戳和时区,其中隐式还包括完整的民用日期时间信息 - 加上时区)...然后我有个坏消息要告诉你:PostgreSQL没有这个数据类型(据我所知,其他数据库也没有)。您必须设计自己的存储,也许在一对字段中:可能是上述两种类型(高度冗余,尽管对于检索和计算有效),或者其中一种加上时间偏移(您丢失了时区信息,一些计算变得困难,有些不可能),或者其中一种加上时区(作为字符串;某些计算可能非常昂贵)。


答案 2

java.time

它并不漂亮,但这就是我使用Java 8及更高版本中的新java.time框架的ZonedDateTime实例(教程)的工作原理:

ZonedDateTime receivedTimestamp = some_ts_value;
Timestamp ts = new Timestamp(receivedTimestamp.toInstant().toEpochMilli());
ps.setTimestamp(
   1, 
   ts, 
   Calendar.getInstance(TimeZone.getTimeZone(receivedTimestamp.getZone()))
);