From 1f2081af5d7642f6eeec92210ded14e3b9e1bc0d Mon Sep 17 00:00:00 2001 From: Denis Papec Date: Mon, 15 Jan 2024 02:01:10 +0000 Subject: [PATCH] Add option to specify a timezone for events (#2623) * Add option to specify a timezone for events * Amend message, update docs --- docs/widgets/services/calendar.md | 1 + src/widgets/calendar/agenda.jsx | 10 ++++++---- src/widgets/calendar/event.jsx | 4 ++++ src/widgets/calendar/integrations/ical.jsx | 12 ++++++++---- src/widgets/calendar/monthly.jsx | 10 ++++------ 5 files changed, 23 insertions(+), 14 deletions(-) diff --git a/docs/widgets/services/calendar.md b/docs/widgets/services/calendar.md index f9ed62849..74751f6ec 100644 --- a/docs/widgets/services/calendar.md +++ b/docs/widgets/services/calendar.md @@ -27,6 +27,7 @@ widget: url: https://domain.url/with/link/to.ics # URL with calendar events name: My Events # required - name for these calendar events color: zinc # optional - defaults to pre-defined color for the service (zinc for ical) + timezone: America/Los_Angeles # optional - force timezone for events (if it's the same - no change, if missing or different in ical - will be converted to this timezone) params: # optional - additional params for the service showName: true # optional - show name before event title in event line - defaults to false ``` diff --git a/src/widgets/calendar/agenda.jsx b/src/widgets/calendar/agenda.jsx index 903592697..9313cb8e4 100644 --- a/src/widgets/calendar/agenda.jsx +++ b/src/widgets/calendar/agenda.jsx @@ -2,7 +2,7 @@ import { DateTime } from "luxon"; import classNames from "classnames"; import { useTranslation } from "next-i18next"; -import Event from "./event"; +import Event, { compareDateTimezoneAware } from "./event"; export default function Agenda({ service, colorVariants, events, showDate }) { const { widget } = service; @@ -15,8 +15,10 @@ export default function Agenda({ service, colorVariants, events, showDate }) { const eventsArray = Object.keys(events) .filter( (eventKey) => - showDate.minus({ days: widget?.previousDays ?? 0 }).startOf("day").ts <= - events[eventKey].date?.startOf("day").ts, + showDate + .setZone(events[eventKey].date.zoneName) + .minus({ days: widget?.previousDays ?? 0 }) + .startOf("day").ts <= events[eventKey].date?.startOf("day").ts, ) .map((eventKey) => events[eventKey]) .sort((a, b) => a.date - b.date) @@ -56,7 +58,7 @@ export default function Agenda({ service, colorVariants, events, showDate }) { event={event} colorVariants={colorVariants} showDate={j === 0} - showTime={widget?.showTime && event.date.startOf("day").ts === showDate.startOf("day").ts} + showTime={widget?.showTime && compareDateTimezoneAware(showDate, event)} /> ))} diff --git a/src/widgets/calendar/event.jsx b/src/widgets/calendar/event.jsx index 5d3699d7e..7d3482858 100644 --- a/src/widgets/calendar/event.jsx +++ b/src/widgets/calendar/event.jsx @@ -39,3 +39,7 @@ export default function Event({ event, colorVariants, showDate = false, showTime ); } + +export function compareDateTimezoneAware(date, event) { + return date.setZone(event.date.zoneName).startOf("day").valueOf() === event.date.startOf("day").valueOf(); +} diff --git a/src/widgets/calendar/integrations/ical.jsx b/src/widgets/calendar/integrations/ical.jsx index c14aa347b..4c4ec9ca5 100644 --- a/src/widgets/calendar/integrations/ical.jsx +++ b/src/widgets/calendar/integrations/ical.jsx @@ -23,8 +23,9 @@ export default function Integration({ config, params, setEvents, hideErrors }) { } } - const startDate = DateTime.fromISO(params.start); - const endDate = DateTime.fromISO(params.end); + const zone = config?.timezone || null; + const startDate = DateTime.fromISO(params.start, { zone }); + const endDate = DateTime.fromISO(params.end, { zone }); if (icalError || !parsedIcal || !startDate.isValid || !endDate.isValid) { return; @@ -43,12 +44,15 @@ export default function Integration({ config, params, setEvents, hideErrors }) { const duration = event.dtend.value - event.dtstart.value; const days = duration / (1000 * 60 * 60 * 24); + const now = DateTime.now().setZone(zone); + const eventDate = DateTime.fromJSDate(date, { zone }); + for (let j = 0; j < days; j += 1) { eventsToAdd[`${event?.uid?.value}${i}${j}${type}`] = { title, - date: DateTime.fromJSDate(date).plus({ days: j }), + date: eventDate.plus({ days: j }), color: config?.color ?? "zinc", - isCompleted: DateTime.fromJSDate(date) < DateTime.now(), + isCompleted: eventDate < now, additional: event.location?.value, type: "ical", }; diff --git a/src/widgets/calendar/monthly.jsx b/src/widgets/calendar/monthly.jsx index 62f869ed7..ddb9cd874 100644 --- a/src/widgets/calendar/monthly.jsx +++ b/src/widgets/calendar/monthly.jsx @@ -3,7 +3,7 @@ import { DateTime, Info } from "luxon"; import classNames from "classnames"; import { useTranslation } from "next-i18next"; -import Event from "./event"; +import Event, { compareDateTimezoneAware } from "./event"; const cellStyle = "relative w-10 flex items-center justify-center flex-col"; const monthButton = "pl-6 pr-6 ml-2 mr-2 hover:bg-theme-100/20 dark:hover:bg-white/5 rounded-md cursor-pointer"; @@ -12,9 +12,7 @@ export function Day({ weekNumber, weekday, events, colorVariants, showDate, setS const currentDate = DateTime.now(); const cellDate = showDate.set({ weekday, weekNumber }).startOf("day"); - const filteredEvents = events?.filter( - (event) => event.date?.startOf("day").toUnixInteger() === cellDate.toUnixInteger(), - ); + const filteredEvents = events?.filter((event) => compareDateTimezoneAware(cellDate, event)); const dayStyles = (displayDate) => { let style = "h-9 "; @@ -173,7 +171,7 @@ export default function Monthly({ service, colorVariants, events, showDate, setS
{eventsArray - ?.filter((event) => showDate.startOf("day").ts === event.date?.startOf("day").ts) + ?.filter((event) => compareDateTimezoneAware(showDate, event)) .slice(0, widget?.maxEvents ?? 10) .map((event) => ( ))}