Basil Bourque
Basil Bourque

Reputation: 339372

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

Upvotes: 3

Views: 1236

Answers (3)

Anonymous
Anonymous

Reputation: 86324

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.

Upvotes: 2

user8097737
user8097737

Reputation:

You used a wrong field for TemporalAdjusters.dayOfWeekInMonth(int ordinal, DayOfWeek dayOfWeek) since ordinal is the week within the month but you used ALIGNED_DAY_OF_WEEK_IN_MONTH¹.

But how to you get the week of month in java.time?:
For this you need WeekFields, the most common is WeekFields.ISO.
Now you can create a ChronoField for week of month with WeekFields.ISO.weekOfMonth();.


With your code this should look like:

LocalDate ld = LocalDate.parse( "2018-03-05" );
DayOfWeek dayOfWeek = ld.getDayOfWeek();
int ordinal = ld.get(WeekFields.of(dayOfWeek, 7).weekOfMonth());
YearMonth ym = YearMonth.from( ld );

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

¹: ChronoField.ALIGNED_DAY_OF_WEEK_IN_MONTH is the n-th day in a week which doesn't start with a fixed DAY_OF_WEEK like Monday but with the first day of the month.

Upvotes: 1

Joakim Danielson
Joakim Danielson

Reputation: 52003

This code isn't fancy at all but it works :)

public static void main(String[] args) {    
    LocalDate ld = LocalDate.parse( "2018-03-04" );
    DayOfWeek dayOfWeek = DayOfWeek.WEDNESDAY;

    LocalDate first = ld.withDayOfMonth(1);

    List<LocalDate> firstDays = new ArrayList<>();
    //I deliberately included the start date in the loop. 
    //Easily fixed if it shouldn't be included
    for (int i = 0; i <= 6; i++) {
        LocalDate firstDay = getFirstDay(first.plusMonths(i), dayOfWeek);
        firstDays.add(firstDay);
    }
}

static LocalDate getFirstDay(LocalDate date, DayOfWeek searchedDay) {
    LocalDate temp = date;
    int i = 0;
    while (temp.getDayOfWeek() != searchedDay) {
         temp = date.plusDays(i++);
    }
    return temp;
}

Upvotes: 0

Related Questions