默认 Java 中的最后一周年数不正确

问题描述

代码示例:

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
---------------------

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

美国格式:计算方法 - 第一周是 1 月 1 日发生的那一周,下周从下一个星期日开始。

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

附言Locale.getDefault() --> en_US

解决方法

你在比较苹果和香蕉。

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

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

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

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

    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 周 #:52

Java 不正确吗?

www.timeanddate.com我构建了this 2000 calendar for the United States。在“更多选项”->“高级定制”->“周”下,我选择了“是的 - 1 月 1 日总是在第 1 周”,这适用于美国。正如您在链接中看到的,12 月 31 日现在是第 1 周。

您是对的,在 ISO 中 2000 年 12 月 31 日是在第 52 周。在 ISO 中,其他年份的 12 月 31 日可能在下一年的第 1 周。

用Java模拟IBM的WEEKNUM函数

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

经验

IBM 因此既不遵循 Java 所谓的周中,也不遵循 Java 所称的基于周的年。让我们在链接的示例日期中看看 Java 的意见:

    LocalDate[] sampleDates = {
            LocalDate.of(2000,Month.JANUARY,1),LocalDate.of(2000,2),3),31),LocalDate.of(2014,Month.JUNE,9),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

与您的表格相比,对于美国来说,IBM 似乎正在生产 Java 所谓的“周”。对于 ISO,IBM 生成 Java 所说的基于周的周。

文档

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

  • WEEKNUM 函数将给定的儒略/格里高利日期转换为周数。此功能有 2 个版本,标准美国 格式和 ISO 格式。
  • WEEKNUM=USA 函数返回一个 1 到 54 范围内的整数,表示一年中的第几周。一周从星期日开始,然后 1 月 1 日总是在第一周。
  • WEEKNUM=ISO 函数返回一个 1 到 53 范围内的整数,表示一年中的第几周。一周从星期一开始 包括7天。第 1 周是一年中包含一个 星期四,相当于包含 1 月 4 日的第一周。

虽然这不是非常精确,但它与我之前的解释一致。没有任何地方基于周的年达到 54,因此要在美国发生这种情况,我们需要对年的周进行解释,其中周数可能会在新年从 54 变为 1。相反,如果第 1 周晚于 1 月 1 日开始,则 ISO 年周可能为 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",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 年期间每年的第一周和最后两周的日期测试该方法。

链接