如何将ZonedDateTime转换为Date?tl;博士其他正确答案将服务器保持在 UTC 格式指定时区卡桑德拉型Timestamp国际标准化组织 ISO 8601精度:毫秒与纳秒示例代码关于 java.time

我正在尝试在我的数据库中设置一个与服务器无关的日期时间,我相信这样做的最佳做法是设置一个UTC DateTime。我的数据库服务器是 Cassandra,Java 的 db 驱动程序只知道 Date 类型。

因此,假设在我的代码中,我正在使用新的Java 8 ZonedDateTime来获取现在的UTC(),那么我如何将这个ZonedDateTime实例转换为“legacy”Date类?ZonedDateTime.now(ZoneOffset.UTC)


答案 1

您可以将ZonedDateTime转换为即时,您可以直接与Date一起使用。

Date.from(java.time.ZonedDateTime.now().toInstant());

答案 2

tl;博士

java.util.Date.from(  // Transfer the moment in UTC, truncating any microseconds or nanoseconds to milliseconds.
    Instant.now() ;   // Capture current moment in UTC, with resolution as fine as nanoseconds.
)

虽然上面的代码中没有意义。两者都表示 UTC 中的某个时刻,始终以 UTC 为单位。上面的代码具有相同的效果:java.util.DateInstant

new java.util.Date()  // Capture current moment in UTC.

此处使用 没有好处。如果已有 ,请通过提取 .ZonedDateTimeZonedDateTimeInstant

java.util.Date.from(             // Truncates any micros/nanos.
    myZonedDateTime.toInstant()  // Adjust to UTC. Same moment, same point on the timeline, different wall-clock time.
)

其他正确答案

ssoltanid的答案正确地解决了您的具体问题,如何将新派java.time对象()转换为旧派java.util.Date对象。从 ZonedDateTime 中提取 ,并传递到 java.util.Date.from()ZonedDateTimeInstant

数据丢失

请注意,您将遭受数据丢失,因为自纪元以来跟踪纳秒,而自纪元以来跟踪毫秒Instantjava.util.Date

diagram comparing resolutions of millisecond, microsecond, and nanosecond

您的问题和意见会引发其他问题。

将服务器保持在 UTC 格式

通常,您的服务器应将其主机操作系统设置为 UTC 作为最佳做法。JVM 在 I 所知道的 Java 实现中将此主机操作系统设置作为其默认时区。

指定时区

但是,您永远不应该依赖 JVM 的当前默认时区。启动 JVM 时传递的标志可以设置另一个时区,而不是选择主机设置。更糟糕的是:任何应用程序的任何线程中的任何代码在任何时候都可以调用java.util.TimeZone::setDefault以在运行时更改默认值!

卡桑德拉型Timestamp

任何像样的数据库和驱动程序都应该自动处理将传递的日期时间调整为UTC以进行存储。我不使用Cassandra,但它似乎确实对日期时间有一些基本的支持。文档说它的时间戳类型是来自同一纪元(UTC中1970年的第一个时刻)的毫秒数。

国际标准化组织 ISO 8601

此外,Cassandra 接受 ISO 8601 标准格式的字符串输入。幸运的是,java.time使用ISO 8601格式作为其解析/生成字符串的默认值。Instant类的toString实现会做得很好。

精度:毫秒与纳秒

但首先,我们需要将 ZonedDateTime 的纳秒精度降低到毫秒。一种方法是使用毫秒创建一个新的即时。幸运的是,java.time有一些方便的方法来转换到毫秒和从毫秒转换。

示例代码

下面是 Java 8 Update 60 中的一些示例代码。

ZonedDateTime zdt = ZonedDateTime.now( ZoneId.of( "America/Montreal" ) );
…
Instant instant = zdt.toInstant();
Instant instantTruncatedToMilliseconds = Instant.ofEpochMilli( instant.toEpochMilli() );
String fodderForCassandra = instantTruncatedToMilliseconds.toString();  // Example: 2015-08-18T06:36:40.321Z

或者根据此Cassandra Java驱动程序文档,您可以传递java.util.Date实例(不要与java.sqlDate混淆)。因此,您可以在上面的代码中从中制作一个j.u.Date。instantTruncatedToMilliseconds

java.util.Date dateForCassandra = java.util.Date.from( instantTruncatedToMilliseconds );

如果经常这样做,你可以做一个单行。

java.util.Date dateForCassandra = java.util.Date.from( zdt.toInstant() );

但是创建一个小小的实用方法会更整洁。

static public java.util.Date toJavaUtilDateFromZonedDateTime ( ZonedDateTime zdt ) {
    Instant instant = zdt.toInstant();
    // Data-loss, going from nanosecond resolution to milliseconds.
    java.util.Date utilDate = java.util.Date.from( instant ) ;
    return utilDate;
}

请注意所有这些代码与问题中的区别。问题的代码正在尝试将 ZonedDateTime 实例的时区调整为 UTC。但这不是必要的。概念:

ZonedDateTime = Instant + ZoneId

我们只是提取即时部分,该部分已经在UTC中(基本上是在UTC中,请阅读类文档以获取确切的详细信息)。


Table of date-time types in Java, both modern and legacy


关于 java.time

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

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

要了解更多信息,请参阅 Oracle 教程。搜索 Stack Overflow 以获取许多示例和解释。规格是JSR 310

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

从哪里获取 java.time 类?

Table of which java.time library to use with which version of Java or Android

ThreeTen-Extra 项目通过其他类扩展了 java.time。这个项目是未来可能添加到java.time的试验场。您可以在此处找到一些有用的课程,例如IntervalYearWeekYearQuarter


推荐