按照@JB Nizet's comment 的建议,拨打toInstant().toEpochMilli() 是正确的答案,但您必须注意有关使用当地日期的一些小细节。
但在此之前,还有一些其他的小细节:
- 您可以使用内置常量
ZoneOffset.UTC,而不是ZoneId.of("GMT")。它们是等价的,但如果 API 已经提供了完全相同的对象,则无需创建额外的冗余对象。
- 您可以直接调用
LocalDate.now(),而不是先调用LocalDateTime.now(),然后再调用.toLocalDate() - 它们是等价的。
现在是棘手的细节:当您调用 now() 方法时(对于 LocalDateTime 或 LocalDate),它使用 JVM 的默认时区来获取当前日期的值,并且该值可能会有所不同,具体取决于在 JVM 中配置的时区。
在我使用的JVM中,默认时区是America/Sao_Paulo,这里的当地时间是09:37 AM。所以LocalDate.now() 返回2017-08-22(2017 年 8 月 22th)。
但如果我将默认时区更改为Pacific/Kiritimati,它会返回2017-08-23。那是因为在 Kiritimati,现在已经是 2017 年 8 月 23th(我写这篇文章的那一刻,那里的当地时间是 02:37 AM)。
所以,如果我在默认时区为 Pacific/Kiritimati 时运行此代码:
LocalDate dtNow = LocalDate.now(); // 2017-08-23
System.out.println(dtNow.atStartOfDay(ZoneOffset.UTC).toInstant().toEpochMilli());
输出是:
1503446400000
相当于 UTC 时间 2017 年 8 月 23th 午夜。
如果我在默认时区为America/Sao_Paulo 时运行相同的代码,结果将是:
1503360000000
相当于 2017 年 8 月 22th UTC 午夜。
使用now() 使您的代码依赖于JVM 的默认时区。而这个配置can be changed without notice, even at runtime,让你的代码在发生这种变化时返回不同的结果。
而且您不需要这种极端情况(例如有人将 JVM 错误配置为“非常远”的时区)。例如,在America/Sao_Paulo 时区,如果我在晚上11 点 运行代码,LocalDate 将返回 August 22th,但 UTC 的当前日期已经是 8 月 23th。这是因为圣保罗的晚上 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)
因此,使用LocalDate.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 月 22th UTC 午夜。
另一种选择是将时区传递给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());