什么?一键穿越到2020-12-31?什么啊。今天明明才1月2号。刚刚享受过元旦的快乐,你就告诉我又到年底了?

nonono,实际上是今天我在V站看到了这篇帖子。觉得很有意思。所以拿来分享一下。

写代码的时候,你常用的时间格式是YYYY-MM-dd,还是yyyy-MM-dd呢?

可能这些的区别,你曾经在上课的时候听老师提起过。也可能你从来没有注意过他们的区别。但是无论如何,今年已经2020了。你可一定要搞清楚这些的区别!

我们先来复现一下贴中的BUG:

1
2
3
4
5
6
7
8
9
@Test
void TestYearBug() {
LocalDateTime before2020 = LocalDateTime.of(2019, Month.DECEMBER, 25, 8, 0, 0);
for (int day = 0; day < 8; day++) {
String dateFormatToString = before2020.format(DateTimeFormatter.ofPattern("YYYY-MM-dd"));
System.out.println(dateFormatToString);
before2020 = before2020.plusDays(1L);
}
}

如果你不认得LocalDateTime和DateTimeFormatter的话,那你真的是Out啦,不要再用Date和SimpleDateFormatter啦。快去了解一下这两个崭新的类吧。

话归正题,让我们来看看结果是什么:

😮

什么鬼啊,为什么2019-12-28下面直接就是2020-12-29了?

其实这个问题在 Stack OverFlow 上很早就有讨论了。Y returns 2012 while y returns 2011 in SimpleDateFormat.

我们去Java Doc上翻一翻,很容易就找到答案了:Y是“Week-Based-Year”,而y是“Year”。

那什么是 “Week-Based-Year”呢,我找到的一篇文章中这样解释:

Week number according to the ISO-8601 standard, weeks starting on Monday. The first week of the year is the week that contains that year’s first Thursday (=’First 4-day week’). The highest week number in a year is either 52 or 53.

根据 ISO-8601 标准,一周始于周一,一年的第一周是包含这一年第一个星期四的那一周。因此,2020-01-02,也就是今天,是周四。因此,这周就是2020年第一周。所以作为“Week Year”,YYYY就会把同在本周的2019-….

写到这里我迷惑了。看看上面的运行结果。如果说因为这周是2020第一周,所以把周一周二的12-30、12-31归于2020年,我理解。但是12-29呢?它是属于上周日啊?

于是我就去找了其他地方的文章,最后在掘金看到有人说:YYYYweek-based-year,表示:当天所在的周属于的年份,一周从周日开始,周六结束,只要本周跨年,那么这周就算入下一年。所以2019年12月29日那天在这种表述方式下就已经 2020 年了。

这样说的话就解释了运行结果了。但是我又运行了一下代码:

1
2
3
4
5
6
7
8
9
10
@Test
void TestYearBug() {
LocalDateTime before2020 = LocalDateTime.of(2019, Month.DECEMBER, 25, 8, 0, 0);
for (int day = 0; day < 8; day++) {
String dateFormatToString = before2020.format(DateTimeFormatter.ofPattern("YYYY-MM-dd w e E"));
System.out.print("dayOfWeek:"+before2020.get(WeekFields.ISO.dayOfWeek())+" ");
System.out.println(dateFormatToString);
before2020 = before2020.plusDays(1L);
}
}

运行结果如下:

🤨

难道这不是按照ISO-8601来的吗?所以上面我阅读ISO标准的时候才会被迷惑?

这就留待日后我去找寻答案了。

附表:All letters ‘A’ to ‘Z’ and ‘a’ to ‘z’ are reserved as pattern letters. The following pattern letters are defined:

  Symbol  Meaning                     Presentation      Examples
  ------  -------                     ------------      -------
   G       era                         text              AD; Anno Domini; A
   u       year                        year              2004; 04
   y       year-of-era                 year              2004; 04
   D       day-of-year                 number            189
   M/L     month-of-year               number/text       7; 07; Jul; July; J
   d       day-of-month                number            10

   Q/q     quarter-of-year             number/text       3; 03; Q3; 3rd quarter
   Y       week-based-year             year              1996; 96
   w       week-of-week-based-year     number            27
   W       week-of-month               number            4
   E       day-of-week                 text              Tue; Tuesday; T
   e/c     localized day-of-week       number/text       2; 02; Tue; Tuesday; T
   F       week-of-month               number            3

   a       am-pm-of-day                text              PM
   h       clock-hour-of-am-pm (1-12)  number            12
   K       hour-of-am-pm (0-11)        number            0
   k       clock-hour-of-am-pm (1-24)  number            0

   H       hour-of-day (0-23)          number            0
   m       minute-of-hour              number            30
   s       second-of-minute            number            55
   S       fraction-of-second          fraction          978
   A       milli-of-day                number            1234
   n       nano-of-second              number            987654321
   N       nano-of-day                 number            1234000000

   V       time-zone ID                zone-id           America/Los_Angeles; Z; -08:30
   z       time-zone name              zone-name         Pacific Standard Time; PST
   O       localized zone-offset       offset-O          GMT+8; GMT+08:00; UTC-08:00;
   X       zone-offset 'Z' for zero    offset-X          Z; -08; -0830; -08:30; -083015; -08:30:15;
   x       zone-offset                 offset-x          +0000; -08; -0830; -08:30; -083015; -08:30:15;
   Z       zone-offset                 offset-Z          +0000; -0800; -08:00;

   p       pad next                    pad modifier      1

   '       escape for text             delimiter
   ''      single quote                literal           '
   [       optional section start
   ]       optional section end
   #       reserved for future use
   {       reserved for future use
   }       reserved for future use