Search code examples
javascriptdatetimegoogle-calendar-api

Find available time slots between events with Google Calendar API in js


I need to create an application with showing time slots for user. I retrieve all events for day with Google API and let show example, already booked events:

const events = [
  { startTime: new Date('2023-10-18T00:00:00+0000'), endTime: new Date('2023-10-18T08:00:00+0000') },
  { startTime: new Date('2023-10-18T09:00:00+0000'), endTime: new Date('2023-10-18T10:00:00+0000') },
  { startTime: new Date('2023-10-18T11:00:00+0000'), endTime: new Date('2023-10-18T12:30:00+0000') },
  { startTime: new Date('2023-10-18T16:00:00+0000'), endTime: new Date('2023-10-18T17:30:00+0000') },
  { startTime: new Date('2023-10-18T18:00:00+0000'), endTime: new Date('2023-10-19T00:00:00+0000') },
];

and I want to create available time slots between these events with interval 15 minutes and meeting duration should be 60 minutes so based on that events I would like to have output:

const availableSlots = ['8:00', '10:00', '12:30', '12:45', '13:00', '13:15', '13:30', '13:45', '14:00', '14:15', '14:30', '14:45', '15:00']

I need to create an application similar to booksy:

booksy planer

I tried something like that, but output is incorrect:

const meetingDuration = 60; // minutes
const interval = 15; // minutes
const workdayStart = new Date('2023-10-18T08:00:00'); // Start of workday
const workdayEnd = new Date('2023-10-18T17:00:00'); // End of workday

const availableSlots = [];

let currentSlotStart = new Date(workdayStart);

while (currentSlotStart < workdayEnd) {
  const currentSlotEnd = new Date(currentSlotStart);
  currentSlotEnd.setMinutes(currentSlotEnd.getMinutes() + meetingDuration);

  if (events.every(event => (currentSlotStart < event.startTime && currentSlotEnd <= event.startTime) || (currentSlotEnd >= event.endTime && currentSlotEnd >= event.endTime))) {
    availableSlots.push({ start: currentSlotStart.toUTCString(), end: currentSlotEnd.toUTCString() });
  }

  currentSlotStart = new Date(currentSlotStart);
  currentSlotStart.setMinutes(currentSlotStart.getMinutes() + interval);
}

console.log(availableSlots);

Please help! :D


Solution

  • SUGGESTION

    I've made a slight tweak to the main logic operation on your function to check if a booking slot is available.

    From:

    if (events.every(event => (currentSlotStart < event.startTime && currentSlotEnd <= event.startTime) || (currentSlotEnd >= event.endTime && currentSlotEnd >= event.endTime))) {
        availableSlots.push({ start: currentSlotStart.toUTCString(), end: currentSlotEnd.toUTCString() });
      }
    

    To:

    if (events.every(event => (currentSlotStart < event.startTime && currentSlotEnd <= event.startTime) || (currentSlotStart >= event.endTime))) {
          availableSlots.push({ start: currentSlotStart.toUTCString(), end: currentSlotEnd.toUTCString() });
        }
    
    1. The 1st logic was unchanged, since it already checks if the current timestamp (both the start and endpoint) does not conflict with an existing event's starting time (this also covers for future event booking conflicts)

    2. I've changed the 2nd logic however, to just check if the current timestamp conflicts the ending point of a finished event; this is to validate that any finished events will not coincide with the booking time.

    The full code snippet:

      const meetingDuration = 60; // minutes
      const interval = 15; // minutes
      // just added UTC timezone stamp for consistency
      const workdayStart = new Date('2023-10-18T08:00:00+0000'); // Start of workday
      const workdayEnd = new Date('2023-10-18T17:00:00+0000'); // End of workday
    
      const availableSlots = [];
    
      let currentSlotStart = new Date(workdayStart);
    
      while (currentSlotStart < workdayEnd) {
        const currentSlotEnd = new Date(currentSlotStart);
        currentSlotEnd.setMinutes(currentSlotEnd.getMinutes() + meetingDuration);
    
        if (events.every(event => (currentSlotStart < event.startTime && currentSlotEnd <= event.startTime) || (currentSlotStart >= event.endTime))) {
          availableSlots.push({ start: currentSlotStart.toUTCString() });
        }
    
        currentSlotStart = new Date(currentSlotStart);
        currentSlotStart.setMinutes(currentSlotStart.getMinutes() + interval);
      }
    
      console.log(availableSlots);
    

    OUTPUT

    image