'How could I get the the Nth Monday, Tuesday, etc. of every month between two dates using JavaScript

I have a program where a user can set an event, with an start date, end date and the period of repetition the event shall repeat, weekly, monthly by date, monthly by weekday and yearly. After the user creates the event, it gets saved in the database and the event is displayed in a calendar on the main page of my program.

So far I've been able to devise the algorithms for repeating dates weekly, monthly by dates and yearly, but not monthly by weekday. By "monthly by weekday" I'm referring to an event that repeats every same weekday once a month, in the period the start date and end date occupies.

For example, an event that repeats the first Monday of every month between March 1st and November 1st, March 1st is the first Monday of March, so I would like to generate a date that is the first Monday of April, which is April 5th, and so on on every month between March and November.

The snippet below is my function for repeating dates monthly by dates, this is for events that repeat every 15th of any month between the start date and end date.

function repeatEventMonthly(jsonArray, num){

//First I get the start date and end date as strings on a JSON, I separate
//them by the "/" and then use the individual parts to construct two Date Objects

var split = jsonArray[num].Fecha.split("/");
var split2 = jsonArray[num].fechaFin.split("/");

var dd = split[1];
var mm = split[0];
var yy = split[2];

var dd2 = split2[1];
var mm2 = split2[0];
var yy2 = split2[2];

var starDate = new Date();
var endDate = new Date();

starDate.setFullYear(yy);
starDate.setMonth(mm-1);
starDate.setDate(dd);
endDate.setFullYear(yy2);
endDate.setMonth(mm2-1);
endDate.setDate(dd2);

//the variable top means how many days are between the startDate and endDate.
//I use a function that calculates this number

var top = getDaysInDates(starDate, endDate);

if (jsonArray[num].tipoRepeticion == "2") {

 //the attribute "tipoRepeticion" in my JSON object lets me know what type of
 //repetition the event must be set, 2 means Monthly by Weekday and 3 is Monthly by Date

}else if(jsonArray[num].tipoRepeticion == "3"){

    //If the event has repetition type 3, then inside this for loop I generate a new date
    //which I create with the value of the startDate and then I add the index number in the for
    //loop cycle, meaning that if the startDate is March 3, the in index 1 is March 4, Index 2
    //is March 5 and so on, then with an If statement I check that if the date on the
    //generated date object is the same as the date of the startDate, I push the date to  
    //another JSON array that I use to display the dates on the calendar.

    for (let index = 0; index < top; index++) {
        let sd = new Date(starDate);
        sd.setDate(sd.getDate()+index);
        if (sd.getDate() == starDate.getDate()) {
            let str = ((sd.getMonth()+1) + "/" + sd.getDate() + "/" + sd.getFullYear()).toString();
            eventMen.push({

                // the function "construcDates" helps me create a valid String for my program
                // to display at the user.

                date: constructDates(str, 0, jsonArray[num].tipoRepeticion),
                title: jsonArray[num].titulo,
                descripcion: jsonArray[num].descripcion,
                tipo: jsonArray[num].tipo,
                tipoRepeticion : jsonArray[num].tipoRepeticion
            });
        }
    }
}

So this function effectively generates events one month apart but on the same date, meaning that if my event starts March 3rd, and ends on December 3rd, in my calendar it displays the same event on April 3rd, May 3rd, June 3rd, July 3rd... all the way to November 3rd. This method may be more complicated that I needs to be, but I'm still learning JavaScript so this is what I came up with.

However, despite having solved the logic for other type of repeating dates, (weekly, monthly by dates and yearly) repeating dates monthly by weekday is turning up to be pretty difficult, since there isn't the same number of days in every month, I can't just sum up 30 or 31 days to each month and hope the date lands on the same "first Monday of a month" for example. I would like to ask if anyone has any suggestion or solution as to how I could calculate this conundrum, also worth pointing out that the example of "first Monday of any month" is not the only possible type of repeating date by month, also it could the second Tuesday of every month or the last Friday as well.



Solution 1:[1]

Could you just get the day of the week of the first of the month, then from that figure out how many days until the desired day of the week. Then add multiplies of 7 until you get the desired date.

For example:

const startDate = new Date("2021-03-01"); // Mon Mar 01 2021
const friday = 5;        // Friday is the 5th day of the week (Monday is 1)
const weekOfMonth = 2;   // We want the 3rd week of the month (1st week is 0)

// Start calculations
const dayOfWeek = startDate.getUTCDay(); // 1
const diff = friday - dayOfWeek;         // 4
const dateOf3rdFriday = new Date(
    startDate.getUTCFullYear(),
    startDate.getUTCMonth(),
    startDate.getUTCDay() + diff + 7 * weekOfMonth
); // Fri Mar 19 2021

For the last Friday of the month, you'd just need to do the same sort of thing but in reverse.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Rebecca Stevens