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

新手上路,请多包涵

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

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

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

所以,一些疑问:

  • 最好存储多久并在恢复信息时转换还是以上述数据格式存储?
  • 访问日期最好是在软件上设置还是在数据库中插入?
  • 在 Java 中,处理日期的最佳类是什么?

原文由 Renato Dinhani 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 1.3k
2 个回答

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

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

我的食谱如下。


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

(不要让 PostgreSQL 特殊的数据类型 WITH TIMEZONE / WITHOUT TIMEZONE 让你感到困惑:它们实际上都没有存储时区)

Some boilerplate code: the following assumes that ps is a PreparedStatement , rs a ResultSet and tzUTC is a静态 Calendar 对应于 UTC 时区的对象。

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

Instant 写入数据库 TIMESTAMPTZ

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

从数据库中读取 Instant TIMESTAMPTZ

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

如果您的 PG 类型是 TIMESTAMPTZ (在这种情况下, calendarUTC 对该代码没有影响;但始终建议不要依赖默认时区,则此操作安全)。 “安全”意味着结果将 不依赖于服务器或数据库时区 或时区信息:该操作是完全可逆的,无论时区设置发生什么变化,您将始终获得与最初在爪哇方面。


如果您处理的不是时间戳(物理时间线上的一个瞬间),而是 “民间”本地日期时间(即字段集 {year-month-day hour:min:sec(:msecs)} ),您将使用:

从数据库中读取 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 规则等。


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

原文由 leonbloy 发布,翻译遵循 CC BY-SA 4.0 许可协议

java.time

这不是很漂亮,但这是对我有用的 ZonedDateTime 在 Java 8 及更高版本中使用新的 java.time 框架的实例( 教程):

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

原文由 Pablo Santa Cruz 发布,翻译遵循 CC BY-SA 3.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题