Java 8 LocalDateTime.now() 只提供毫秒级精度

24

在Java 8中能否获取微秒?Java 8 LocalDateTime类有一个.getNano()方法可以返回纳秒,但在Linux(Ubuntu)和OS X(10.11.5)中,在这两个操作系统上它只返回毫秒(当我运行它时,它返回301000000,相当于301毫秒),而我真的需要能够获取微秒

我知道在我的计算机上可以获取纳秒(从而获取微秒),因为javascript方法process.hrtime()返回精确的值。

在任何人开始精确与准确争论之前,我知道纳秒在线程之间完全不可靠,不应用于比较。

编辑

要明确的是,LocalDateTime类是Java 8 java.time类集的一部分。

更新

所以我意识到Javascript的process.hrtime类似于Java的System.nanoTime(),实际上与挂钟无关,它是自某些任意值以来的时间,这些值在两种语言之间是不同的。

新问题:是否有一种方法可以从这些值中解析时钟时间?也就是说,如果我获取System.currentTimeMillis()System.nanoTime(),并将其与另一组这些值进行比较,我能否获得第二组值的实际时间?

我的问题是我需要使用Java和Javascript进行记录,它们需要在两者之间具有一致的微秒字段。


https://dev59.com/f1wX5IYBdhLWcg3wtBbG - Jens
1
那篇帖子最初发布于2009年,更新的答案是我遇到问题的解决方案。我正在寻找不同的东西。 - annedroiid
https://docs.oracle.com/javase/8/docs/api/java/time/Instant.html#now-- 是获取最精确时间戳的最佳方法。 - Oleg Estekhin
3个回答

60

简短概述

在Java 8中是否能获取微秒?

不行,在Java 8中不能。请使用Java 9或更高版本。

Instant.now()  // Returns a value in microseconds in Java 9 and later, but is restricted to mere milliseconds in Java 8.

这指的是基于OpenJDK开源代码实现的Java。其他可能会有所不同。
Java 9及以后版本基于OpenJDK实现,具有全新的实现 java.time.Clock,能够以比毫秒(位小数)更高的分辨率捕获当前时刻。
实际分辨率取决于主机计算机硬件时钟的限制。在macOS Sierra上使用Oracle Java 9.0.4,我得到的当前时刻是微秒级别的(位小数)。
Instant.now().toString()

2018-03-09T21:03:33.831515Z

Java 8

Java 8新增了java.time类。这些类被定义为可以容纳纳秒(小数点后九位)。但是在Java 8中,捕获当前时刻仅限于毫秒,在Java 9中得以提升,以更细的微秒精度捕获当前时刻。

2018-03-09T21:03:33.831Z

其他问题

System.currentTimeMillis()

如果我得到System.currentTimeMillis()和System.nanoTime(),以后就不需要再使用System.currentTimeMillis()了。相反,可以使用java.time.Instant获取UTC时刻,并具有纳秒级的分辨率。
如果确实需要从1970-01-01T00:00Z这个参考时刻开始计算毫秒数,请询问Instant对象。请注意数据丢失,因为您将忽略Instant中存在的任何微秒或纳秒。
long millisSinceEpoch = instant.now().toEpochMilli() ;

有没有办法从这些值中解析出时钟时间?
是的,您可以将自1970-01-01T00:00Z时代以来的毫秒数转换为Instant。
Instant instant = Instant.ofEpochMilli( millisSinceEpoch ) ;

System.nanoTime()

关于 System.nanoTime(),它旨在用于跟踪经过的时间,例如基准测试代码的性能。调用 System.nanoTime() 不会告诉您任何有关当前日期时间的信息。
该值是从某个未公开的起始时间点开始计算的纳秒数。实际上,我看到这个数字似乎跟踪自JVM启动以来的时间,但这种行为没有记录,因此您不应依赖它。 LocalDateTime 不是一个瞬间
“我的问题是我需要使用Java和Javascript进行日志记录,并且它们需要具有一致的微秒字段。”
首先,对于日志记录,您不应该使用 LocalDateTime 类。该类故意缺乏任何时区或偏移量的概念。因此,LocalDateTime 不表示一个瞬间,不是时间轴上的一个点。 LocalDateTime 是关于大约26-27小时范围内潜在时刻的想法。仅在时区/偏移量未知(不是很好的情况),或者如果这代表类似于“圣诞节从2018年12月25日的第一个时刻开始”,其中圣诞节在全球各地的不同区域的不同时刻开始,从太平洋地区向西逐渐推进,每个午夜后都会开始。
记录日志时应该使用UTC时间。在Java中,这将是Instant类,根据定义始终为UTC。只需调用Instant.now()
当序列化为文本(例如用于记录日志)时,始终使用标准的ISO 8601格式。当解析/生成字符串时,java.time类默认使用这些标准格式。在本答案中您可以看到示例。
请参见另一个问题,Instant和LocalDateTime之间有什么区别?

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

