Booking page now displays localized dates, times, and timezones (#7130)

pull/7159/head
Aaron Presley 2023-02-16 09:16:22 -08:00 committed by GitHub
parent 78656f2116
commit 09cc6f07e4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 80 additions and 14 deletions

View File

@ -28,7 +28,11 @@ import {
import { parseRecurringEvent } from "@calcom/lib";
import CustomBranding from "@calcom/lib/CustomBranding";
import { APP_NAME } from "@calcom/lib/constants";
import { formatTime } from "@calcom/lib/date-fns";
import {
formatToLocalizedDate,
formatToLocalizedTime,
formatToLocalizedTimezone,
} from "@calcom/lib/date-fns";
import { getDefaultEvent } from "@calcom/lib/defaultEvents";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import useTheme from "@calcom/lib/hooks/useTheme";
@ -820,7 +824,10 @@ export function RecurringBookings({
isCancelled,
}: RecurringBookingsProps) {
const [moreEventsVisible, setMoreEventsVisible] = useState(false);
const { t } = useLocale();
const {
t,
i18n: { language },
} = useLocale();
const recurringBookingsSorted = recurringBookings
? recurringBookings.sort((a: ConfigType, b: ConfigType) => (dayjs(a).isAfter(dayjs(b)) ? 1 : -1))
@ -843,11 +850,13 @@ export function RecurringBookings({
{eventType.recurringEvent?.count &&
recurringBookingsSorted.slice(0, 4).map((dateStr: string, idx: number) => (
<div key={idx} className={classNames("mb-2", isCancelled ? "line-through" : "")}>
{dayjs.tz(dateStr, timeZone()).format("dddd, DD MMMM YYYY")}
{formatToLocalizedDate(dayjs.tz(dateStr, timeZone()), language, "full")}
<br />
{formatTime(dateStr, is24h ? 24 : 12, timeZone().toLowerCase())} -{" "}
{formatTime(dayjs(dateStr).add(duration, "m"), is24h ? 24 : 12, timeZone().toLowerCase())}{" "}
<span className="text-bookinglight">({timeZone()})</span>
{formatToLocalizedTime(dayjs(dateStr), language, undefined, !is24h)} -{" "}
{formatToLocalizedTime(dayjs(dateStr).add(duration, "m"), language, undefined, !is24h)}{" "}
<span className="text-bookinglight">
({formatToLocalizedTimezone(dayjs(dateStr), language)})
</span>
</div>
))}
{recurringBookingsSorted.length > 4 && (
@ -861,11 +870,13 @@ export function RecurringBookings({
{eventType.recurringEvent?.count &&
recurringBookingsSorted.slice(4).map((dateStr: string, idx: number) => (
<div key={idx} className={classNames("mb-2", isCancelled ? "line-through" : "")}>
{dayjs.tz(dateStr, timeZone()).format("dddd, DD MMMM YYYY")}
{formatToLocalizedDate(dayjs.tz(date, timeZone()), language, "full")}
<br />
{formatTime(dateStr, is24h ? 24 : 12, timeZone().toLowerCase())} -{" "}
{formatTime(dayjs(dateStr).add(duration, "m"), is24h ? 24 : 12, timeZone().toLowerCase())}{" "}
<span className="text-bookinglight">({timeZone()})</span>
{formatToLocalizedTime(date, language, undefined, !is24h)} -{" "}
{formatToLocalizedTime(dayjs(date).add(duration, "m"), language, undefined, !is24h)}{" "}
<span className="text-bookinglight">
({formatToLocalizedTimezone(dayjs(dateStr), language)})
</span>
</div>
))}
</CollapsibleContent>
@ -877,11 +888,11 @@ export function RecurringBookings({
return (
<div className={classNames(isCancelled ? "line-through" : "")}>
{dayjs.tz(date, timeZone()).format("dddd, DD MMMM YYYY")}
{formatToLocalizedDate(dayjs.tz(date, timeZone()), language, "full")}
<br />
{formatTime(date, is24h ? 24 : 12, timeZone().toLowerCase())} -{" "}
{formatTime(dayjs(date).add(duration, "m"), is24h ? 24 : 12, timeZone().toLowerCase())}{" "}
<span className="text-bookinglight">({timeZone()})</span>
{formatToLocalizedTime(date, language, undefined, !is24h)} -{" "}
{formatToLocalizedTime(dayjs(date).add(duration, "m"), language, undefined, !is24h)}{" "}
<span className="text-bookinglight">({formatToLocalizedTimezone(date, language)})</span>
</div>
);
}

View File

@ -29,6 +29,61 @@ export const formatTime = (
: dayjs(date).format(timeFormat === 12 ? "h:mma" : "HH:mm");
};
/**
* Returns a localized and translated date or time, based on the native
* Intl.DateTimeFormat available to JS. Undefined values mean the browser's
* locale will be used.
*/
export const formatLocalizedDateTime = (
date: Date | Dayjs,
options: Intl.DateTimeFormatOptions = {},
locale: string | undefined = undefined
) => {
const theDate = date instanceof dayjs ? (date as Dayjs).toDate() : (date as Date);
return Intl.DateTimeFormat(locale, options).format(theDate);
};
/**
* Returns a localized and translated calendar day based on the
* given Date object and locale. Undefined values mean the defaults
* associated with the browser's current locale will be used.
*/
export const formatToLocalizedDate = (
date: Date | Dayjs,
locale: string | undefined = undefined,
dateStyle: Intl.DateTimeFormatOptions["dateStyle"] = "long"
) => formatLocalizedDateTime(date, { dateStyle }, locale);
/**
* Returns a localized and translated time of day based on the
* given Date object and locale. Undefined values mean the defaults
* associated with the browser's current locale will be used.
*/
export const formatToLocalizedTime = (
date: Date | Dayjs,
locale: string | undefined = undefined,
timeStyle: Intl.DateTimeFormatOptions["timeStyle"] = "short",
hour12: Intl.DateTimeFormatOptions["hour12"] = undefined
) => formatLocalizedDateTime(date, { timeStyle, hour12 }, locale);
/**
* Returns a translated timezone based on the given Date object and
* locale. Undefined values mean the browser's current locale
* will be used.
*/
export const formatToLocalizedTimezone = (
date: Date | Dayjs,
locale: string | undefined = undefined,
timeZoneName: Intl.DateTimeFormatOptions["timeZoneName"] = "long"
) => {
// Intl.DateTimeFormat doesn't format into a timezone only, so we must
// formatToParts() and return the piece we want
const theDate = date instanceof dayjs ? (date as Dayjs).toDate() : (date as Date);
return Intl.DateTimeFormat(locale, { timeZoneName })
.formatToParts(theDate)
.find((d) => d.type == "timeZoneName")?.value;
};
/**
* Sorts two timezones by their offset from GMT.
*/