【问题标题】:Different behavior of WeekFields on JVM 8 and JVM 10WeekFields 在 JVM 8 和 JVM 10 上的不同行为
【发布时间】:2019-08-03 23:29:20
【问题描述】:

我这里有一个非常简单的程序:

 public static void main(String[] args) {
        LocalDate year = LocalDate.ofYearDay(2022, 100);
        System.out.println(year);

        System.out.println(WeekFields.of(Locale.GERMAN).weekOfYear());

        System.out.println(year.with(WeekFields.of(Locale.GERMAN).weekOfYear(), 0));
        System.out.println(year.with(WeekFields.of(Locale.GERMAN).weekOfYear(), 0).with(TemporalAdjusters.previousOrSame(DayOfWeek.MONDAY)));
    }

但它在 JVM 8 和 JVM 10 上的行为不同。问题似乎是 WeekFields.of(Locale.GERMAN).weekOfYear() 的实现。

在 JVM 10 上我得到以下结果:

JVM 10

2022-04-10
WeekOfYear[WeekFields[SUNDAY,1]]
2021-12-19
2021-12-13

而在 JVM 8 上:

JVM 8

2022-04-10
WeekOfYear[WeekFields[MONDAY,4]]
2022-01-02
2021-12-27

为什么会这样?我是否在做一些可能导致未定义行为的事情?还是在某处指定了这种行为变化?

JVM10:

$ java -version
openjdk version "10.0.2" 2018-07-17
OpenJDK Runtime Environment (build 10.0.2+13-Ubuntu-1ubuntu0.18.04.4)
OpenJDK 64-Bit Server VM (build 10.0.2+13-Ubuntu-1ubuntu0.18.04.4, mixed mode)

JVM8

$ java -version
openjdk version "1.8.0_191"
OpenJDK Runtime Environment (build 1.8.0_191-8u191-b12-2ubuntu0.18.04.1-b12)
OpenJDK 64-Bit Server VM (build 25.191-b12, mixed mode)

编辑: JVM 9 的行为与 JVM 8 相同,JVM 11 的行为类似于 JVM 10

编辑 2: 我实际上找到了改变行为的提交 -> here on github,我很好奇为什么会改变。

【问题讨论】:

  • 我会亲自将该问题报告给bugreport.java.com/bugreport
  • 还有 jvm-11...
  • a CLDR page really concerned with Ireland 上,我发现了一个有趣的括号:“(因此默认为星期一,001 的默认 firstDay = 世界)”。如果没有指定国家/地区,听起来星期一应该是默认的一周的第一天,例如“德国”(与“德国”相反,后者显然 一个国家)。

标签: java java-8 jvm java-10 localdate


【解决方案1】:

这样的星期字段是高度本地化的,因此依赖于底层 JVM 的本地化资源,这些资源可以从一个版本更改为另一个版本。

我认为 JVM10 更正确,因为 Locale.GERMAN 没有指代任何国家,因此 Java 简单地假设为美国(将这个国家作为世界标准处理有点问题,但 Java 也是如此)。

