如何在Java 8中从LocalDateTime获取毫秒数

420

我想知道是否有一种方法可以使用Java 8新的LocalDateLocalTimeLocalDateTime类获取自1970年1月1日(纪元)以来的当前毫秒数。

已知的方法如下:

long currentMilliseconds = new Date().getTime();
或者
long currentMilliseconds = System.currentTimeMillis();

8
System.currentTimeMillis() 函数有什么问题? - Dawood ibn Kareem
39
他想要获取一个日期的时间,而不是当前时间? - Anubian Noob
2
"...获取当前毫秒的方法..." - Dawood ibn Kareem
毫秒计数从1970年1月1日开始。我在想是否有一种方法可以使用Java 8的新类LocalDate、LocalTime和LocalDateTime来获取它们,因为我没有找到这样的方法。 - Georgios Syngouroglou
1
我对这些类的目的的理解是,它是对时间的“以人为本”的理解,而currentTimeMillis在这种情况下是无关紧要的。可以认为它就像具有非常高精度的日历和挂钟,而且不用担心时区和地域性。因此,没有办法从LocalTime返回到“UTC时间”。 - Gus
类似问题:如何将LocalDateTime转换为Long?,并且有一个好的答案 - Basil Bourque
14个回答

445

我不完全确定你所说的“当前毫秒”是什么意思,但我会假设它是自1970年1月1日UTC午夜(epoch)以来的毫秒数。

如果你想要找到从现在开始到epoch的毫秒数,那么就像Anubian Noob指出的那样,使用System.currentTimeMillis()。如果是这样的话,就没有必要使用任何新的java.time API。

但是,也许你已经有一个类似于LocalDateTime的对象,并且想将其转换为自epoch以来的毫秒数。由于LocalDateTime对象系列没有时区信息,因此无法直接进行转换。因此,需要提供时区信息以找到相对于UTC中的epoch时间。

假设你有一个像这样的LocalDateTime

LocalDateTime ldt = LocalDateTime.of(2014, 5, 29, 18, 41, 16);

您需要应用时区信息,生成一个ZonedDateTime对象。我和洛杉矶在同一时区,所以我会这样做:

ZonedDateTime zdt = ldt.atZone(ZoneId.of("America/Los_Angeles"));

当然,这假设了时区是确定的。还有一些边缘情况可能会发生,例如,如果当地时间恰好与夏令时转换时间附近的时间重合。让我们将这些情况搁置一下,但您应该知道存在这些情况。

无论如何,如果您可以获取有效的ZonedDateTime,您可以像这样将其转换为自纪元以来的毫秒数:

long millis = zdt.toInstant().toEpochMilli();

6
请注意,ZonedDateTime 已经通过 ChronoZonedDateTime默认实现 拥有了 getEpochSecond 方法。无需使用 Instant - mabi
26
没问题,如果你只需要秒而不是毫秒。 - Stuart Marks
1
哦,我表达不够准确:ZonedDateTime 也有 InstantgetNano 方法,所以你能否在你的示例中将 inst 替换为 zdt - mabi
26
使用zdt.get(ChronoField.MILLI_OF_SECOND)可以避免在nanos上进行数学计算。使用zdt.toInstant().toEpochMilli()可以避免进行所有的数学计算。 - JodaStephen
1
@JodaStephen 使用 zdt.getLong 而不是只用 get(返回 int)。 - adoalonso
显示剩余2条评论

141

我不指定时区的做法是:

System.out.println("ldt " + LocalDateTime.now().atZone(ZoneId.systemDefault()).toInstant().toEpochMilli());
System.out.println("ctm " + System.currentTimeMillis());

提供

ldt 1424812121078 
ctm 1424812121281

如你所见,这些数字几乎一样,只有执行时间略微不同。

如果你不喜欢使用 System.currentTimeMillis,可以使用 Instant.now().toEpochMilli()


6
不,Epoch相对于UTC是相对的,但您从本地时区获取当前时间。 - Rafael Membrives
3
自纪元以来经过的毫秒数是与时区无关的事实。无论何时调用它,经过的毫秒数都是相同的。这两个结果是不同的,因为Brian的计算机花费了203毫秒来执行第二个命令。 - Fletch
当用户可能处于不同的时区时,使用协调世界时(UTC)是否可行? - GilbertS
5
@GilbertS,你最好永远不要直接使用这段代码,而是按照API的意图来使用。日期时间在计算机上存储或传输到其他用户时,应始终使用UTC。通常应根据用户的时区设置以本地日期时间向用户呈现。 - brian
就像@Fletch已经提到的,结果与时区无关。差异背后的原因是第二个命令执行了203毫秒。您可以使用System.out.println("ldt " + LocalDateTime.now().atZone(ZoneId.systemDefault()).toInstant().toEpochMilli());替换System.out.println("ctm " + System.currentTimeMillis());即可执行完全相同的语句两次,但您仍将看到与自上次执行相同语句以来经过的毫秒数相等的差异。 - Arvind Kumar Avinash

26
自Java 8起,您可以调用java.time.Instant.toEpochMilli()
例如,调用:
final long currentTimeJava8 = Instant.now().toEpochMilli();

给你与...相同的结果

final long currentTimeJava1 = System.currentTimeMillis();

