WeekFields在JVM 8和JVM 10上的不同行为

问题描述 投票:14回答:3

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

 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

为什么会这样?我做了什么,可能会导致未定义的行为?或者是否在指定某处的行为发生了变化?

ZHVM10:

$ 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 9JVM 8具有相同的行为,JVM 11表现得像JVM 10

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

java java-8 jvm java-10 localdate
3个回答
11
投票

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

我认为JVM10更正确,因为Locale.GERMAN没有引用任何国家,因此Java简单地假定美国(以某种方式将这个国家作为世界标准来处理是有问题的,但Java也是如此)。

你应该更好地使用Locale.GERMANY。这个国家确实使用星期一作为一周的第一天(与周日开始的美国形成对比,后者被用作GERMAN的后备,current CLDR data只是一种语言,而不是一个国家。

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

system property列表为后备国家/地区“001”(=全球)周定义(星期一为星期几,1 =日历年第一周的最小天数)。令人惊讶的是,这与美国定义(星期日,1)不同。我认为,Oracle刚刚做了自己的事情。就个人而言,我同意@Holger,而是期望ISO-8601作为后备(星期一,4)。

但是,您可以通过设置以下java.locale.providers=COMPAT,CLDR,SPI (未测试)来恢复JVM-10计算机上的Java-8行为:

Locale

4
投票

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


4
投票

怎么修

以下两个选项是等效的。选择最适合您情况的那个。

  • WeekFields.of(Locale.GERMANY)
  • java.locale.providers使用国家,德国,而不是语言,德语。

为什么会这样? 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数据的新名称。

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

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

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

Java 9很特别

正如您所观察到的,在Java 9上使用默认语言环境数据,即使CLDR应该是第一个默认值,您也会从java.locale.providers获得“Monday 4”。另一方面,如果我将CLDR单独设置为LocaleServiceProvider,我会在Java 10和11中获得“Sunday 1”。

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

链接

Java 8的文档,包含有关区域设置数据提供程序和默认提供程序规范的信息:

CLDR链接:

© www.soinside.com 2019 - 2024. All rights reserved.