Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Get a given weekday in a given month with JavaScript

I'm trying to get the nth weekday—for example, the second Sunday—of a month in which a certain date falls.

For instance, if the date is August 24, 2015, I'd like to do this:

nthDayOfMonth(0, 2, new Date(2015, 7, 24))

and get August 9, 2015 (2nd Sunday in August). I'd then like to be able to add a month to the date, call the function again, and get September 13, 2015 (2nd Sunday in September).

For some reason the below code is not working.

What am I missing?

function nthDayOfMonth(day, n, date) {                                                                                              
    console.log(day);
    console.log(n);
    var count = 0; 
    var idate = new Date(date);                                                                                                       

    idate.setDate(1);                                                                                                                 

    while ((count) < n) {                                                                                                             
      idate.setDate(idate.getDate() + 1);
      if (idate.getDay() == day) {
        count++;                                                                                                                      
      }                                                                                                                               
    }                                                                                                                                 

return idate;                                                                                                                     

}

like image 948
Jonathon Klem Avatar asked Aug 24 '15 22:08

Jonathon Klem


2 Answers

You have to check idate.getDay() before incrementing the day of the month. Otherwise you'll get an incorrect answer if the desired weekday falls on the first of the month.

The following snippet demonstrates the corrected function.

function print(s) {
  document.write(s + '<br />');
}

function nthWeekdayOfMonth(weekday, n, date) {
  var count = 0,
      idate = new Date(date.getFullYear(), date.getMonth(), 1);
  while (true) {
    if (idate.getDay() === weekday) {
      if (++count == n) {
        break;
      }
    }
    idate.setDate(idate.getDate() + 1);
  }
  return idate;
}

             // Second Sunday of the current month.
var date = new Date();
print(date = nthWeekdayOfMonth(0, 2, date)); 
             // Second Sunday of next month.
date.setMonth(date.getMonth() + 1);
print(date = nthWeekdayOfMonth(0, 2, date));

             // First Tuesday of September 2015.
print(nthWeekdayOfMonth(2, 1, new Date(2015, 8)));
             // First Wednesday of September 2015.
print(nthWeekdayOfMonth(3, 1, new Date(2015, 8)));
             // Second Tuesday of September 2015.
print(nthWeekdayOfMonth(2, 2, new Date(2015, 8)));
             // Second Wednesday of September 2015.
print(nthWeekdayOfMonth(3, 2, new Date(2015, 8)));
body {
  font-family: sans-serif;
}

There's an even better approach that calculates the desired date without looping. We start by considering the weekday of the first day of the month. Suppose it's a Saturday, which JavaScript calls 6, and you're looking for a Sunday, which is 0.

To get to the first Sunday of the month, you have to advance the date by this number of days:

0 - 6 + 7

The result is 1. How does the calculation work? 0 - 6 is the number of days from weekday 6 to weekday 0, and to turn a negative value into a valid weekday, we add 7.

In general, the number of days from weekday a to weekday b is

(b - a + 7) % 7

To continue the example, suppose that we wanted the first Sunday of the month. In that case, we've arrived. But if we want the second day of the month, we have to advance the date by 7 more days. In general, given n such that n == 1 means the first occurrence of a given weekday, we have to advance by (n - 1) * 7 days.

To put it all together, if date is the first day of the month, we can get to the nth occurrence of weekday by advancing

(weekday - date.getDay() + 7) % 7 + (n - 1) * 7

days past the first day of the month.

This approach is implemented below.

function print(s) {
  document.write(s + '<br />');
}

function nthWeekdayOfMonth(weekday, n, date) {
  var date = new Date(date.getFullYear(), date.getMonth(), 1),
      add = (weekday - date.getDay() + 7) % 7 + (n - 1) * 7;
  date.setDate(1 + add);
  return date;
}

             // Second Sunday of the current month.
var date = new Date();
print(date = nthWeekdayOfMonth(0, 2, date)); 
             // Second Sunday of next month.
date.setMonth(date.getMonth() + 1);
print(date = nthWeekdayOfMonth(0, 2, date));

             // First Tuesday of September 2015.
print(nthWeekdayOfMonth(2, 1, new Date(2015, 8)));
             // First Wednesday of September 2015.
print(nthWeekdayOfMonth(3, 1, new Date(2015, 8)));
             // Second Tuesday of September 2015.
print(nthWeekdayOfMonth(2, 2, new Date(2015, 8)));
             // Second Wednesday of September 2015.
print(nthWeekdayOfMonth(3, 2, new Date(2015, 8)));
body {
  font-family: sans-serif;
}
like image 102
Michael Laszlo Avatar answered Sep 23 '22 16:09

Michael Laszlo


Your code seems to work fine. To add a month, you can use d.setMonth(d.getMonth()+1).

Try this demo:

function nthDayOfMonth(day, n, date) {
	var count = 0,
	    idate = new Date(date);
	idate.setDate(1);

	while (count < n) {
		idate.setDate(idate.getDate() + 1);
		if (idate.getDay() == day) { count++; }
	}

	return idate;
}

// Today : 2015-08-24
var today = new Date();

// Second Sunday of current month : 2015-08-09
var res = nthDayOfMonth(0, 2, today);

// res plus 1 month : 2015-09-09 (Wednesday)
var plusOne = new Date( res );
plusOne.setMonth(plusOne.getMonth() + 1);

// Second Sunday of next month : 2015-09-13
var res2 = nthDayOfMonth(0, 2, plusOne);

document.body.innerHTML = 'Today is <br>' + today + '<br>'
                        + 'Second Sunday of current month is <br>'  + res + '<br>'
                        + 'If you add a month to it, you get <br>'  + plusOne + '<br>'
                        + 'And second Sunday of that month is <br>' + res2;
like image 26
blex Avatar answered Sep 20 '22 16:09

blex