Java中的当前时间(以微秒为单位)


104

在Unix系统上,是否有办法在Java中获得具有微秒级精度的时间戳?类似于C的gettimeofday函数。


6
请记住,计算机时钟的设置不会达到该精度水平附近的任何地方-不同计算机上的两个时钟通常会相差至少几毫秒,除非您花了一些力气来设置高精度的同步过程(当这样的事情在物理上甚至是可能的时候)。我无法想象知道计算机的时间到微秒会有什么意义。(如果您要尝试在一台计算机上精确地测量一个间隔,那么可以肯定,这是完全合理的,但是您不需要完整的时间戳。)
David Z

2
同样,某些版本的Unix / Linux在微秒字段中仅返回1毫秒或10毫秒粒度。
Stephen C

如果连接到诸如Postgres之类的数据库(以微秒为单位捕获当前时间),则间接方法是通过JDBC
罗勒·布尔克

2
Java 9及更高版本:Instant.now()
Basil Bourque's

使用即时来计算自纪元以来的微秒:val instant = Instant.now(); val currentTimeMicros = instant.getEpochSecond() * 1000_000 + instant.getNano() / 1000;
Michael M.

Answers:


148

不,Java没有该功能。

它确实具有System.nanoTime(),但是仅提供了一些先前已知时间的偏移量。因此,虽然您不能从中获取绝对数字,但可以使用它来测量纳秒(或更高)的精度。

请注意,JavaDoc表示,尽管这提供了纳秒级的精度,但这并不意味着纳秒级的精度。因此,取一些适当大的返回值模量。


3
可以猜想Java没有getTimeInMicroseconds()的原因包括:1)许多平台上没有精度; 2)在微秒调用中返回毫秒精度会导致应用程序可移植性问题。
Stephen C

9
在Linux上,System.nanoTime()调用clock_gettime(CLOCK_MONOTONIC,_)。布莱恩·奥克斯利(Brian Oxley)挖掘了Java的源代码以找到这个块。
David Weber 2014年

7
这个答案现在已经过时了。Java 9的OpenJDK和Oracle实现附带一个新的实现,Clock该实现以小于毫秒的分辨率捕获当前时刻。在MacBook Pro Retina上,我正在获取当前时间(以微秒为单位)(十进制的六位数)。实际结果和准确性取决于主机的基础时钟硬件。
罗勒·布尔克

1
@BasilBourque许多系统仍在Java 8或更早的版本上运行,因为不需要迁移它们。虽然答案已经过时,但仍然有用。
Dragas

1
@Dragas其实,有是一个需要迁移他们......让微秒当前时间问这个问题。
罗勒·布尔克

78

tl; dr

Java 9和更高版本:捕获当前时刻时,分辨率高达纳秒。那是9位数的小数。

Instant.now()   

2017-12-23T12:34:56.123456789Z

要限制为微秒,请截断

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现代传统计算机硬件时钟捕获的仅几微秒,而纳秒精度不高。

细节

从Java 8开始,其他答案有些过时了。

java.time

Java 8和更高版本附带了java.time框架。这些新类取代了Java最早版本(例如java.util.Date/.Calendar和java.text.SimpleDateFormat)附带的有缺陷的麻烦的日期时间类。该框架由受Joda-Time启发,由ThreeTen-Extra项目扩展的JSR 310定义。

java.time中的类解析为纳秒,比旧的日期时间类和Joda-Time所使用的毫秒要细得多。并且比问题中要求的微秒更好。

在此处输入图片说明

Clock 实作

尽管java.time类支持表示以纳秒为单位的值的数据,但这些类尚未生成以纳秒为单位的值。这些now()方法使用与旧的日期时间类相同的旧时钟实现System.currentTimeMillis()。我们Clock在java.time中具有新接口,但是该接口的实现是相同的旧毫秒时钟。

因此,您可以设置结果的文本表示的格式,ZonedDateTime.now( ZoneId.of( "America/Montreal" ) )以查看小数秒的9位数字,但是只有前3位数字会具有以下数字:

2017-12-23T12:34:56.789000000Z

Java 9中的新时钟

Java 9的OpenJDK和Oracle实现具有Clock粒度更细的新默认实现,最大可达java.time类的纳秒级能力。

请参见OpenJDK问题,提高java.time.Clock.systemUTC()的实现精度。该问题已成功实施。

2017-12-23T12:34:56.123456789Z

在装有macOS Sierra的MacBook Pro(15英寸,2013年末,视网膜)上,我得到的当前时刻以微秒为单位(最多十进制的六位数)。

2017-12-23T12:34:56.123456Z

硬件时钟

请记住,即使采用新的更精细的Clock实现,您的结果也可能因计算机而异。Java依靠底层计算机硬件的时钟来了解当前时刻。

  • 硬件时钟的分辨率差异很大。例如,如果特定计算机的硬件时钟仅支持微秒级的粒度,则任何生成的日期时间值将仅具有小数秒的六位数字,最后三位为零。
  • 硬件时钟的精度差异很大。仅仅因为时钟生成的值具有几分之一秒的小数位,所以这些数字可能不准确,仅是近似值,会从原子钟读取的实际时间中漂移。换句话说,仅仅因为您看到小数点右边的一堆数字并不意味着您可以相信两次读数之间的经过时间是正确的。

它们可能解析为纳秒,但仍基于System.currentTimeMillis,因此它们仅在末尾添加一堆0。如果您试图以微秒/毫微秒为单位的时间完全没有帮助,则用户可以手动将毫秒添加0。
annedroiid

