上周的默认年份数 Java 不正确

Last week number of year in default Java is not correct

代码示例:

LocalDate date = LocalDate.of(2000, 12, 31);
System.out.println("Default Java week #" + date.format(DateTimeFormatter.ofPattern("ww")));
System.out.println("Iso week #" + date.get(WeekFields.ISO.weekOfYear()));
System.out.println("Usa week #" + date.get(WeekFields.SUNDAY_START.weekOfYear()));
---------------------
Default Java week #01
Iso week #52
Usa week #54
---------------------

ISO 8601 对第 01 周的定义是公历年(即一月)的第一个星期四。以下基于本周属性的定义是相互等价的,因为 ISO 周从星期一开始: 这是第一周,其大部分天数(4 天或更多)在一月。

美国格式:计算方法-第一周为1月1日,下周从下周日开始。

2000 年 12 月 31 日 - 星期日。参考周的标准,12 月 31 日(星期日)是任何标准(54 或 52)中最后一年的周。为什么Java说是01周?

P.S。 Locale.getDefault() --> en_US

你在比较苹果和香蕉。

格式模式字母 w 为您提供 基于周的年份。在所有日历系统中,我知道这是在 1 到 53 的范围内。它使用格式化程序区域设置的周编号方案。在您的情况下,这是 JVM 的默认语言环境,因为您没有明确设置语言环境。在许多(几乎所有?)周编号系统中,第 1 周有时可能在新年之前开始。在这种情况下,第 1 周是 12 月 31 日。

weekOfYear() 为您提供一年中的第几周,从 0 到 54。它不同于新年前后(并且仅在那时)基于一周的一周。与基于年的周周相反,年周总是为您提供与日历年相关的周数,在您的示例中为 2000 年,因此 12 月 31 日永远不会为 1。

如何从格式化程序中获取期望值

格式化程序也可以使用 WeekFields 对象中的 weekOfYear(),以便您获得一致的结果。例如:

    LocalDate date = LocalDate.of(2000, Month.DECEMBER, 31);
    
    DateTimeFormatter formatter = new DateTimeFormatterBuilder()
            .appendValue(WeekFields.ISO.weekOfYear())
            .toFormatter();

    System.out.println("Iso week # from formatter: " + date.format(formatter));

输出:

Iso week # from formatter: 52

Java不正确吗?

www.timeanddate.com I constructed this 2000 calendar for the United States。在“更多选项”->“高级定制”->“周数”下,我选择适合美国的“是 - 1 月 1 日始终在第 1 周”。正如您在 link 中看到的那样,12 月 31 日现在是第 1 周。

在 ISO 中,2000 年 12 月 31 日在第 52 周是正确的。在 ISO 中,其他年份的 12 月 31 日也可能在次年的第 1 周。

在Java

中模拟IBM的WEEKNUM函数

编辑:很明显,IBM 和 Java 并不完全同意周数的定义。与其讨论谁对谁错,我更想在 Java 中模拟 IBM 的方式,因为我知道这才是你真正需要的。

根据经验

IBM 因此既不遵循 Java 所谓的一年中的一周,也不遵循 Java 所谓的一年中的一周。让我们看看 Java 在您的 link:

样本日期中的意见
    LocalDate[] sampleDates = {
            LocalDate.of(2000, Month.JANUARY, 1),
            LocalDate.of(2000, Month.JANUARY, 2),
            LocalDate.of(2000, Month.JANUARY, 3),
            LocalDate.of(2000, Month.DECEMBER, 31),
            LocalDate.of(2014, Month.JANUARY, 2),
            LocalDate.of(2014, Month.JUNE, 9),
            LocalDate.of(2014, Month.DECEMBER, 31) };
    
    System.out.println("              US          US       ISO         ISO");
    System.out.println("Input date  week based  of year  week based  of year");
    for (LocalDate date : sampleDates) {
        System.out.format("%s    %02d          %02d        %02d          %02d%n", date,
                date.get(WeekFields.SUNDAY_START.weekOfWeekBasedYear()), 
                date.get(WeekFields.SUNDAY_START.weekOfYear()), 
                date.get(WeekFields.ISO.weekOfWeekBasedYear()), 
                date.get(WeekFields.ISO.weekOfYear()));
    }

输出:

              US          US       ISO         ISO
Input date  week based  of year  week based  of year
2000-01-01    01          01        52          00
2000-01-02    02          02        52          00
2000-01-03    02          02        01          01
2000-12-31    01          54        52          52
2014-01-02    01          01        01          01
2014-06-09    24          24        24          24
2014-12-31    01          53        01          53

与您的 table 相比,美国 IBM 似乎正在生产 Java 所谓的一年中的一周。对于 ISO,IBM 生成了 Java 所谓的基于周的年份。

文档

我相信我找到了与您一致的 IBM 的 WEEKNUM 函数的描述。它是:

  • WEEKNUM function converts a given Julian/Gregorian date to number of week. There are 2 versions of this function, the standard USA format and the ISO format.
  • WEEKNUM=USA function returns an integer in the range of 1 to 54 that represents the week of the year. The week starts with Sunday, and January 1 is always in the first week.
  • WEEKNUM=ISO function returns an integer in the range of 1 to 53 that represents the week of the year. The week starts with Monday and includes 7 days. Week 1 is the first week of the year to contain a Thursday, which is equivalent to the first week containing January 4.

虽然这不是非常精确,但它与我之前的解释一致。基于年的周数没有任何地方可以达到 54,因此要在美国发生这种情况,我们需要年中的周解释,其中周数可能会在新年从 54 变为 1。相反,如果第 1 周开始时间晚于 1 月 1 日,ISO week-of-year 可能为 0,但 IBM 不允许为 0,因此这里他们可能使用基于年的周。

在Java中模拟所有这些还不错:

public static enum IBM_WEEKNUM_VERSION { USA, ISO };

public static int ibmWeeknum(IBM_WEEKNUM_VERSION version, LocalDate date) {
    switch (version) {
    case USA:
        return date.get(WeekFields.SUNDAY_START.weekOfYear());
    case ISO:
        return date.get(WeekFields.ISO.weekOfWeekBasedYear());
    default:
        throw new IllegalArgumentException(String.valueOf(version));
    }
}

在相同的样本日期试用:

    for (LocalDate date : sampleDates) {
        System.out.format("%s    %02d          %02d%n", date,
                ibmWeeknum(IBM_WEEKNUM_VERSION.USA, date),
                ibmWeeknum(IBM_WEEKNUM_VERSION.ISO, date));
    }
2000-01-01    01          52
2000-01-02    02          52
2000-01-03    02          01
2000-12-31    54          52
2014-01-02    01          01
2014-06-09    24          24
2014-12-31    53          01

一方面,它 100% 同意你的例子。另一方面,根据我们所知,我们不能 100% 确定 IBM 职能部门的工作情况。例如,我建议您在 20 年的每一年的第一周和最后两周的日期测试该方法。

链接