扎心了!新年遇到的第一个Bug!
source link: https://developer.51cto.com/art/202001/609173.htm
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
扎心了!新年遇到的第一个Bug!
2019 年最后一天,在家里看着跨年晚会,享受着这一年最后一天的闲暇时光,女朋友在旁边玩手机。看了一会之后她突然问我一些很奇怪的问题。
2019 年最后一天,在家里看着跨年晚会,享受着这一年最后一天的闲暇时光,女朋友在旁边玩手机。看了一会之后她突然问我一些很奇怪的问题。
图片来自 Pexels
于是我拿过她的手机,看到了下面这一幕:
这是微信官方出的公众号管理的 App,上面赫然写着一篇文章的发文日期是 2020/12/29。
SimpleDateFormat
SimpleDateFormat 是 Java 提供的一个格式化和解析日期的工具类。它允许进行格式化(日期→文本)、解析(文本→日期)和规范化。
SimpleDateFormat 使得可以选择任何用户定义的日期-时间格式的模式。
在 Java 中,可以使用 SimpleDateFormat 的 format 方法,将一个 Date 类型转化成 String 类型,并且可以指定输出格式。
- // Date转String
- Date data = new Date();
- SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
- String dataStr = sdf.format(data);
- System.out.println(dataStr);
以上代码,转换的结果是:2018-11-25 13:00:00,日期和时间格式由“日期和时间模式”字符串指定。如果你想要转换成其他格式,只要指定不同的时间模式就行了。
在 Java 中,可以使用 SimpleDateFormat 的 parse 方法,将一个 String 类型转化成 Date 类型。
- // String转Data
- System.out.println(sdf.parse(dataStr));
日期和时间模式表达方法
在使用 SimpleDateFormat 的时候,需要通过字母来描述时间元素,并组装成想要的日期和时间模式。
常用的时间元素和字母的对应表(JDK 1.8)如下:
模式字母通常是重复的,其数量确定其精确表示。如前面我们用过的"yyyy-MM-dd HH:mm:ss"。
什么是 Week Year
我们知道,不同的国家对于一周的开始和结束的定义是不同的。如在中国,我们把星期一作为一周的第一天,而在美国,他们把星期日作为一周的第一天。
同样,如何定义哪一周是一年当中的第一周?这也是一个问题,有很多种方式。
比如下图是 2019 年 12 月-2020 年 1 月的一份日历:
到底哪一周才算 2020 年的第一周呢?不同的地区和国家,甚至不同的人,都有不同的理解:
- 1 月 1 日是周三,到下周三(1 月 8 日),这 7 天算作这一年的第一周。
- 因为周日(周一)才是一周的第一天,所以,要从 2020 年的第一个周日(周一)开始往后推 7 天才算这一年的第一周。
- 因为 12.29、12.30、12.31 是 2019 年,而 1.1、1.2、1.3 才是 2020 年,而 1.4 周日是下一周的开始,所以,第一周应该只有 1.1、1.2、1.3 这三天。
ISO 8601
因为不同人对于日期和时间的表示方法有不同的理解,于是,大家就共同制定了了一个国际规范:ISO 8601 。
国际标准化组织的国际标准 ISO 8601 是日期和时间的表示方法,全称为《数据存储和交换形式·信息交换·日期和时间的表示方法》。
在 ISO 8601 中,对于一年的第一个日历星期有以下四种等效说法:
- 本年度第一个星期四所在的星期。
- 1 月 4 日所在的星期。
- 本年度第一个至少有 4 天在同一星期内的星期。
- 星期一在去年 12 月 29 日至今年 1 月 4 日以内的星期。
根据这个标准,我们可以推算出:2020 年第一周:2019.12.29-2020.1.4。
所以,根据 ISO 8601 标准,2019 年 12 月 29 日、2019 年 12 月 30 日、2019 年 12 月 31 日这三天,其实不属于 2019 年的最后一周,而是属于 2020 年的第一周。
JDK 针对 ISO 8601 提供的支持
根据 ISO 8601 中关于日历星期和日表示法的定义,2019.12.29-2020.1.4 是 2020 年的第一周。
日常工作中,我们可能有这样的需求:我们希望输入一个日期,然后程序告诉我们,根据 ISO 8601 中关于日历日期的定义,这个日期到底属于哪一年。
比如我输入 2019-12-20,他告诉我是 2019;而我输入 2019-12-30 的时候,他告诉我是 2020。
为了提供这样的数据,Java 7 引入了「YYYY」作为一个新的日期模式来作为标识。
使用「YYYY」作为标识,再通过 SimpleDateFormat 就可以得到一个日期所属的周属于哪一年了。
所以,我们通过代码可以验证:
- public class WeekYearTest {
- public static void main(String[] args) {
- SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
- SimpleDateFormat sdf1 = new SimpleDateFormat("YYYY");
- System.out.println(sdf1.format(sdf.parse("2019-12-01")));
- System.out.println(sdf1.format(sdf.parse("2019-12-30")));
- System.out.println(sdf1.format(sdf.parse("2020-01-01")));
输出结果为:
可见, 2019-12-30 日因为属于 2020 年的第一周,所以返回的年份是 2020 年。
而如果将「YYYY」改成「yyyy」的话,输出结果就为:
因为有这样的情况,所以我们日常开发的时候,如果把 y 写成了 Y,那就可能导致日期输出的结果不符合我们的预期。
当我们要表示日期的时候,一定要使用 yyyy-MM-dd 而不是 YYYY-MM-dd ,这两者的返回结果大多数情况下都一样,但是极端情况就会有问题了。
因为作者的 IDEA 中安装了<阿里巴巴开发手册的插件>,所以在代码中使用「YYYY」的时候,IDEA 会弹出以下提示:
好啦,大家快去排查下你的代码,有没有'YYYY-MM-dd'这种形式的代码吧,如果有的话,一定要改掉哦!~
【编辑推荐】
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK