10小时前  Java系列 |   抢沙发  6 
文章评分 0 次,平均分 0.0

问题陈述

快速回答,不要想得太复杂,对于日期2025-12-28,你期望这段代码会输出什么?

var dateFormat = DateTimeFormatter.ofPattern("YYYY-MM-dd");
 var today = LocalDate.of(2025, 12, 28);

 IO.println(today.format(dateFormat));

实际上,它打印…

2026-12-28

整整一年没工作了!怎么会这样?

问题在于,我们使用周年代(YYYY)来格式化日期,而不是日历年份(yyyy)。犯这样的错误会导致我们的日志文件、数据库表、事件、API调用和业务逻辑中的日期看起来像是未来一年的日期。如果你以前从未见过这个问题,并且正在进行代码审查,你会发现像这样的微妙错误吗?

Week Year

在基于周的日历系统中,使用“一周年”来表示一年。基于周的日历具有以下特性:

  • 一年由完整的周(7天为一个周期)组成。
  • 每周都完全属于某一年(每周从星期一开始)。
  • 日历年的第一天和最后几天实际上可能属于前一周年或下一周年。
  • 一年中的某一周可以包含52周或53周。

周年用于需要基于ISO周的报告系统,或需要按周一致地汇总数据的系统。

哪些日期格式组合有意义

此表列举了你可能会遇到的年份/周组合。有些组合显然合情合理,你会期望它们在常用中见到,而有些组合则应让你停下来思考它们是否有效。

Year Format Week/Month Format Description Valid?
yyyy MM Calendar Year with Calendar Month Yes
YYYY ww Week Year with Week in Year Yes
YYYY MM Week Year with Calendar Month No
yyyy ww Calendar Year with Week in Year No

一如既往,可能存在这样的情况:我列出的无效组合在您的用例中却是有效的。我可以想象,在某个地方有一个遗留系统,它是使用yyyy-ww而不是YYYY-ww构建的,而业务已经广泛依赖于这种对我们来说正确但对大众来说错误的逻辑。但这个表格是一个不错的起点。

我该如何解决这个问题?

如果你的代码库较小,或者没有进行大量的日期格式化/解析工作,那么手动搜索YYYY并确保没有将其用于合法的周年份(week year)场景可能是最快捷的方法。

否则,OpenRewrite有一个名为ReplaceWeekYearWithYear的规则,可以应用于您的项目。

All sources parsed, running active recipes: org.openrewrite.staticanalysis.ReplaceWeekYearWithYear
Changes have been made to src\main\java\com\ginsberg\SomeExampleCode.java by:
    org.openrewrite.staticanalysis.ReplaceWeekYearWithYear
Please review and commit the results.

注意:在OpenRewrite的rewrite-static-analysis v2.25.0版本中,此问题已得到修复。截至本文撰写之时,OpenRewrite的ReplaceWeekYearWithYear规则仍会错误地将合法的YYYY-ww实例中的YYYY转换为yyyy。因此,请检查它生成的任何输出(正如构建输出所提示的那样!),或者确保您没有合法的用例。
无论你决定如何修复这个问题(手动修复还是半自动化修复),你可能都需要检查所有已生成的日期,并同时进行更正。当你处理的是不可变数据,如财务交易记录或审计记录时,这一点会变得尤为棘手。祝你好运!

预防措施

修复所有现有的周年使用问题固然很好,但我们应确保未来不再添加此类额外代码。我已经找到了一些预防此类问题的方法,我相信还有更多方法。

IntelliJ IDEA 警告

IntelliJ IDEA 在您编辑代码时提供了相应的工具来捕捉此类问题。首先,当编辑器检测到 YYYY 使用不当时,会在编辑器中以简单高亮显示(请注意,它不会高亮显示正确的大小写)。

Week Year:使用YYYY而非yyyy的Java日期格式化

有趣的是,IntelliJ IDEA能够正确忽略合法的YYYY-ww用例。

IntelliJ IDEA代码检查

IntelliJ IDEA的内置代码分析功能(右键单击->分析->检查代码)也会对疑似错误使用的情况发出警告。

Week Year:使用YYYY而非yyyy的Java日期格式化

代码检查还可以用于将YYYY更正为yyyy。同样,这里不会标记出合法的用例。

ErrorProne

我通常喜欢使用的一个工具是谷歌的ErrorProne,它是一款静态分析工具,能在编译时捕捉常见错误。在其众多实用规则中,MisusedWeekYear会在检测到YYYY使用不当的情况下导致构建失败。

SomeExampleCode.java:24: error: [MisusedWeekYear] Use of "YYYY" (week year) in a date pattern 
    without "ww" (week in year). 
    
    You probably meant to use "yyyy" (year) instead.

    var dateFormat = DateTimeFormatter.ofPattern("YYYY-MM-dd");
                                              ^
    (see https://errorprone.info/bugpattern/MisusedWeekYear)
    
    Did you mean 'var dateFormat = DateTimeFormatter.ofPattern("yyyy-MM-dd");'?

注意:对于真正合法的用例(你可能没有,请参见上表),你可以根据具体情况使用 @SuppressWarnings("MisusedWeekYear") 来关闭此警告。MisusedWeekYear 规则确实能正确忽略合法的 YYYY-ww 用例。

长话短说

  • 使用YYYY有正当理由,但并不常见,因此需谨慎。
  • 有一些工具可以在大规模上纠正这个问题(我真的希望你在大规模上没有这个问题)。
  • 有一些工具可以从源头上防止这种情况的发生。

原文链接: https://todd.ginsberg.com/post/java/beware-of-week-year/

  
 

除特别注明外,本站所有文章均为老K的Java博客原创,转载请注明出处来自https://javakk.com/3038.html

关于

发表评论

表情 格式

暂无评论

登录

忘记密码 ?

切换登录

注册