将java.sql.Timestamp转换为Java 8 ZonedDateTime?

27

将Joda时间迁移到Java 8

Joda:

UserObject user = new UserObject()
user.setCreatedAt(new DateTime(rs.getTimestamp("columnName")));`

迁移到Java 8

这是我的代码,它可以编译,但我怀疑它是否能正常运行:

ZonedDateTime.ofInstant(rs.getTimestamp("columnName").toLocalDateTime().toInstant(ZoneOffset.UTC),ZoneId.of("UTC")));
在某些情况下,日期是错误的。有什么建议吗?
建议:请检查日期格式并确保输入的日期是正确的。

1
数据库列的数据类型是什么?您能否举一个正确值的例子,如在SQL查询中和ZonedDateTime中显示?然后,举一个错误日期的例子,如在数据库中,本应该是正确的ZonedDateTime,以及实际结果与正确结果的差异是什么?“在某些情况下,日期是错误的”——坦率地说——这种表述过于模糊。 - Ole V.V.
抱歉,这似乎可以工作:ZonedDateTime.ofInstant(rs.getTimestamp("column").toInstant(), ZoneId.of("UTC"))。 - Parameswar
3个回答

35

简介

如果要跟踪历史上的某个时刻,请使用Instant作为类成员变量的类型。具体而言,这个时刻被视为UTC的日期和时间。

public class UserObject() {
    Instant createdAt ;
    …
    public void setCreatedAt( Instant instantArg ) {
        this.createdAt = instantArg ;
    {
}

使用方法,捕捉当前时刻。

UserObject user = new UserObject() ;
user.setCreatedAt( Instant.now() ) ;

使用方法,从数据库中填充值。

UserObject user = new UserObject() ;
Instant instant = myResultSet.getObject( "when_created" , Instant.class ) ;
user.setCreatedAt( instant ) ;

JDBC 4.2不需要支持Instant(UTC时间点)。如果你的驱动程序不支持该类,请切换到OffsetDateTime,该类是必需的。

UserObject user = new UserObject() ;
OffsetDateTime odt = myResultSet.getObject( "when_created" , OffsetDateTime.class ) ;  
user.setCreatedAt( odt.toInstant() ) ;  // Convert from an `OffsetDateTime` (for any offset-from-UTC) to `Instant` (always in UTC). 

Table of date-time types in Java (both modern and legacy) and in standard SQL.

呈现给用户,本地化为用户界面。

user               // Your business object.
.getCreatedAt()    // Returns a `Instant` object.
.atZone(           // Adjust from UTC to a time zone. Same moment, same point on the timeline, different wall-clock time.
    ZoneId.of( "Pacific/Auckland" )  // Specify the user’s desired/expected time zone.
)                  // Returns a `ZonedDateTime` object. 
.format(           // Generate a `String` representing the value of date-time object.
    DateTimeFormatter.ofLocalizedDateTime(
        FormatStyle.FULL   // Specify how long or abbreviated the string.
    )
    .withLocale(   // Specify `Locale` to determine human language and cultural norms for localization.
        Locale.CANADA_FRENCH 
    )
)                  // Returns a `String`.

请注意,Locale与时区无关,这是一个正交问题。上面的代码可能适用于来自Québec并正在旅行New Zealand的商务人士。她想看到周围kiwis使用的挂钟时间,但她更喜欢用她的母语法语阅读它的文本显示。时区和语言环境都最好只留给演示;在您的其余代码中通常最好使用UTC。因此,我们将成员变量createdAt定义为Instant,其中Instant根据定义始终处于UTC。

避免使用java.sql.Timestamp

  • java.sql.Timestampjava.sql.Datejava.util.DateCalendar一起,都是可怕的旧日期时间类的一部分,这些类多年前被java.time类所取代。
  • Joda-Time也已被java.time类所取代,正如该项目网站首页所述。

java.time

JDBC 4.2版本开始,我们可以直接使用java.time对象与数据库进行交互。

Instant

使用Instant类将当前时刻发送到数据库。Instant类表示以UTC为基准的时间线上的某一时刻,分辨率为纳秒(最多九位小数)。

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

检索和获取。

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

ZonedDateTime

要通过一个特定地区人们使用的挂钟时间(时区)来查看同一时刻,请应用ZoneId以获取ZonedDateTime

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

LocalDateTime不是一个瞬间

.toLocalDateTime().

当表示特定时间时,永远不要使用LocalDateTime。该类故意缺乏任何时区或与UTC的偏移概念。因此,它无法表示瞬间,也不是时间轴上的点。它只是关于可能出现在大约26-27小时范围内的瞬间的模糊想法(时区的范围)。


关于java.time

java.time框架是Java 8及以上版本内置的。这些类取代了老旧的legacy日期时间类,如java.util.Date, CalendarSimpleDateFormat等,使其更易使用。

欲了解更多信息,请参阅Oracle教程。同时在Stack Overflow上搜索许多示例和解释。规范是JSR 310

Joda-Time项目现在处于维护模式,建议迁移到java.time类。

您可以直接与数据库交换java.time对象。使用符合JDBC 4.2或更高版本的JDBC驱动程序。无需使用字符串,也不需要java.sql.*类。Hibernate 5和JPA 2.2支持java.time

如何获取java.time类?


3
哇塞,感谢您提供的砍刀,让我顺利穿越Java + JDBC时间丛林! - markdsievers
getObject(..., Instant.class) 给出的错误信息是 conversion to class java.time.Instant from timestamp not supported (Java 11)。 - mangusta
@mangusta,是的,我需要修复它。对于Instant 的支持是可选的。JDBC 4.2规范要求支持 OffsetDateTime - Basil Bourque

16

这似乎有效:-

ZonedDateTime.ofInstant(rs.getTimestamp("columnname").toInstant(), ZoneId.of("UTC"))

5
使用以下内容:
rs.getTimestamp("columnName").toInstant()
    .atZone(ZoneId.[appropriate-zone-ID-here])

您需要使用适合该地区的 ZoneId(您可以先尝试使用 ZoneId.systemDefault())。

有关各种Java-Time API之间差异的更多详细信息,请参见这个很棒的答案


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接