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>
62 lines
2.0 KiB
TypeScript
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())];
|
|
};
|