【问题标题】:Find first day of last week of year with java.time使用 java.time 查找一年中最后一周的第一天
【发布时间】:2016-05-12 13:35:23
【问题描述】:

我需要使用 Java 8 日期和时间 API (java.time) 找到一年中最后一周的第一天,最后得出了这个解决方案:

LocalDate date = LocalDate.of(2016, 2, 17);
LocalDate lastWeekOfYear = LocalDate.of(date.getYear() + 1, 1, 7)
    .with(WeekFields.ISO.weekOfYear(), 1)
    .with(TemporalAdjusters.previousOrSame(DayOfWeek.MONDAY)).minusDays(7);

此解决方案查找下一年的第一周,如有必要,将星期几调整为星期一,并向后移动 7 天。有没有更聪明的方法来达到同样的效果?

【问题讨论】:

  • 对我来说看起来不错,虽然我可能会使用minusWeeks(1) 而不是minusDays(7),并将它放在一个新行上。你可以为lastWeekOfWeekYear写你自己的TemporalAdjuster
  • 另一种选择(不一定更具可读性)是date.with(lastDayOfYear()).minusDays(7 - WeekFields.ISO.getMinimalDaysInFirstWeek()).with(previousOrSame(MONDAY))
  • @Oxolotl 我建议您提取您提出的解决方案,并将其作为答案发布。您稍后可以接受它以将问题标记为已关闭。完全可以在 Stack Overflow 上回答您自己的问题。
  • 使用我的库 Time4J 启用 more elegant solution。调整代码将减少到date = date.with(ISO.weekOfYear().maximized()).with(DAY_OF_WEEK, MONDAY); 否则-仅使用java.time-package时-我强烈建议使用weekOfWeekBasedYear()-另请参阅java.time-API中与weekOfYear()的子图差异。
  • 感谢@MenoHochschild 建议使用weekOfWeekBasedYear(),我相应地更新了解决方案。但是当我要求一个简单的 java.time 解决方案时,另一个库的使用超出了这个问题的范围。

标签: java date java-time


【解决方案1】:

按照建议,我将自己回答这个问题(通过 cmets 的小幅改进),因为似乎没有更简单的解决方案。

LocalDate date = LocalDate.of(2016, 2, 17);
LocalDate lastWeekOfYear = LocalDate.of(date.getYear() + 1, 1, 7)
    .with(WeekFields.ISO.weekOfWeekBasedYear(), 1)
    .with(TemporalAdjusters.previousOrSame(DayOfWeek.MONDAY)).minusWeeks(1);

