Java 如何将 ZonedDateTime 转换为日期?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/32274369/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-11 12:16:28  来源:igfitidea点击:

How to convert ZonedDateTime to Date?

javajava-8java.util.datedatetime-conversionzoneddatetime

提问by Milen Kovachev

I am trying to set a server agnostic date time in my database and I believe the best practice to do so is to set a UTC DateTime. My db server is Cassandra and the db driver for Java understands only the Date type.

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

So assuming that in my code I am using the new Java 8 ZonedDateTime to get the UTC now (ZonedDateTime.now(ZoneOffset.UTC)), how can I convert this ZonedDateTime instance to the "legacy" Date class?

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

采纳答案by Slim Soltani Dridi

You can convert ZonedDateTime to an instant, which you can use directly with Date.

您可以将 ZonedDateTime 转换为可以直接与 Date 一起使用的瞬间。

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

回答by Peter Lawrey

You can do this using the java.timeclasses built into Java 8 and later.

您可以使用Java 8 及更高版本中内置的java.time类来执行此操作。

ZonedDateTime temporal = ...
long epochSecond = temporal.getLong(INSTANT_SECONDS);
int nanoOfSecond = temporal.get(NANO_OF_SECOND);
Date date = new Date(epochSecond * 1000 + nanoOfSecond / 1000000);

回答by Jacob Eckel

If you are interested in now only, then simply use:

如果您只对现在感兴趣,那么只需使用:

Date d = new Date();

回答by Basil Bourque

tl;dr

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

Though there was no point in that code above. Both java.util.Dateand Instantrepresent a moment in UTC, always in UTC. Code above has same effect as:

虽然上面的代码没有意义。双方java.util.DateInstant表示UTC片刻,总是在UTC。上面的代码具有相同的效果:

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

No benefit here to using ZonedDateTime. If you already have a ZonedDateTime, adjust to UTC by extracting a Instant.

在这里使用ZonedDateTime. 如果您已经有了ZonedDateTime,请通过提取Instant.

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

Other Answer Correct

其他答案正确

The Answerby ssoltanidcorrectly addresses your specific question, how to convert a new-school java.time object (ZonedDateTime) to an old-school java.util.Dateobject. Extract the Instantfrom the ZonedDateTime and pass to java.util.Date.from().

答案通过ssoltanid正确解决您的具体问题,如何在新的学校java.time对象(转换ZonedDateTime),以一个老派的java.util.Date对象。Instant从 ZonedDateTime 中提取并传递给java.util.Date.from()

Data Loss

数据丢失

Note that you will suffer data loss, as Instanttracks nanosecondssince epochwhile java.util.Datetracks millisecondssince epoch.

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

diagram comparing resolutions of millisecond, microsecond, and nanosecond

比较毫秒、微秒和纳秒分辨率的图表

Your Question and comments raise other issues.

您的问题和评论引发了其他问题。

Keep Servers In UTC

将服务器保持在 UTC

Your servers should have their host OS set to UTC as a best practice generally. The JVM picks up on this host OS setting as its default time zone, in the Java implementations that I'm aware of.

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

Specify Time Zone

指定时区

But you should never rely on the JVM's current default time zone. Rather than pick up the host setting, a flag passed when launching a JVM can set another time zone. Even worse: Any code in any thread of any app at any moment can make a call to java.util.TimeZone::setDefaultto change that default at runtime!

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

Cassandra TimestampType

卡桑德拉Timestamp

Any decent databaseand driver should automatically handle adjusting a passed date-time to UTC for storage. I do not use Cassandra, but it does seem to have some rudimentary support for date-time. The documentation says its Timestamptype is a count of milliseconds from the same epoch (first moment of 1970 in UTC).

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

ISO 8601

ISO 8601

Furthermore, Cassandra accepts string inputs in the ISO 8601standard formats. Fortunately, java.time uses ISO 8601 formats as its defaults for parsing/generating strings. The Instantclass' toStringimplementation will do nicely.

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

Precision: Millisecond vs Nanosecord

精度:毫秒 vs 纳米记录

But first we need to reduce the nanosecond precision of ZonedDateTime to milliseconds. One way is to create a fresh Instant using milliseconds. Fortunately, java.time has some handy methods for converting to and from milliseconds.

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

