从Postgres获取的日期不正确

时间:2018-07-02 09:25:47

标签: java postgresql postgresql-9.4 jdbc-postgres

我有一个类型为deadline_date的{​​{1}}字段,值为timestamp with time zone

2018-05-03 19:00:00-05.

但是从我的Java代码查询时,它返回的值为select deadline_date from invoice_details where file_name='BHDHSB.pdf'; deadline_date ------------------------ 2018-05-03 19:00:00-05 (1 row)

我认为它正在根据我的本地时区转换日期。

有什么方法可以获取表中存储的值?

我对Postgres使用以下Maven依赖项

2018-05-04 05:30:00+05:30

Test.java

<dependency>
     <groupId>org.postgresql</groupId>
     <artifactId>postgresql</artifactId>
     <version>9.4.1212</version>
</dependency>

3 个答案:

答案 0 :(得分:2)

请勿使用getString()来获取时间戳(实际上:仅用于字符串。不适用于数字,日期和时间戳)。

如果要处理时区识别的时间戳,则应在Java中使用OffsetDateTime

Postgres JDBC驱动程序直接通过getObject()方法支持此操作:

OffsetDateTime deadline = resultSet.getObject("deadline_date", OffsetDateTime.class);

答案 1 :(得分:0)

tl; dr

Postgres 总是将时刻存储在UTC TIMESTAMP WITH TIME ZONE类型的列中。

  • 进入数据库,调整为UTC。
  • 外出,UTC。

当心数据库访问工具:它可能会应用区域/偏移量调整来更改检索到的UTC值。这实际上是将其存储在该区域/偏移量中的瞬间产生了一种错觉,而实际上是将其存储在UTC中。

使用对象而非字符串与数据库交换日期时间值。

仅使用 java.time 类,而不使用旧版Date / Calendar。对于UTC时刻,请使用java.time.Instant类。

Instant instant = Instant.now() ;
myPreparedStatement.setObject( … , instant ) ;

检索。

Instant instant = myResultSet.getObject( … , Instant.class ) ;

智能对象,而不是哑字符串

在Java与数据库之间交换日期时间值时,建议您使用对象而不是字符串是正确的。重点是JDBC和您的JDBC driver,来回marshal data,同时调解差异和细节。

但是,其他答案错误地建议在Java中使用错误的类。

UTC

Postgres仅在UTC中保存时刻。输入类型TIMESTAMP WITH TIME ZONE的数据库列的伴随输入的任何时区或UTC偏移信息都用于将值调整为UTC。然后,UTC值将保存到数据库,并且忘记了原始的区域/偏移量。

同样,当从类型为TIMESTAMP WITH TIME ZONE的数据库列中检索值时,您总是以UTC形式获得值。

但是,可以选择在您和数据库之间使用的中间件或工具来更改传输中的UTC值,以应用时区或偏移量,从而调整该值。尽管我理解其背后的良好意图,但我认为这是一个糟糕的设计选择。但是这种行为会产生一种错觉,即存储的数据中存在区域/偏移量,而没有。

请注意,我在这里描述Postgres的行为。 SQL标准定义了我在这里讨论的数据类型,但没有定义它们的行为。不幸的是,SQL标准几乎没有涉及日期时间问题。因此,各种数据库实现的行为各不相同。 Postgres的适应UTC的行为对我来说很有意义。但是请注意,如果您关心日期时间值的原始区域/偏移量,则必须将其自己存储在单独的列中。

在Java方面,只需使用Instant从类型TIMESTAMP WITH TIME ZONE的数据库列发送和检索时刻。

发送。

Instant instant = Instant.now() ;  // Capture the current moment in UTC.
myPreparedStatement.setObject( … , instant ) ;

检索。

Instant instant = myResultSet.getObject( … , Instant.class );

您可以将UTC调整为某个特定时区。

ZoneId z = ZoneId.of( "Africa/Tunis" ) ;
ZonedDateTime zdt = instant.atZone( z ) ;

如果您正在使用OffsetDateTimeZonedDateTime对象处理业务逻辑,请通过调用其Instant方法来提取toInstant

Instant instant = myZonedDateTime.toInstant() ;  // Extract an `Instant` object, effectively adjusting from some particular time zone to UTC.

未分区

一种完全不同的日期时间是一种故意缺少时区或UTC偏移量的概念的日期时间。因此,这种 not 表示特定时刻,是 not 在时间轴上的一点。此类型表示沿大约26-27小时(各个时区的范围)内的潜在时刻。

对于这种日期时间:

  • 在Java中,使用LocalDateTime类。
  • 在Postgres中,使用TIMESTAMP WITHOUT TIME ZONE类型。

将输入发送到Postgres中此类型的列时,将忽略所有伴随的区域/偏移信息。日期和日期保持原样并直接存储在数据库中。检索后,您将获得相同的日期和时间,而无需调整任何特定的时区。


关于 java.time

java.time框架已内置在Java 8及更高版本中。这些类取代了麻烦的旧legacy日期时间类,例如java.util.DateCalendarSimpleDateFormat

目前位于Joda-Timemaintenance mode项目建议迁移到java.time类。

要了解更多信息,请参见Oracle Tutorial。并在Stack Overflow中搜索许多示例和说明。规格为JSR 310

您可以直接与数据库交换 java.time 对象。使用符合JDBC driver或更高版本的JDBC 4.2。不需要字符串,不需要java.sql.*类。

在哪里获取java.time类?

ThreeTen-Extra项目使用其他类扩展了java.time。该项目为将来可能在java.time中添加内容提供了一个试验场。您可能会在这里找到一些有用的类,例如IntervalYearWeekYearQuartermore

答案 2 :(得分:-1)

您可以在sql中完成此操作,只需更改查询即可。

select to_char(iiv_prescan_invoice_details.deadline_date, "yyyy:MM:dd HH24:MI:SS") as deadline_date 
from iiv_prescan_invoice_details

或者您可以在Java中完成

LocalDateTime dateTime = resultSet.getTimestamp("deadline_date").toLocalDateTime();
System.out.println("Date Time: " + dateTime);