ISO 8601

在ISO 8601标准中,一秒钟的小数部分可以有任意数量的数字。因此,您不必关心记录事件是以毫秒、微秒还是纳秒为单位。

Instant instant = Instant.parse( "2018-03-09T21:03:33.123456789Z" ) ;
Instant instant = Instant.parse( "2018-03-09T21:03:33.123456Z" ) ;
Instant instant = Instant.parse( "2018-03-09T21:03:33.123Z" ) ;

截断

如果您确实认为需要统一的分辨率,您可以截断 Instant

Instant instant = Instant.now().truncatedTo( ChronoUnit.MILLIS ) ; // Strip away any microseconds or nanoseconds.

不必担心分辨率问题

我的问题是,我需要使用Java和Javascript进行日志记录,并且它们需要在微秒级别上保持一致的时间戳。

首先,我怀疑您是否真的需要关注这个问题。如果您使用标准的ISO 8601格式以及Java中的Instant类,您可以成功地将一个时间点序列化和恢复到毫秒、微秒或纳秒。

而且,即使小数秒分辨率不同,ISO 8601格式化的字符串也会按照时间顺序方便地按字母顺序排列。

其次,如果您正在尝试以微秒为单位跟踪实际时刻,您可能会感到失望。截至2018年,传统计算机时钟在微秒范围内不可靠。


关于java.time

java.time框架是Java 8及以上版本内置的。这些类取代了老旧的legacy日期时间类,如java.util.DateCalendarSimpleDateFormat

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

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

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

如何获取java.time类?


11
多么出色的回答! - Hannes Schneidermayer
一个优秀而全面的答案。学到了很多! - Kervvv
在Android上,您仍将从Desugared java.time实现中仅获得毫秒精度的后代记录:https://github.com/google/desugar_jdk_libs/blob/be076b13963efbfca6137a56b155398533df53aa/jdk11/src/java.base/share/classes/java/time/Clock.java#L477 - Selali Adobor

4

LocalDate.now() 依赖于 SystemClock::instant() 方法,该方法使用 System.currentTimeMillis(),因此默认时钟无法获得更精确的分辨率。

但是,您可以实现自己的高精度 Clock 并与 LocalDate 一起使用:

LocalDate hpDate = LocalDate.now(microsecondClock);

为了提高精度,你可以使用具有微秒刻度的 TickClock
Clock microsecondClock = Clock.tick(Clock.systemUTC(), Duration.ofNanos(1000));

或者继承Clock并实现自己的高精度时钟,例如使用System.currentTimemillis()和System.nanoTime()。


我该如何实现自己的时钟? - annedroiid
class YourClock extends Clock { .... } 这里假设有多种方法可以实现,其中一种是运行一个计时器线程,在两次 System.currentTimeMillis() 时间测量之间计算循环迭代次数,从而推导出该时间间隔内的微秒或纳秒数。但请记住,你获得的精度越高,它的准确性就会越低。 - Gerald Mücke
我需要使用微秒/纳秒来发送日志,并将其与从JavaScript项目发送的日志进行比较,因此我需要能够获取系统范围内的微秒值。 - annedroiid

2
当你的计算机能够报告比毫秒更精确(但可能不准确,至少在壁钟时间上不准确)的东西时,这并不改变Java默认使用基于System.currentTimeMillis()Clock的事实。
你必须提供一个更精确的Clock才能获得比毫秒更精确的值。也就是说,在Java 9之前,需要这样做。

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