YYYY-ww 的 DateTimeFormatter 在 mac 和 ubuntu 上的解析不同

DateTimeFormatter of YYYY-ww resolves differently on mac and ubuntu

System.out.println(
    DateTimeFormatter.ofPattern("YYYY-ww").withZone(ZoneOffset.UTC).format(Instant.parse("2022-05-10T00:00:00.00Z"))
);
System.out.println(
    DateTimeFormatter.ofPattern("YYYY-ww").withZone(ZoneOffset.UTC).format(Instant.parse("2022-05-17T00:00:00.00Z"))
);

为什么这个模式 YYYY-ww 在 Ubuntu 和 Mac 上的解析不同:

Ubuntu:(默认语言环境 en_US,我的电脑)

2022-20
2022-21

Mac:(默认语言环境 en_GB)

2022-19
2022-20

编辑

System.out.println(
    DateTimeFormatter.ofPattern("YYYY-ww").withLocale(Locale.UK).withZone(ZoneOffset.UTC).format(Instant.parse("2022-05-10T00:00:00.00Z"))
);
System.out.println(
    DateTimeFormatter.ofPattern("YYYY-ww").withLocale(Locale.UK).withZone(ZoneOffset.UTC).format(Instant.parse("2022-05-17T00:00:00.00Z"))
);

returns:

2022-19
2022-20 

不过,问题是为什么模式 ww 是 Locale 特定的?我在 https://docs.oracle.com/javase/8/docs/api/java/time/temporal/WeekFields.html 的文档中没有看到这一点 或 https://docs.oracle.com/javase/8/docs/api/java/time/format/DateTimeFormatter.html

在美国,一年的第一周可以有 1 到 7 天,而在英国,一年的第一周至少需要 4 天。

WeekFields.of(Locale.UK).getMinimalDaysInFirstWeek() // 4
WeekFields.of(Locale.US).getMinimalDaysInFirstWeek() // 1

此外,美国的一周从周日开始,而英国的一周从周一开始。

这意味着对于美国来说,2022 年的第一周是 2022-01-01 的单个星期六,2022-01-02 是第二周的开始。另一方面,对于英国,2022 年的第一周从 2022-01-03 开始​​,因为一年的前两天不构成一周。这就是美国机器额外一周的来源。

使用 ofPattern 创建 DateTimeFormatter 时,使用机器的默认格式化语言环境:

The formatter will use the default FORMAT locale. This can be changed using withLocale(Locale) on the returned formatter

因此有所不同。

我猜你想在此处使用标准 ISO 8601 weeks,而不想与语言环境有任何关系。一种方法是使用 IsoFields:

构造一个 DateTimeFormatter
var dtf = new DateTimeFormatterBuilder()
  .  appendValue(IsoFields.WEEK_BASED_YEAR, 4)
    .appendLiteral('-')
    .appendValue(IsoFields.WEEK_OF_WEEK_BASED_YEAR, 2)
    .toFormatter().withZone(ZoneOffset.UTC);
System.out.println(
    dtf.format(Instant.parse("2022-05-10T00:00:00.00Z"))
);
System.out.println(
    dtf.format(Instant.parse("2022-05-17T00:00:00.00Z"))
);

“周”的定义因地区而异

是正确的。一周有多种定义,它们因地区而异。

您问的是:

Still, the question is why pattern ww is Locale specific?

因为的定义是文化。

即使将我们的范围限制在西方目前常见的 seven-day 周,这七天中的哪一天是一周的第一天?星期一在欧洲很常见,而美国则使用星期日。第一周应该从一周的第一天的第一次出现开始吗?或者一周应该从 1 月 1 日开始?

ISO 8601 标准

如果您想要一致性,请考虑使用标准 ISO 8601 周定义:

  • 星期一是一周的第一天,星期天是最后一天。
  • 第 1 周包含日历年的第一个星期四。
  • week-based 年有 52 或 53 个完整的 7 天周。
  • 第一周和最后一周可能分别是 previous/next 日历年的零天或多天。

标准格式是 four-digit 年、连字符、一个 W 和两位数的周数。标准格式经过巧妙设计,可以避免歧义,易于机器解析,并且可以被不同文化背景的人理解。

要使用 ISO 8601 周,我建议添加 ThreeTen-Extra library to your project for its YearWeek class。

YearWeek yw = YearWeek.parse( "2022-W19" ) ;
String output = yw.toString() ;

按日期获取 year-week。

LocalDate ld = LocalDate.parse( "2022-05-10" ) ;
YearWeek yw = YearWeek.from( ld ) ;

从以标准 ISO 8601 格式的文本表示的 UTC 时刻获取 year-week。下面看到的末尾的 Z 表示与 UTC 的偏移量为零 hours-minutes-seconds,发音为“Zulu”。

String input = "2022-05-10T00:00:00.00Z" ; 
Instant instant = Instant.parse( input ) ;
OffsetDateTime odt = instant.at( ZoneOffset.UTC ) ;
YearWeek yw = YearWeek.from( odt ) ;