我这里有一个非常简单的程序:
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,并且很好奇为什么更改了它。
答案 0 :(得分:10)
这样的星期字段是高度本地化的,因此取决于基础JVM的本地化资源,该资源可以从一个发行版更改为另一个发行版。
我认为JVM10更正确,因为Locale.GERMAN
并没有指向任何国家,因此Java简单地假定了美国(将这个国家作为世界标准来处理是有问题的,而Java也是如此)。
您最好使用 Locale.GERMANY
。该国家确实确实将星期一作为一周的第一天(与美国从星期日开始相反,后者被用作GERMAN
的备用,java.locale.providers=COMPAT,CLDR,SPI
只是一种语言,而不是一个国家。
后备国家/地区“ 001”(=全球)的current CLDR data列表中的周定义(星期一为一周的第一天,1 =日历年第一周的最少天)。令人惊讶的是,这与美国定义不同(星期日,1)。我认为,Oracle只是做了自己的事。就个人而言,我同意@Holger的看法,而是希望将ISO-8601作为后备广告(星期一,4)。
但是,您可以通过设置以下system property(未测试)来恢复JVM-10-计算机上的Java-8行为:
com.fasterxml.jackson.databind.exc.InvalidFormatException: Cannot deserialize value of type `java.util.Date` from String "2019-03-11T10:14:14Z[UTC]": not a valid representation (error: Failed to parse Date value '2019-03-11T10:14:14Z[UTC]': Cannot parse date "2019-03-11T10:14:14Z[UTC]": while it seems to fit format 'yyyy-MM-dd'T'HH:mm:ss.SSSZ', parsing fails (leniency? null))
答案 1 :(得分:4)
Locale
枚举区分对语言有用的实例(如GERMAN
)和对国家有用的实例(如GERMANY
)。如果要设置其他语言设置并保留本地Locale
,请使用前者,另一方面,请使用后者来设置时间和语言设置。
答案 2 :(得分:3)
以下两个选项是等效的。选择最适合自己情况的一个。
WeekFields.ISO
WeekFields.of(Locale.GERMANY)
使用国家(德国)而不是德语。这里有两个区别:
在不同语言环境中定义周方案是语言环境数据的一部分。 Java最多可以从四个来源获取其语言环境数据。 Java包含了早期版本中自己的语言环境数据,这些是Java 8之前的默认数据。从Java 8起,CLDR(Unicode通用语言环境数据存储库)数据也包括在内,这些数据成为Java 9的默认数据。功能,并破坏了一些旧代码,如您所见。更准确地说,默认值为:
可以通过设置系统属性java.locale.providers
覆盖默认值。因此,我们可以通过将此属性设置为COMPAT,SPI
来获得Java 9及更高版本中的Java 8行为。相反,我们可以通过将Java 10的行为设置为CLDR,JRE
来获得Java 10的行为。因此,从根本上讲,这在Java版本之间并没有太大的区别,只是它们的默认版本之间。
从Java数据到CLDR数据的更改是这样的:Java语言环境数据将周定义分配给仅基于语言的语言环境(如德语)。相反,CLDR的理念是您可以在世界上任何国家/地区使用任何语言,并且您宁愿根据国家/地区而不是语言来选择周计划。因此,未指定国家/地区的语言环境(例如德语)都使用全球默认周定义。
为什么我不理解CLDR中全球默认周定义为“星期日,1”。与其他人一样,我期望并喜欢使用ISO国际标准“ Monday,4”。正如我在评论中说的那样,我还发现一条便条说应该是这种情况,但事实并非如此(至少在Java 8至11中使用的CLDR版本中不是)。
如您所见,在具有默认语言环境数据的Java 9上,您从Locale.GERMAN
获得“ Monday 4”
即使CLDR应该是第一个默认值。另一方面,如果我单独将java.locale.providers
设置为CLDR
,那么我确实会像Java 10和11一样得到“星期日1”。
可能的解释是,Java 9中使用的CLDR版本不包含德语的星期定义。因此,使用默认提供程序CLDR,COMPAT,Java会退回到COMPAT,COMPAT为德语提供“ Monday,4”。当我单独使用CLDR时,它将改为使用全球范围内的默认默认值“ Sunday,1”。如果这个解释是正确的(我不能保证),则似乎Java 10和11中使用的CLDR数据版本确实包含了德语的星期定义。
LocaleServiceProvider
的文档,其中包含有关区域设置数据提供程序和默认提供程序的规范的信息:
CLDR链接: