From a885904304506e91b95cea4d222f4c8cc3c693e4 Mon Sep 17 00:00:00 2001 From: Kristjan ESPERANTO <35647502+KristjanESPERANTO@users.noreply.github.com> Date: Sun, 11 Jan 2026 23:36:13 +0100 Subject: [PATCH 1/3] refactor(calendar): clean up dead code after node-ical 0.23.0 upgrade Remove ineffective UNTIL modification and unnecessary tzid clearing. Simplify timed event conversion. The removed code had no effect because rule.options is a read-only getter in rrule-temporal. --- modules/default/calendar/calendarfetcherutils.js | 5 ----- 1 file changed, 5 deletions(-) diff --git a/modules/default/calendar/calendarfetcherutils.js b/modules/default/calendar/calendarfetcherutils.js index c1cc07ce9f..6d16e237b9 100644 --- a/modules/default/calendar/calendarfetcherutils.js +++ b/modules/default/calendar/calendarfetcherutils.js @@ -66,11 +66,6 @@ const CalendarFetcherUtils = { const searchFromDate = pastLocalMoment.clone().subtract(Math.max(durationInMs, oneDayInMs), "milliseconds").toDate(); const searchToDate = futureLocalMoment.clone().add(1, "days").toDate(); - // For all-day events, extend "until" to end of day to include the final occurrence - if (isFullDayEvent && rule.options?.until) { - rule.options.until = moment(rule.options.until).endOf("day").toDate(); - } - const dates = rule.between(searchFromDate, searchToDate, true) || []; // Convert dates to moments in the event's timezone. From eb9bf242f85149226931f99628aed5105700f62b Mon Sep 17 00:00:00 2001 From: Kristjan ESPERANTO <35647502+KristjanESPERANTO@users.noreply.github.com> Date: Sun, 11 Jan 2026 23:39:21 +0100 Subject: [PATCH 2/3] chore: update node-ical --- package-lock.json | 20 ++++++++++---------- package.json | 2 +- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/package-lock.json b/package-lock.json index 65e7b9c9d0..89e3e3ca7a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -27,7 +27,7 @@ "ipaddr.js": "^2.3.0", "moment": "^2.30.1", "moment-timezone": "^0.6.0", - "node-ical": "^0.23.0", + "node-ical": "^0.23.1", "nunjucks": "^3.2.4", "pm2": "^6.0.14", "socket.io": "^4.8.3", @@ -950,9 +950,9 @@ } }, "node_modules/@csstools/css-syntax-patches-for-csstree": { - "version": "1.0.24", - "resolved": "https://registry.npmjs.org/@csstools/css-syntax-patches-for-csstree/-/css-syntax-patches-for-csstree-1.0.24.tgz", - "integrity": "sha512-T0pSTcd9eYEHV+llVPSkZU7URdVGu87BpSvozMwRoLJYXmLXvEHgYfv0yDsQH9+DIdLzkJCOJBABqWWnwTGPvg==", + "version": "1.0.25", + "resolved": "https://registry.npmjs.org/@csstools/css-syntax-patches-for-csstree/-/css-syntax-patches-for-csstree-1.0.25.tgz", + "integrity": "sha512-g0Kw9W3vjx5BEBAF8c5Fm2NcB/Fs8jJXh85aXqwEXiL+tqtOut07TWgyaGzAAfTM+gKckrrncyeGEZPcaRgm2Q==", "dev": true, "funding": [ { @@ -9426,9 +9426,9 @@ } }, "node_modules/node-ical": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/node-ical/-/node-ical-0.23.0.tgz", - "integrity": "sha512-1XMNdOVH1j1JgHdCrIzJ3MPYhPn0LZidrF65tjzGGJYgps1id8+bNd8qs8SuQMjxoKM5VPpfT5D8ro5y1MsBkw==", + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/node-ical/-/node-ical-0.23.1.tgz", + "integrity": "sha512-4+FON2u++r7vU9VfTCris9hgxS5darN4P7aQZOJkzDazXLRS7cExb0G5OWO3CzRs8gHd5um1fdSMF0e45f7xAg==", "license": "Apache-2.0", "dependencies": { "@js-temporal/polyfill": "^0.5.1", @@ -12070,9 +12070,9 @@ "license": "MIT" }, "node_modules/systeminformation": { - "version": "5.30.2", - "resolved": "https://registry.npmjs.org/systeminformation/-/systeminformation-5.30.2.tgz", - "integrity": "sha512-Rrt5oFTWluUVuPlbtn3o9ja+nvjdF3Um4DG0KxqfYvpzcx7Q9plZBTjJiJy9mAouua4+OI7IUGBaG9Zyt9NgxA==", + "version": "5.30.3", + "resolved": "https://registry.npmjs.org/systeminformation/-/systeminformation-5.30.3.tgz", + "integrity": "sha512-NgHJUpA+y7j4asLQa9jgBt+Eb2piyQIXQ+YjOyd2K0cHNwbNJ6I06F5afOqOiaCuV/wrEyGrb0olg4aFLlJD+A==", "license": "MIT", "os": [ "darwin", diff --git a/package.json b/package.json index 3d592675e6..e9c9f4a9f8 100644 --- a/package.json +++ b/package.json @@ -92,7 +92,7 @@ "ipaddr.js": "^2.3.0", "moment": "^2.30.1", "moment-timezone": "^0.6.0", - "node-ical": "^0.23.0", + "node-ical": "^0.23.1", "nunjucks": "^3.2.4", "pm2": "^6.0.14", "socket.io": "^4.8.3", From b486466300d702d676fcfc7fd89341aaab8243eb Mon Sep 17 00:00:00 2001 From: Kristjan ESPERANTO <35647502+KristjanESPERANTO@users.noreply.github.com> Date: Sun, 11 Jan 2026 23:52:52 +0100 Subject: [PATCH 3/3] fix(calendar): correct recurrence lookup for full-day events in eastern timezones When looking up recurrence overrides (moved/modified instances), the dateKey was always calculated from UTC time. This caused a mismatch for full-day events in timezones east of UTC (e.g., Europe/Berlin). Example: - Full-day event on Oct 12 in Europe/Berlin (UTC+2) - Old: dateKey = "2024-10-11" (from UTC: Oct 11 22:00) - New: dateKey = "2024-10-12" (from local date components) node-ical's getDateKey() uses local date components for VALUE=DATE events, as per RFC 5545 specification. MagicMirror now matches this behavior. This fixes moved full-day recurring events not being found in their recurrence override table, causing them to appear on the wrong date. Related: node-ical upgrade to 0.23.1 --- modules/default/calendar/calendarfetcherutils.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/modules/default/calendar/calendarfetcherutils.js b/modules/default/calendar/calendarfetcherutils.js index 6d16e237b9..e0971f5759 100644 --- a/modules/default/calendar/calendarfetcherutils.js +++ b/modules/default/calendar/calendarfetcherutils.js @@ -308,7 +308,12 @@ const CalendarFetcherUtils = { let recurringEventStartMoment = startMoment.clone().tz(CalendarFetcherUtils.getLocalTimezone()); let recurringEventEndMoment = recurringEventStartMoment.clone().add(durationMs, "ms"); - const dateKey = recurringEventStartMoment.tz("UTC").format("YYYY-MM-DD"); + // For full-day events, use local date components to match node-ical's getDateKey behavior + // For timed events, use UTC to match ISO string slice + const isFullDay = CalendarFetcherUtils.isFullDayEvent(event); + const dateKey = isFullDay + ? recurringEventStartMoment.format("YYYY-MM-DD") + : recurringEventStartMoment.tz("UTC").format("YYYY-MM-DD"); // Check for overrides if (curEvent.recurrences !== undefined) {