【讨论】:

    【解决方案2】:

    根据您的评论更新:

    您可以从您的日期开始,获取一年中的最后一天,然后返回到星期一。获取该结果的 WeekBasedYear,如果 weekBasedYear 与 day 年份相同则接受,否则返回上周一接受结果:

    LocalDate day = LocalDate.of(2012, 2, 17);
    LocalDate result =  day.with(TemporalAdjusters.lastDayOfYear())
                                .with(TemporalAdjusters.previousOrSame(DayOfWeek.MONDAY));      
    
    //if day and result weekBasedYear are in the same weekYear then result else go back to previous Mondays
    result = result.get(WeekFields.ISO.weekBasedYear())== day.getYear()? result :
                    result.with(TemporalAdjusters.previous(DayOfWeek.MONDAY));
    
    System.out.println(result);
    

    输出:

    2012-12-24
    

    希望这能有所帮助。

    【讨论】:

    • 我认为对于一年中的最后一天是星期一、星期二或星期三的情况,此解决方案确实会返回错误的结果。例如,在 2012 年,一年中的最后一周从 2012-12-24 开始,而上面的代码将返回 2012-12-31。我应该在我的问题中指出,我需要根据 ISO 8601 周的最后一周(参见 [en.wikipedia.org/wiki/ISO_8601#Week_dates])。
    • @Oxolotl 是的,你当然应该定义你的术语,在这种情况下你的意思是一年中的一周。请使用此类说明编辑您的问题,而不是作为 cmets 发布。
    【解决方案3】:

    我认为 OP 找到的最后一个解决方案仍然不正确,因为我们是在 ISO 周日期而不是普通日历日期的背景下移动。所以表达式LocalDate.of(date.getYear() + 1, 1, 7) 在年份边界附近通常是错误的(参见示例方法lastWeekOfYearOld(...))。相反,我们必须添加一个基于周的年份,而不是日历年(绑定到 1 月至 12 月),它现在已在第二种方法 lastWeekOfYearCorrect(...) 中使用。我还根据周字段的上下文相关值范围展示了另一个正确的替代方案(参见方法lastWeekOfYearAlternative(...)

    public static void main(String[] args) {
        System.out.println(
                  lastWeekOfYearOld(LocalDate.of(2015, 12, 31))); // 2015-12-28 (OK)
        System.out.println(
                  lastWeekOfYearOld(LocalDate.of(2016, 1, 1))); // 2016-12-26 (Wrong)
    
        System.out.println(
                  lastWeekOfYearCorrect(LocalDate.of(2015, 12, 31))); // 2015-12-28 (OK)
        System.out.println(
                  lastWeekOfYearCorrect(LocalDate.of(2016, 1, 1))); // 2015-12-28 (OK)
    
        System.out.println(
                  lastWeekOfYearAlternative(LocalDate.of(2015, 12, 31))); // 2015-12-28 (OK)
        System.out.println(
                  lastWeekOfYearAlternative(LocalDate.of(2016, 1, 1))); // 2015-12-28 (OK)
    }
    
    private static LocalDate lastWeekOfYearOld(LocalDate date) {
        return LocalDate.of(date.getYear() + 1, 1, 7)
             .with(WeekFields.ISO.weekOfWeekBasedYear(), 1)
             .with(TemporalAdjusters.previousOrSame(DayOfWeek.MONDAY)).minusWeeks(1);
    }
    
    private static LocalDate lastWeekOfYearCorrect(LocalDate date) {
        date =
          date.plus(1, IsoFields.WEEK_BASED_YEARS)
              .with(DayOfWeek.MONDAY)
              .with(WeekFields.ISO.weekOfWeekBasedYear(), 1);
        return date.minusWeeks(1);
    }
    
    private static LocalDate lastWeekOfYearAlternative(LocalDate date) {
        TemporalField woyField = WeekFields.ISO.weekOfWeekBasedYear();
        date = date.with(woyField, woyField.rangeRefinedBy(date).getMaximum());
        return date.with(TemporalAdjusters.previousOrSame(DayOfWeek.MONDAY));
    }
    

    【讨论】:

    • 上述解决方案对我来说没有按预期工作,但我理解你的观点并认为我的问题可能仍然模棱两可。我一直在寻找“2016 年最后一周是什么时候?”这个问题的解决方案。 - 因此,传递的LocalDate 的日期和月份部分无关紧要。如果我要求实现带有签名LocalDate lastWeekOfYear(int year) 的方法会更清楚。
    【解决方案4】:

    tl;博士

    org.threeten.extra.YearWeek.from(  // Represents an entire week at a time, using standard ISO 8601 definition of “week”. 
        LocalDate.of( 2016 , Month.FEBRUARY , 17 )
    )                                  // Returns a `YearWeek` object to represent the week containing that date. 
    .plusYears( 1 )                    // Move to following year.
    .withWeek( 1 )                     // Move to the first week of that following year.
    .minusWeeks( 1 )                   // Move to the prior week, which would be the last week of our desired week-based-year.
    .atDay(                            // Determine the date of a particular day-of-week within that week.
        DayOfWeek.MONDAY               // Use the handy `java.time.DayOfWeek` enum predefining seven objects, one for each day of the week.
    )                                  // Returns a `LocalDate` object.
    .toString()                        // Generate a String representing this `LocalDate` value in standard ISO 8601 format.
    

    2016-12-26

    YearWeek

    ThreeTen-Extra 库提供了额外的日期时间类来补充 java.time 中的那些。

    该库中的 YearWeek 类适合您的目的。此类使用标准 ISO 8601 定义表示基于一周的一年中的整周。

    获取当前的年-周。

    YearWeek yw = YearWeek.now( ZoneId.of( "Africa/Tunis" ) ) ;  // Get the current year-week as seen in the wall-clock time of people inhabiting a particular region (a time zone). 
    

    或者获取特定日期的YearWeek

    LocalDate ld = LocalDate.of( 2016 , Month.FEBRUARY , 17 ) ;  // Feb 2, 2016.
    YearWeek yw = YearWeek.from( ld ) ;
    

    确定今年的最后一周数,52 或 53。这里我们使用三元运算符而不是较长的 if-else 语句。

    int w = yw.is53WeekYear​() ? 53 : 52 ;  // Determine last week number, either 52 or 53.
    

    移至本年度的最后一周。

    YearWeek ywLastOfYear = yw.withWeek( w ) ; // Produce a new `YearWeek` object based on the original’s values but for some adjustment - here we use the same year but use a different week number.
    

    最后,得到该周第一天的日期,第一天是星期一。

    LocalDate firstDayOfLastYearWeek = ywLastOfYear.atDay( DayOfWeek.MONDAY ) ; 
    

    这是一种方法。有关另一种方法,请参阅上面的 tl;dr 部分。

    【讨论】:

      猜你喜欢
      • 2021-03-11
      • 2014-03-08
      • 2011-06-14
      • 1970-01-01
      • 2014-12-30
      • 1970-01-01
      • 2016-11-29
      • 2020-04-03
      • 1970-01-01
      相关资源
      最近更新 更多