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 依靠底层计算机硬件的时钟来了解当前时刻。

硬件时钟的分辨率变化很大。例如,如果特定计算机的硬件时钟仅支持microseconds 粒度,则任何生成的日期时间值都将只有六位小数秒,最后三位为零。 硬件时钟的准确度差异很大。仅仅因为时钟生成的值具有几位秒的十进制小数,这些数字可能不准确,只是近似值,与可能从atomic clock 读取的实际时间有偏差。换句话说,仅仅因为您在小数点右侧看到一堆数字并不意味着您可以相信这些读数之间经过的时间在该分钟程度上是真实的。

【讨论】:

它们可能会解析到纳秒,但它们仍然基于 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中的当前时间(以毫秒为单位)(只是时间,而不是日期)

如何像 Java 一样获取自 1970 年以来的当前时间戳(以毫秒为单位)

mongoDB 以微秒为单位获取查询的执行时间

如何使用 jQuery 获取当前时间

以毫秒为单位偏移当前系统时间到时区 GMT

c_cpp 以微秒为单位测量时间