使用java.time.Duration.between计算时间差抛出ZonedDateTime异常排查
问题:使用java.time计算时间戳与本地时间差值时抛出异常
我想计算毫秒级时间戳与本地当前时间的差值,尝试用标准java.time API实现,代码如下:
val eventTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(epochinMilis), ZoneId.systemDefault()) val now = LocalDateTime.now.atZone(ZoneId.systemDefault()) Duration.between(now, eventTime).toHours
执行时抛出如下异常:
java.time.DateTimeException: Unable to obtain ZonedDateTime from TemporalAccessor: 2023-06-26T22:26:36.917 of type java.time.LocalDateTime at java.base/java.time.ZonedDateTime.from(ZonedDateTime.java:566) at java.base/java.time.ZonedDateTime.until(ZonedDateTime.java:2130) at java.base/java.time.Duration.between(Duration.java:490)
改用以下代码可得到预期结果:
now = LocalDateTime.now.atZone(ZoneId.systemDefault()) (epochinMilis/1000 - now.toEpochSecond()) / 3600
我想知道使用标准API时出错的原因是什么?
错误原因分析
核心问题是**Duration.between要求两个入参必须是包含瞬时/时区信息的同类型时间对象**:
- 你的代码中,
eventTime是LocalDateTime类型——仅包含日期和时间,没有时区或偏移信息,属于“本地时间”,不代表某个确定的时刻; - 而
now是ZonedDateTime类型——包含时区信息,是一个确定的全球瞬时时刻; - 当
Duration.between尝试计算两者差值时,需要将两个对象转换为同一类型,但LocalDateTime没有时区信息,无法被转换为ZonedDateTime(同一本地时间在不同时区对应不同的瞬时时刻),因此抛出DateTimeException。
正确的标准API实现方式
只需将两个时间对象统一为包含瞬时信息的类型即可,推荐两种写法:
方式1:统一为ZonedDateTime
val eventTime = Instant.ofEpochMilli(epochinMilis).atZone(ZoneId.systemDefault()) val now = ZonedDateTime.now(ZoneId.systemDefault()) val hoursDiff = Duration.between(now, eventTime).toHours()
方式2:直接使用Instant(更高效,因为时间戳本质是瞬时值)
val eventInstant = Instant.ofEpochMilli(epochinMilis) val nowInstant = Instant.now() val hoursDiff = Duration.between(nowInstant, eventInstant).toHours()
内容的提问来源于stack exchange,提问作者dr jerry
相关产品推荐
相关产品推荐

