Bugfix/5271 wrong availability displayed on nov 6 (#5365)
* Fixes the localisation issue with recurring events * Implement DST as Dayjs doesn't * Fixed generated booking URL when different TZ is set * manually apply DST offset to times * Fix type error
This commit is contained in:
parent
328a354f4d
commit
db9911a264
|
@ -83,7 +83,7 @@ const AvailableTimes: FC<AvailableTimesProps> = ({
|
||||||
pathname: router.pathname.endsWith("/embed") ? "../book" : "book",
|
pathname: router.pathname.endsWith("/embed") ? "../book" : "book",
|
||||||
query: {
|
query: {
|
||||||
...router.query,
|
...router.query,
|
||||||
date: dayjs(slot.time).format(),
|
date: dayjs.utc(slot.time).tz(timeZone()).format(),
|
||||||
type: eventTypeId,
|
type: eventTypeId,
|
||||||
slug: eventTypeSlug,
|
slug: eventTypeSlug,
|
||||||
/** Treat as recurring only when a count exist and it's not a rescheduling workflow */
|
/** Treat as recurring only when a count exist and it's not a rescheduling workflow */
|
||||||
|
|
|
@ -497,26 +497,17 @@ const BookingPage = ({
|
||||||
<div className="text-bookinghighlight flex items-start text-sm">
|
<div className="text-bookinghighlight flex items-start text-sm">
|
||||||
<Icon.FiCalendar className="mr-[10px] ml-[2px] mt-[2px] inline-block h-4 w-4" />
|
<Icon.FiCalendar className="mr-[10px] ml-[2px] mt-[2px] inline-block h-4 w-4" />
|
||||||
<div className="text-sm font-medium">
|
<div className="text-sm font-medium">
|
||||||
{(rescheduleUid || !eventType.recurringEvent?.freq) &&
|
{(rescheduleUid || !eventType.recurringEvent?.freq) && `${parseDate(date, i18n)}`}
|
||||||
`${formatTime(dayjs(date).toDate(), user?.timeFormat, user?.timeZone)}, ${dayjs(
|
|
||||||
date
|
|
||||||
).format("dddd, D MMMM YYYY")}`}
|
|
||||||
{!rescheduleUid &&
|
{!rescheduleUid &&
|
||||||
eventType.recurringEvent?.freq &&
|
eventType.recurringEvent?.freq &&
|
||||||
recurringDates.slice(0, 5).map((aDate, key) => {
|
recurringStrings.slice(0, 5).map((timeFormatted, key) => {
|
||||||
return (
|
return <p key={key}>{timeFormatted}</p>;
|
||||||
<p key={key}>{`${formatTime(aDate, user?.timeFormat, user?.timeZone)}, ${dayjs(
|
|
||||||
aDate
|
|
||||||
).format("dddd, D MMMM YYYY")}`}</p>
|
|
||||||
);
|
|
||||||
})}
|
})}
|
||||||
{!rescheduleUid && eventType.recurringEvent?.freq && recurringStrings.length > 5 && (
|
{!rescheduleUid && eventType.recurringEvent?.freq && recurringStrings.length > 5 && (
|
||||||
<div className="flex">
|
<div className="flex">
|
||||||
<Tooltip
|
<Tooltip
|
||||||
content={recurringDates.slice(5).map((aDate, key) => (
|
content={recurringStrings.slice(5).map((timeFormatted, key) => (
|
||||||
<p key={key}>{`${formatTime(aDate, user?.timeFormat, user?.timeZone)}, ${dayjs(
|
<p key={key}>{timeFormatted}</p>
|
||||||
aDate
|
|
||||||
).format("dddd, D MMMM YYYY")}`}</p>
|
|
||||||
))}>
|
))}>
|
||||||
<p className="dark:text-darkgray-600 text-sm">
|
<p className="dark:text-darkgray-600 text-sm">
|
||||||
{t("plus_more", { count: recurringStrings.length - 5 })}
|
{t("plus_more", { count: recurringStrings.length - 5 })}
|
||||||
|
|
|
@ -20,15 +20,6 @@ export const parseDate = (date: string | null | Dayjs, i18n: I18n) => {
|
||||||
return processDate(date, i18n);
|
return processDate(date, i18n);
|
||||||
};
|
};
|
||||||
|
|
||||||
// tzid is currently broken in rrule library.
|
|
||||||
// @see https://github.com/jakubroztocil/rrule/issues/523
|
|
||||||
const dateWithZone = (d: Date, timeZone?: string) => {
|
|
||||||
const dateInLocalTZ = new Date(d.toLocaleString("en-US", { timeZone: "UTC" }));
|
|
||||||
const dateInTargetTZ = new Date(d.toLocaleString("en-US", { timeZone: timeZone || "UTC" }));
|
|
||||||
const tzOffset = dateInTargetTZ.getTime() - dateInLocalTZ.getTime();
|
|
||||||
return new Date(d.getTime() - tzOffset);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const parseRecurringDates = (
|
export const parseRecurringDates = (
|
||||||
{
|
{
|
||||||
startDate,
|
startDate,
|
||||||
|
@ -48,16 +39,21 @@ export const parseRecurringDates = (
|
||||||
const rule = new RRule({
|
const rule = new RRule({
|
||||||
...restRecurringEvent,
|
...restRecurringEvent,
|
||||||
count: recurringCount,
|
count: recurringCount,
|
||||||
dtstart: dayjs(startDate).utc(true).toDate(),
|
dtstart: new Date(dayjs(startDate).valueOf()),
|
||||||
});
|
|
||||||
// UTC times with tzOffset applied to account for DST
|
|
||||||
const times = rule.all().map((t) => dateWithZone(t, timeZone));
|
|
||||||
const dateStrings = times.map((t) => {
|
|
||||||
// undo DST diffs for localized display.
|
|
||||||
return processDate(dayjs.utc(t).tz(timeZone), i18n);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
return [dateStrings, times];
|
const startUtcOffset = dayjs(startDate).utcOffset();
|
||||||
|
// UTC still need to have DST applied, rrule does not do this.
|
||||||
|
const times = rule.all().map((t) => {
|
||||||
|
// applying the DST offset.
|
||||||
|
return dayjs.utc(t).add(startUtcOffset - dayjs(t).utcOffset(), "minute");
|
||||||
|
});
|
||||||
|
const dateStrings = times.map((t) => {
|
||||||
|
// finally; show in local timeZone again
|
||||||
|
return processDate(t.tz(timeZone), i18n);
|
||||||
|
});
|
||||||
|
|
||||||
|
return [dateStrings, times.map((t) => t.toDate())];
|
||||||
};
|
};
|
||||||
|
|
||||||
export const extractRecurringDates = (
|
export const extractRecurringDates = (
|
||||||
|
|
|
@ -111,7 +111,24 @@ const getSlots = ({ inviteeDate, frequency, minimumBookingNotice, workingHours,
|
||||||
});
|
});
|
||||||
|
|
||||||
slotsTimeFrameAvailable.forEach((item) => {
|
slotsTimeFrameAvailable.forEach((item) => {
|
||||||
const slot = startOfInviteeDay.add(item.startTime, "minute");
|
// XXX: Hack alert, as dayjs is supposedly not aware of timezone the current slot may have invalid UTC offset.
|
||||||
|
const timeZone = (startOfInviteeDay as unknown as { $x: { $timezone: string } })["$x"]["$timezone"];
|
||||||
|
/*
|
||||||
|
* @calcom/web:dev: 2022-11-06T00:00:00-04:00
|
||||||
|
* @calcom/web:dev: 2022-11-06T01:00:00-04:00
|
||||||
|
* @calcom/web:dev: 2022-11-06T01:00:00-04:00 <-- note there is no offset change, but we did lose an hour.
|
||||||
|
* @calcom/web:dev: 2022-11-06T02:00:00-04:00
|
||||||
|
* @calcom/web:dev: 2022-11-06T03:00:00-04:00
|
||||||
|
* ...
|
||||||
|
*/
|
||||||
|
let slot = dayjs.tz(
|
||||||
|
startOfInviteeDay.add(item.startTime, "minute").format("YYYY-MM-DDTHH:mm:ss"),
|
||||||
|
timeZone
|
||||||
|
);
|
||||||
|
// If the startOfInviteeDay has a different UTC offset than the slot, a DST change has occurred.
|
||||||
|
// As the time has now fallen backwards, or forwards; this difference -
|
||||||
|
// needs to be manually added as this is not done for us. Usually 0.
|
||||||
|
slot = slot.add(startOfInviteeDay.utcOffset() - slot.utcOffset(), "minutes");
|
||||||
// Validating slot its not on the past
|
// Validating slot its not on the past
|
||||||
if (!slot.isBefore(startDate)) {
|
if (!slot.isBefore(startDate)) {
|
||||||
slots.push(slot);
|
slots.push(slot);
|
||||||
|
|
Loading…
Reference in New Issue
Block a user