mirror of
https://github.com/bulwarkmail/native.git
synced 2026-10-31 16:06:32 +00:00
fix: place calendar events at their clock time on DST change days
This commit is contained in:
@@ -14,6 +14,7 @@ import {
|
|||||||
layoutOverlappingEvents,
|
layoutOverlappingEvents,
|
||||||
CALENDAR_COLOR_PALETTE,
|
CALENDAR_COLOR_PALETTE,
|
||||||
reopenedEdit,
|
reopenedEdit,
|
||||||
|
getTimedEventBoundsForDay,
|
||||||
} from '../calendar-utils';
|
} from '../calendar-utils';
|
||||||
import type { CalendarEvent } from '../../api/types';
|
import type { CalendarEvent } from '../../api/types';
|
||||||
import { useSettingsStore } from '../../stores/settings-store';
|
import { useSettingsStore } from '../../stores/settings-store';
|
||||||
@@ -294,3 +295,37 @@ describe('reopenedEdit', () => {
|
|||||||
expect('locations' in reopened).toBe(false);
|
expect('locations' in reopened).toBe(false);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('getTimedEventBoundsForDay on DST change days', () => {
|
||||||
|
it('places events by wall-clock time, not elapsed time since midnight', () => {
|
||||||
|
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 = ev({
|
||||||
|
start: '2026-10-25T12:51:00',
|
||||||
|
timeZone: 'Europe/Berlin',
|
||||||
|
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 = ev({
|
||||||
|
start: '2026-03-29T09:00:00',
|
||||||
|
timeZone: 'Europe/Berlin',
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|||||||
+18
-10
@@ -263,6 +263,15 @@ export interface TimedDayBounds {
|
|||||||
continuesAfter: boolean;
|
continuesAfter: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function wallClockMinutes(date: Date): number {
|
||||||
|
return (
|
||||||
|
date.getHours() * 60 +
|
||||||
|
date.getMinutes() +
|
||||||
|
date.getSeconds() / 60 +
|
||||||
|
date.getMilliseconds() / 60000
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
export function getTimedEventBoundsForDay(
|
export function getTimedEventBoundsForDay(
|
||||||
event: CalendarEvent,
|
event: CalendarEvent,
|
||||||
day: Date,
|
day: Date,
|
||||||
@@ -276,16 +285,15 @@ export function getTimedEventBoundsForDay(
|
|||||||
|
|
||||||
if (eventEnd <= dayStart || eventStart >= nextDayStart) return null;
|
if (eventEnd <= dayStart || eventStart >= nextDayStart) return null;
|
||||||
|
|
||||||
const clippedStart = eventStart > dayStart ? eventStart : dayStart;
|
// Wall-clock minutes, not elapsed time since midnight: on a DST change day
|
||||||
const clippedEnd = eventEnd < nextDayStart ? eventEnd : nextDayStart;
|
// the day has 23 or 25 hours, and elapsed time would shift every event
|
||||||
const startMinutes = Math.max(
|
// after the change by an hour.
|
||||||
0,
|
const startMinutes =
|
||||||
Math.floor((clippedStart.getTime() - dayStart.getTime()) / 60000),
|
eventStart > dayStart ? Math.floor(wallClockMinutes(eventStart)) : 0;
|
||||||
);
|
const endMinutes =
|
||||||
const endMinutes = Math.min(
|
eventEnd < nextDayStart
|
||||||
1440,
|
? Math.max(startMinutes, Math.ceil(wallClockMinutes(eventEnd)))
|
||||||
Math.ceil((clippedEnd.getTime() - dayStart.getTime()) / 60000),
|
: 1440;
|
||||||
);
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
startMinutes,
|
startMinutes,
|
||||||
|
|||||||
Reference in New Issue
Block a user