DateTimeFormatter基于周的年差异

问题描述

在 场,根据的Javadoc,取决于两两件事:什么是一周的第一天,天在第一周的最低数量

ISO标准将星期一定义为一周中的第一天,并且在第一周中至少要有4天:

System.out.println(WeekFields.ISO.getFirstDayOfWeek()); // Monday
System.out.println(WeekFields.ISO.getMinimalDaysInFirstWeek()); // 4

WeekFields.ISO.weekBasedYear()相当于IsoFields.WEEK_BASED_YEAR,与其他日历系统略有不同

考虑到,例如,1月2日第二 2009年,这是一个星期五。检查javadoc中 字段

第一周(1)是从getFirstDayOfWeek()开始的一周,一年中至少有getMinimalDaysInFirstWeek()天。因此,第一周可能会在今年年初之前开始。

考虑到ISO定义(周从星期一开始,第一周的最少天数是4),第1周从2008 年12月29 日开始,到2009年1月4 日结束(这是从星期一开始的第一周,并且至少有4周2009年天),因此1月2日第二 2009年以来一个 相当于2009年(与ISO的定义):

// January 2st 2009
LocalDate dt = LocalDate.of(2009, 1, 2);
System.out.println(dt.get(WeekFields.ISO.weekBasedYear())); // 2009
System.out.println(dt.get(WeekFields.ISO.weekOfWeekBasedYear())); // 1
// WeekFields.ISO and IsoFields are equivalent
System.out.println(dt.get(IsoFields.WEEK_BASED_YEAR)); // 2009
System.out.println(dt.get(IsoFields.WEEK_OF_WEEK_BASED_YEAR)); // 1

但是,如果我考虑一个语言环境WeekFields实例en_MT(英语(马耳他)),则一周的第一天为星期日,而第一周的最少天数为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的语言环境,1月2日第二 2009年属于 的的 2008年。

现在,如果我使用ar_SA语言环境(阿拉伯语(沙特阿拉伯)),则该周从星期六开始,第一周的最少天数为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

对于这个区域,本周1点开始在12月27日日 2008年结束在1月2日第二日(它的第一个星期是在一个周六开始,并具有至少1天,2009年)。因此, 的1月2日第二,2009年ar_SA区域也是2009年(我已经使用了相同的值IsoFields, )。


IsoFields.WEEK_BASED_YEAR使用ISO的定义时,该模式YYYY将使用与WeekFields格式化程序中设置的语言环境相对应的实例(如果未设置,则为JVM默认语言环境)。

根据每个语言环境的定义(一周的第一天和第一周的最少天数),本地化模式()中 YYYY可能与ISO字段的值相同(或不相同)。

尽管一个星期可以在另一年开始或结束听起来很奇怪,但是javadoc说它是完全有效的:

一年的第一周和最后一周可能分别包含前一个日历年或下一个日历年的天。


模式字母java.time 基于CLDR(Unicode通用语言环境数据存储库)。关于基于周的模式的此链接说:

Y表示的年份通常从 的一周的第一天开始,到一周的最后一天结束

无论如何,CLDR都是关于本地化的,Y本地化也是如此-正如斯蒂芬·科尔本(StephenColebourne)在下面[的评论中所述:

CLDR的全部目的是本地化,因此,是的,“ Y”模式字母已本地化。尽管我了解对希望始终使用ISO规则运行的模式字母的渴望,但它并不存在,要添加CLDR很难甚至不可能。(Java紧紧追随CLDR)


我的结论是,如果要使用ISO周字段,请不要使用本地化模式。或者,作为一种(不太理想,非常丑陋的)解决方法,请使用与ISO星期定义匹配的语言环境(在我的JVM中,请按提示进行Locale.FRENCH操作,作为WeekFields.ISO.equals(WeekFields.of(Locale.FRENCH))returntrue)。唯一的问题是,语言环境还会影响其他字段(如果您有月或星期几的名称,例如MMMEEE,以及任何其他语言环境敏感数据)。

解决方法

我正在将应用程序从Joda-Time迁移到Java 8 java.time

我遇到的一件事是使用中的图案打印基于周的年份DateTimeFormatter

根据文档

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));
}

输出:

Value(YearOfEra)' '(Value(YearOfEra,4,19,EXCEEDS_PAD))' -- 'Value(WeekBasedYear)' '(Localized(WeekBasedYear,EXCEEDS_PAD))
2000 2000 -- 1999 2000
2001 2001 -- 2001 2001
2002 2002 -- 2002 2002
2003 2003 -- 2003 2003
2004 2004 -- 2004 2004
2005 2005 -- 2004 2005
2006 2006 -- 2006 2006
2007 2007 -- 2007 2007
2008 2008 -- 2008 2008
2009 2009 -- 2009 2009
2010 2010 -- 2009 2010
2011 2011 -- 2010 2011
2012 2012 -- 2012 2012
2013 2013 -- 2013 2013
2014 2014 -- 2014 2014
2015 2015 -- 2015 2015
2016 2016 -- 2015 2016
2017 2017 -- 2017 2017
2018 2018 -- 2018 2018
2019 2019 -- 2019 2019

从重要的年份(例如2000、2005、2009和2016)来看,.appendValue(IsoFields.WEEK_BASED_YEAR)和的输出.ofPattern("YYYY")是不同的。

在Java的时间与DateTimeFormatter基于周周的–一年模式分析中指出,这与本地化做的(如可以清楚地看到作为一个差toString()DateTimeFormatter)。

现在有几件事我不理解/需要:

  1. 因此,“以周为基础的年份”随语言环境的不同而不同。但是,我不明白的是,在某些语言环境中,以周为基准年显然总是与“正常”年份相同。这是为什么?

  2. 为什么没有将的解析YYYY映射到ISO-8601定义,而不是(非常混乱!)本地化形式。

  3. 在哪里可以找到适当的文档?至少可以说,Oracle提供的明显的“官方”文档含糊不清。 :我发现有关DateTimeFormatterBuilder的文档更多 。

猜你在找的技术问答相关文章

如何检查配对的蓝牙设备是打印机还是扫描仪(Android)
是否允许实体正文进行HTTP DELETE请求?
如何将ZipInputStream转换为InputStream?
java.util.logging Java 8中的变量
PowerMockito.doReturn返回null
Java中的RESTful调用
Swing / Java:如何正确使用getText和setText字符串
特殊字符和重音字符
Android Studio中的ndk.dir错误
错误“找不到主类”