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

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

247012 次浏览

不,Java 没有这种能力。

它确实有 System.nanTime () ,但是这只是偏离了一些以前已知的时间。因此,虽然你不能从中得到绝对数字,但你可以用它来测量纳秒(或更高)的精度。

请注意,JavaDoc 表示,虽然这提供了纳秒级的精度,但这并不意味着纳秒级的精度。所以取一些适当大的返回值模数。

你可以使用 System.nanoTime():

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

得到纳秒级的时间,但这是一个严格的相对度量。它没有绝对的意义。它只有在与其他纳米时间进行比较时才有用,用来测量某件事情需要多长时间才能完成。

下面是如何创建 UnsignedLong 当前时间戳的示例:

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

正如其他海报已经指出,您的系统时钟可能不同步到微秒的实际世界时间。尽管如此,微秒精度时间戳作为指示当前壁时间和测量/分析事物持续时间的混合体是非常有用的。

我使用诸如“2012-10-2119:13:45.267128”这样的时间戳来标记写入日志文件的所有事件/消息。它们既可以传递发生的 什么时候(“墙”时间) ,也可以用来测量这个事件和日志文件中下一个事件之间的 持续时间(以微秒为单位的相对差)。

要实现这一点,需要将 System.currentTimeMillis ()与 System.nanTime ()链接起来,并从那一刻起专门使用 System.nanTime ()。示例代码:

/**
* 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) ;
}
}

如果您打算将其用于实时系统,那么 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();

如果你对 Linux 感兴趣: If you fish out the source code to "currentTimeMillis()", you'll see that, on Linux, if you call this method, it gets a microsecond time back. However Java then truncates the microseconds and hands you back milliseconds. This is partly because Java has to be cross platform so providing methods specifically for Linux was a big no-no back in the day (remember that cruddy soft link support from 1.6 backwards?!). It's also because, whilst you clock can give you back microseconds in Linux, that doesn't necessarily mean it'll be good for checking the time. At microsecond levels, you need to know that NTP is not realigning your time and that your clock has not drifted too much during method calls.

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

我最终选择了一种“快速而肮脏”的解决方案:

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

更新:

I originally went with System.nanoTime but then I found out it should only be used for elapsed time, I eventually changed my code to work with milliseconds or at some places use:

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

但这只会在值的末尾添加零(micros = milis * 1000)

把这个答案留在这里作为“警告信号”,以防其他人想到 nanTime:)

博士

Java9和更高版本: 当捕获当前时刻时,最高达到 纳秒分辨率。即小数部分的9位数。

Instant.now()

2017-12-23T12:34:56.123456789 Z

限制到 微秒截短

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.123456 Z

In practice, you will see only microseconds captured with .now as contemporary conventional computer hardware clocks are not accurate in 纳秒.

细节

其他的解答在 Java8中已经有些过时了。

爪哇时间

Java8和更高版本的 爪哇时间框架。这些新类取代了 Java 最早版本(如 Java.util)中带有缺陷的麻烦日期时间类。日期/。日历和 java.text。SimpleDateFormat.该框架由 JSR 310定义,受到 Joda 时间的启发,并由 ThreeTen-Ultra 项目进行了扩展。

Time 中的类解析为 纳秒,比旧的 date-time 类和 Joda-Time 使用的 毫秒要好得多。比问题中的 微秒要好。

enter image description here

Clock的实施

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

因此,你可以将 ZonedDateTime.now( ZoneId.of( "America/Montreal" ) )的结果的文本表示格式化为小数秒的9个数字,但是只有前3个数字会有这样的数字:

2017-12-23T12:34:56.789000000Z

Java9中的新时钟

Java 9的 OpenJDK 和 Oracle 实现有一个新的默认 Clock实现,具有更细的粒度,可以达到 Java.time 类的完全纳秒级功能。

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

2017-12-23T12:34:56.123456789Z

在配备 MacOS Sierra 的 MacBook Pro (Retina,15英寸,2013年底)上,我以微秒为单位(小数部分最多可达6位)获得当前时刻。

2017-12-23T12:34:56.123456Z

五金钟

请记住,即使使用新的更好的 Clock实现,您的结果也可能因计算机而异。Java 依赖于底层计算机硬件的时钟来知道当前时刻。

  • 硬件时钟的 resolution变化很大。例如,如果一台特定计算机的硬件时钟只支持 微秒粒度,那么生成的任何日期时间值将只有六位小数秒,最后三位为零。
  • 硬件时钟的 accuracy变化很大。仅仅因为一个时钟产生一个数位小数分数为一秒的值,这些数字可能是不准确的,只是近似值,从实际时间漂移,因为可以从 原子钟原子钟读取。换句话说,仅仅因为你看到小数点右边的一串数字,并不意味着你可以相信这些读数之间的间隔时间是真实的。

Java 通过 TimeUnit枚举支持微秒。

下面是 java 文档: 枚举时间单元

You can get microseconds in java by this way:

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

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

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

您可以创建一个组件来确定 System.nanTime ()和 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
...

使用 Instant 计算新纪元以来的微秒:

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