Java SimpleDateFormat 的格式化字符串里的Y

2017年元旦假期三天, 程序员小吴没有出去玩. 早上10点多了, 还在家睡懒觉. 突然电话响起,吵醒了半睡半醒的他. 他心想是推销还是贷款专员? 休假都被叫醒, 很是生气. 拿起手机, 发现竟然是公司打来的电话,顿时感觉不妙. 接通电话, 那头说有个紧急的site issue, 可能跟小吴的代码相关, 让他紧急上线看一下. 小吴一下子清醒了很多, 连忙登录VPN, 检查最新邮件, 确实发现有个问题, 跟自己几个月前上线的代码有关系.

这段代码巨简单, 就是取服务器当前的时间, 然后格式化一下, 显示在页面上. 同时,页面上有些数据统计信息, 会通过Ajax请求的方式去慢慢取,然后展示在页面. 去取的时候, 把本来展示在页面的这个格式化后的日期, 发给后台服务器. 问题就出现在, 本来这天是2017年12月31日, 可是页面上却显示的是 2018/12/31, 默认加了1年, 非常的奇怪.

小吴首先把线上已部署的代码的commit id 找到,然后把这个commit拉下来, 仔细检查了一番, 并没有发现什么情况. 这段代码已经在生产环境运行了8个多月, 之后一直没有人改过. 之前的测试以及线上从来没出现过问题, 真是奇了怪了. 小吴只好本地debug了一下, 发现本地能完美重现... 用当天的日期格式化一把, 格式化之后一直是2018年12月31日. 之前从来没有问题, 难道这天是啥好日子? 经过一番修改测试, 终于发现了问题:

public static void main(String[] args) {
    Calendar cal = Calendar.getInstance();
    cal.set(2017, 11, 29);
    SimpleDateFormat sdf0 = new SimpleDateFormat("YYYY/MM/dd");
    SimpleDateFormat sdf1 = new SimpleDateFormat("yyyy/MM/dd");
    Date date = null;
    for (int i = 0; i < 4; i++) {
        date = cal.getTime();
        System.out.println(sdf0.format(date));
        System.out.println(sdf1.format(date));
        cal.add(Calendar.DATE, 1);
    }
}

运行结果:

2017/12/29
2017/12/29
2017/12/30
2017/12/30
2018/12/31
2017/12/31
2018/01/01
2018/01/01

从上面代码片段及运行结果看, 只有2017年12月31日出问题了. 问题出现在格式化的时候, 使用是大写的Y还是小写的y. 那么根据官方文档, 差别是什么呢?

y: Year Year 如: 1996; 96
Y: Week year Year 如: 2009; 09

一个是year, 一个 week year. year 很好理解, 就是我们所说的年, 那么 week year 呢? week year的计算涉及到2个东西, 一个是: 一周的第一天是从周一开始算还是从周日开始算, 有的人/地方从周日开始算, 有的从周一开始算. 第二个是, 我们经常听说这是今年第几周, 试想如果一个week跨过1年, 那么这周算到那年里?
JDK 文档举的例子是: 1998年1月1日是周四.
如果我们设定一周的第一天是周一, 那么这周属于1998年的就有4天(周4,5,6,7), 属于1997年的只有3天, 那么这周算在1998年,这是1998年的第一周. 那么1997年12月29, 30, 31日的 week year 就是1998, 而不是1997了.
如果我们设定一周的第一天是周日, 那么这周属于1998年的就有3天(周4,5,6), 属于1997年的就有4天了. 那么这周算在1997年,这是1997年的最后一周,1998年的第一周从1月4号开始, 那么1998年,1月1,2.3号的 week year 就是1997了.

FirstDayOfWeek 是根据JDK所在的系统设置, 有个默认设置的.

所以这个问题, 只要改回正确的yyyy问题就解决了. 另外还发现有人误以为这是JDK的bug, 去提了bug: https://bugs.openjdk.java.net/browse/JDK-8194625

参考:
https://docs.oracle.com/javase/7/docs/api/java/text/SimpleDateFormat.html
https://docs.oracle.com/javase/9/docs/api/java/util/GregorianCalendar.html#week_year

 

原文地址: http://www.tianxiaohui.com/index.php/Java%E7%9B%B8%E5%85%B3/Java-SimpleDateFormat-%E7%9A%84%E6%A0%BC%E5%BC%8F%E5%8C%96%E5%AD%97%E7%AC%A6%E4%B8%B2%E9%87%8C%E7%9A%84Y.html

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值