Handling monthly recurring meetings on the same day of the week within the same week of the month

120 Views Asked by At

I'm facing difficulty handling monthly recurring meetings in my application. The requirement is for these meetings to consistently fall on the same day of the week within the same week of the month, regardless of the specific date.

For example, if the initial meeting is on the 4th of June, and it happens to be on a Monday, the subsequent meetings should always fall on a Monday within the same week of the month (second weeks of the month), even if the specific date changes (e.g., the 5th of July).

I have implemented a solution in Node.js using an API endpoint that fetches meeting data from a CSV file. The endpoint receives the room and date parameters and checks for availability based on the recurring meetings stored in the CSV file.

// API endpoint to retrieve the CSV data (Fetching recurring meetings)
app.get('/api/checkAvailability', (req, res) => {
  const { room, date } = req.query;

  const results = [];

  fs.createReadStream('3meetings.csv')
    .pipe(csv())
    .on('data', (data) => results.push(data))
    .on('end', () => {
      const occupiedSlots = results.filter((meeting) => {
        if (meeting.room === room && meeting.date <= date) {
          const interval = meeting.interval.toUpperCase();

          // Calculate the number of days between the meeting date and the provided date
          const daysDiff = Math.floor(
            (new Date(date) - new Date(meeting.date)) / (1000 * 60 * 60 * 24)
          );

          if (interval === 'D') {
            // For daily intervals, check if the days difference is divisible by 1
            if (daysDiff % 1 === 0) {
              return true; // Occupied slot for the provided date and daily interval
            }
          } else if (interval === 'W') {
            // For weekly intervals, check if the days difference is divisible by 7
            if (daysDiff % 7 === 0) {
              return true; // Occupied slot for the provided date and weekly interval
            }
          } else if (interval === 'W2') {
            // For weekly intervals with 1 week gap, check if the days difference is divisible by 14
            if (daysDiff % 14 == 0) {
              return true;
            }
          }
        }
        return false;
      });

      if (occupiedSlots.length > 0) {
        res.json({ isAvailable: false, occupiedSlots });
      } else {
        res.json({ isAvailable: true });
      }
    });
});

The existing code handles daily (D), weekly (W), and biweekly (W2) recurring intervals. However, it does not account for the monthly intervals as described above.

Could you please provide guidance on modifying the code to support monthly recurring meetings that consistently fall on the same day of the week within the same week of the month?

2

There are 2 best solutions below

0
derpirscher On BEST ANSWER

A simple possibility to calculate which occurence of a weekday a specific date is, is to just count how often you can go a week into the past before you reach a different month.

//This gives a result like "2023-06-27 is the 4th Wednesday of the month"
function weekCount(d) {
  let c = 0, month = d.getMonth();
  while (month == d.getMonth()) {
    c++;
    d = new Date(d.getTime() - 604800000);
  }
  return c;
}

Then use the result of this function to check, if the date in question and the meeting.date share the same weekday and the same weekcount

else if (interval === 'M') {
  let d = new Date(date), md = new Date(meeting.date);
  return d.getDay() === md.getDay() && //dates must be same weekday
         weekCount(d) === weekCount(md);  //and same weekCount
}
3
Terry Lennox On

You can add a recurrence interval of 'M' to signify these monthly meetings.

I've created getDayOfWeek() and getWeekOfMonth() functions, using the answer here to create the getWeekOfMonth value.

If the week of the month and the day of the week are equal for a room, an occupied status will be returned.

I'd suggest adding some unit tests to the getOccupiedSlots() to ensure it's returning the expected result for a given set of inputs.

// Using function from https://stackoverflow.com/questions/3280323/get-week-of-the-month
function getWeekOfMonth(date) {
  date = new Date(date)
  const firstWeekday = new Date(date.getFullYear(), date.getMonth(), 1).getDay();
  const offsetDate = date.getDate() + firstWeekday - 1;
  return Math.floor(offsetDate / 7);
}

function getDayOfWeek(date) {
  return new Date(date).getDay();
}

function weekOfMonthEqual(date1, date2) {
  return getWeekOfMonth(date1) === getWeekOfMonth(date2);
}

function dayOfWeekEqual(date1, date2) {
  return getDayOfWeek(date1) === getDayOfWeek(date2);
}

function getOccupiedSlots(results, room, date) { 
  return results.filter((meeting) => {
    if (meeting.room === room && meeting.date <= date) {
      const interval = meeting.interval.toUpperCase();
  
      // Calculate the number of days between the meeting date and the provided date
      const daysDiff = Math.floor(
        (new Date(date) - new Date(meeting.date)) / (1000 * 60 * 60 * 24)
      );
  
      if (interval === 'D') {
        // For daily intervals, check if the days difference is divisible by 1
        if (daysDiff % 1 === 0) {
          return true; // Occupied slot for the provided date and daily interval
        }
      } else if (interval === 'W') {
        // For weekly intervals, check if the days difference is divisible by 7
        if (daysDiff % 7 === 0) {
          return true; // Occupied slot for the provided date and weekly interval
        }
      } else if (interval === 'W2') {
        // For weekly intervals with 1 week gap, check if the days difference is divisible by 14
        if (daysDiff % 14 == 0) {
          return true;
        }
      } else if (interval === 'M') {
        // For monthly intervals check if week of month and day of week are equal
        if (weekOfMonthEqual(date, meeting.date) && dayOfWeekEqual(date, meeting.date)) {
          return true;
        }
      }
    }
    return false;
  });
}

// API endpoint to retrieve the CSV data (Fetching recurring meetings)
app.get('/api/checkAvailability', (req, res) => {
  const { room, date } = req.query;

  const results = [];

  fs.createReadStream('3meetings.csv')
    .pipe(csv())
    .on('data', (data) => results.push(data))
    .on('end', () => {
      const occupiedSlots = getOccupiedSlots(results, room, date);
      if (occupiedSlots.length > 0) {
        res.json({ isAvailable: false, occupiedSlots });
      } else {
        res.json({ isAvailable: true });
      }
    });
});