16

扎心了!新年遇到的第一个Bug!

 4 years ago
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 年最后一天,在家里看着跨年晚会,享受着这一年最后一天的闲暇时光,女朋友在旁边玩手机。看了一会之后她突然问我一些很奇怪的问题。

177d6c6fb2d80b2efcd6f4a86d616f29.jpg-wh_651x-s_2931521346.jpg

图片来自 Pexels

f666f5b0acc032130a88db104ee6736c.jpg-wh_600x-s_731292748.jpg

6885424e95ab79f32a086659d5ed4c9c.jpg-wh_600x-s_2135965594.jpg

c6542931908e320d86b2f18d3d2c62a7.jpg-wh_600x-s_2511869264.jpg

23a53fbc69eee193e05cdc4f918bb31c.jpg-wh_600x-s_1814093216.jpg

于是我拿过她的手机,看到了下面这一幕:

0274ef9716a6359904fbe664f0b877b0.jpg-wh_600x-s_945581617.jpg

这是微信官方出的公众号管理的 App,上面赫然写着一篇文章的发文日期是 2020/12/29。

492abe916821ae8d78fc8b833cde3e28.jpg-wh_600x-s_3908790711.jpg

ed6fd8688d9108e660ce39e07231fd2f.jpg-wh_600x-s_752248804.jpg

6558bb7d0748dde34e64fdd3cd9bfba4.jpg-wh_600x-s_860164422.jpg

edc7e20c899eea887a3e49982e6fc49f.jpg-wh_600x-s_151465264.jpg

SimpleDateFormat

SimpleDateFormat 是 Java 提供的一个格式化和解析日期的工具类。它允许进行格式化(日期→文本)、解析(文本→日期)和规范化。

SimpleDateFormat 使得可以选择任何用户定义的日期-时间格式的模式。

在 Java 中,可以使用 SimpleDateFormat 的 format 方法,将一个 Date 类型转化成 String 类型,并且可以指定输出格式。



  1. // Date转String 
  2. Date data = new Date(); 
  3. SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); 
  4. String dataStr = sdf.format(data); 
  5. System.out.println(dataStr); 

以上代码,转换的结果是:2018-11-25 13:00:00,日期和时间格式由“日期和时间模式”字符串指定。如果你想要转换成其他格式,只要指定不同的时间模式就行了。

在 Java 中,可以使用 SimpleDateFormat 的 parse 方法,将一个 String 类型转化成 Date 类型。



  1. // String转Data 
  2. System.out.println(sdf.parse(dataStr)); 

82530209f72352b9e0a001ee5f986efd.jpg-wh_600x-s_2119933993.jpg

afac70a4591b3a46d2f0c05d82045955.jpg-wh_600x-s_1512673074.jpg

3b446740f7120fabc8622e3ff669d03f.jpg-wh_600x-s_251372317.jpg

f33dec73f0db6ea19840a3e69e71dd0a.jpg-wh_600x-s_1462641515.jpg

日期和时间模式表达方法

在使用 SimpleDateFormat 的时候,需要通过字母来描述时间元素,并组装成想要的日期和时间模式。

常用的时间元素和字母的对应表(JDK 1.8)如下:

24736e5853a908717d1b1f8dc493785c.jpg-wh_600x-s_3109216900.jpg

模式字母通常是重复的,其数量确定其精确表示。如前面我们用过的"yyyy-MM-dd HH:mm:ss"。

eae4359699aa80850e8c1a858530d048.jpg-wh_600x-s_264233408.jpg

628f40d5c9c3c4524a67cf171783256a.jpg-wh_600x-s_3970847877.jpg

4032dd344a4f51ca17b78a0424885f50.jpg-wh_600x-s_3955583587.jpg

0ba1dc49ee8e4cd3303d7f46a1919572.jpg-wh_600x-s_3348930159.jpg

9090148111dd6d52749095071a152111.jpg-wh_600x-s_1375805649.jpg

什么是 Week Year

我们知道,不同的国家对于一周的开始和结束的定义是不同的。如在中国,我们把星期一作为一周的第一天,而在美国,他们把星期日作为一周的第一天。

同样,如何定义哪一周是一年当中的第一周?这也是一个问题,有很多种方式。

比如下图是 2019 年 12 月-2020 年 1 月的一份日历:

a0a9e65dc22b93e82e83ac2590f65e67.jpg-wh_600x-s_3970188827.jpg

到底哪一周才算 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 这三天。

117e4a94fa9bc63ff2a2a49cd8c923c9.jpg-wh_600x-s_2784063271.jpg

d1435055e95ddd0d87adb19dfdadd8c3.jpg-wh_600x-s_3081349581.jpg

980f550aabe5f1c02e8820d0993c87bc.jpg-wh_600x-s_971864476.jpg

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 年的第一周。

d592e7504a3d112d21b3fd2eb32cdbec.jpg-wh_600x-s_3705256374.jpg

7afb8d1cc6528c362b949b748e924bdd.jpg-wh_600x-s_1088042290.jpg

8e5274f04941674dda3a0a7bfe0d2fdf.jpg-wh_600x-s_3574240452.jpg

813ba31dfe00ccfd45e29b37de6f39d1.jpg-wh_600x-s_2712370414.jpg

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 就可以得到一个日期所属的周属于哪一年了。

所以,我们通过代码可以验证:



  1. public class WeekYearTest { 
  2.     public static void main(String[] args) { 
  3.         SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); 
  4.         SimpleDateFormat sdf1 = new SimpleDateFormat("YYYY"); 
  5.         System.out.println(sdf1.format(sdf.parse("2019-12-01"))); 
  6.         System.out.println(sdf1.format(sdf.parse("2019-12-30"))); 
  7.         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 会弹出以下提示:

efeacd0d5db077582f93a3f7a719d202.jpg-wh_600x-s_4227053087.jpg

f17aa27bc058881d7aa11d03b5d28778.jpg-wh_600x-s_3179955703.jpg

c8e74966b99a81cf31960e7dd6b6fc23.jpg-wh_600x-s_4127003362.jpg

38009c5f722d58b4f8b4ad32ecee821f.jpg-wh_600x-s_1563493135.jpg

7c60c0f47c6ad04a61f48223363cccdc.jpg-wh_600x-s_2112414377.jpg

7ddd9deabdacdc71707d79a125573c80.jpg-wh_600x-s_987496080.jpg

b8912bc41e50a0885c83aada561c0478.jpg-wh_600x-s_271142230.jpg

好啦,大家快去排查下你的代码,有没有'YYYY-MM-dd'这种形式的代码吧,如果有的话,一定要改掉哦!~

06b2a54c97a0c09401603bab7aea68d4.gif-wh_600x-s_2946624692.gif

【编辑推荐】

【责任编辑:武晓燕 TEL:(010)68476606】

点赞 0


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK