27

我现在创建日期:

ZoneId gmt = ZoneId.of("GMT");
LocalDateTime localDateTime = LocalDateTime.now();
LocalDate localDateNow = localDateTime.toLocalDate();

然后我想以毫秒为单位返回这个日期:

localDateNow.atStartOfDay(gmt) - 22.08.2017
localDateNow.atStartOfDay(gmt).toEpochSecond(); - 1503360000 (18.01.70)

我怎样才能LocalDate.now()在毫秒内返回?

4

1 回答 1

43

正如@JB Nizet 的评论toInstant().toEpochMilli()所建议的那样,调用是正确的答案,但是您必须注意有关使用本地日期的一些小而棘手的细节。

但在此之前,还有一些其他的小细节:

  • 而不是ZoneId.of("GMT")你可以使用内置的常量ZoneOffset.UTC。它们是等价的,但如果 API 已经提供了完全相同的对象,则无需创建额外的冗余对象。
  • 您可以直接调用而不是调用LocalDateTime.now()then ——它们是等价的。.toLocalDate()LocalDate.now()

现在是棘手的细节:当您调用该now()方法(对于LocalDateTimeor LocalDate)时,它使用 JVM 的默认时区来获取当前日期的值,并且该值可能会根据 JVM 中配置的时区而有所不同。

在我使用的 JVM 中,默认时区是America/Sao_Paulo,这里的本地时间是09:37 AM。所以LocalDate.now()返回2017-08-22(2017 年 8 月22)。

但是,如果我将默认时区更改为Pacific/Kiritimati,它会返回2017-08-23。那是因为在 Kiritimati,现在已经是 2017 年 8 月23(而在我写这篇文章的那一刻,那里的当地时间是02:37 AM)。

因此,如果我在默认时区为时运行此代码Pacific/Kiritimati

LocalDate dtNow = LocalDate.now(); // 2017-08-23
System.out.println(dtNow.atStartOfDay(ZoneOffset.UTC).toInstant().toEpochMilli());

输出是:

1503446400000

这相当于 2017 年 8 月23UTC 午夜。

如果我在默认时区为 时运行相同的代码America/Sao_Paulo,结果将是:

1503360000000

这相当于 2017 年 8 月22UTC 午夜。

Usingnow()使您的代码依赖于 JVM 的默认时区。并且即使在运行时也可以在不通知的情况下更改此配置,从而使您的代码在发生此类更改时返回不同的结果。

而且您不需要这样的极端情况(例如有人将 JVM 错误配置为“非常远”的时区)。例如,在我的情况下,在America/Sao_Paulo时区中,如果我在11 PM运行代码,LocalDate将返回 August 22 th,但 UTC 的当前日期已经是 August 23 th。这是因为圣保罗的晚上 11 点与UTC的第二天凌晨2 点相同:

// August 22th 2017, at 11 PM in Sao Paulo
ZonedDateTime z = ZonedDateTime.of(2017, 8, 22, 23, 0, 0, 0, ZoneId.of("America/Sao_Paulo"));
System.out.println(z); // 2017-08-22T23:00-03:00[America/Sao_Paulo]
System.out.println(z.toInstant()); // 2017-08-23T02:00:00Z (in UTC is already August 23th)

因此,使用 aLocalDate.now()不能保证我将始终拥有UTC 中的当前日期


如果您想要UTC 中的当前日期(无论 JVM 默认时区如何)并将时间设置为午夜,最好使用ZonedDateTime

// current date in UTC, no matter what the JVM default timezone is 
ZonedDateTime zdtNow = ZonedDateTime.now(ZoneOffset.UTC);
// set time to midnight and get the epochMilli
System.out.println(zdtNow.with(LocalTime.MIDNIGHT).toInstant().toEpochMilli());

输出是:

1503360000000

这相当于 2017 年 8 月22UTC 午夜。

另一种选择是将时区传递给LocalDate.now,因此它可以获得指定区域上当前日期的正确值:

// current date in UTC, no matter what the JVM default timezone is 
LocalDate dtNowUtc = LocalDate.now(ZoneOffset.UTC);
// set time to midnight and get the epochMilli
System.out.println(dtNow.atStartOfDay(ZoneOffset.UTC).toInstant().toEpochMilli());
于 2017-08-22T13:11:19.463 回答