cal.com icon indicating copy to clipboard operation
cal.com copied to clipboard

[CAL-3982] Booking limits are being calculated from the attendee's (booker's) perspective (timezone).

Open MehulZR opened this issue 1 year ago • 3 comments

Issue Summary

Booking limit are being calculated form the attendee perspective

Steps to Reproduce

  1. Create event with a booking limit
  2. Book a event from the host's tz ( for simplicity sake)
  3. Change the attendee tz which overlaps the host's tz but doesn't include the booking we created (day finishes before the event).

Actual Results

https://github.com/calcom/cal.com/assets/86384652/09b1461b-31fe-4057-9dd8-4a5a811ff0a5

Expected Results

Limits should be calculated respective to the host timezone.

Technical details

  • Firefox 126
  • Node 20
  • Fedora 40

CAL-3982

MehulZR avatar Jun 06 '24 19:06 MehulZR

@MehulZR Hello there! I'm here to help you with any bugs, questions, or becoming a contributor. Let me know how I can assist you!

To ensure that booking limits are calculated based on the host's timezone rather than the attendee's timezone, you need to set the timeZone parameter to the host's timezone when calling the getSchedule function.

Here is an example from the provided test cases where the timezone is set to IST (Indian Standard Time) for the host:

const scheduleForEvent = await getSchedule({
  input: {
    eventTypeId: 1,
    eventTypeSlug: "",
    usernameList: [],
    // Because this time is in GMT, it will be 00:00 in IST with todayDateString
    startTime: `${yesterdayDateString}T18:30:00.000Z`,
    endTime: `${plus5DateString}T18:29:59.999Z`,
    timeZone: Timezones["+5:30"], // Host's timezone
    isTeamEvent: false,
  },
});

In this example, the timeZone parameter is set to Timezones["+5:30"], which corresponds to IST. This ensures that the booking limits are calculated based on the host's timezone.

Additionally, the checkBookingLimits function from the @calcom/lib/server/checkBookingLimits.ts module can be used to ensure that all date and time calculations are correctly aligned with the host's local time. This function takes a timeZone parameter, which should be set to the host's timezone. Here is a relevant snippet:

export async function checkBookingLimits(
  bookingLimits: IntervalLimit,
  eventStartDate: Date,
  eventId: number,
  rescheduleUid?: string | undefined,
  timeZone?: string | null
) {
  const parsedBookingLimits = parseBookingLimit(bookingLimits);
  if (!parsedBookingLimits) return false;

  const limitCalculations = ascendingLimitKeys.map((key) =>
    checkBookingLimit({
      key,
      limitingNumber: parsedBookingLimits[key],
      eventStartDate,
      eventId,
      timeZone,
      rescheduleUid,
    })
  );

  try {
    return !!(await Promise.all(limitCalculations));
  } catch (error) {
    throw new HttpError({ message: getErrorFromUnknown(error).message, statusCode: 401 });
  }
}

export async function checkBookingLimit({
  eventStartDate,
  eventId,
  key,
  limitingNumber,
  rescheduleUid,
  timeZone,
}: {
  eventStartDate: Date;
  eventId: number;
  key: keyof IntervalLimit;
  limitingNumber: number | undefined;
  rescheduleUid?: string | undefined;
  timeZone?: string | null;
}) {
  const eventDateInOrganizerTz = timeZone ? dayjs(eventStartDate).tz(timeZone) : dayjs(eventStartDate);

  if (!limitingNumber) return;

  const unit = intervalLimitKeyToUnit(key);

  const startDate = dayjs(eventDateInOrganizerTz).startOf(unit).toDate();
  const endDate = dayjs(eventDateInOrganizerTz).endOf(unit).toDate();
  const bookingsInPeriod = await prisma.booking.count({
    where: {
      status: BookingStatus.ACCEPTED,
      eventTypeId: eventId,
      startTime: {
        gte: startDate,
      },
      endTime: {
        lte: endDate,
      },
      uid: {
        not: rescheduleUid,
      },
    },
  });

  if (bookingsInPeriod < limitingNumber) return;

  throw new HttpError({
    message: `booking_limit_reached`,
    statusCode: 403,
  });
}

By using the timeZone parameter in both the getSchedule and checkBookingLimits functions, you can ensure that all booking limits are calculated based on the host's timezone [1][2][3].

To continue talking to Dosu, mention @dosu.

dosubot[bot] avatar Jun 06 '24 19:06 dosubot[bot]

image

Hey @MehulZR , I tried reproducing the bug . Looks like it shows the availablity (based on atendee's time zone) but does not let you book the meeting if limit is already reached (in host's timezone). Correct me if am wrong.

heyhexadecimal avatar Jun 10 '24 20:06 heyhexadecimal

It might be because while creating the booking, it checks from the host's timezone (I haven't checked it). I tried fixing it but the dayjs lib was behaving weirdly for me.

MehulZR avatar Jun 10 '24 21:06 MehulZR