java中的当前时间(以微秒为单位)
Posted
技术标签:
【中文标题】java中的当前时间(以微秒为单位)【英文标题】:Current time in microseconds in java 【发布时间】:2010-12-15 07:04:13 【问题描述】:在 Unix 系统上,有没有办法在 Java 中获得微秒级精度的时间戳?类似于 C 的 gettimeofday
函数。
【问题讨论】:
请记住,计算机时钟并未设置为接近该精度水平 - 不同计算机上的两个时钟通常会相差至少几毫秒,除非您付出了一些努力建立一个高精度的同步程序(当这样的事情甚至在物理上是可能的时候)。我无法想象知道计算机的时间到微秒会有什么意义。 (如果您想在一台计算机上精确测量 间隔,那么当然,这是完全合理的,但您不需要完整的时间戳。) 另外,某些版本的 Unix/Linux 仅在微秒字段中返回 1 毫秒或 10 毫秒的粒度。 间接方式是通过JDBC,如果连接到以微秒为单位捕获当前时间的数据库(如 Postgres)。 Java 9 及更高版本:Instant.now()
使用 Instant 计算自 Epoch 以来的微秒数:val instant = Instant.now();
val currentTimeMicros = instant.getEpochSecond() * 1000_000 + instant.getNano() / 1000;
【参考方案1】:
不,Java 没有这种能力。
它确实有 System.nanoTime(),但这只是给出了与某个先前已知时间的偏移量。因此,虽然您无法从中获取绝对数字,但您可以使用它来测量纳秒(或更高)精度。
请注意,JavaDoc 表示虽然这提供了纳秒级精度,但这并不意味着纳秒级精度。所以取一些适当大的返回值模数。
【讨论】:
可以推测,Java 没有 getTimeInMicroseconds() 的原因包括 1) 精度在许多平台上不可用,2) 在微秒调用中返回毫秒精度会导致应用程序可移植性问题。 在 Linux 上,System.nanoTime() 调用 clock_gettime(CLOCK_MONOTONIC,_)。 Brian Oxley 挖掘 Java 的源代码来找到这个金块。 此答案现已过时。 Java 9 的 OpenJDK 和 Oracle 实现附带了Clock
的新实现,它提供了以小于毫秒的分辨率捕获当前时刻。在 MacBook Pro Retina 上,我得到以微秒为单位的当前时间(六位小数)。实际结果和准确性取决于主机的底层时钟硬件。
@BasilBourque 许多系统仍然在 Java 8 甚至更早版本上运行,因为不需要迁移它们。虽然答案已经过时,但它仍然有用。
@Dragas 实际上,将需要迁移它们……以获取此问题所要求的以微秒为单位的当前时间。【参考方案2】:
你可以使用System.nanoTime()
:
long start = System.nanoTime();
// do stuff
long end = System.nanoTime();
long microseconds = (end - start) / 1000;
以纳秒为单位获取时间,但这是一个严格的相对度量。它没有绝对的意义。它仅用于与其他纳米时间进行比较以衡量某件事需要多长时间。
【讨论】:
【参考方案3】:这是一个如何创建 UnsignedLong 当前时间戳的示例:
UnsignedLong current = new UnsignedLong(new Timestamp(new Date().getTime()).getTime());
【讨论】:
错误答案。 java.util.Date 类的分辨率为毫秒,而不是问题中指定的微秒。制作 java.sql.Timestamp 不会从空中提取额外的数据。【参考方案4】:正如其他海报已经指出的那样;您的系统时钟可能与实际世界时间不同步至微秒。尽管如此,微秒精度的时间戳还是有用的,可以作为指示当前墙上时间和测量/分析事物持续时间的混合体。
我使用诸如“2012-10-21 19:13:45.267128”之类的时间戳标记写入日志文件的所有事件/消息。这些既传达何时它发生(“墙”时间),也可用于测量日志文件中此事件与下一个事件之间的持续时间(相对差异)微秒)。
要实现这一点,您需要将 System.currentTimeMillis() 与 System.nanoTime() 链接,并从那时起专门与 System.nanoTime() 一起工作。示例代码:
/**
* Class to generate timestamps with microsecond precision
* For example: MicroTimestamp.INSTANCE.get() = "2012-10-21 19:13:45.267128"
*/
public enum MicroTimestamp
INSTANCE ;
private long startDate ;
private long startNanoseconds ;
private SimpleDateFormat dateFormat ;
private MicroTimestamp()
this.startDate = System.currentTimeMillis() ;
this.startNanoseconds = System.nanoTime() ;
this.dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS") ;
public String get()
long microSeconds = (System.nanoTime() - this.startNanoseconds) / 1000 ;
long date = this.startDate + (microSeconds/1000) ;
return this.dateFormat.format(date) + String.format("%03d", microSeconds % 1000) ;
【讨论】:
这个想法不错,但你必须时不时地重新同步。系统时间 (currentTime) 和 CPU 时钟 (nanoTime) 不同步,因为它们通常基于不同的硬件时钟。此外,如果可用,您的操作系统的时间服务器会将系统时钟与外部时间源重新同步。因此,您看似非常精确的课程会产生可能有偏差的时间戳! 该代码似乎不是线程安全的。两个同时调用MicroTimestamp.INSTANCE.get()
可能会出现问题。
@Ahmed 为什么你认为代码不是线程安全的? get()
方法中没有更新成员变量的内容;它只使用临时局部变量。【参考方案5】:
如果您打算将它用于实时系统,也许 java 不是获取时间戳的最佳选择。但是,如果您要使用 if 作为唯一键,那么 Jason Smith 的答案就足够了。但为了以防万一,要预计 2 个项目最终获得相同的时间戳(如果这 2 个项目几乎同时处理,则可能),您可以循环直到最后一个时间戳不等于当前时间戳。
String timestamp = new String();
do
timestamp = String.valueOf(MicroTimestamp.INSTANCE.get());
item.setTimestamp(timestamp);
while(lasttimestamp.equals(timestamp));
lasttimestamp = item.getTimestamp();
【讨论】:
【参考方案6】:如果您对 Linux 感兴趣: 如果您将源代码提取到“currentTimeMillis()”,您会看到,在 Linux 上,如果您调用此方法,它会返回微秒时间。然而,Java 会截断微秒并将毫秒返回给您。这部分是因为 Java 必须是跨平台的,因此提供专门为 Linux 提供的方法在当时是一个很大的禁忌(还记得从 1.6 向后的粗陋软链接支持吗?!)。这也是因为,虽然你的时钟可以在 Linux 中返回微秒,但这并不一定意味着它对检查时间有好处。在微秒级别,您需要知道 NTP 不会重新调整您的时间,并且您的时钟在方法调用期间没有漂移太多。
这意味着,理论上,在 Linux 上,您可以编写一个与 System 包中的相同的 JNI 包装器,但不会截断微秒。
【讨论】:
【参考方案7】:我最终采用的“快速而肮脏”的解决方案:
TimeUnit.NANOSECONDS.toMicros(System.nanoTime());
更新:
我最初使用 System.nanoTime,但后来我发现它只能用于经过的时间,我最终更改了我的代码以使用毫秒或在某些地方使用:
TimeUnit.MILLISECONDS.toMicros(System.currentTimeMillis());
但这只会在值的末尾添加零 (micros = millis * 1000)
将此答案留在这里作为“警告标志”,以防其他人想到 nanoTime :)
【讨论】:
doc 明确表示不使用System.nanoTime
作为挂钟时间:“此方法只能用于测量经过的时间,与任何系统或挂钟时间的其他概念。”
@BasilBourque 你是对的,写完这篇我最终发现并更改了我的代码但忘记在这里更新,感谢您的评论!【参考方案8】:
tl;博士
Java 9 及更高版本:捕获当前时刻时分辨率高达nanoseconds。那是 9 位小数。
Instant.now()
2017-12-23T12:34:56.123456789Z
限制为microseconds、truncate。
Instant // Represent a moment in UTC.
.now() // Capture the current moment. Returns a `Instant` object.
.truncatedTo( // Lop off the finer part of this moment.
ChronoUnit.MICROS // Granularity to which we are truncating.
) // Returns another `Instant` object rather than changing the original, per the immutable objects pattern.
2017-12-23T12:34:56.123456Z
实际上,.now
仅捕获微秒,因为nanoseconds 中的当代传统计算机硬件时钟并不准确。
详情
从 Java 8 开始,其他答案有些过时了。
java.time
Java 8 及更高版本带有java.time 框架。这些新类取代了早期 Java 版本中存在缺陷的、麻烦的日期时间类,例如 java.util.Date/.Calendar 和 java.text.SimpleDateFormat。该框架由 JSR 310 定义,受Joda-Time 启发,由 ThreeTen-Extra 项目扩展。
java.time 中的类解析为 nanoseconds,比旧日期时间类和 Joda-Time 使用的 milliseconds 好得多。并且比问题中提出的microseconds 更好。
Clock
实现
虽然 java.time 类支持以纳秒为单位表示值的数据,但这些类尚未生成以纳秒为单位的值。 now()
方法使用与旧日期时间类 System.currentTimeMillis()
相同的旧时钟实现。我们在 java.time 中有新的Clock
接口,但该接口的实现是相同的旧毫秒时钟。
因此,您可以格式化 ZonedDateTime.now( ZoneId.of( "America/Montreal" ) )
结果的文本表示形式,以查看小数秒的九位数字,但只有前三位数字具有如下数字:
2017-12-23T12:34:56.789000000Z
Java 9 中的新时钟
Java 9 的 OpenJDK 和 Oracle 实现具有新的默认 Clock
实现,具有更精细的粒度,最高可达 java.time 类的完整纳秒能力。
请参阅 OpenJDK 问题,Increase the precision of the implementation of java.time.Clock.systemUTC()。该问题已成功实施。
2017-12-23T12:34:56.123456789Z
在装有 macOS Sierra 的 MacBook Pro(Retina,15 英寸,2013 年末)上,我得到了以微秒为单位的当前时刻(最多六位小数)。
2017-12-23T12:34:56.123456Z
硬件时钟
请记住,即使使用新的更精细的Clock
实现,您的结果也可能因计算机而异。 Java 依靠底层计算机硬件的时钟来了解当前时刻。
【讨论】:
它们可能会解析到纳秒,但它们仍然基于 System.currentTimeMillis,所以它们只是在末尾添加了一堆 0。如果您试图以微/纳秒为单位获取时间,则根本没有帮助,用户可以手动将 0 添加到毫秒时间。 @annedroiderid 我在我的回答中提到了这一点。在 Java 8 中,您可以存储 纳秒分辨率的时刻,但捕获当前时刻 仅以毫秒为单位。在 Java 9 中使用Clock
的新实现进行了补救。即便如此,在 Java 8 中,java.time 类有助于保存由其他来源捕获的值,例如 Postgres 数据库中的微秒。但要注意微秒和纳秒范围内的值不准确;普通的计算机硬件可能无法准确地携带硬件时钟跟踪时间。六位或九位小数秒的值可能不正确。
不是ChronoUnit.MICROS吗?
@Roland 是的,现在已修复,谢谢。仅供参考,在 Stack Overflow 上,您被邀请直接编辑答案以修复此类错误。【参考方案9】:
Java 通过TimeUnit
枚举支持微秒。
这里是 java 文档: Enum TimeUnit
你可以通过这种方式在java中获得微秒:
long microsenconds = TimeUnit.MILLISECONDS.toMicros(System.currentTimeMillis());
您还可以将微秒转换回其他时间单位,例如:
long seconds = TimeUnit.MICROSECONDS.toSeconds(microsenconds);
【讨论】:
这是不正确的,你会得到 MICRO 分辨率/长度的时间,但时间本身总是只有 MILI 精度(意味着最后 3 位数字总是 0)。【参考方案10】:您也许可以创建一个组件来确定 System.nanoTime() 和 System.currentTimeMillis() 之间的偏移量,并有效地获得自纪元以来的纳秒。
public class TimerImpl implements Timer
private final long offset;
private static long calculateOffset()
final long nano = System.nanoTime();
final long nanoFromMilli = System.currentTimeMillis() * 1_000_000;
return nanoFromMilli - nano;
public TimerImpl()
final int count = 500;
BigDecimal offsetSum = BigDecimal.ZERO;
for (int i = 0; i < count; i++)
offsetSum = offsetSum.add(BigDecimal.valueOf(calculateOffset()));
offset = (offsetSum.divide(BigDecimal.valueOf(count))).longValue();
public long nowNano()
return offset + System.nanoTime();
public long nowMicro()
return (offset + System.nanoTime()) / 1000;
public long nowMilli()
return System.currentTimeMillis();
以下测试在我的机器上产生了相当好的结果。
final Timer timer = new TimerImpl();
while (true)
System.out.println(timer.nowNano());
System.out.println(timer.nowMilli());
差异似乎在 +-3ms 范围内波动。我想可以稍微调整一下偏移量计算。
1495065607202174413
1495065607203
1495065607202177574
1495065607203
...
1495065607372205730
1495065607370
1495065607372208890
1495065607370
...
【讨论】:
【参考方案11】:使用 Instant 计算自 Epoch 以来的微秒数:
val instant = Instant.now();
val currentTimeMicros = instant.getEpochSecond() * 1000_000 + instant.getNano() / 1000;
【讨论】:
【参考方案12】:LocalDateTime.now().truncatedTo(ChronoUnit.MICROS)
【讨论】:
以上是关于java中的当前时间(以微秒为单位)的主要内容,如果未能解决你的问题,请参考以下文章
获取Java中的当前时间(以毫秒为单位)(只是时间,而不是日期)