简体   繁体   中英

Find first day of last week of year with java.time

I need to find the first day of the last week of a year using Java 8 Date and Time API ( java.time ) and finally came to this solution:

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

This solution finds the first week of the next year, adjusts the day of week to Monday if necessary and moves 7 days back. Is there a smarter way to achieve the same result?

As suggested, I'll answer the question myself (with a minor enhancement from the comments) as there does not seem to be a significantly simpler solution.

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

UPDATED based on your comment:

You can start from your date, get the last day of the year and go back till Mondays. Get the WeekBasedYear of this result and accept it if the weekBasedYear is the same as the day year, otherways go back to the previous Monday and accept it as result:

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

output:

2012-12-24

hope this can help.

I think the last solution found by OP is still not correct because we are moving in the context of ISO-week-dates, not ordinary calendar dates . So the expression LocalDate.of(date.getYear() + 1, 1, 7) will often be wrong around the year boundary (see the example method lastWeekOfYearOld(...) ). Instead we have to add a week-based-year, not a calendar year (bound to January-December) which has now been used in the second method lastWeekOfYearCorrect(...) . I also show another correct alternative based on the context-sensitive value range of the week-of-year-field (see method 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));
}

tl;dr

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

The ThreeTen-Extra library provides additional date-time classes that complement those in java.time .

The YearWeek class from that library suits your purpose. This class represents the entire week of a week-based year using standard ISO 8601 definition.

Get the current year-week.

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). 

Or get the YearWeek for a particular date.

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

Determine the last week number of this year, either 52 or 53. Here we use a ternary operator instead of a longer if-else statement.

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

Move to that last week of this week-based-year.

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.

Finally, get the date of the first day of that week, where the first day is Monday.

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

That is one approach. See the tl;dr section above for another approach.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM