时间(4)
入门。
不同的语言有不同的格式化时间的方法,如 Java 中用M
表示月,d
表示日,有趣的是Y
和y
都可以表示年,而且看起来好像也没啥差别:
jshell> import java.time.format.DateTimeFormatter;
jshell> import java.time.LocalDate;
jshell> DateTimeFormatter.ofPattern("YYYY-MM-dd").format(LocalDate.of(2018, 10, 1))
$1 ==> "2018-10-01"
jshell> DateTimeFormatter.ofPattern("yyyy-MM-dd").format(LocalDate.of(2018, 10, 1))
$2 ==> "2018-10-01"
YYYY
和yyyy
的结果完全一样。难道是不分大小写?
放弃?
别急着下结论,看看这两个:
jshell> DateTimeFormatter.ofPattern("YYYY-MM-dd").format(LocalDate.of(2018, 12, 30))
$3 ==> "2019-12-30"
jshell> DateTimeFormatter.ofPattern("yyyy-MM-dd").format(LocalDate.of(2018, 12, 30))
$4 ==> "2018-12-30"
从国庆改到年底,YYYY
就迫不及待的跳到下一年去了。难道是java.time
引入的 bug?用老办法试试:
jshell> new java.text.SimpleDateFormat("YYYY-MM-dd").format(new java.util.Date("12/30/2018"))
$5 ==> "2019-12-30"
jshell> new java.text.SimpleDateFormat("YYYY-MM-dd").parse("2018-12-30")
$6 ==> Sun Dec 31 00:00:00 GMT 2017
同样的format
的结果跑到了 2019,而parse
的结果回到了 2017...
进阶!
显然YYYY
和yyyy
是不一样的,那区别在哪的?注意看文档,y
是year-of-era
,也就是日历年;而Y
则是week-based-year
,基于周的年,因为 1 月 1 号可能出现在一周的任何一天,比如 2019 年的 1 月 1 号是周二,2018 年的 12 月 30 号是周日。如果一周是从周日开始算的话,2018 年 12 月 30 号确实是在 2019 年的第一周,所以当把这一天格式化输出的时候,YYYY
一看,已经2019
了;而由于年月日是分别进行格式化的,所以MM
和dd
还是正确的12
和30
,导致结果为2019-12-30
。
同样道理,当用YYYY
去解析2018
的时候,YYYY
的搭档是w
(week-of-week-based-year
)和e
(localized day-of-week
),也就是第几周和第几天,而MM
(12)和dd
(30)这两个猪队友没有提供任何有效的信息,所以只能当做是默认的第一周第一天,也就是 2018 年的第一周的那个周日,就是 2017 年的 12 月 31 日。来验证一下:
jshell> DateTimeFormatter.ofPattern("YYYY-w-e").parse("2018-1-1")
$7 ==> {},ISO resolved to 2017-12-31
YYYY-w-e
表示基于周的年-第几周-这一周的第几天
,2018 年第一周第一天果然就是2017-12-31
。
这么说感觉像是个很不起眼的小问题,但 Twitter 就曾被这个小问题给坑了:2014 年年底,由于错误的使用了week-based-year
,Twitter 的安卓客户端突然以为自己是在 2015 年的年底了,很多用户被迫被 log out,并无法登陆;即使问题修复之后,很多用户的时间线也因此乱了。从原始新闻的截图可以看到,出问题的是 2014 年 12 月 29 号:
jshell> DateTimeFormatter.ofPattern("YYYY-MM-dd").format(LocalDate.of(2014, 12, 29))
$8 ==> "2015-12-29"