Example Code

示例代码

Here is some example code in Java 8 Update 60.

这是 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

Or according to this Cassandra Java driver doc, you can pass a java.util.Dateinstance (not to be confused with java.sqlDate). So you could make a j.u.Date from that instantTruncatedToMillisecondsin the code above.

或者根据这个Cassandra Java driver doc,您可以传递一个java.util.Date实例(不要与 混淆java.sqlDate)。所以你可以从instantTruncatedToMilliseconds上面的代码中创建一个 juDate 。

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

If doing this often, you could make a one-liner.

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

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

But it would be neater to create a little utility method.

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

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;
}

Notice the difference in all this code than in the Question. The Question's code was trying to adjust the time zone of the ZonedDateTime instance to UTC. But that is not necessary. Conceptually:

请注意所有这些代码与问题中的不同之处。问题的代码试图将 ZonedDateTime 实例的时区调整为 UTC。但这不是必要的。从概念上讲:

ZonedDateTime = Instant + ZoneId

ZonedDateTime = 即时 + ZoneId

We just extract the Instant part, which is already in UTC (basically in UTC, read the class doc for precise details).

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



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

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



About java.time

关于java.time

The java.timeframework is built into Java 8 and later. These classes supplant the troublesome old legacydate-time classes such as java.util.Date, Calendar, & SimpleDateFormat.

java.time框架是建立在Java 8和更高版本。这些类取代了麻烦的旧的遗留日期时间类,例如java.util.Date, Calendar, & SimpleDateFormat

The Joda-Timeproject, now in maintenance mode, advises migration to the java.timeclasses.

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

To learn more, see the Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310.

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

You may exchange java.timeobjects directly with your database. Use a JDBC drivercompliant with JDBC 4.2or later. No need for strings, no need for java.sql.*classes.

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

Where to obtain the java.time classes?

从哪里获得 java.time 类?

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

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

The ThreeTen-Extraproject extends java.time with additional classes. This project is a proving ground for possible future additions to java.time. You may find some useful classes here such as Interval, YearWeek, YearQuarter, and more.

ThreeTen-额外项目与其他类扩展java.time。该项目是未来可能添加到 java.time 的试验场。你可能在这里找到一些有用的类,比如IntervalYearWeekYearQuarter,和更多

回答by Asanka Siriwardena

Here is an example converting current system time to UTC. It involves formatting ZonedDateTime as a String and then the String object will be parsed into a date object using java.text DateFormat.

这是将当前系统时间转换为 UTC 的示例。它涉及将 ZonedDateTime 格式化为 String,然后使用 java.text DateFormat 将 String 对象解析为日期对象。

    ZonedDateTime zdt = ZonedDateTime.now(ZoneOffset.UTC);
    final DateTimeFormatter DATETIME_FORMATTER = DateTimeFormatter.ofPattern("yyyyMMdd HH:mm:ss");
    final DateFormat FORMATTER_YYYYMMDD_HH_MM_SS = new SimpleDateFormat("yyyyMMdd HH:mm:ss");
    String dateStr = zdt.format(DATETIME_FORMATTER);

    Date utcDate = null;
    try {
        utcDate = FORMATTER_YYYYMMDD_HH_MM_SS.parse(dateStr);
    }catch (ParseException ex){
        ex.printStackTrace();
    }

回答by David Rawson

If you are using the ThreeTen backportfor Android and can't use the newer Date.from(Instant instant)(which requires minimum of API 26) you can use:

如果您使用的是适用于 Android的ThreeTen backport并且不能使用较新的Date.from(Instant instant)(至少需要 API 26),您可以使用:

ZonedDateTime zdt = ZonedDateTime.now();
Date date = new Date(zdt.toInstant().toEpochMilli());

or:

或者:

Date date = DateTimeUtils.toDate(zdt.toInstant());

Please also read the advice in Basil Bourque's answer

另请阅读Basil Bourque 的回答中的建议

回答by beehuang

I use this.

我用这个。

public class TimeTools {

