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:
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}`,
|
||||
})
|
||||
: [];
|
||||
|
||||
|
||||
Reference in new issue
Block a user