Java DateTimeFormatter基于周的年差异
我正在将我的应用程序从Joda Time迁移到Java8Java DateTimeFormatter基于周的年差异,java,datetime,java-8,java-time,dayofweek,Java,Datetime,Java 8,Java Time,Dayofweek,我正在将我的应用程序从Joda Time迁移到Java8Java.Time 我遇到的其中一件事是,使用中的模式打印以周为单位的年份 注:我看到这个问题: 根据文件 y year-of-era year 2004; 04 Y week-based-year year 1996; 96 然而,当我尝试这两种方法时,Y似乎总是返回与Y相同的结果 我的测试代码: Dat
Java.Time
我遇到的其中一件事是,使用中的模式打印以周为单位的年份
注:我看到这个问题:
根据文件
y year-of-era year 2004; 04
Y week-based-year year 1996; 96
然而,当我尝试这两种方法时,Y
似乎总是返回与Y
相同的结果
我的测试代码:
DateTimeFormatter yearF = DateTimeFormatter.ofPattern("yyyy").withZone(ZoneOffset.UTC);
DateTimeFormatter weekYearF = DateTimeFormatter.ofPattern("YYYY").withZone(ZoneOffset.UTC);
DateTimeFormatter dateTimeFormatter = new DateTimeFormatterBuilder()
.appendValue(ChronoField.YEAR_OF_ERA) .appendLiteral(" ") .append(yearF)
.appendLiteral(" -- ")
.appendValue(IsoFields.WEEK_BASED_YEAR) .appendLiteral(" ") .append(weekYearF)
.toFormatter()
.withZone(ZoneOffset.UTC);
System.out.println(dateTimeFormatter.toString());
ZonedDateTime dateTime = ZonedDateTime.ofInstant(Instant.ofEpochMilli(946778645000L), ZoneOffset.UTC);
for (int i = 2000 ; i < 2020; i ++ ) {
System.out.println(dateTime.withYear(i).format(dateTimeFormatter));
}
查看重要年份(如2000年、2005年、2009年和2016年),模式(“YYYY”)的.appendValue(IsoFields.WEEK\u-BASED\u-YEAR)
和.appendValue的输出是不同的
其中指出,这与本地化有关(可以清楚地看出,DateTimeFormatter
的toString()
中存在差异)
现在有几件事我不明白/不需要:
所以“以周为基础的年份”因地区而异。然而,我不明白的是,显然在某些地区,周基准年总是与“正常”年相同。为什么呢
为什么没有将YYYY
的解析映射到ISO-8601定义,而不是(令人非常困惑的!)本地化形式
我在哪里可以找到这方面的适当文件?至少可以说,甲骨文明显的“官方”文件是模糊的。
答案:我在
根据,基于周的年字段取决于两件事:一周的第一天是什么,以及第一周的最小天数
ISO标准将星期一定义为一周的第一天,第一周至少4天:
System.out.println(WeekFields.ISO.getFirstDayOfWeek()); // Monday
System.out.println(WeekFields.ISO.getMinimalDaysInFirstWeek()); // 4
(WeekFields.ISO.weekBasedYear()
相当于IsoFields.WEEK\u-BASED\u-YEAR
,带有)
例如,考虑到2009年1月2日,星期五。检查:
第一周(1)是从getFirstDayOfWeek()开始的一周,其中一年中至少有getMinimalDaysInFirstWeek()天。因此,第一周可能在年初之前开始
考虑到ISO定义(一周从周一开始,第一周的最少天数为4天),第1周从2008年12月29日开始,到2009年1月4日结束(这是从周一开始的第一周,2009年至少有4天),因此2009年1月2日的周年等于2009年(ISO定义):
但是,如果我考虑<代码>周字段< /代码>实例,一周的第一天是星期日,第一周的最小天数是4:
WeekFields wf = WeekFields.of(new Locale("en", "MT"));
System.out.println(wf.getFirstDayOfWeek()); // Sunday
System.out.println(wf.getMinimalDaysInFirstWeek()); // 4
System.out.println(dt.get(wf.weekBasedYear())); // 2008
System.out.println(dt.get(wf.weekOfWeekBasedYear())); // 53
2009年从星期日开始,至少有4天的第一周是从1月4日到10日的一周。因此,根据en_MT
locale的周定义,2009年1月2日属于基于周的年份2008年的53周
现在,如果我选择,本周从周六开始,第一周的最小天数为1:
WeekFields wf = WeekFields.of(new Locale("ar", "SA"));
System.out.println(wf.getFirstDayOfWeek()); // Saturday
System.out.println(wf.getMinimalDaysInFirstWeek()); // 1
System.out.println(dt.get(wf.weekBasedYear())); // 2009
System.out.println(dt.get(wf.weekOfWeekBasedYear())); // 1
对于该地区,第一周从2008年12月27日开始,到2009年1月2日结束(这是第一周从周六开始,2009年至少有一天)。因此,2009年1月2日的ar_SA
地区的基于周的年份也为2009年(我使用IsoFields
,得到的值相同,尽管周定义与ISO完全不同)
当IsoFields.WEEK\u-BASED\u-YEAR
使用ISO的定义时,模式YYYY
将使用与格式化程序中设置的区域设置相对应的WeekFields
实例(或者JVM默认区域设置,如果未设置)
根据每个区域设置的定义(一周的第一天和第一周的最小天数),本地化模式(yyy
)中基于周的年份可能具有相同(或不相同)的ISO字段值
虽然听起来很奇怪,一周可以在另一年开始或结束,但报告说这是完全正确的:
一年的第一周和最后几周可能分别包含上一个日历年或下一个日历年的天数
java.time
的模式字母(The)。说:
Y表示的年份通常从区域设置的一周的第一天开始,到一周的最后一天结束
无论如何,CLDR都是关于本地化的,所以Y
也是本地化的-如下所述:
CLDR的全部目的是本地化,“Y”模式字母是本地化的。虽然我理解人们对总是使用ISO规则的模式字母的渴望,但它并不存在,要让CLDR添加它是很难做到的。(Java正在密切关注CLDR)
我的结论是,如果您想要ISO week字段,请不要使用本地化模式。或者,作为一种不理想、相当丑陋的解决方法,使用一个与ISO的周定义相匹配的区域设置(在我的JVM中,locale.FRENCH
起作用,正如WeekFields.ISO.equals(WeekFields.of(locale.FRENCH))
返回true
)。唯一的问题是,区域设置也会影响其他字段(如果您有月或周名称,如MMM
或EEE
,以及任何其他区域设置敏感数据)。对于第一个问题,2000年1月2日仍然是1999年第52周。这让你回到1999年。2005年和2016年也是如此。@YoshuaNahar:我的意思是,我希望每年和每周都会有所不同。我感到惊讶的是,显然在某些地区没有区别。CLDR的全部目的是本地化,所以是的,“Y”模式字母是本地化的。虽然我理解人们对总是使用ISO规则的模式字母的渴望,但它并不存在,要让CLDR添加它是很难做到的。(Java正在密切关注CLDR)。然而,这里的关键是,与Java7不同,可以创建一个可以
WeekFields wf = WeekFields.of(new Locale("en", "MT"));
System.out.println(wf.getFirstDayOfWeek()); // Sunday
System.out.println(wf.getMinimalDaysInFirstWeek()); // 4
System.out.println(dt.get(wf.weekBasedYear())); // 2008
System.out.println(dt.get(wf.weekOfWeekBasedYear())); // 53
WeekFields wf = WeekFields.of(new Locale("ar", "SA"));
System.out.println(wf.getFirstDayOfWeek()); // Saturday
System.out.println(wf.getMinimalDaysInFirstWeek()); // 1
System.out.println(dt.get(wf.weekBasedYear())); // 2009
System.out.println(dt.get(wf.weekOfWeekBasedYear())); // 1