Fix/DST issues (#7462)

* use the organizer timezone and invitee timezone to show slots

* type fixes

* set start date on selected TZ on booking payload

* add considerations when only the invitee is on DTS

* Apply suggestions from code review

Co-authored-by: alannnc <alannnc@gmail.com>
Co-authored-by: Omar López <zomars@me.com>

* fixes typo

* Apply suggestions from code review

Co-authored-by: alannnc <alannnc@gmail.com>

* cleaning

---------

Co-authored-by: Alex van Andel <me@alexvanandel.com>
Co-authored-by: Omar López <zomars@me.com>
Co-authored-by: alannnc <alannnc@gmail.com>
This commit is contained in:
authored and GitHub committed 2023-03-03 09:33:16 -07:00
1 parent b8b24c04dc
commit 2d26d73eb3
7 files changed
+134 -19

No files matched your search

@@ -19,7 +19,7 @@ import { cancelScheduledJobs, scheduleTrigger } from "@calcom/app-store/zapier/l
import EventManager from "@calcom/core/EventManager";
import { getEventName } from "@calcom/core/event";
import { getUserAvailability } from "@calcom/core/getUserAvailability";
import type { ConfigType } from "@calcom/dayjs";
import type { ConfigType, Dayjs } from "@calcom/dayjs";
import dayjs from "@calcom/dayjs";
import {
sendAttendeeRequestEmail,
@@ -35,6 +35,7 @@ import { deleteScheduledSMSReminder } from "@calcom/features/ee/workflows/lib/re
import getWebhooks from "@calcom/features/webhooks/lib/getWebhooks";
import { isPrismaObjOrUndefined, parseRecurringEvent } from "@calcom/lib";
import { getVideoCallUrl } from "@calcom/lib/CalEventParser";
import { getDSTDifference, isInDST } from "@calcom/lib/date-fns";
import { getDefaultEvent, getGroupName, getUsernameList } from "@calcom/lib/defaultEvents";
import { getErrorFromUnknown } from "@calcom/lib/errors";
import getPaymentAppData from "@calcom/lib/getPaymentAppData";
@@ -111,16 +112,40 @@ const isWithinAvailableHours = (
timeSlot: { start: ConfigType; end: ConfigType },
{
workingHours,
organizerTimeZone,
inviteeTimeZone,
}: {
workingHours: WorkingHours[];
organizerTimeZone: string;
inviteeTimeZone: string;
}
) => {
const timeSlotStart = dayjs(timeSlot.start).utc();
const timeSlotEnd = dayjs(timeSlot.end).utc();
const isOrganizerInDST = isInDST(dayjs().tz(organizerTimeZone));
const isInviteeInDST = isInDST(dayjs().tz(organizerTimeZone));
const isOrganizerInDSTWhenSlotStart = isInDST(timeSlotStart.tz(organizerTimeZone));
const isInviteeInDSTWhenSlotStart = isInDST(timeSlotStart.tz(inviteeTimeZone));
const organizerDSTDifference = getDSTDifference(organizerTimeZone);
const inviteeDSTDifference = getDSTDifference(inviteeTimeZone);
const sameDSTUsers = isOrganizerInDSTWhenSlotStart === isInviteeInDSTWhenSlotStart;
const organizerDST = isOrganizerInDST === isOrganizerInDSTWhenSlotStart;
const inviteeDST = isInviteeInDST === isInviteeInDSTWhenSlotStart;
const getTime = (slotTime: Dayjs, minutes: number) =>
slotTime
.startOf("day")
.add(
sameDSTUsers && organizerDST && inviteeDST
? minutes
: minutes -
(isOrganizerInDSTWhenSlotStart || isOrganizerInDST
? organizerDSTDifference
: inviteeDSTDifference),
"minutes"
);
for (const workingHour of workingHours) {
// TODO: Double check & possibly fix timezone conversions.
const startTime = timeSlotStart.startOf("day").add(workingHour.startTime, "minute");
const endTime = timeSlotEnd.startOf("day").add(workingHour.endTime, "minute");
const startTime = getTime(timeSlotStart, workingHour.startTime);
const endTime = getTime(timeSlotEnd, workingHour.endTime);
if (
workingHour.days.includes(timeSlotStart.day()) &&
// UTC mode, should be performant.
@@ -247,7 +272,7 @@ async function ensureAvailableUsers(
eventType: Awaited<ReturnType<typeof getEventTypesFromDB>> & {
users: IsFixedAwareUser[];
},
input: { dateFrom: string; dateTo: string },
input: { dateFrom: string; dateTo: string; timeZone: string },
recurringDatesInfo?: {
allRecurringDates: string[] | undefined;
currentRecurringIndex: number | undefined;
@@ -271,6 +296,8 @@ async function ensureAvailableUsers(
{ start: input.dateFrom, end: input.dateTo },
{
workingHours,
organizerTimeZone: eventType.timeZone || eventType?.schedule?.timeZone || user.timeZone,
inviteeTimeZone: input.timeZone,
}
)
) {
@@ -567,6 +594,7 @@ async function handler(
{
dateFrom: reqBody.start,
dateTo: reqBody.end,
timeZone: reqBody.timeZone,
},
{
allRecurringDates,
@@ -1,8 +1,8 @@
import classNames from "classnames";
import React from "react";
import { ITimezone } from "react-timezone-select";
import type { ITimezone } from "react-timezone-select";
import { Dayjs } from "@calcom/dayjs";
import type { Dayjs } from "@calcom/dayjs";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import getSlots from "@calcom/lib/slots";
import { trpc } from "@calcom/trpc/react";
@@ -42,6 +42,7 @@ export default function TeamAvailabilityTimes(props: Props) {
workingHours: data?.workingHours || [],
minimumBookingNotice: 0,
eventLength: props.frequency,
organizerTimeZone: `${data?.timeZone}`,
})
: [];