【问题标题】:Last week number of year in default Java is not correct默认 Java 中的上周年份数不正确
【发布时间】:2021-09-14 08:30:00
【问题描述】:

代码示例:

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

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

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

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

附: Locale.getDefault() --> en_US

【问题讨论】:

  • 可能取决于系统Locale,因为在我的(德语)系统上,打印了"Default Java week #52"。你的Locale.getDefault() 是什么?您可以拨打DateTimeFormatter.ofPattern(String, Locale)
  • 可能是因为是次年的第01周?
  • @kaya3 是的,但我想得到 12 月 31 日的确切时间。在现有标准中,没有第一周是 12 月 31 日星期日。
  • 好的,但是没有什么可以说默认使用特定的标准。你问为什么它说 01,我提出了一个可能的原因。如果您的问题实际上是“Java 默认使用什么标准(如果有的话)?”那你应该说清楚。
  • @deHaar 好吧,也许吧,但在任何地区都没有这样的标准,即这个日期(12 月 31 日 SUN)是第一周。顺便说一句,我的 Locale.getDefault() 是 en_US

标签: java iso localdate week-number dateformatter


【解决方案1】:

你在比较苹果和香蕉。

格式模式字母w 为您提供week-of-week-based-year。在所有日历系统中,我知道它在 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 周#: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, 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

与您的表格相比,对于美国来说,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", 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 年期间每年的第一周和最后两周的日期测试该方法。

链接

【讨论】:

  • 过去一年只能包含 ISO 标准中的第一周。我的默认格式化程序显然不能以 ISO 格式工作,即使以 ISO 格式工作,去年的一周也不能是第一周,除非 1 月 1 日是周五、周六或周日。在我们的例子中,2001 年 1 月 1 日是星期一。没有普遍接受的标准可以说这个特定日期是指第一周。 (我没有比较,为了清楚起见,我展示了应该是什么以及默认格式化程序返回什么)底线:2000 年 12 月 31 日可能不是任何标准的第一周,但 Java 使它成为第一周。
  • 我使用 IBM 文档,我需要在那里得到确切的日期,正如它所说的那样。 prnt.sc/181k8ux 显然美国标准相当模糊))谢谢
  • 那个很有趣。立即显示,他们对美国使用一年中的一周,对 ISO 使用一周中的一周基准年。但是,如果没有上下文,我无法确定这是怎么回事。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-09-07
  • 2015-10-30
  • 2016-06-25
相关资源
最近更新 更多