问与答 使用Java在PostgreSQL中存储时间的最推荐方法是什么?

jim · 2020-02-22 13:00:04 · 热度: 208

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

我有些疑问,存储这些值的最佳策略是什么。

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

因此,有些疑问:

  • 最好存储尽可能长的时间,并根据信息恢复进行转换或以上述数据格式存储?
  • 最好在软件上或数据库的插入位置设置访问日期?
  • Java 中,最好的类如何处理日期?

猜你喜欢:
共收到 3 条回复
ben #1 · 2020-02-22 13:00:05

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

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

我的食谱如下。


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

  • 执行:1(java 3或jodatima)。
  • JDBC:LocalDateTime
  • PostgreSQL:LocalDateTimeLocalDateTime

(不要让PostgreSQL特殊的数据类型LocalDateTime/TIMESTAMP混淆了您:它们都没有实际存储时区)

一些样板代码:以下假设LocalDateTimeTIMESTAMPrsResultSettzUTC是对应于Calendar时区的静态Calendar对象。

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

LocalDateTime写到数据库TIMESTAMP

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

从数据库TIMESTAMP中读取LocalDateTime

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

如果您的PG类型为LocalDateTime(在这种情况下,TIMESTAMP在该代码中无效;但是始终建议不要依赖默认时区),这可以安全地工作。“安全”是指结果将不依赖于服务器或数据库时区或时区信息:该操作是完全可逆的,并且无论时区设置发生什么,您将始终获得与原始时间相同的“即时时间” Java方面。


如果您正在处理的是“民用”本地日期时间(即字段集LocalDateTime),而不是时间戳(物理时间轴上的瞬时),则应使用:

  • 执行:1(java 3或jodatima)。
  • JDBC:LocalDateTime
  • PostgreSQL:LocalDateTimeLocalDateTime

从数据库TIMESTAMP中读取LocalDateTime

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); 

同样,此策略很安全,您可以安然入睡:如果存储了ZonedDatetime,则无论如何都将始终检索到这些精确字段(小时= 23,分钟= 59等)-即使明天您的时区 Postgresql服务器(或客户端)更改,或者您的JVM或您的操作系统时区,或者您所在的国家/地区修改了其DST规则等。


补充:如果您想(似乎很自然地)要存储完整的日期时间规范(一个ZonedDatetime:时间戳和时区,其中还隐含了完整的民用日期时间信息-加上时区)...那么我很不好 给您的消息:PostgreSQL没有为此提供数据类型(据我所知,没有其他数据库)。 您必须在两个字段中设计自己的存储:可能是以上两种类型(高度冗余,尽管对于检索和计算很有效),或者其中之一加上时间偏移(丢失时区信息,一些计算将变为 困难,有些则不可能),或者其中之一加上时区(作为字符串;某些计算可能会非常昂贵)。

jon #2 · 2020-02-22 13:00:06

java.time

这不是很漂亮,但这对我来说很有效,它使用了Java559及更高版本(教程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()))
); 
aiden #3 · 2020-02-22 13:00:07

在Java应用程序中使用java.util.Date,在数据库中使用timestamp with time zone

需要 登录 后方可回复, 如果你还没有账号请点击这里 注册