javajava-timedayofweek

For a given date, detect the nth day-of-week in the month, then determine the same in next 6 months, in Java


For a given date, such as 2018-03-05, how do I detect that it is the first Monday of that month?

And after determining that fact, how do I calculate the same nth day-of-week in the month for the following six months? For example, Monday 2018-04-02 and Monday 2018-05-03.

Just like this Question but using Java.

I tried this code, but do not get the results I expect. What I expect:

[2018-04-02, 2018-05-07, 2018-06-04, 2018-07-02, 2018-08-06, 2018-09-03]

Perhaps I misunderstand ChronoField.ALIGNED_DAY_OF_WEEK_IN_MONTH.

LocalDate ld = LocalDate.parse( "2018-03-05" );
int alignedDayOfWeekInMonth = ld.get( ChronoField.ALIGNED_DAY_OF_WEEK_IN_MONTH );
DayOfWeek dayOfWeek = ld.getDayOfWeek();
YearMonth ym = YearMonth.from( ld );

int countMonths = 6;
List < LocalDate > localDates = new ArrayList <>( countMonths );
TemporalAdjuster ta = TemporalAdjusters.dayOfWeekInMonth( alignedDayOfWeekInMonth , dayOfWeek );
for ( int i = 1 ; i <= countMonths ; i++ ) {
    LocalDate firstOfMonth = ym.plusMonths( i ).atDay( 1 );
    LocalDate localDate = firstOfMonth.with( ta );
    localDates.add( localDate );
}

Dump to console.

System.out.println( "ld.toString(): " + ld );
System.out.println( "alignedDayOfWeekInMonth: " + alignedDayOfWeekInMonth );
System.out.println( "dayOfWeek.toString(): " + dayOfWeek );
System.out.println("ym.toString(): " + ym);
System.out.println( "localDates: " + localDates );

When run.

ld.toString(): 2018-03-05
alignedDayOfWeekInMonth: 5
dayOfWeek.toString(): MONDAY
ym.toString(): 2018-03
localDates: [2018-04-30, 2018-06-04, 2018-07-02, 2018-07-30, 2018-09-03, 2018-10-01]

A related Question, but uses the legacy date-time classes rather than the modern java.time classes: Find which nth occurrence of a day in a month for a date in Java


Solution

  • This simple change fixes your program:

        int alignedWeekOfMonth = ld.get(ChronoField.ALIGNED_WEEK_OF_MONTH);
    

    I have renamed your variable alignedDayOfWeekInMonth, though, so you need to carry the name change through to the two places where you are using it. Then your program prints:

    ld.toString(): 2018-03-05
    alignedWeekOfMonth: 1
    dayOfWeek.toString(): MONDAY
    ym.toString(): 2018-03
    localDates: [2018-04-02, 2018-05-07, 2018-06-04, 2018-07-02, 2018-08-06, 2018-09-03]
    

    The list agrees with what you said you expected.

    It seems you were correct in that you had misunderstood ChronoField.ALIGNED_DAY_OF_WEEK_IN_MONTH. With reference to the concept of aligned weeks in month March 5 is the 5th day in the 1st aligned week. The chrono field you used gave you the 5, not the 1. To get the 1, use ChronoField.ALIGNED_WEEK_OF_MONTH instead. Then everything works.