@annedroiid我在答案中提到了这一点。在Java 8中,您可以存储具有纳秒分辨率的力矩,但是捕获当前力矩仅需毫秒。使用Java 9的新实现在Java 9中进行了修复Clock。即便如此,在Java 8中,java.time类对于保留其他来源(例如,微秒)在Postgres数据库上捕获的值也很有帮助。但是要注意,微秒和纳秒范围内的值不准确;常见的计算机硬件可能无法准确地跟踪硬件时钟。六位数或九位数的小数秒的值可能不正确。
罗勒·布尔克


@Roland是的,现在修复,谢谢。仅供参考,在“堆栈溢出”中,您被邀请直接编辑答案以解决此类错误。
罗勒·布尔克

55

您可以使用System.nanoTime()

long start = System.nanoTime();
// do stuff
long end = System.nanoTime();
long microseconds = (end - start) / 1000;

可以获取时间(以纳秒为单位),但这是一个严格的相对度量。它没有绝对的意义。它仅用于与其他纳米时间进行比较以测量某项操作需要多长时间。


14

正如其他海报已经指出的那样;您的系统时钟可能未与实际世界时间同步最多微秒。尽管如此,微秒级精确时间戳还是可以用作混合信号,用于指示当前的墙壁时间以及测量/分析事物的持续时间。

我使用“ 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) ;
   }
}

5
这个想法很好,但是您必须不时地重新同步。系统时间(currentTime)和CPU时钟(nanoTime)不同步,因为它们通常基于不同的硬件时钟。此外,操作系统的时间服务器(如果有)将系统时钟与外部时间源重新同步。因此,您看似非常精确的课程将产生可能很遥远的时间戳!
h2stein 2014年

3
该代码似乎不是线程安全的。同时调用两个MicroTimestamp.INSTANCE.get()可能会出现问题。
艾哈迈德(Ahmed)

@Ahmed为什么您认为代码不是线程安全的?该get()方法中没有更新成员变量的内容。它仅使用临时局部变量。
贾森·史密斯

6

您可能会创建一个组件,该组件确定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
...

2

如果您对Linux感兴趣:如果将源代码输出到“ currentTimeMillis()”,则会看到,在Linux上,如果调用此方法,则可以返回一微秒的时间。但是,Java随后会截断微秒,并将您递回毫秒。部分原因是因为Java必须是跨平台的,因此提供专门用于Linux的方法在今天是不容错过的事情(还记得从1.6反向支持粗鲁的软链接吗?!)。这也是因为,尽管您的时钟可以在Linux中为您提供微秒级的信息,但这并不一定意味着它可以很好地检查时间。在微秒级别,您需要知道NTP不会重新调整您的时间,并且在方法调用期间时钟的漂移不会太大。

从理论上讲,这意味着在Linux上,您可以编写与System软件包中的JNI包装相同的JNI包装,但不能截断微秒。


2

我最终使用的“快速而肮脏的”解决方案:

TimeUnit.NANOSECONDS.toMicros(System.nanoTime());

更新:

我最初使用System.nanoTime,但是后来我发现它仅应用于经过的时间,最终我更改了代码以使其可以使用毫秒或在某些地方使用:

TimeUnit.MILLISECONDS.toMicros(System.currentTimeMillis());

但这只会在值的末尾添加零(微秒=毫秒* 1000)

如果有人想到nanoTime,则将此答案留在此处作为“警告信号” :)


1
文档明确表示不要使用System.nanoTime挂钟时间:“此方法只能用于测量经过时间,并且与系统或挂钟时间的任何其他概念无关。”
罗勒·布尔克

1
@BasilBourque您是正确的,写完此后,我最终发现并更改了我的代码,但忘了在此处更新,谢谢您的评论!
keisar,2015年

2

Java通过TimeUnit枚举支持微秒。

这是Java文档: Enum TimeUnit

您可以通过以下方式在Java中获得微秒:

long microsenconds = TimeUnit.MILLISECONDS.toMicros(System.currentTimeMillis());

您还可以将微秒转换回另一个时间单位,例如:

long seconds = TimeUnit.MICROSECONDS.toSeconds(microsenconds);

这是不正确的,您将获得以MICRO分辨率/长度为单位的时间,但是时间本身始终仅具有MILI精度(这意味着最后3位始终为0)。
Michalsx

0

如果打算将其用于实时系统,那么java并不是获取时间戳的最佳选择。但是,如果您要使用if作为唯一密钥,那么Jason Smith的答案就足够了。但是,以防万一,可以预期2个项目最终获得相同的时间戳(如果这两个项目几乎同时被处理,则可能会循环播放),直到最后一个时间戳与当前时间戳不相等为止。

String timestamp = new String();
do {
    timestamp = String.valueOf(MicroTimestamp.INSTANCE.get());
    item.setTimestamp(timestamp);
} while(lasttimestamp.equals(timestamp));
lasttimestamp = item.getTimestamp();

0

使用即时计算自纪元以来的微秒:

val instant = Instant.now();
val currentTimeMicros = instant.getEpochSecond() * 1000_000 + instant.getNano() / 1000;


-3

这是一个如何创建UnsignedLong当前时间戳的示例:

UnsignedLong current = new UnsignedLong(new Timestamp(new Date().getTime()).getTime());

2
不正确的答案。java.util.Date类的分辨率为毫秒,而不是Question中指定的微秒。制作java.sql.Timestamp不会浪费额外的数据。
罗勒·布尔克
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.