昨天下班的时候,运营反馈了一个问题,明明设置的是两天后解封,为什么提示却是三天后呢。
比如今天(6.16)被拉入黑名单了,用户报名会提示 “6.19号恢复报名”,但是现在却提示6月20号才能报名,经过排查发现,就是解封的时间被多加了 1s 中,本来应该是存2025-06-18 23:59:59,但是数据库却是2025-06-19 00:00:00。
看了数据库有接近一半的数据是正确的,有一半的数据是第二天0晨(百思不得其解啊)
「代码逻辑实现:」
LocalDateTime currentTime = LocalDateTime.now();
LocalDateTime futureTime = currentTime.plus(2, ChronoUnit.DAYS);
// 设置了为当天的最后一秒啊
LocalDateTime resultTime = futureTime.withHour(23).withMinute(59).withSecond(59);
BlackAccount entity = new BlackAccount();
// 实体字段类型为Date,数据库是timestamp
entity.setDeblockTime(Date.from(resultTime.atZone(ZoneId.systemDefault()).toInstant()));
blackAccountService.save(entity);
看到上面的代码,有没有大佬已经发现问题了。确实上面的代码存在问题
二、解决过程
1、排查思路
1)排除代码问题
1.确认了项目中所有设置DeblockTime的地方 只有1处。排除了代码逻辑问题,被其他代码覆盖问题;
2)问问AI:
LocalDateTime futureTime = currentTime.plus(oaConfigActivityBlacklist.getBlockDay(), ChronoUnit.DAYS);
LocalDateTime resultTime = futureTime.withHour(23).withMinute(59).withSecond(59);
Date date = Date.from(resultTime.atZone(ZoneId.systemDefault()).toInstant());
这种写法能保证最后的date 的时分秒为 23时59分59秒么?
「ai的答案如下」 :
①「夏令时(DST)影响」
「示例」:
②「时区转换问题」
「示例」:
看了数据的时间分布,任何时间点 都存在 23:59:59 和 00:00:00的情况,就算创建时间相差几分钟的情况下,都存在这两种数据。
3)批量插入数据测试
看看能不能复现这个问题,会不会插入时候精度等其他问题:
for (int i = 0; i < 100; i++) {
Thread.sleep(100);
LocalDateTime currentTime = LocalDateTime.now();
LocalDateTime futureTime = currentTime.plus(2, ChronoUnit.DAYS);
// 设置了为当天的最后一秒啊 LocalDateTime resultTime = futureTime.withHour(23).withMinute(59).withSecond(59);
BlackAccount entity = new BlackAccount();
// 实体字段类型为Date,数据库是timestamp
entity.setDeblockTime(Date.from(resultTime.atZone(ZoneId.systemDefault()).toInstant()));
blackAccountService.save(entity);
}
果然还真复现了,有一半的数据是2025-06-19 23:59:59 有一半的数据是2025-06-20 00:00:00
2、定位问题
通过demo的复现,可以确认是在存数据库的时候出了问题。 因为Date的精度是控制在毫秒,pgsql 中TimeStamp 的精度用的默认值,精确到秒,所以在插入的时候Date的毫秒部分大于等于500的时候就会加1秒处理。入库之后就变成了第二天的00:00:00呢
3、解决方案
要么将java对象的时间精度和 数据库的精度保持一致,要么就将java对象多余的精度置为0,解决方案如下:
修改前: futureTime.withHour(23).withMinute(59).withSecond(59);
修改后: futureTime.withHour(23).withMinute(59).withSecond(59).withNano(0);
修改前:
![]()
修改后:
![]()
三、知识扩展
1、Date 和 LocalDateTime

2、mysql 中的timestamp 和 datetime

3、适用场景建议
java 中尽量用LocalDateTime吧,毕竟LocalDateTime 主要就是用来取代Date对象的,区别如下

数据库到底是用timestamp 还是 datetime呢,跨国业务用timestamp 其他场景建议用datetime:

四、总结
本文主要讲述了在处理用户解封时间时,因 Java 代码中时间精度与数据库TIMESTAMP类型精度不一致,导致约一半数据存储时间比预期多 1 秒的问题。通过排查与测试,定位问题并给出了 Java 对象时间精度和调整数据库精度两种解决方案,同时对比了 Java 和数据库中多种时间类型的特性及适用场景 。
作者丨提前退休的java猿
来源丨公众号:稀土掘金技术社区(ID:juejin1024)
dbaplus社群欢迎广大技术人员投稿,投稿邮箱:editor@dbaplus.cn
如果字段的最大可能长度超过255字节,那么长度值可能…
只能说作者太用心了,优秀
感谢详解
一般干个7-8年(即30岁左右),能做到年入40w-50w;有…
230721