共计 2193 个字符,预计需要花费 6 分钟才能阅读完成。
在进行数据查问时,控制台报了 Caused by: com.mysql.cj.exceptions.WrongArgumentException: HOUR_OF_DAY: 0 -> 1
异样,查问得悉:这是因为查 mysql 库,转换类型为 datetime
类型的字段引起的。
网上的解决方案有多种,大多数都是通过设置时区来解决的,但遗憾的是通过测试我发现即便在将数据跑在时区正确的数据库上,在执行起来我这依然出错。
最初发现我的问题呈现在夏令时上。
夏令时
记得小时候过过几个夏令时,大略的意思就是在某一天把表调快 1 个小时,而后再到某一天把表再调慢 1 个小时。这间接造成的问题的是:xxxx 年 xx 月 xx 日会对应上两个工夫戳。
比方咱们假如把表调慢的那一天是 2021 年 10 月 4 日的 12 点。具体的操作是过后钟第一次通过 2021 年 10 月 4 日 12 点时,咱们把表调到 2021 年 10 月 4 日 11 点。所以在 2021 年 10 月 4 日 11 点至 12 点,咱们会从新过一次。
对于工夫这块,已经回达到一个工夫戳为负的问题,也有那么点意思:https://segmentfault.com/q/1010000038248983,赶趣味的能够看看。
那么问题来了,比咱们记录用户的出世工夫,准确到分钟。如果这个人录入的是 2021 年 10 月 4 日 11 点 20 分,那咱们的零碎没有方法来精确的判断这个工夫是第一个 11 点 20 分,还是过 1 小时后的第二个 11 点 20 分。
夏令时,还给咱们带来的另一个问题。有些工夫是对应不上工夫戳的。
再比方咱们设置在 2021 年 5 月 1 日 0 时,将表调快 1 时,则在历史上不会呈现 2021 年 5 月 1 日 0 时至 1 时的工夫,所以如果咱们统计出世工夫点,用户写的是:2021 年 5 月 1 日 0 时 30 分,则该数据必须是个假数据。
排查
夏令时讲完后,咱们讲下排查过程。其实并不是所有的数据在查问时,都会报这种异样,所以要把那个非凡的点找进去,这里给一种最笨的展现办法:
boolean last = false;
int page = 0;
Pageable pageable = PageRequest.of(page, 1);
while (!last) {
try {Page<Resident> residents = this.residentRepository.findAll(specification, pageable);
page++;
pageable = PageRequest.of(page, 1);
last = residents.isLast();} catch (Exception e) {
last = true;
e.printStackTrace();
this.logger.info("当前页" + pageable.getPageNumber());
}
}
最终控制台打印信息:2021-11-04 13:25:38.562 INFO 4226 --- [nio-8081-exec-7] c.y.s.service.ResidentServiceImpl : 当前页 1089
而后咱们去数据表中把这条记录查出来:
select * FROM resident limit 1089, 1
咱们发现此人的出生日期是 1947 年 4 月 15 日 0 时 0 分 0 分。其实这个日期用户仅仅是输出了 1947-4-15,只是咱们存的时候主动增加了 0 时 0 分 0 秒。但凑巧,这个数字对应的工夫戳,它恰好就是一个有效数字。
测试代码如下:
@Test
void time() {Calendar calendar = Calendar.getInstance();
// 启用严格查看模式
calendar.setLenient(false);
calendar.set(1947, 3, 15, 0, 0, 0);
System.out.println(calendar.getTime());
}
异样内容:java.lang.IllegalArgumentException: HOUR_OF_DAY: 0 -> 1
它是在说:你说本人是 0 点出世的,然而本 JAVA 大牛查了一下,1947 年 4 月 15 日就没有 0 点,当天的最小值是 1 点。
解决问题
问题找到了,解决便是最简略的一环。
- 找到报错的历史数据,将 0 点改成 8 点。
- 找到历史的代码,将 0 点改成 8 点。
public static Timestamp getTimeStampFormIdNumber(String idNumber) {
// 进行出生日期赋值
int year = Integer.valueOf(idNumber.substring(6, 10));
int month = Integer.valueOf(idNumber.substring(10, 12));
int day = Integer.valueOf(idNumber.substring(12, 14));
Calendar calendar = Calendar.getInstance();
- calendar.set(year, month - 1, day, 0, 0, 0);
+ calendar.set(year, month - 1, day, 12, 0, 0);
return new Timestamp(calendar.getTimeInMillis());
}
至于为什么改成 12 点,是因为我发现夏令时的批改(调快或调慢)都躲避了 12 点,所以工夫为 12 点,则能够无效防止有效工夫戳的问题. 当然了,你还能够改成 13 点,只有不是凌晨的那几个小时,都没有什么问题。