cal.pub0.org/apps/web/lib/parseDate.ts

89 lines
3.1 KiB
TypeScript

import { I18n } from "next-i18next";
import { RRule } from "rrule";
import dayjs, { Dayjs } from "@calcom/dayjs";
import { detectBrowserTimeFormat } from "@calcom/lib/timeFormat";
import { inferQueryOutput } from "@calcom/trpc/react";
import type { RecurringEvent } from "@calcom/types/Calendar";
import { parseZone } from "./parseZone";
const processDate = (date: string | null | Dayjs, i18n: I18n) => {
const parsedZone = parseZone(date);
if (!parsedZone?.isValid()) return "Invalid date";
const formattedTime = parsedZone?.format(detectBrowserTimeFormat);
return formattedTime + ", " + dayjs(date).toDate().toLocaleString(i18n.language, { dateStyle: "full" });
};
export const parseDate = (date: string | null | Dayjs, i18n: I18n) => {
if (!date) return ["No date"];
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 = (
{
startDate,
timeZone,
recurringEvent,
recurringCount,
}: {
startDate: string | null | Dayjs;
timeZone?: string;
recurringEvent: RecurringEvent | null;
recurringCount: number;
},
i18n: I18n
): [string[], Date[]] => {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const { count, ...restRecurringEvent } = recurringEvent || {};
const rule = new RRule({
...restRecurringEvent,
count: recurringCount,
dtstart: dayjs(startDate).utc(true).toDate(),
});
// 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];
};
export const extractRecurringDates = (
booking: inferQueryOutput<"viewer.bookings">["bookings"][number] & {
eventType: { recurringEvent: RecurringEvent | null };
recurringEventId: string | null;
recurringBookings: inferQueryOutput<"viewer.bookings">["recurringInfo"];
},
timeZone: string | undefined,
i18n: I18n
): [string[], Date[]] => {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const { count = 0, ...rest } =
booking.eventType.recurringEvent !== null ? booking.eventType.recurringEvent : {};
const recurringInfo = booking.recurringBookings.find(
(val) => val.recurringEventId === booking.recurringEventId
);
const allDates = new RRule({
...rest,
count: recurringInfo?._count.recurringEventId,
dtstart: recurringInfo?._min.startTime,
}).all();
const utcOffset = dayjs(recurringInfo?._min.startTime).tz(timeZone).utcOffset();
const dateStrings = allDates.map((r) => {
return processDate(dayjs.utc(r).utcOffset(utcOffset), i18n);
});
return [dateStrings, allDates];
};