应使用 Instant.until(instant2, ChronoUnit.MILLIS) 获取毫秒差,它直接返回 long 类型的精确毫秒值;Duration.between() 因内部存储格式会导致 toMillis() 截断亚毫秒部分,不适用于高精度场景。

Duration 不能直接计算两个 Instant 的毫秒差
Duration 本身不提供「从 Instant A 到 Instant B」的构造方法;它只是时间量的容器,比如「3秒」「500毫秒」。你真正需要的是 ChronoUnit.MILLIS.between() 或 Instant.until() —— 后者返回的才是 Duration,但前提是明确指定单位。
正确做法:用 Instant.until() 指定 MILLIS
最直观、不易出错的方式是调用 instant1.until(instant2, ChronoUnit.MILLIS),它直接返回 long 类型的毫秒差(支持负值),无需中间转 Duration:
Instant start = Instant.now(); // ... some operation Instant end = Instant.now(); <p>long millisDiff = start.until(end, ChronoUnit.MILLIS); // ✅ 直接得 long
- 如果只需要毫秒数,这是首选——避免了
Duration.toMillis()的精度截断风险 -
ChronoUnit.MILLIS是唯一能保证「精确到毫秒」的单位;SECONDS或MICROS都会改变量纲 - 注意:结果是
end - start,顺序反了会得负数,但这是符合直觉的
为什么不用 Duration.between()?
Duration.between(instant1, instant2) 确实存在,但它返回的是一个「尽可能规整」的 Duration 实例,内部可能以秒+纳秒形式存储,调用 .toMillis() 时会向下取整到毫秒,丢失亚毫秒部分:
Instant a = Instant.ofEpochSecond(0, 999_999); // 0.999999 秒 Instant b = Instant.ofEpochSecond(1, 0); // 1.0 秒 Duration d = Duration.between(a, b); // 实际存为 PT0.000001S(1 微秒) System.out.println(d.toMillis()); // 输出 0 ❌
- 这种截断在性能打点、高精度计时场景下会导致数据失真
-
Duration的设计目标是「人类可读的时间跨度」,不是「原始毫秒刻度」 - 若硬要用
Duration.between(),应改用.toNanos()再除以 1_000_000,但不如直接用until(..., MILLIS)干净
跨时区或系统时钟漂移是否影响结果?
不影响。Instant 是基于 UTC 的绝对时间轴上的点,所有运算都基于 System.nanoTime() 或 NTP 校准后的单调时钟,和本地时区、夏令时、LocalDateTime 完全无关:
- 两次
Instant.now()调用之间哪怕系统时间被手动修改,只要 JVM 进程没重启,until(..., MILLIS)仍保持单调递增(依赖底层System.nanoTime) - 但要注意:如果用
new Date().toInstant()这类方式构造Instant,而系统时钟不准,那起点/终点本身就有偏差——问题不在计算逻辑,而在输入源
毫秒差这件事,本质是两个 Instant 在时间轴上的投影距离,只要它们来自同一套单调时钟源,结果就可信。别被 Duration 的名字带偏去套用它。

















