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

Posted

技术标签:

【中文标题】将 java.sql.Timestamp 转换为 Java 8 ZonedDateTime?【英文标题】:Convert java.sql.Timestamp to Java 8 ZonedDateTime? 【发布时间】:2018-12-24 09:20:22 【问题描述】:

将 Joda 时间迁移到 Java 8

乔达:

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")));

在某些情况下,日期是错误的。有什么建议吗?

【问题讨论】:

什么是数据库列数据类型?您能否举一个正确值的示例,如 SQL 查询中所示,然后为 ZonedDateTime?然后是错误日期的示例,例如数据库中的正确日期ZonedDateTime,实际结果有何不同? “在某些情况下,日期是错误的”——坦率地说——非常模糊。 对不起,这似乎工作: ZonedDateTime.ofInstant(rs.getTimestamp("column").toInstant(), ZoneId.of("UTC")) 【参考方案1】:

tl;博士

要跟踪历史时刻,请使用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). 

呈现给用户,针对用户界面进行本地化。

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 时间线上的时刻,分辨率为nanoseconds(最多九 (9) 位小数)。

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.DateCalendarSimpleDateFormat

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

Joda-Time 项目现在位于maintenance mode,建议迁移到java.time 类。

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

从哪里获取 java.time 类?

Java SE 8Java SE 9Java SE 10Java SE 11 和更高版本 - 具有捆绑实现的标准 Java API 的一部分。 Java 9 带来了一些小功能和修复。 Java SE 6Java SE 7 大部分 java.time 功能在ThreeTen-Backport 中向后移植到 Java 6 和 7。 Android java.time 类的更高版本的 android (26+) 捆绑实现。 对于早期的 Android (API desugaring 的进程带来了最初未内置于 Android 中的 subset of the java.time 功能。 如果脱糖不能满足您的需求,ThreeTenABP 项目会将ThreeTen-Backport(如上所述)适配到 Android。见How to use ThreeTenABP…

【讨论】:

天哪,感谢砍刀在 Java + JDBC 时间丛林中导航! getObject(..., Instant.class) 给出conversion to class java.time.Instant from timestamp not supported (Java 11) @mangusta 是的,我需要解决这个问题。对Instant 的支持是可选的。 JDBC 4.2 规范需要支持OffsetDateTime【参考方案2】:

这似乎有效:-

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

【讨论】:

【参考方案3】:

使用以下内容:

rs.getTimestamp("columnName").toInstant()
    .atZone(ZoneId.[appropriate-zone-ID-here])

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

有关各种 Java-Time API 之间差异的更多详细信息,请参阅this great answer。

【讨论】:

以上是关于将 java.sql.Timestamp 转换为 Java 8 ZonedDateTime?的主要内容,如果未能解决你的问题,请参考以下文章

将 java.time.Instant 转换为没有区域偏移的 java.sql.Timestamp

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

将 java.sql.Timestamp 转换为 javax.xml.datatype.XMLGregorianCalendar 而不会丢失 nanos

如何从 java.sql.Timestamp 转换为 java.util.Date?

无法在 Spring Data Cloud Spanner 中将 java.sql.Timestamp 转换为 com.google.cloud.Timestamp

java的date转换为oracle中的timestamp