mirror of
https://github.com/bulwarkmail/webmail.git
synced 2026-10-31 00:12:39 +00:00
fix: place calendar events at their clock time on DST change days
This commit is contained in:
@@ -177,6 +177,42 @@ describe('calendar-utils all-day handling', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('places events by wall-clock time on DST change days', () => {
|
||||
const tz = process.env.TZ;
|
||||
process.env.TZ = 'Europe/Berlin';
|
||||
try {
|
||||
// 2026-10-25 has 25 hours in Berlin; 12:51 CET is 13h51m after midnight.
|
||||
const fallBack = makeEvent({
|
||||
start: '2026-10-25T12:51:00',
|
||||
duration: 'PT25M',
|
||||
timeZone: 'Europe/Berlin',
|
||||
showWithoutTime: false,
|
||||
utcStart: '2026-10-25T11:51:00Z',
|
||||
utcEnd: '2026-10-25T12:16:00Z',
|
||||
});
|
||||
expect(getTimedEventBoundsForDay(fallBack, new Date(2026, 9, 25))).toMatchObject({
|
||||
startMinutes: 12 * 60 + 51,
|
||||
endMinutes: 13 * 60 + 16,
|
||||
});
|
||||
|
||||
// 2026-03-29 has 23 hours; 09:00 CEST is 8h after midnight.
|
||||
const springForward = makeEvent({
|
||||
start: '2026-03-29T09:00:00',
|
||||
duration: 'PT1H',
|
||||
timeZone: 'Europe/Berlin',
|
||||
showWithoutTime: false,
|
||||
utcStart: '2026-03-29T07:00:00Z',
|
||||
utcEnd: '2026-03-29T08:00:00Z',
|
||||
});
|
||||
expect(getTimedEventBoundsForDay(springForward, new Date(2026, 2, 29))).toMatchObject({
|
||||
startMinutes: 9 * 60,
|
||||
endMinutes: 10 * 60,
|
||||
});
|
||||
} finally {
|
||||
process.env.TZ = tz;
|
||||
}
|
||||
});
|
||||
|
||||
it('lays out continued timed events using clipped bounds for the active day', () => {
|
||||
const event = makeEvent({
|
||||
start: '2026-03-14T22:00:00',
|
||||
|
||||
+11
-4
@@ -102,6 +102,10 @@ export function getEventDayBounds(event: CalendarEvent): { startDay: Date; endDa
|
||||
};
|
||||
}
|
||||
|
||||
function wallClockMinutes(date: Date): number {
|
||||
return date.getHours() * 60 + date.getMinutes() + date.getSeconds() / 60 + date.getMilliseconds() / 60000;
|
||||
}
|
||||
|
||||
export function getTimedEventBoundsForDay(
|
||||
event: CalendarEvent,
|
||||
day: Date,
|
||||
@@ -117,10 +121,13 @@ export function getTimedEventBoundsForDay(
|
||||
return null;
|
||||
}
|
||||
|
||||
const clippedStart = eventStart > dayStart ? eventStart : dayStart;
|
||||
const clippedEnd = eventEnd < nextDayStart ? eventEnd : nextDayStart;
|
||||
const startMinutes = Math.max(0, Math.floor((clippedStart.getTime() - dayStart.getTime()) / 60000));
|
||||
const endMinutes = Math.min(1440, Math.ceil((clippedEnd.getTime() - dayStart.getTime()) / 60000));
|
||||
// Wall-clock minutes, not elapsed time since midnight: on a DST change day
|
||||
// the day has 23 or 25 hours, and elapsed time would shift every event
|
||||
// after the change by an hour.
|
||||
const startMinutes = eventStart > dayStart ? Math.floor(wallClockMinutes(eventStart)) : 0;
|
||||
const endMinutes = eventEnd < nextDayStart
|
||||
? Math.max(startMinutes, Math.ceil(wallClockMinutes(eventEnd)))
|
||||
: 1440;
|
||||
|
||||
return {
|
||||
startMinutes,
|
||||
|
||||
Reference in New Issue
Block a user