时间(5)

Updated: 2019-01-05

入门

接着上一篇的话说,每个语言格式化时间的方法都是不一样的,但week-based-year的概念是不变的,并不是个 bug,而是个 feature,使用时一定要仔细看文档。比如 bash 中,看看date的文档($ man date),你会发现类似的两个选项:

%G  year of ISO week number
...
%Y  year

打印出来 2018 年 12 月 31 日的“年”看看:

$ date -u "+%G" --date="2018-12-31"
2019

$ date -u "+%Y" --date="2018-12-31"
2018

%G跟 Java 的Y类似,而%Y跟 Java 的y类似。记不住是吧?养成看文档的好习惯吧。

坑!

等等,上一篇的例子是 2018 年 12 月 30 日,这里用的 31 日,为什么不用一样的?因为是个坑嘛!

$ date -u "+%G" --date="2018-12-30"
2018

$ date -u "+%G" --date="2018-12-31"
2019

回顾一下 Java:

jshell> DateTimeFormatter.ofPattern("YYYY-MM-dd").format(LocalDate.of(2018, 12, 30))
$1 ==> "2019-12-30"

之前明明说 2018 年 12 月 30 日是周日,是 2019 第一周的第一天,为什么 bash 里 30 日还是 2018,而 31 日才是 2019 呢?

进阶

好吧,是week-based-year的原理我们还没解释清楚。这个“年”取决于两个因素:

  1. 一周是从周几开始的?周日还是周一?
  2. 第一周至少包含几天在新的一年?只要 1 月 1 号在这周里就算新的一年还是要超过这周半数才算?

Bash 采用的是 ISO 标准:一周从周一开始;至少有 4 天在新的一年才叫做第一周:

jshell> java.time.temporal.WeekFields.ISO.getFirstDayOfWeek()
$1 ==> MONDAY

jshell> java.time.temporal.WeekFields.ISO.getMinimalDaysInFirstWeek()
$2 ==> 4

而 Java 则是取决于地点。以美国为例,一周是从周日开始的,只要有一天在新的一年这周就叫做第一周:

jshell> java.time.temporal.WeekFields.of(Locale.US).getFirstDayOfWeek()
$3 ==> SUNDAY

jshell> java.time.temporal.WeekFields.of(Locale.US).getMinimalDaysInFirstWeek()
$4 ==> 1

比较有趣的是,Locale 中既有CHINA也有CHINESE,但看起来好像差不多。

jshell> java.time.temporal.WeekFields.of(Locale.CHINA).getFirstDayOfWeek()
$5 ==> SUNDAY

jshell> java.time.temporal.WeekFields.of(Locale.CHINESE).getFirstDayOfWeek()
$6 ==> SUNDAY

但不要想当然的认为全世界都是这样的,比如法国FRANCE和法语FRENCH就不同,前者的周从周一开始,而后者从周日开始:

jshell> java.time.temporal.WeekFields.of(Locale.FRANCE).getFirstDayOfWeek()
$7 ==> MONDAY

jshell> java.time.temporal.WeekFields.of(Locale.FRENCH).getFirstDayOfWeek()
$8 ==> SUNDAY