Files
calendar/apps/web/lib/parseDate.ts
T
229aeeb0c9 Fix booking time hydration error (#7253)
Root cause of the error is that both server and client try to parse date differently due to localStorage. Since server does not know localStorage when render initial HTML it renders page as if there is no value set for time format.
 On the other hand, client side know the value of time format when it tries to render.

To fix the issue, we force it to parse date with default format at the initial rendering time.

- Add `withDefaultTimeFormat` argument to `parseDate` function
- Add `withDefaultTimeFormat` argument to `parseRecurringDates` function
- Leverage useEffect to dynamically update `withDefaultTimeFormat` arg passed to function

Co-authored-by: sangboak.lee <sangboak.lee@woowahan.com>
Co-authored-by: zomars <zomars@me.com>
2023-03-11 00:24:01 +00:00

62 lines
2.0 KiB
TypeScript

import type { I18n } from "next-i18next";
import { RRule } from "rrule";
import type { Dayjs } from "@calcom/dayjs";
import dayjs from "@calcom/dayjs";
import { detectBrowserTimeFormat, TimeFormat } from "@calcom/lib/timeFormat";
import type { RecurringEvent } from "@calcom/types/Calendar";
import { parseZone } from "./parseZone";
const processDate = (date: string | null | Dayjs, i18n: I18n, withDefaultTimeFormat: boolean) => {
const parsedZone = parseZone(date);
if (!parsedZone?.isValid()) return "Invalid date";
const formattedTime = parsedZone?.format(
withDefaultTimeFormat ? TimeFormat.TWELVE_HOUR : detectBrowserTimeFormat
);
return formattedTime + ", " + dayjs(date).toDate().toLocaleString(i18n.language, { dateStyle: "full" });
};
export const parseDate = (date: string | null | Dayjs, i18n: I18n, withDefaultTimeFormat: boolean) => {
if (!date) return ["No date"];
return processDate(date, i18n, withDefaultTimeFormat);
};
export const parseRecurringDates = (
{
startDate,
timeZone,
recurringEvent,
recurringCount,
withDefaultTimeFormat,
}: {
startDate: string | null | Dayjs;
timeZone?: string;
recurringEvent: RecurringEvent | null;
recurringCount: number;
withDefaultTimeFormat: boolean;
},
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: new Date(dayjs(startDate).valueOf()),
});
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, withDefaultTimeFormat);
});
return [dateStrings, times.map((t) => t.toDate())];
};