4
"给你相同的结果,但需要更多的 CPU 资源,并且会更加强烈地影响垃圾回收器。" - VasiliNovikov
7
我愿意看到以上内容得到量化(特别是对于普通用途的情况——例如不针对某些性能关键的实时应用程序...) - Brian Agnew

24

为避免使用ZoneId,您可以这样做:

LocalDateTime date = LocalDateTime.of(1970, 1, 1, 0, 0);

System.out.println("Initial Epoch (TimeInMillis): " + date.toInstant(ZoneOffset.ofTotalSeconds(0)).toEpochMilli());

得到0作为值,没错!


13
ZoneOffset.ofTotalSeconds(0)ZoneOffset.UTC 是相同的。 - Anton N
1
是的,是我给@AntonNovopashin的评论点赞了。既然你需要UTC,那就没问题了(你可能想在答案中提到这一点)。顺便说一句,ZoneOffsetZoneId的子类,所以我不会完全说你已经避免了它,但只要你满意就好了... - Ole V.V.
我的第一条评论有点严厉,但并不是有意冒犯。你感到受到了冒犯。我很抱歉。我已经删除了它。让我换个方式问:我们避免使用ZoneId的原因可能是什么? - Ole V.V.
@OleV.V 使用UTC时间是否可以?难道只有生活在UTC时区的人才能使用吗? - GilbertS
1
@GilbertS 我不认为有人住在UTC时区。在跨越多个时区使用的时间上使用UTC被推荐为一种良好的实践。例如,请参见Java最佳实践:针对地理多样化用户进行日期操作/存储。或者也许我没有理解你的问题? - Ole V.V.

15
你可以使用java.sql.Timestamp来获取毫秒数。
LocalDateTime now = LocalDateTime.now();
long milliSeconds = Timestamp.valueOf(now).getTime();
System.out.println("MilliSeconds: "+milliSeconds);

1
你正在忽略时区的重要需求。我不建议这样做。此外,Timestamp类已经过时且存在设计问题,因此我宁愿避免使用它,特别是当我们可以使用java.time中的类,如LocalDateTime - Ole V.V.
@OleV.V.,我对“Timestamp”的设计问题很好奇,你能分享一些相关文章吗?这将有助于我避免在我的代码中使用“Timestamp”。谢谢! - Nalam
2
简而言之,它是Date的子类实现,但通常不能作为Date处理。继承自Date的大多数方法和一个构造函数已被弃用。其toString方法使用JVM的时区,在不同的计算机上打印相同的Timestamp可能会导致混淆(例如)。 - Ole V.V.
这非常适合单元测试。只需用 LocalDate.of(year, month, day) 替换现在即可。 - G_V
这应该是绕过“始终为UTC值”的解决方法的被接受答案。 - LeYAUable

13
要获取自纪元以来的当前时间(以毫秒为单位),请使用System.currentTimeMillis()

13

你可以尝试这个:

long diff = LocalDateTime.now().atZone(ZoneOffset.UTC).toInstant().toEpochMilli();

请添加一些解释。 - Sabito stands with Ukraine
如果您在没有本地偏移量的情况下使用 LocalDateTime(始终仅使用UTC),那么最好仅针对REST、Websockets和其他逻辑使用 Instant,然后当您想要使用本地偏移量格式化日期时间以供UI使用时,可以将该 Instant 传递给 ZonedDateTime。在这种情况下,您不需要使用 LocalDateTime - user924
LocalDateTime.now().toInstant(OffsetDateTime.now().offset) 更容易。 - user924

7

为什么没有人提到 LocalDateTime.toEpochSecond() 方法:

LocalDateTime localDateTime = ... // whatever e.g. LocalDateTime.now()
long time2epoch = localDateTime.toEpochSecond(ZoneOffset.UTC);

这似乎比上面许多建议的答案要短得多...

这正是我在寻找的。那个被接受的答案让我感到不安。 - Brill Pappin
抱歉,它只在API 26中可用 :{ - Brill Pappin
6
因为它只提供了秒数。奇怪的是,考虑到你可以在LocalDateTime中指定纳秒,却没有“toEpochMilli”方法:有一个方法“LocalDateTime.of(int year, int month, int dayOfMonth, int hour, int minute, int second, int nanoOfSecond)”。 - izogfif
如果你只知道秒和毫秒之间的区别... - user924
问题是关于毫秒的,那个人说很奇怪没有人提到秒 :D - user924

6

这里有一些方法可供使用,但没有人在这里提到。但我不明白为什么它们不能起作用。

对于LocalDate,您可以使用toEpochDay()方法。 它返回自 1970 年 01/01 起的天数。 然后,该数字可以轻松转换为毫秒:

long dateInMillis = TimeUnit.DAYS.toMillis(myLocalDate.toEpochDays());

文档可以在这里找到。

对于LocalDateTime,您可以使用toEpochSecond()方法。它返回自1970年01月01日以来的秒数。该数字还可以转换为毫秒:

long dateTimeInMillis = TimeUnit.SECONDS.toMillis(myLocalDateTime.toEpochSeconds());

这方面的文档可以在这里找到。


5

对于LocalDateTime,我是这样做的:

LocalDateTime.of(2021,3,18,7,17,24,341000000)
    .toInstant(OffsetDateTime.now().getOffset())
    .toEpochMilli()

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接