你最好使用Locale.GERMANY。该国家/地区确实使用星期一作为一周的第一天(相比之下,美国从星期日开始,用作 GERMAN 的后备,这只是一种语言而不是国家/地区。

更新——我对 CLDR 数据的研究:

后备国家/地区“001”(= 全球)的 current CLDR data 列表定义周定义(星期一为一周的第一天,1 = 日历年第一周的最少天数)。令人惊讶的是,这与美国的定义不同(星期日,1)。我认为,甲骨文只是做了自己的事情。就个人而言,我同意@Holger 的观点,并希望 ISO-8601 作为后备(星期一,4)。

但是,您可以通过设置以下 system property(未测试)来恢复 JVM-10 机器上的 Java-8 行为:

java.locale.providers=COMPAT,CLDR,SPI

【讨论】:

  • 我认为,对于 OP 的问题,getMinimalDaysInFirstWeek()getFirstDayOfWeek() 更重要。但是,两者都可以通过使用Locale.GERMANY 来解决。尽管在没有国家/地区而不是 ISO 8601 的情况下获得美国默认值仍然看起来很奇怪。
  • 也许 OP 在他的机器上使用美国语言环境?
  • @Lukas 也许我更新的关于设置系统属性的提示对你有用。
  • @MenoHochschild 感谢您的研究,非常感谢!我只是好奇,为什么这在各种 JVM 上的工作方式不同。因为这是我的代码,所以我使用 Locale.GERMANY 而不是 GERMAN
  • 我相信美国默认值一直是 Java 中缺少语言环境数据的后备方案。由于 Java 1.6 被编码为Locale.ROOT。我更好奇为什么它们似乎也在(较新的)CLDR 中(我也同意预期和首选 ISO)。
【解决方案2】:

如何解决

以下两个选项是等价的。选择您认为最适合您的情况的一种。

  • WeekFields.ISO
  • WeekFields.of(Locale.GERMANY) 使用国家/地区德国,而不是语言德语。

为什么会这样? CLDR 和国家/地区与语言

这里有两个不同之处:

  1. 不同 Java 版本中的默认语言环境数据不同。
  2. 正如其他人所说,仅语言区域设置与包含国家/地区的区域设置之间的区别。

不同地区的周计划定义是地区数据的一部分。 Java 最多可以从四个来源获取其语言环境数据。 Java 包含了它自己的早期版本的语言环境数据,这些是 Java 8 之前的默认设置。从 Java 8 开始,CLDR(Unicode 通用语言环境数据存储库)数据也包括在内,这些成为 Java 9 的默认设置。这显然改变了一些正如您所经历的那样,功能并破坏了一些旧代码。更准确地说,默认值是:

  • Java 8:JRE,SPI,其中 JRE 指的是 Java 自己的语言环境数据。
  • Java 9、10 和 11:CLDR,COMPAT,其中 CLDR 是所说的,而 COMPAT 只是 JRE 数据的新名称。

可以通过设置系统属性java.locale.providers 来覆盖默认值。因此,我们可以通过将此属性设置为COMPAT,SPI 来获取 Java 9 及更高版本中的 Java 8 行为。相反,我们可以通过将其设置为CLDR,JRE 来获得 Java 8 中的 Java 10 行为。所以在它的基础上,这在 Java 版本之间并没有太大的区别,只是在它们的默认值之间。

从 Java 到 CLDR 数据的变化是这样的:Java 区域设置数据根据主要使用语言的地区将周定义分配给仅语言区域设置(如德语)。相比之下,CLDR 的理念是您可以在世界上任何国家说任何语言,并且您宁愿根据国家/地区而不是语言来选择周计划。因此,未指定国家/地区(如德语)的语言环境都使用全球默认周定义。

为什么 CLDR 中的全球默认周定义是“星期日,1”我不明白。和其他人一样,我会期望并更喜欢 ISO,即国际标准“星期一,4”。正如我在评论中所说,我还发现一个注释说应该是这种情况,但它仍然不是(至少在 Java 8 到 11 中使用的 CLDR 版本中不是)。

Java 9 很特别

正如您所观察到的,在具有默认语言环境数据的 Java 9 上,您会从 Locale.GERMAN 获得“星期一 4” 即使 CLDR 应该是第一个默认值。另一方面,如果我将 java.locale.providers 单独设置为 CLDR,我会得到“星期日 1”,就像在 Java 10 和 11 中一样。

一个可能的解释是 Java 9 中使用的 CLDR 版本不包括德语的星期定义。因此,对于默认提供程序 CLDR、COMPAT,Java 会退回到 COMPAT,它为德语提供“星期一,4”。当我单独使用 CLDR 时,它转而使用全球基本默认值“Sunday, 1”。如果这个解释是正确的(我不能保证),Java 10 和 11 中使用的 CLDR 数据版本似乎确实包含德语的星期定义。

链接

LocaleServiceProvider 的文档,其中包含有关区域设置数据提供者和默认提供者规范的信息:

CLDR 链接:

【讨论】:

    【解决方案3】:

    Locale 枚举区分对语言有用的实例(如GERMAN)和对国家有用的实例(如GERMANY)。如果您想设置不同的语言设置并保留本地 Locale,请使用第一个,另一方面,使用后者来设置时间和语言设置。

    【讨论】:

      猜你喜欢
      • 2012-12-14
      • 1970-01-01
      • 2012-02-01
      • 2016-06-01
      • 2010-12-30
      • 2014-08-11
      • 2014-06-05
      • 2018-12-30
      • 2013-09-16
      相关资源
      最近更新 更多