SpringBoot JUnit ExpectedDatabase测试中SimpleDateFormat日期解析异常
日期解析出现1799年偏差的原因及解决办法
问题场景
测试预期的XML数据中日期为:
<VEH_DATE_MEC="1800-01-01"/>
执行以下代码解析日期后:
SimpleDateFormat isoFormat = new SimpleDateFormat("yyyy-MM-dd"); isoFormat.setTimeZone(TimeZone.getTimeZone("GMT+1")); Date date = isoFormat.parse("1800-01-01"); vehiculeFrontDto.setDatePremiereImmat(date);
Setter方法实现:
public void setDatePremiereImmat(Date datePremiereImmat) { this.datePremiereImmat = DateCopyUtils.copyDate(datePremiereImmat); }
调试发现date变量的实际值为:Tue Dec 31 23:00:00 UTC 1799,测试断言失败:
- 预期值:
<1[800-01-01]> - 实际值:
<1[799-12-31 23:00:00.0]>
问题原因
- 旧日期API的历史时区缺陷:
SimpleDateFormat和Date是Java早期的日期处理类,对1900年之前的历史时区规则支持存在bug。1800年时还没有统一的GMT+1时区偏移标准,SimpleDateFormat依赖的旧时区数据在计算时,错误地将1800-01-01 GMT+1转换为UTC时间1799-12-31 23:00:00。 - 时区上下文不一致:测试断言时以UTC时区展示实际日期值,而预期值是不带时区的纯日期,两者时区基准不同,导致日期看起来偏差一天。
解决办法
方案1:改用Java 8+的java.time新日期API(推荐)
新API彻底修复了旧类的历史时区问题,设计更严谨:
// 解析纯日期,无需考虑时区 LocalDate targetDate = LocalDate.parse("1800-01-01"); // 若需兼容旧代码中的Date类型: Date utilDate = Date.from(targetDate.atStartOfDay(ZoneId.of("GMT+1")).toInstant()); vehiculeFrontDto.setDatePremiereImmat(utilDate);
如果业务仅需日期信息,建议直接用LocalDate存储,完全规避时区干扰。
方案2:修复旧API的使用逻辑
若必须继续使用SimpleDateFormat,可通过以下方式调整:
- 统一解析和断言的时区:要么解析时不设置
GMT+1时区,要么断言时用GMT+1时区格式化实际值,确保和预期值的时区基准一致:
SimpleDateFormat isoFormat = new SimpleDateFormat("yyyy-MM-dd"); // 使用和断言逻辑一致的时区(或默认时区) Date date = isoFormat.parse("1800-01-01");
- 更新JDK时区数据:升级JDK到包含最新tzdata的版本,修复旧日期的时区计算bug,但这种方式不如换用新API彻底。
方案3:调整测试断言逻辑
如果测试仅关心日期部分(忽略时间和时区),可将实际值和预期值都转换为纯日期字符串后再对比:
// 用GMT+1时区格式化实际日期,得到1800-01-01 SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd"); formatter.setTimeZone(TimeZone.getTimeZone("GMT+1")); String actualDateStr = formatter.format(vehiculeFrontDto.getDatePremiereImmat()); // 对比actualDateStr和"1800-01-01"
内容的提问来源于stack exchange,提问作者Andrew
相关产品推荐
相关产品推荐

