使用Java在PostgreSQL中存储时间的最推荐方法是什么?
我在PostgreSQL数据库中存储了两个日期。首先是网页访问的数据,第二个日期是网页最后一次修改的日期(这是一个很长的日期) 我怀疑什么是存储这些价值的最佳策略 我只需要天/月/年和小时:秒,这将仅用于统计建议 因此,有一些疑问:使用Java在PostgreSQL中存储时间的最推荐方法是什么?,java,postgresql,date,jdbc,Java,Postgresql,Date,Jdbc,我在PostgreSQL数据库中存储了两个日期。首先是网页访问的数据,第二个日期是网页最后一次修改的日期(这是一个很长的日期) 我怀疑什么是存储这些价值的最佳策略 我只需要天/月/年和小时:秒,这将仅用于统计建议 因此,有一些疑问: 最佳存储是否与恢复信息时一样长并转换,还是以上述数据格式存储 最好是在软件上设置访问日期,还是在数据库中插入 在Java中,处理日期的最佳类是什么 我认为,在PostgreSQL中存储日期和时间数据的任何策略都应该依赖以下两点: 您的解决方案不应依赖于服务器或客
- 最佳存储是否与恢复信息时一样长并转换,还是以上述数据格式存储
- 最好是在软件上设置访问日期,还是在数据库中插入
- 在Java中,处理日期的最佳类是什么李>
- 您的解决方案不应依赖于服务器或客户端时区设置
- 目前,PostgreSQL(与大多数数据库一样)没有数据类型来存储完整的日期和时区时间。因此,您需要在数据类型和数据类型之间做出选择
如果要在发生特定事件时记录物理的瞬间(真实的“时间戳”,通常是一些创建/修改/删除事件),则使用:
- Java:(Java8或Jodatime)
- JDBC:
java.sql.Timestamp
- PostgreSQL:(
)TIMESTAMPTZ
,,,而不带时区的数据类型
迷惑你:它们实际上都没有存储时区)
一些样板代码:以下假设ps
是一个PreparedStatement
,rs
aResultSet
,tzUTC
是一个与UTC
时区相对应的静态Calendar
对象
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!
Timestamp ts = rs.getTimestamp(col,tzUTC); // column is TIMESTAMPTZ
Instant inst = ts !=null ? Instant.ofEpochMilli(ts.getTime()) : null;
从数据库TIMESTAMPTZ
读取Instant
:
Instant instant = ...;
Timestamp ts = instant != null ? new Timestamp(instant.toEpochMilli()) : null;
ps.setTimestamp(col, ts, tzUTC); // column is TIMESTAMPTZ!
Timestamp ts = rs.getTimestamp(col,tzUTC); // column is TIMESTAMPTZ
Instant inst = ts !=null ? Instant.ofEpochMilli(ts.getTime()) : null;
如果您的PG类型为timestamtz
(在这种情况下,calendarUTC
在该代码中无效;但始终建议不要依赖默认时区)。
“安全”意味着结果将不依赖于服务器或数据库时区或时区信息:该操作是完全可逆的,无论时区设置发生什么变化,您将始终获得与Java端相同的“时间瞬间”
如果您处理的不是时间戳(物理时间轴上的一个瞬间),而是“civil”本地日期时间(即,字段集{year-month-day-hour:min:sec(:msecs)}
),则您将使用:
- Java:(Java8或Jodatime)
- JDBC:
java.sql.Timestamp
- PostgreSQL:(
时间戳
)
从数据库中读取LocalDateTime
:
Timestamp ts = rs.getTimestamp(col, tzUTC); //
LocalDateTime localDt = null;
if( ts != null )
localDt = LocalDateTime.ofInstant(Instant.ofEpochMilli(ts.getTime()), ZoneOffset.UTC);
Timestamp ts = null;
if( localDt != null)
ts = new Timestamp(localDt.toInstant(ZoneOffset.UTC).toEpochMilli()), tzUTC);
ps.setTimestamp(colNum,ts, tzUTC);
将LocalDateTime
写入数据库TIMESTAMP
:
Timestamp ts = rs.getTimestamp(col, tzUTC); //
LocalDateTime localDt = null;
if( ts != null )
localDt = LocalDateTime.ofInstant(Instant.ofEpochMilli(ts.getTime()), ZoneOffset.UTC);
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:时间戳和时区,其中隐式地还包括完整的civil datetime信息-加上时区)。。。然后我有一个坏消息告诉你:PostgreSQL没有用于此的数据类型(据我所知,其他数据库也没有)。您必须设计自己的存储,可能是在一对字段中:可以是上述两种类型(高度冗余,尽管检索和计算效率很高),或者其中一种加上时间偏移量(您丢失时区信息,一些计算变得困难,一些不可能),或者其中一种加上时区(作为字符串;某些计算可能非常昂贵)。java.time
这并不漂亮,但这正是我在使用Java 8和更高版本()中的新框架的实例中所做的工作:
“date”这个词的意思太多了,但它最常见的意思是年-月-日,没有时间。也许你最好在文章标题和正文中说“datetime”或“timestamp”。(a)这个答案并不完全符合问题的意图。(b)这段代码有一种更直接的方法。旧的类,如java.sql.Timestamp
在java 8和更高版本中进行了改进,使用方便的方法转换为新的java.time类型。因此:java.sql.Timestamp ts=java.sql.Timestamp.fromInstant(myZonedDateTime.toInstant())
答案很好,但在Java 8及更高版本中已经过时,我们现在有了内置框架(灵感来自Joda Time)@BasilBourque,我从未使用过JodaTime,我刚刚开始使用Java 8。对我来说,这个答案与Java 8 API几乎是1比1。我不能确定yoda,但在使用Java 8 Instant时,你不应该用TZ保存时间戳。Instant是UTC。时间戳也是UTC。它不需要TZ,保存它可能会导致错误。我为此付出了很多代价ral hours,使用Jooq,因为它正确地写入了timestamtz,但在读取值时没有按照预期使用时区。关于时区文字的存储,我的问题由Craig Ringer回答,可能也有帮助:。存储客户端最初在瞬间选择的时区是一个好主意,尤其是f或日历应用程序。@mlorber我意识到您的评论很旧,但不正确,您肯定应该在几乎所有情况下使用带有tz的时间戳。我建议阅读。注意:您确实需要将JVM设置为UTC,以便postgres正确解释传入的字符串。