当前位置: 首页 > 知识库问答 >
问题:

DateTimeFormatter周年差异

戈睿识
2023-03-14

我正在将我的应用程序从Joda-Time迁移到Java8Java.time

我遇到的一件事是使用DateTimeFormatter中的模式打印以周为单位的年份。

注意:我已经看到这个问题:Java Time的week-of-week-based-year模式用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,4,19,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 Time使用DateTimeFormatter进行的基于周年的模式解析中,说明了这与本地化有关(可以清楚地看到DateTimeFormattertostring()的不同)。

现在我不明白/不需要的事情很少了:

>

  • 所以“以周为基础的年份”随地区的不同而变化,很好。然而,我不明白的是,显然在某些地方,周基年总是与“正常”年相同。为什么会这样?

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

    我在哪里可以找到适当的文档?甲骨文表面上的“官方”文档至少可以说是模糊的。答:我找到了DateTimeFormatterBuilder更广泛的文档。

  • 共有1个答案

    强金鑫
    2023-03-14

    根据javadoc的说法,基于Week-year字段取决于两个方面:一周的第一天是什么,以及第一周的最短天数。

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

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

    (weekfields.iso.weekbasedyear()等效于isofields.week_based_year,在其他日历系统中略有不同)

    例如,考虑到2009年1月2日nd,这是一个星期五。在javadoc中检查基于周的年份字段:

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

    考虑到ISO定义(一周从星期一开始,第一周的最短天数为4),第1周从2008年12月29日开始,到2009年1月4日结束(这是从星期一开始的第一周,在2009年至少有4天),因此,2009年1月2日的周年等于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
    

    但是,如果考虑en_mt区域设置(英语(马耳他))的WeekFields实例,则一周的第一天是星期日,第一周的最短天数为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区域设置的周定义,2009年1月2日nd属于基于2008年的第53个周。

    现在,如果使用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周开始于2008年12月27日th,结束于2009年1月2日nd(2009年第一周开始于星期六,至少有一天)。因此,在AR_SA区域设置中,2009年1月2日nd的基于周的年份也是2009年(与使用isofields得到的值相同,尽管周定义与ISO完全不同)。

    虽然isoFields.week_based_year使用ISO的定义,但模式yyyy将使用与格式化程序中设置的区域设置相对应的weekFields实例(如果没有设置,则为JVM默认区域设置)。

    根据每个区域设置的定义(一周的第一天和第一周的最小天数),本地化模式(yyyy)中基于周的年份可能具有相同的ISO字段值(或不是)。

    虽然一周可以在另一年开始或结束这听起来很奇怪,但javadoc认为这是完全有效的:

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

    java.time的模式字母基于CLDR(Unicode Common Locale Data Repository)。这个基于周的模式的链接说:

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

    无论如何,CLDR完全是关于本地化的,所以y也是本地化的--正如Stephen Colebourne在下面的评论中所述:

    CLDR的全部目的是本地化,所以是的,“Y”模式字母是本地化的。虽然我理解对一个总是使用ISO规则操作的模式字母的渴望,但它并不存在,让CLDR添加它将是很难甚至不可能的。(Java正在密切关注CLDR)

    我的结论是,如果您想要ISO周字段,不要使用本地化模式。或者,作为一种--不理想,也很难看--的解决办法,使用与ISO的周定义相匹配的区域设置(在我的JVM中,locale.french可以做到这一点,因为weekfields.iso.equals(weekfields.of(locale.french))返回true)。唯一的问题是,区域设置还会影响其他字段(如果您有月份或星期名称,如MMMEEE,以及任何其他区域设置敏感的数据)。

     类似资料:
    • 问题内容: 我正在将应用程序从Joda-Time迁移到Java 8 。 我遇到的一件事是使用中的图案打印基于周的年份。 根据文档 但是,当我尝试这两个时,似乎总是会返回与相同的结果。 我的测试代码: 输出: 从重要的年份(例如2000、2005、2009和2016)来看,和的输出是不同的。 在Java的时间与DateTimeFormatter基于周周的–一年模式分析中指出,这与本地化做的(如可以清

    • 问题内容: Java 8的类有一个方法,,它使您可以从字符串A-z,a-z字母定义格式。这些例子并没有明确的区别y,今年的时代和Y,以星期为一年。它是什么? 问题答案: 与2006-W52一样,这是“年-周”样式日期的年值。如果有问题的一周跨越年份边界,则可能偏离年份值+1或-1。

    • 我需要以-的格式输出当前日期,即使用ISO周日期,其中一周总是从周一开始,一年的第一周是1月份至少有四天的第一周(因此是1月份有第一个周四的一周)。 由于2015年12月31日为星期四,星期五至星期日,即2016年1月1日至3日,均属2015年第53周(“漫长的一年”),而2016年第一周于1月4日星期一开始。 从DateTimeFormatter规范中,我希望可以使用模式来实现这一点(是,是)。

    • 问题内容: 我想计算用户输入的总日差 例如,当用户输入 和 现在,我正在使用此代码查找差异 总计的结果为6。但是问题是我不想包括周末(星期六和星期日)的日子 所以结果将是4 -—更新- 我有一个包含日期的表,该表的名称是假期 例如表格包含 因此,总天数为3,因为它没有计算假期日期 我该如何将输入中的日期等同于表中的日期? 问题答案: 很容易与我的最爱,和

    • 问题内容: 我每年进行一次嵌套聚合,然后每年在Elasticsearch中进行每周一次嵌套聚合。years年有53周,但是ElasticSearch的结果给出的是year年的最后一周key =“ 1”而不是“ 53”。如何让ElasticSearch在上周返回53而不是1? 这是我的查询: 结果(删除的数据在中间): 2008年是a年,最后一个星期有“ key_as_string”:“ 1”。我希

    • 我对Java中的DateTimeFormatter有问题。 然而,我得到了这个错误: java.time.format.DateTimeParseException:无法分析文本“29/09/2016”:无法从TemporalAccessor:{}获取LocalDateTime,ISO解析为2016-09-29,类型为java.time.format.Parsed],根本原因为java.time.