首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >如何解决 Java 中 `YYYY-MM-dd` 导致的跨年日期显示错误?深度剖析 Java 中 yyyy vs YYYY 的区别,揭秘 ISO-8601 周年制背后的玄机,并给出多种规避方案

如何解决 Java 中 `YYYY-MM-dd` 导致的跨年日期显示错误?深度剖析 Java 中 yyyy vs YYYY 的区别,揭秘 ISO-8601 周年制背后的玄机,并给出多种规避方案

作者头像
猫头虎
发布2025-07-24 08:39:42
发布2025-07-24 08:39:42
1890
举报

一个由 “YYYY-MM-dd” 引发的惨案!元旦来临前警惕 ~

前端跨年 Bug 截图
前端跨年 Bug 截图

前言

年底将至,元旦假期就在眼前。准备好出行的我在 2019-12-30 下单机票时,App 居然给我显示成了 2020-12-30!一脸懵逼,差点以为自己买错了整整一年的票。 究其原因,是开发者在日期格式化时把年份格式写成了 YYYY-MM-dd,结果导致跨周跨年的那一周被当成了下一年!这个小细节不注意,足以让用户 怒刷差评,也可能让程序员被“祭天”……

本文将带你 深度剖析 Java 中 yyyy vs YYYY 的区别,揭秘 ISO-8601 周年制背后的玄机,并给出多种规避方案,助你在跨年时节不再翻车。


一、问题复现

  1. 场景:12 月 30 日或 12 月 31 日这两天,使用日期格式化输出订单日期。
  2. 格式化字符串
    • 正确写法: yyyy-MM-dd(calendar year)
    • 错误写法: YYYY-MM-dd(week-based year)

二、演示代码

代码语言:javascript
复制
package com.rumenz;

import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;

public class DateTest {
    public static void main(String[] args) {
        // 设置日期:2019-12-31
        Calendar cal = Calendar.getInstance();
        cal.set(2019, Calendar.DECEMBER, 31);
        Date date = cal.getTime();

        // 正确的年:yyyy
        DateFormat fmt1 = new SimpleDateFormat("yyyy-MM-dd");
        System.out.println("yyyy 格式: " + fmt1.format(date));

        // 错误的年:YYYY
        DateFormat fmt2 = new SimpleDateFormat("YYYY-MM-dd");
        System.out.println("YYYY 格式: " + fmt2.format(date));
    }
}

三、运行结果与现象

代码语言:javascript
复制
yyyy 格式: 2019-12-31
YYYY 格式: 2020-12-31
  • yyyy:表示「年-时代」(year-of-era),2019
  • YYYY:表示「基于周的年」(week-based-year),2020

为何 2019-12-31 会被当成 2020 年?背后的原因就是 ISO-8601 周年制


四、yyyy vs YYYY:背后原理

符号

含义

启始点

y

year-of-era(公历年)

1 月 1 日

Y

week-based-year(ISO 周年)

周一为一周开始,跨周跨年归属下一年

根据 ISO-8601:

  • 一年中的 第一周(Week 01)必须包含该年 1 月 4 日,或者至少有 4 天 属于该年。
  • 周一为一周的第一天,周日为最后一天。
  • 2019-12-31 是周二,但这周是跨年周(2019 年的最后一周有 6 天在 2019 年,1 天在 2020 年)。
  • 按 ISO 规则,这一周 归属下一年(2020),因此 YYYY 会输出 2020。
4.1 ISO 周年示例

日期

星期

周编号

周年(YYYY)

公历年(yyyy)

2019-12-29

52

2019

2019

2019-12-30

01

2020

2019

2019-12-31

01

2020

2019

2020-01-01

01

2020

2020


五、更多坑点示例

代码语言:javascript
复制
String[] patterns = {"yyyy-'W'ww-u", "YYYY-'W'WW-u"};
for (String p : patterns) {
    DateFormat df = new SimpleDateFormat(p);
    System.out.println(p + ": " + df.format(date));
}
代码语言:javascript
复制
yyyy-'W'ww-u: 2019-W52-2
YYYY-'W'WW-u: 2020-W01-3
  • ww:周编号,两位
  • u:周中第几天(1=Monday, …,7=Sunday)

由此可见,用错大写字母,不仅年会错,**“周数”**也可能错!


六、Java 8+ 推荐写法

使用 java.time API,显式区分公历年和周年制:

代码语言:javascript
复制
import java.time.*;
import java.time.format.DateTimeFormatter;

public class JavaTimeDemo {
    public static void main(String[] args) {
        LocalDate date = LocalDate.of(2019, 12, 31);

        // 公历年
        String s1 = date.format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));
        // ISO 周年
        String s2 = date.format(DateTimeFormatter.ofPattern("YYYY-'W'WW-u"));

        System.out.println("公历年: " + s1);
        System.out.println("ISO 周年: " + s2);
    }
}
  • 建议:凡非按周统计的场景,一律使用 yyyy,避免“跨年周”陷阱。

七、前端/其他语言

  • JavaScriptdate-fnsmoment.js):
    • YYYY:ISO 年
    • yyyy:公历年
  • Pythonstrftime):
    • %Y:公历年
    • %G:ISO 年
  • C# (.NET):
    • yyyy:公历年
    • YYYY 并不支持 ISO 年,需要 ddd 等另行计算

小结:跨语言同样存在此坑,查看文档时务必核对大小写含义。


八、最佳实践与总结

  • ✔️ 默认使用 yyyy:绝大多数日期显示、订单、日志场景应使用公历年。
  • ✔️ 特殊周报/周统计:若确实按“ISO 周”分组,才用 YYYYwwu 等周年制模式。
  • ✔️ 增强测试覆盖:在年底编写单元测试,覆盖 12 月 29 日–1 月 3 日 期间,确保无误。
  • ✔️ 静态检查:代码审查/CI 可引入正则检查,禁止在常规场景中使用 YYYY

这次“YYYY”小坑足以让人“跨年”买错机票,希望你也能在元旦前弥补这个细节,避免被用户或老板“祭天”!祝大家新年代码无 Bug,生活多惊喜!

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2025-07-23,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一个由 “YYYY-MM-dd” 引发的惨案!元旦来临前警惕 ~
    • 前言
    • 一、问题复现
    • 二、演示代码
    • 三、运行结果与现象
    • 四、yyyy vs YYYY:背后原理
      • 4.1 ISO 周年示例
    • 五、更多坑点示例
    • 六、Java 8+ 推荐写法
    • 七、前端/其他语言
    • 八、最佳实践与总结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档