如何在Java 8中迭代遍历一年的每个星期?

7

如果我在这里混淆了任何与ISO日期相关的术语,请提前道歉。

我想能够迭代给定年份(比如2015年)中的每一周。我知道可以计算出2015年1月1日到12月31日之间的周数,但这并不符合ISO标准的一周定义方式。相反,它给出的是在两个日期之间的7天周期数。一年中的第一个ISO周不一定从2015年1月1日开始。

如果我能得到第一周的第一天日期,我可以通过ZonedDateTime.plusWeeks(1)迭代52周。你可以通过字段访问器获取任意日期的周数:

ZonedDateTime date = ZonedDateTime.now();
WeekFields weekFields = WeekFields.of(Locale.getDefault());
int weekNumber = date.get(weekFields.weekOfWeekBasedYear());

基于此,我认为在Java8 Time API中获取特定年份第一周第一天的日期是可能的,但我还没有找到方法。


请参阅我的示例,了解在不同库中构建ISO周日期的方法。即使是旧版JDK使用java.util.GregorianCalendar也显然更简单,因为JSR-310(java.time-package)在这里缺乏一个简单的工厂方法。 - Meno Hochschild
1个回答

6
您可以使用以下代码构造日期并将其调整为一年中第一周的第一天:
int year = 2016;
WeekFields weekFields = WeekFields.ISO;
LocalDate date = LocalDate.now().with(weekFields.weekBasedYear(), year)
                                .with(weekFields.weekOfWeekBasedYear(), 1)
                                .with(ChronoField.DAY_OF_WEEK, 1);

感谢JodaStephen的评论,另一个方法是使用IsoFields类。
LocalDate date = LocalDate.now().with(IsoFields.WEEK_BASED_YEAR, year)
                                .with(IsoFields.WEEK_OF_WEEK_BASED_YEAR, 1)
                                .with(ChronoField.DAY_OF_WEEK, 1);

WeekFields.ISO代表了一周的ISO定义:

ISO-8601定义了一周从星期一开始,第一周至少有4天。

ISO-8601标准基于周的概念定义了一个日历系统。它使用基于周的年和周的概念来分割时间的流逝,而不是使用标准的年/月/日。

请注意,第一周可能从前一个日历年开始。同时,一个日历年的前几天可能属于与前一个日历年对应的基于周的年。

根据这个定义,您可以获得以下内容:

  • weekBasedYear()代表基于周的年字段:

    这表示以固定星期几为起始日的年份概念,例如星期一,每周属于且仅属于一个年份。

    在这种情况下,我们希望将其设置为所需的年份。

  • weekOfWeekBasedYear()代表基于周的年中周的数量

    这表示一年中周数的计数概念,其中每周以固定星期几为起始日,例如星期一,每周属于且仅属于一个年份。

    在这种情况下,我们希望将其设置为基于周的年的第一周,因此将其设置为1。

  • ChronoField.DAY_OF_WEEK代表星期几。在这种情况下,我们希望将其设置为一周的第一天,因此将其设置为1。

然后,使用这样的日期,您确实可以通过调用LocalDate.plusWeeks(1)来迭代一年中的所有周。问题是:您需要迭代多少次?一年中可能有超过52周。基于周的年中可能有52或53周。

以下是翻译的结果:

您可以通过以下方式获取周数。调用此函数rangeRefinedBy(date),以检索给定日期的一年中的周字段的有效值,并获取其最大值。

long maxWeekOfYear = weekFields.weekOfWeekBasedYear().rangeRefinedBy(date).getMaximum();

你需要使用 weekOfWeekBasedYear()weekBasedYear(),而不是 weekOfYear()dayOfWeek()。这两者是不同的。然而,在这种情况下,更有意义的做法是使用 IsoFields.WEEK_OF_WEEK_BASED_YEARIsoFields.WEEK_BASED_YEAR,这将避免混淆。 - JodaStephen
@JodaStephen 能否再详细解释一下?我测试了2010年到2020年的结果似乎是正确的,但我承认有时候会对所有这些定义感到困惑 :) (而且我甚至不知道 IsoFields 的存在...)。 - Tunaki
1
weekOfYear()函数在一个日历年内操作。weekOfWeekBasedYear()函数在一个基于周的年份内操作(基于周的年份的第一天可能在前一个日历年,最后一天可能在下一个日历年)。由于OP询问的是ISO基于周的年份,所以你需要使用基于周的年份方法。 - JodaStephen
@JodaStephen 非常感谢您的建议,我已经进行了编辑。 - Tunaki

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接