Java 日历 WEEK_OF_YEAR 不符合 ISO-8601 标准?

Java Calendar WEEK_OF_YEAR not ISO-8601compliant?

ISO-8601 标准规定

"The first week of a year is the week that contains the first Thursday of the year (and, hence, always contains 4 January)."

意味着一年中的第一周不是包含 1 月 1 日的那一周,而是包含新年至少四天的第一周。

根据那个星期一,2016 年 1 月 11 日是第 2 周。 Here is a list of week numbers for 2016.

Ubuntu 反映在其时间部件中:

并且 cal 命令也执行:

Oracle用TO_CHAR的"iw"参数支持它:

> select to_char(to_date('11/01/2016','dd/mm/yyyy'),'iw') weekno from dual;
> WEEKNO
    02

但是 Java 说 2016 年 1 月 11 日星期一是第 3 周

Calendar c = Calendar.getInstance();
System.out.println(c.getTime());
System.out.println(c.get(Calendar.WEEK_OF_YEAR));

Output:
Mon Jan 11 09:02:35 VET 2016
3

Java 认为一年中的第一周是包含 1 月 1 日的那一周。

- Java 有没有办法使用符合 ISO-8601 标准的周编号?

正如我在评论中指出的那样,默认行为 是特定于语言环境的。有些语言环境会给出 3,有些会给出 2。

幸运的是,对于给定的 Calendar,您可以指定一年中第一周必须出现的天数。正如你在上面所写的,对于 ISO 8601,这个数字是 4,因此下面的代码应该有效:

Calendar c = Calendar.getInstance();
c.setMinimalDaysInFirstWeek(4); // For ISO 8601
System.out.println(c.getTime());
System.out.println(c.get(Calendar.WEEK_OF_YEAR));

无论区域设置如何,这都应该使输出正确。

测试输出:

Mon Jan 11 14:54:22 CET 2016
2

tl;博士

myZonedDateTime.get( IsoFields.WEEK_OF_WEEK_BASED_YEAR ) 

……和……

myZonedDateTime.get( IsoFields.WEEK_BASED_YEAR )

避免遗留 date-time classes

正如 所解释的那样,Calendar class 对周的定义因地区而异。而 well-intentioned,这令人困惑。

您应该避免使用 Calendar 和相关的 class,例如 Date。它们现在被 java.time classes 所取代。

ISO 8601 周

至于ISO 8601 week,要清楚意思是:

  • 第一天是星期一,运行 到星期日。
  • week-based 年的第一个星期包含日历年的第一个星期四。
  • week-based 年有 52 周或 53 周。
  • 日历年的first/last几天可能出现在previous/nextweek-based年。

java.time

java.timeclasses 包括对 ISO 8601 standard weeks. Call the get method on various classes such as LocalDate and ZonedDateTime. Pass the TemporalField implementations found as constants in the IsoFieldsclass.

的有限支持
int week = myZonedDateTime.get( IsoFields.WEEK_OF_WEEK_BASED_YEAR ) ;
int weekBasedYear = myZonedDateTime.get( IsoFields.WEEK_BASED_YEAR ) ;

ThreeTen-Extra

更好的是,添加 ThreeTen-Extra library to your project to use YearWeek class.

org.threeten.extra.YearWeek yw = YearWeek.from( myZonedDateTime ) ;

注意日历软件设置

永远不要假设周数的定义。请确保此类数字的来源与您具有相同的周定义,例如 ISO 8601 定义。

例如,Calendar app supplied by Apple with macOS defaults to a "Gregorian" calendar definition of week. As for what that means, I do not know as I could not find any documentation about their intent/definition. For ISO 8601 weeks, you must change a setting away from default.


关于java.time

java.time framework is built into Java 8 and later. These classes supplant the troublesome old legacy date-time classes such as java.util.Date, Calendar, & SimpleDateFormat.

Joda-Time project, now in maintenance mode, advises migration to the java.time classes.

要了解更多信息,请参阅 Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310

使用 JDBC driver compliant with JDBC 4.2 或更高版本,您可以直接与数据库交换 java.time 对象。不需要字符串,也不需要 java.sql.* classes.

从哪里获得java.time classes?

  • Java SE 8, Java SE 9,及以后
    • Built-in。
    • 标准 Java API 的一部分,带有捆绑实施。
    • Java 9 添加了一些小功能和修复。
  • Java SE 6 and Java SE 7
    • java.time 的大部分功能是 back-ported 到 Java ThreeTen-Backport 中的 6 和 7。
  • Android
    • Android java.time classes.
    • 捆绑实施的更高版本
    • 对于较早的 Android,ThreeTenABP project adapts ThreeTen-Backport (mentioned above). See

ThreeTen-Extra project extends java.time with additional classes. This project is a proving ground for possible future additions to java.time. You may find some useful classes here such as Interval, YearWeek, YearQuarter, and more.