Java里的时间精度陷阱:那一次被毫秒“坑惨”的经历

203 阅读5分钟



“小米哥,你不加班啦?”

“嘿嘿,今晚不写代码,写点文章放松放松~”

在Java的世界里,LocalDateTime 是个神奇的家伙。它看上去干干净净、温文尔雅,却常常因为一个“小细节”,让我们这些搬砖工崩溃大喊:“怎么又出bug了?!”

今天我们就来聊聊这个隐藏很深但超实用的知识点——如何清除掉 LocalDateTime 的毫秒精度?

而这个话题的源头,要从我前不久踩的一个坑说起。

被“毫秒”坑惨的那一次

那天是个周五的晚上,我像往常一样,准备下班前提交最后一行代码。我们团队正在开发一个定时任务平台,需要比对两个时间,判断任务是否该执行。

我的代码逻辑很简单:任务的执行时间是 LocalDateTime 类型,只要现在的时间大于等于这个时间,就可以执行:

看起来没毛病,对吧?

可奇怪的事情发生了。有个任务死活不跑,我看了日志,当前时间明明是:

而任务的时间是:

你能看出问题了吗?就是那可恶的 .123 ——123毫秒

因为这个毫秒级别的差异,isAfter 变成了 false,任务就卡在那儿不执行了!

这不是 bug,是 feature!

你或许会问:为啥 Java 要保留毫秒啊?我不需要这么精准的时间,只想对到“秒”!

其实,LocalDateTime 默认的精度是纳秒(yes,纳米秒...),而我们平时写的:

它底层调用的是系统时钟(System.currentTimeMillis 或 Clock.systemUTC()) ,这些都是带着“毫秒”甚至“纳秒”的。

于是,你以为的:

实际上是:

这就是 bug 背后的“feature”——默认情况下,Java 可是时间精准狂魔

老大一句话:把精度“砍掉”!

就在我郁闷地准备调日志的时候,老大淡定地说了一句:

“把毫秒砍掉,不就好了?”

听起来轻松,但怎么砍?这是LocalDateTime,不是Date,也不是Calendar,不像以前直接 setMillis(0) 那么简单。

我陷入了沉思,然后开始一番搜索、测试、实践……最终,找到了几种常用又优雅的“时间净化术”。

清除毫秒的几种方法(亲测有效)

方法一:使用 truncatedTo(ChronoUnit.SECONDS)

这是我目前最推荐的一种方式,优雅、直观、语义清晰

输出示例:

truncatedTo 的作用就是——保留指定单位,更小单位全部清零

所以 ChronoUnit.SECONDS 会保留“秒”,但毫秒、微秒、纳秒统统变成0。

✅ 适用场景:

  • 你只想对比时间到“秒”的精度;
  • 使用 Java 8+,不怕新 API;
  • 想要最清晰的代码表达。

方法二:手动设置时间字段(暴力但直白)

适合初学者或不想引入额外 API 的同学:

withNano(0) 表示把纳秒字段设为0,相当于“清除毫秒+微秒+纳秒”。

虽然它看起来没有 truncatedTo 语义那么清晰,但胜在简单粗暴,效果一样。

方法三:通过 DateTimeFormatter 格式化并解析(不推荐)

如果你非要“格式化再解析”,代码会长这样:

虽然也能“清除毫秒”,但:

  • 性能差;
  • 多了一次字符串转换;
  • 可读性差。

所以,我个人不推荐这种写法,除非你正在和字符串打交道。

场景复盘:如何用“清除毫秒”救活定时任务

当我意识到任务无法执行是因为“毫秒精度”后,我立刻调整了代码:

你可能注意到了,我不仅对 now 进行了清理,也对 executeTime 做了清理。

因为你永远不知道那个字段是不是数据库或外部系统存进来的“带毫秒”的时间。

这一次,任务果然“准时”地被执行了!

还有哪些地方需要注意精度问题?

除了定时任务比对,其实在以下场景中,毫秒也是“潜在炸弹”

1. 缓存的失效时间比较

比如你设置了一个缓存对象失效时间为:

但你如果在比较的时候忘了清除毫秒:

2. 日志归档、任务调度

很多日志系统以“秒”为单位进行文件划分,毫秒可能导致同一条日志被划分到错误的文件。

3. 前端传时间参数时忽略毫秒

假如前端传给你的时间是:

但你后台的比较时间是:

这会导致前后端时间不一致、判断逻辑异常

所以最好的方式是:

统一时间格式、统一时间精度,双方都只保留到秒级。

最佳实践总结

你可以把下面这张表收藏下来,用于项目中统一规范:

写在最后:千万别让“毫秒”误了你的正事

Java 里的时间 API 越来越强大,但越强大,就越要注意它的“细节陷阱”。

就像这次的毫秒问题,它看似微不足道,却可能让你的定时任务不执行、缓存提前失效,甚至造成前后端对时间的理解出现差异。

最后送你一句我最喜欢的话:

程序员的世界,从不怕复杂,怕的是不明白的复杂

希望这篇文章能帮你彻底搞清楚 LocalDateTime 的精度问题,下次再遇到奇怪的时间行为时,第一时间就知道往哪里查!

END

我是小米,一个喜欢分享技术的31岁程序员。如果你喜欢我的文章,欢迎关注我的微信公众号“软件求生”,获取更多技术干货!