你知道 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中文网其他相关文章!

本文讨论了使用Maven和Gradle进行Java项目管理,构建自动化和依赖性解决方案,以比较其方法和优化策略。

本文使用Maven和Gradle之类的工具讨论了具有适当的版本控制和依赖关系管理的自定义Java库(JAR文件)的创建和使用。

本文讨论了使用咖啡因和Guava缓存在Java中实施多层缓存以提高应用程序性能。它涵盖设置,集成和绩效优势,以及配置和驱逐政策管理最佳PRA

本文讨论了使用JPA进行对象相关映射,并具有高级功能,例如缓存和懒惰加载。它涵盖了设置,实体映射和优化性能的最佳实践,同时突出潜在的陷阱。[159个字符]

Java的类上载涉及使用带有引导,扩展程序和应用程序类负载器的分层系统加载,链接和初始化类。父代授权模型确保首先加载核心类别,从而影响自定义类LOA


热AI工具

Undresser.AI Undress
人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover
用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool
免费脱衣服图片

Clothoff.io
AI脱衣机

AI Hentai Generator
免费生成ai无尽的。

热门文章

热工具

MinGW - 适用于 Windows 的极简 GNU
这个项目正在迁移到osdn.net/projects/mingw的过程中,你可以继续在那里关注我们。MinGW:GNU编译器集合(GCC)的本地Windows移植版本,可自由分发的导入库和用于构建本地Windows应用程序的头文件;包括对MSVC运行时的扩展,以支持C99功能。MinGW的所有软件都可以在64位Windows平台上运行。

mPDF
mPDF是一个PHP库,可以从UTF-8编码的HTML生成PDF文件。原作者Ian Back编写mPDF以从他的网站上“即时”输出PDF文件,并处理不同的语言。与原始脚本如HTML2FPDF相比,它的速度较慢,并且在使用Unicode字体时生成的文件较大,但支持CSS样式等,并进行了大量增强。支持几乎所有语言,包括RTL(阿拉伯语和希伯来语)和CJK(中日韩)。支持嵌套的块级元素(如P、DIV),

安全考试浏览器
Safe Exam Browser是一个安全的浏览器环境,用于安全地进行在线考试。该软件将任何计算机变成一个安全的工作站。它控制对任何实用工具的访问,并防止学生使用未经授权的资源。

SublimeText3 英文版
推荐:为Win版本,支持代码提示!

SublimeText3 Mac版
神级代码编辑软件(SublimeText3)