    public static Date getTaipeiNowDate() {
        Instant now = Instant.now();
        ZoneId zoneId = ZoneId.of("Asia/Taipei");
        ZonedDateTime dateAndTimeInTai = ZonedDateTime.ofInstant(now, zoneId);
        try {
            return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(dateAndTimeInTai.toString().substring(0, 19).replace("T", " "));
        } catch (ParseException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return null;
    }
}

Because Date.from(java.time.ZonedDateTime.ofInstant(now, zoneId).toInstant()); It's not work!!! If u run your application in your computer, it's not problem. But if you run in any region of AWS or Docker or GCP, it will generate problem. Because computer is not your timezone on Cloud. You should set your correctly timezone in Code. For example, Asia/Taipei. Then it will correct in AWS or Docker or GCP.

因为 Date.from(java.time.ZonedDateTime.ofInstant(now, zoneId).toInstant()); 它不工作!!!如果您在计算机上运行您的应用程序,那就没问题了。但是如果你在 AWS 或 Docker 或 GCP 的任何区域运行,都会产生问题。因为计算机不是您在云上的时区。您应该在代码中设置正确的时区。例如,亚洲/台北。然后它将在 AWS 或 Docker 或 GCP 中更正。

public class App {
    public static void main(String[] args) {
        Instant now = Instant.now();
        ZoneId zoneId = ZoneId.of("Australia/Sydney");
        ZonedDateTime dateAndTimeInLA = ZonedDateTime.ofInstant(now, zoneId);
        try {
            Date ans = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(dateAndTimeInLA.toString().substring(0, 19).replace("T", " "));
            System.out.println("ans="+ans);
        } catch (ParseException e) {
        }
        Date wrongAns = Date.from(java.time.ZonedDateTime.ofInstant(now, zoneId).toInstant());
        System.out.println("wrongAns="+wrongAns);
    }
}

回答by dwe

For a docker application like beehuang commented you should set your timezone.

对于像 beehuang 评论的 docker 应用程序,您应该设置时区。

Alternatively you can use withZoneSameLocal. For example:

或者,您可以使用withZoneSameLocal。例如:

2014-07-01T00:00+02:00[GMT+02:00] is converted by

2014-07-01T00:00+02:00[GMT+02:00] 转换为

Date.from(zonedDateTime.withZoneSameLocal(ZoneId.systemDefault()).toInstant())

to Tue Jul 01 00:00:00 CEST 2014and by

2014 年 7 月 1 日星期二 00:00:00 CEST

Date.from(zonedDateTime.toInstant())

to Mon Jun 30 22:00:00 UTC 2014

2014 年 6 月 30 日星期一 22:00:00 UTC

回答by Avec

The accepted answer did not work for me. The Date returned is always the local Date and not the Date for the original Time Zone. I live in UTC+2.

接受的答案对我不起作用。返回的日期始终是本地日期,而不是原始时区的日期。我住在UTC+2。

//This did not work for me
Date.from(java.time.ZonedDateTime.now().toInstant()); 

I have come up with two alternative ways to get the correct Date from a ZonedDateTime.

我想出了两种从 ZonedDateTime 获取正确日期的替代方法。

Say you have this ZonedDateTime for Hawaii

假设您有夏威夷的 ZonedDateTime

LocalDateTime ldt = LocalDateTime.now();
ZonedDateTime zdt = ldt.atZone(ZoneId.of("US/Hawaii"); // UTC-10

or for UTC as asked originally

或最初询问的UTC

Instant zulu = Instant.now(); // GMT, UTC+0
ZonedDateTime zdt = zulu.atZone(ZoneId.of("UTC"));

Alternative 1

备选方案 1

We can use java.sql.Timestamp. It is simple but it will probably also make a dent in your programming integrity

我们可以使用 java.sql.Timestamp。这很简单,但它也可能会削弱您的编程完整性

Date date1 = Timestamp.valueOf(zdt.toLocalDateTime());

Alternative 2

备选方案 2

We create the Date from millis (answered hereearlier). Note that local ZoneOffset is a must.

我们从millis创建日期(之前在这里回答)。请注意,本地 ZoneOffset 是必须的。

ZoneOffset localOffset = ZoneOffset.systemDefault().getRules().getOffset(LocalDateTime.now());
long zonedMillis = 1000L * zdt.toLocalDateTime().toEpochSecond(localOffset) + zdt.toLocalDateTime().getNano() / 1000000L;
Date date2 = new Date(zonedMillis);