Booking page now displays localized dates, times, and timezones (#7130)
parent
78656f2116
commit
09cc6f07e4
|
@ -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>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
*/
|
||||
|
|
Loading…
Reference in New Issue