为什么我设置的 23:59:59,数据库却存成了第二天的 00:00:00?

124 阅读3分钟

沉默是金,总会发光

大家好,我是沉默

昨天下班前,运营同学抛来一个灵魂拷问:

“用户黑名单设置 2 天后解封,为什么提示是 第 3 天 才能恢复?”

比如 6 月 16 日被拉黑,应该在 6 月 18 日 23:59:59 自动解封,但系统却显示 6 月 19 日 00:00:00

一秒之差,却让用户多等了一天,属实离谱。

更诡异的是——数据库里有 一半数据对的(23:59:59) ,另一半却是 第二天的 00:00:00。这下真让人百思不得其解 🤯。

**-**01-

排查过程

排查思路可以总结成三步走:

第一步:排除逻辑错误

确认了代码里只有一处写 DeblockTime,没有被覆盖。

核心逻辑

LocalDateTime currentTime = LocalDateTime.now();LocalDateTime futureTime = currentTime.plus(2, ChronoUnit.DAYS);LocalDateTime resultTime = futureTime.withHour(23).withMinute(59).withSecond(59);entity.setDeblockTime(    Date.from(resultTime.atZone(ZoneId.systemDefault()).toInstant()));

看似没毛病,为什么一半会变成 第二天 00:00:00

第二步:批量插入复现

写了个小 demo,每隔 100ms 插入一次。结果一眼就发现:

  • 一半数据是 23:59:59
  • 另一半变成 次日 00:00:00

bug 完美复现

第三步:锁定真凶

原来罪魁祸首是 —— Java 的毫秒 vs 数据库的秒精度差异

  • java.util.Date 精度:毫秒(1/1000 秒)
  • Postgres/MySQL 默认 timestamp 精度:秒
  • 入库时如果毫秒 ≥ 500,就会 四舍五入 进 1 秒 → 变成第二天 00:00:00

就像“本来应该关门的地铁,偏偏被最后一个挤上来的秒针硬生生推迟到下一站”。

- 02-

解决方案

这类问题的关键是:对齐 Java 和数据库的时间精度

方案 1:清空毫秒(推荐)

futureTime.withHour(23).withMinute(59).withSecond(59).withNano(0);

确保毫秒恒为 000,入库就不会四舍五入。

方案 2:调整数据库精度

把数据库的 timestamp 设置成毫秒或微秒精度(如 timestamp(3) 或 timestamp(6)),和 Java 对齐。

- 03-

知识扩展(必备面试点)

Java 时间类对比

特性java.util.DateLocalDateTime
精度毫秒纳秒
可变性可变不可变
时区感知不存储时区无时区(需结合 ZonedDateTime
线程安全
推荐程度⭐⭐⭐⭐⭐

MySQL DATETIME vs TIMESTAMP

特性DATETIMETIMESTAMP
范围1000-9999 年1970-2038 年
精度可到微秒 (6)可到微秒 (6)
时区有(存 UTC,取时转会话时区)
存储大小8 字节4 字节
适用场景历史事件/本地时间跨时区/自动更新时间

**-**04-

总结

  • 这次 bug 的本质,是 Java 的毫秒精度 + 数据库的秒精度 → 四舍五入进位

  • 解决办法有两种:清零毫秒 或 提升 DB 精度

  • 扩展知识里,掌握 Java 时间类 & MySQL 时间类型对比,是面试必考点

一句话 takeaway
在时间处理上,不要轻视“1 秒” ,它可能让你的业务逻辑整个跑偏一天!
问题来了:
如果你在实际业务里,遇到过  “1 秒 bug” ,是怎么排查出来的?

**-**05-

粉丝福利

点点关注,送你互联网大厂面试题库,如果你正在找工作,又或者刚准备换工作。可以仔细阅读一下,或许对你有所帮助!

image.png

image.pngimage.png