你知道 Java 日期模式中的 'Y' 和 'y' 字符有什么区别吗?在本文中,我们将探讨不正确的日期格式如何导致错误。我们还将推出新的 Java V6122 诊断规则,让您免于突然的时间旅行。
掸去大TODO笔记本上的灰尘后,我们偶然发现了一个特别有趣的案例。该文章的评论者强调了潜在的问题。
评论
顺便说一句,这里有一个主意:Java 中的 SimpleDateFormat 可以带来新年惊喜——用小写字母书写的年份与用大写字母书写的年份不同。在这一年的最后一周,您可能会突然发现自己身处未来,因为 2021 年 12 月 27 日至 2021 年 12 月 31 日的“YYYY”是 2022 年,而不是某些人预期的 2021 年。
让我们来一探究竟。
如果您忘记了 SimpleDateFormat 是什么,您可以在这里温习一下知识。
为了存储和显示日期,我们经常需要它们遵循特定的模式。 SimpleDateFormat 是一个类,使我们能够根据给定的模式轻松格式化日期。除了格式化之外,SimpleDateFormat还可以解析字符串并将其转换为日期对象。
这就是 Java 所能提供的全部吗?除了 SimpleDateFormat 之外,DateTimeFormatter 类还可以格式化日期。
让我向您展示使用这两个类的示例。
如果我们通过 SimpleDateFormat 格式化日期:
public static void main(String[] args) { Date date = new Date("2024/12/31"); var dateFormatter = new SimpleDateFormat("dd-MM-yyyy"); System.out.println(dateFormatter.format(date)); }
控制台显示以下内容:
2024年12月31日
这里发生了什么?
我们有日期,我们想以特定格式保存/显示它。为了实现它,我们通过将模式字符串传递给构造函数来创建 SimpleDateFormat 对象。日期的格式完全按照此模式进行。 format 方法返回格式化日期的字符串表示形式。这正是我们想要的。
这里是同样的事情,但是我们使用DateTimeFormatter:
public static void main(String[] args) { LocalDate date = LocalDate.of(2024, 12, 31); var formatter = DateTimeFormatter.ofPattern("dd-MM-yyyy"); System.out.println(formatter.format(date)); }
控制台显示以下内容:
2024年12月31日
相同的步骤和结果,但略有不同:DateTimeFormatter 使我们能够仅格式化由实现 TemporalAccessor 接口的类表示的日期。例如,其中包括 LocalDate 和 LocalDateTime。 SimpleDateFormat 仅格式化 Date 类的对象。
让我们回到评论。在日期模式中使用“Y”代替“y”真的会改变结果吗?
看一下代码:
public static void main(String[] args) { Date date = new Date("2024/12/31"); var dateFormatter = new SimpleDateFormat("dd-MM-yyyy"); System.out.println(dateFormatter.format(date)); }
控制台显示以下内容:
2025年12月31日
哎呀。 DateTimeFormatter 也是一样吗?
代码如下:
public static void main(String[] args) { LocalDate date = LocalDate.of(2024, 12, 31); var formatter = DateTimeFormatter.ofPattern("dd-MM-yyyy"); System.out.println(formatter.format(date)); }
控制台显示以下内容:
2025年12月31日
我们确实旅行到了一年后的未来。是时候看看发生了什么了。
我的第一步是查看 SimpleDateFormat 类的文档。下面是表格的一小部分,描述了日期模式如何解释我们感兴趣的字母字符:
Letter | Date or Time Component | Presentation | Examples |
---|---|---|---|
y | Year | Year | 1996; 96 |
Y | Week year | Year | 2009; 09 |
一年一周?让我解释一下。
周年是基于一年中的周数的一年。它是什么意思以及为什么它很重要?
在某些任务中,一年中周的序号很重要。为了确定一周的序列号,我们需要决定首先考虑哪一周。这是因为从一年到另一年的过渡通常会导致两年之间有几周的时间。那么,我们如何判断本周属于哪一年呢? ISO-8601 标准对此进行了规定。
根据这个标准,一年中的第一周必须满足以下条件:
由此,我们可以推导出一个简单的规则:如果星期四是一月,则一周被认为是一年的第一周。
一个现实生活中的例子可以进一步阐明这一点。
让我们从示例中获取日期,即 31.12.2024。以下是包含我们日期的一周的日历片段(2024 年 12 月至 2025 年 1 月):
Mo | Tu | We | Th | Fr | Sa | Su |
---|---|---|---|---|---|---|
30 | 31 | 1 | 2 | 3 | 4 | 5 |
本周被视为 2025 年的第一周,因为它满足上述条件(一月有 5 天)。因此,如果我们使用“Y”说明符,我们就会得到 2025 年。
你可以猜到,DateTimeFormatter 的情况是完全一样的。
需要注意的是,我们不仅可以穿越到未来,还可以穿越到过去。
我们换个日期吧,2027 年 1 月 1 日。
2027 年的第一周包括 1 月 1 日吗?再次,让我们查阅日历。
以下是包含我们日期的一周的日历片段(2026 年 12 月 - 2027 年 1 月):
Mo | Tu | We | Th | Fr | Sa | Su |
---|---|---|---|---|---|---|
28 | 29 | 30 | 31 | 1 | 2 | 3 |
由于这一周只有 3 天(根据要求第一周应该是 4 天),因此算作 2026 年的最后一周。所以,日期使用“Y”格式化时角色,将向我们展示2026。
这是证据。看一下代码:
public static void main(String[] args) { Date date = new Date("2024/12/31"); var dateFormatter = new SimpleDateFormat("dd-MM-yyyy"); System.out.println(dateFormatter.format(date)); }
控制台显示以下内容:
2026 年 1 月 1 日
好的,我们已经剖析了这个问题。我们还发现向 PVS-Studio Java 分析器添加专用诊断规则非常有趣。
诊断规则已经写好了,是时候测试一下了。
在分析器的开发过程中,测试阶段之一涉及运行回归测试。我们有一篇关于此过程的文章。简而言之,当我们添加新的诊断规则时,我们会分析大量开源项目,并将新报告与参考报告进行比较。
在此诊断规则的情况下,分析器针对多个项目发出了新的警告。让我们来看看它们。
在这个项目中,诊断规则指向的代码片段是相同的,所以我只展示其中一个。
看一下代码:
public static void main(String[] args) { LocalDate date = LocalDate.of(2024, 12, 31); var formatter = DateTimeFormatter.ofPattern("dd-MM-yyyy"); System.out.println(formatter.format(date)); }
PVS-Studio 警告:
V6122 检测到使用“Y”(周年)模式:它可能打算使用“y”(年)。 SkeinParameters.java 246
首先,我浏览了GitHub。如果这确实是一个错误,开发人员已经发现它并提交了修复怎么办?这正是发生的事情。这是提交的链接,你可以查看一下。模式中的所有“Y”(周年)字符均替换为“y”(年)。
您可能想知道为什么提交是在相对较长的时间之前进行的。让我解释一下。我们的回归测试并不是为了持续控制这个或那个开源项目的质量。任务是查看添加新诊断规则时报告如何变化:旧警告不应消失,不应出现新错误,因为这表明分析仪存在问题。所以,检查的代码必须是相同的。
现在让我们看一下触发诊断规则的第二个项目。
PVS-Studio 警告:
V6122 检测到使用“Y”(周年)模式:它可能打算使用“y”(年)。 RepositoryInfo.java 77
代码:
public static void main(String[] args) { Date date = new Date("2024/12/31"); var dateFormatter = new SimpleDateFormat("dd-MM-YYYY"); System.out.println(dateFormatter.format(date)); }
就像之前的项目一样,我冲到提交处看看发生了什么。首先,值得注意的是,由于重构,该字段已移至其派生类 Repository(这里是提交的链接)。我进一步搜索并找到了包含修复程序的提交。日期模式中的“Y”字符已替换为“y”:
public static void main(String[] args) { LocalDate date = LocalDate.of(2024, 12, 31); var formatter = DateTimeFormatter.ofPattern("dd-MM-YYYY"); System.out.println(formatter.format(date)); }
所以,这里也是一个错误。
在PVS-Studio,我们欢迎来自社区的任何反馈。在解决 Habr 用户的评论后,我们通过添加一些有用的诊断规则来增强 Java 分析器。因此,如果您有任何想法想与我们分享,我们很乐意在本文的评论部分与您聊天。
顺便说一句,诊断规则已在 10 月 7.33 版本中引入。因此,如果您想尝试我们的分析器,请使用此链接。
仅此而已。让我们把事情总结到这里吧。希望突然的前进(或倒退)一年不会让您措手不及。
以上是YYYY? yyyy!的详细内容。更多信息请关注PHP中文网其他相关文章!