关于postgresql + java中时区的时间戳的问题?

时间:2010-06-09 20:43:15

标签: java postgresql timestamp

我在数据库中有一个列(postgresql) 我想在此列中插入GMT中的当前时间 获取当前时间并将其插入数据库 它被插入服务器时区GMT-5,虽然那个时间是GMT + 0 任何想法如何在GMT时区的数据库中插入这个时间?

3 个答案:

答案 0 :(得分:2)

在 Postgres 中始终使用 UTC

了解 Postgres 总是 将值存储在 TIMESTAMP WITH TIME ZONEUTC 类型的列中,即 offset from UTC 为零时-分-秒.

所以这个名字有点用词不当,因为实际上没有存储时区。相反,Postgres 使用任何随输入到达的时区或偏移指示符来调整到 UTC。调整后,UTC 值被存储到表中,而区域/偏移指示符被丢弃。如果您关心原始时区,则必须自己将其写入第二列。

令人困惑的是工具和中间件,例如 psql 控制台客户端应用程序。这些通常是自以为是,选择将一些默认时区调整注入检索到的值。因此,虽然 Postgres 数据库总是从任何 TIMESTAMP WITH TIME ZONE 列中检索 UTC 日期时间,但您可能会在接收端看到其他情况。这样的功能虽然是善意的,但在我看来却是不幸的反功能。

幸运的是,我还没有看到任何 JDBC driver 执行这样的调整。从 TIMESTAMP WITH TIME ZONE 列(如下所述)检索 OffsetDateTime 时,您应该始终获得 UTC 值。但请务必测试您的特定 JDBC 驱动程序。

JDBC 和 java.time

Java 中处理日期时间的现代解决方案是 java.time 类。

要捕捉当前时刻,请使用 Instant。此类表示以 UTC 格式显示的时刻。

Instant instant = Instant.now() ;  // Capture the current moment as seen in UTC.

然而,尽管 Instant 是最基本也可能是最常用的 java.time 类,JDBC 4.2 团队做出了莫名其妙的决定, 需要它的支持。 (顺便说一下,也不需要 ZonedDateTime 支持。)

相反,JDBC 4.2 规范要求支持 OffsetDateTime。幸运的是,InstantOffsetDateTime 之间的转换是微不足道的。

OffsetDateTime odt = instant.atOffset( ZoneOffset.UTC ) ;

或者你可以跳过 Instant 类就是这种情况。

OffsetDateTime odt = OffsetDateTime.now( ZoneOffset.UTC ) ;

写入数据库。

myPreparedStatement.setObject( … , odt ) ;

从数据库中检索。

OffsetDateTime odt = myResultSet.getObject( … , OffsetDateTime.class ) ;

OffsetDateTime 对象将携带从 UTC 零时分秒的指定偏移量 — 通常简称为“UTC”作为缩写。

如果需要,提取一个 Instant

Instant instant = odt.toInstant() ;

或者调整到时区。

ZoneId z = ZoneId.of( "Asia/Tokyo" ) ;
ZonedDateTime zdt = odt.atZoneSameInstant( z ) ;

关于可能丢失数据的警告:java.time 中的分辨率是 nanoseconds。捕捉当前时刻可能仅限于微秒,因为当今常用的硬件时钟再好不过了。尽管如此,诸如 OffsetDateTime 之类的 java.time 对象可能会携带纳秒。同时,Postgres 日期时间值的分辨率为 microseconds。因此,如果使用带有任何 nanos 的 java.time 对象,使用 Postgres 写入该值将截断 nanos,从而导致稍后检索不同的值。

答案 1 :(得分:1)

我认为manual的第8.5.1.2段可能具有启发性。它声明默认情况下,假定时间没有时区,如果给出一个,则会默默忽略。

为了清楚说明,我认为最好明确表示时间:

pti=> select timestamp with time zone '20100610T180000-5';
      timestamptz        
------------------------
2010-06-11 01:00:00+02
(1 row)

pti=> select timestamp with time zone '20100610T180000PST';
      timestamptz       
------------------------
 2010-06-11 04:00:00+02
(1 row)

很明显,时区的时间已从本地时间正确转换为服务器时间。

答案 2 :(得分:0)

SELECT current_timestamp AT TIME ZONE 'gmt-5';