使用Java从微秒创建DateTime Instant

7

自Java 9以来,Java日期和时间API发生了变化。LocalDateTime现在具有微秒精度。

Java 9实现了全新的java.time.Clock,能够以毫秒(三位小数)以下的分辨率捕获当前时刻。

我们从后端服务获取微秒级的时间。

System.currentTimeMillis  > 1565245051795    > 2019-08-08T06:17:31.795
Service.getTime           > 1565245051795306 > 2019-08-08T06:17:31.795306

为了构建用于我们应用程序的LocalDateTime,我们需要执行以下操作:

long timeMicros = service.getTime();
long timeMillis = timeMicros / 1000;
LocalDateTime ldt = Instant.ofEpochMilli(timeMillis).atZone(ZoneId.systemDefault()).toLocalDateTime();

为了查询服务,我们需要再次使用时间微秒,然后执行以下操作:

long timeMillis = dateTime.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
long timeMicros = timeMillis * 1000;

问题是我们没有得到微秒精度的时间。
是否可以创建一个具有微秒精度的Instant?
我们现在正在使用Java 11,当我们的JUnit测试因增加了微秒精度而失败时,我注意到了这个变化。
对于JUnit测试,我找到了一个解决方法:
private static final LocalDateTime START = LocalDateTime.now().truncatedTo(ChronoUnit.MILLIS);

我不确定这是一个变通方法还是一个实际的解决方案,但似乎将时间戳的最后三个微秒数字添加上去就可以了。

long micros = 306L; //TODO get the last three digits from the timeMicros
ldt.plus(micros, ChronoUnit.MICROS));

5
使用Instant.ofEpochSecond()代替Instant.ofEpochMilli()。用两个参数来表示从纪元开始的秒数和额外的纳秒数。 - kumesana
这是一个可能性,但如何从timeMicros长整型中获取epochSeconds和nanoAdjustment的值呢? - DJViking
秒:将timeMicros除以一百万。纳秒:通过对timeMicros取模获得微秒数,然后将结果乘以一千。 - kumesana
3
顺便说一下,以这种方式使用LocalDateTime通常 不是 您想要做的。使用ZonedDateTime表示一个时刻,时间线上的特定点,因为LocalDateTime根据定义不能表示。我无法想象任何情况下调用LocalDateTime.now()是正确的做法。请参阅:Instant和LocalDateTime之间的区别是什么? - Basil Bourque
1个回答

11
    long timeMicros = 1_565_245_051_795_306L;
    Instant i = Instant.EPOCH.plus(timeMicros, ChronoUnit.MICROS);
    System.out.println(i);

输出结果为:

2019-08-08T06:17:31.795306Z

编辑: 我更喜欢使用内置的微秒支持而不是除以和乘以来转换微秒为毫秒和/或秒。此外,当显式将它们添加到时代时,感觉有点手动。

你已经知道如何将 Instant 转换为 LocalDateTime,在问题中已经展示了,所以我不会重复。

编辑:

你有解决方案可以从 Instant 中获取 timeMicros 吗?

有几种选择。这样计算就不那么复杂了,所以我可能会这样做:

    long microsBack = TimeUnit.SECONDS.toMicros(i.getEpochSecond())
            + TimeUnit.NANOSECONDS.toMicros(i.getNano());
    System.out.println(microsBack);

1565245051795306

为了更符合第一个转换的风格,您可能更喜欢稍微缩短一点:

    long microsBack = ChronoUnit.MICROS.between(Instant.EPOCH, i);

编辑: 可能有点儿吹毛求疵,但也是为了避免任何人误解:LocalDateTime 总是具有纳秒精度。仅在 Java 8 中,now 方法具有毫秒精度。我在某处读到从Java 9开始,精度会因平台而异,但你是正确的,微秒精度似乎很常见。


一个好的解决方案。那么为什么Instant不使用相同的EPOCH常量来创建一个ofEpochMilli的Instant呢?它是使用Math类来创建一个具有秒和nanoOfSecond的Instant。 - DJViking
你有办法从Instant中获取timeMicros吗? - DJViking
1
我在答案末尾添加了几个选项,可以将其转换回微秒。至于为什么没有以类似的方式实现ofEpochMilli,我不知道。这可能是出于效率考虑,另一方面,我并不一定相信该实现是最有效的,所以我不确定。 - Ole V.V.
或许 ofEpochMilli 是在项目早期编写的,还没有 ChronoUnit 枚举,然后后来就没有更改。 - Ole V.V.

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