【问题标题】:Week-based pattern does not work as expected基于周的模式无法按预期工作
【发布时间】:2017-09-04 11:47:52
【问题描述】:

我希望这两个格式化程序是等价的:

DateTimeFormatter fromBuilder = new DateTimeFormatterBuilder()
    .appendValue(IsoFields.WEEK_BASED_YEAR, 4)
    .appendLiteral('-')
    .appendValue(IsoFields.WEEK_OF_WEEK_BASED_YEAR, 2)
    .toFormatter();
DateTimeFormatter fromPattern = DateTimeFormatter.ofPattern("YYYY-ww");

但它们给出的结果不同:

LocalDate date = LocalDate.of(2017, 1, 1);
System.out.printf("from builder: %s%n", fromBuilder.format(date));  // prints 'from builder: 2016-52'
System.out.printf("from pattern: %s%n", fromPattern.format(date));  // prints 'from pattern: 2017-01'

我错过了什么?

【问题讨论】:

  • 您的 fromPattern-formatter 不是基于 ISO,而是您的默认语言环境设置为第一天和第一日历周的最短天数。

标签: java date-formatting java-time week-number


【解决方案1】:

Yw 模式 correspond to a localized version of week-fields,使用 JVM 的默认语言环境 (java.util.Locale)。第二个格式化程序相当于:

// localized week fields (using default Locale)
WeekFields weekFields = WeekFields.of(Locale.getDefault());
// equivalent to YYYY-ww
DateTimeFormatter fmt = new DateTimeFormatterBuilder()
    .appendValue(weekFields.weekBasedYear(), 4)
    .appendLiteral('-')
    .appendValue(weekFields.weekOfWeekBasedYear(), 2)
    .toFormatter();

由于这取决于语言环境,它可以或不能像IsoFields 那样工作。上面创建的 WeekFields 将根据 JVM 的默认语言环境具有不同的行为。

另一方面,IsoFields 遵循 ISO-8601 定义来定义基于周的字段,如 described in the javadoc

以周为基础的年的第一周是标准 ISO 年的第一个以星期一为基础的周,新的一年至少有 4 天。

  • 如果 1 月 1 日st 是星期一,则第 1 周从 1 月 1 日st 开始
  • 如果 1 月 1 日st 是星期二,那么第 1 周将从上一个标准年的 12 月 31 日st开始
  • 如果 1 月 1 日st 是星期三,那么第 1 周将从上一个标准年的 12 月 30 日th开始
  • 如果 1 月 1 日st 是星期四,那么第 1 周开始于上一个标准年的 12 月 29 日th
  • 如果 1 月 1 日st 是星期五,那么第 1 周将从 1 月 4 日开始th
  • 如果 1 月 1 日st 是星期六,则第 1 周从 1 月 3 日开始rd
  • 如果 1 月 1 日st 是星期日,则第 1 周从 1 月 2 日开始nd

由于2017-01-01 是星期日,它对应于上面的最后一行:第 1 周开始于 2017 年 1 月 2 日nd,所以 1 月 1 日st 2017 还是 2016 年的最后一周。


您可以通过调用用于calculate the values of the respecitive week-based fields 的方法getFirstDayOfWeek()getMinimalDaysInFirstWeek() 来检查您的WeekFields 实例与IsoFields 的不同之处:

一周的定义是:

  • 一周的第一天。例如,ISO-8601 标准将星期一视为一周中的第一天。
  • 第一周的最少天数。例如,ISO-8601 标准将第一周视为至少需要 4 天。

这两个值一起可以将一年或一个月划分为几周。

在我使用的 JVM 中,默认语言环境是 pt_BR,而创建的 WeekFields 的第一天是星期天,第一周的最少天数是 1。检查你的,你会发现它也不同于IsoFields


您可以使用constant WeekFields.ISO 检查 ISO 的定义:getFirstDayOfWeek() 返回星期一,getMinimalDaysInFirstWeek() 返回 4


另外,请注意IsoFieldsWeekFields.ISO 之间存在细微差别。引用JodaStephen's comment in this thread:

唯一可以观察到的区别是 WeekFields 在所有日历系统上运行(通过转换为 ISO),而 IsoFields 仅在 ISO 上运行(并拒绝其他日历系统)

【讨论】:

  • 很好的答案!我不敢相信模式符号的描述中没有提到这些:docs.oracle.com/javase/8/docs/api/java/time/format/…
  • 后续问题:是否可以编写使用 ISO 周字段的模式?
  • @glerup 我不这么认为,可能唯一的方法是将DateTimeFormatterBuilder 与自定义字段一起使用。
  • @glerup 在DateTimeFormatterBuilder.appendPattern javadoc 中(离散地)提到了它:"Y ... 为基于数字周的年份附加特殊的 localized WeekFields 元素" 等等。但我同意它应该更好地记录在DateTimeFormatter 类中。
  • @glerup 一种替代方法是使用行为类似于 ISO 的语言环境并将其设置在格式化程序中,例如:DateTimeFormatter.ofPattern("YYYY-ww", Locale.UK)。但我不喜欢这个解决方案。在这种情况下,我更喜欢明确使用IsoFields,因为语言环境并没有告诉(至少以特定方式)我想使用基于 ISO 周的字段。而且我不确定特定于语言环境的行为是否会在不同的 Java 版本之间发生变化(可能不会,但无论如何,我宁愿不冒险)。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-05-02
  • 1970-01-01
  • 2019-12-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多