首页 >Java >java教程 >YYYY? yyyy!

YYYY? yyyy!

Susan Sarandon
Susan Sarandon原创
2024-11-17 14:51:02883浏览

你知道 Java 日期模式中的 'Y' 和 'y' 字符有什么区别吗?在本文中,我们将探讨不正确的日期格式如何导致错误。我们还将推出新的 Java V6122 诊断规则,让您免于突然的时间旅行。

YYYY? yyyy!

介绍

掸去大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 接口的类表示的日期。例如,其中包括 LocalDateLocalDateTimeSimpleDateFormat 仅格式化 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中文网其他相关文章!

声明:
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn