diff --git a/src/calendar-utils.ts b/src/calendar-utils.ts index 380de97..6b392a8 100644 --- a/src/calendar-utils.ts +++ b/src/calendar-utils.ts @@ -158,7 +158,13 @@ function getExcludedSeconds( return 0; } const { addSeconds, getDay, addDays } = dateAdapter; - const endDate: Date = addSeconds(startDate, seconds - 1); + let endDate = addSeconds(startDate, seconds - 1); + if (precision === 'days') { + // otherwise when changing to DST hour may shift before the hour of the start date + const daysDiff = Math.round(seconds / SECONDS_IN_DAY); + endDate = addSeconds(addDays(startDate, daysDiff), -1); + } + const dayStart: number = getDay(startDate); const dayEnd: number = getDay(endDate); let result = 0; // Calculated in seconds @@ -232,13 +238,8 @@ function getWeekViewEventSpan( totalDaysInView: number; } ): number { - const { - max, - differenceInSeconds, - addDays, - endOfDay, - differenceInDays, - } = dateAdapter; + const { max, differenceInSeconds, addDays, endOfDay, differenceInDays } = + dateAdapter; let span: number = SECONDS_IN_DAY; const begin: Date = max([event.start, startOfWeekDate]); diff --git a/test/calendar-utils-dst.spec.ts b/test/calendar-utils-dst.spec.ts index d8d6f14..67acdf8 100644 --- a/test/calendar-utils-dst.spec.ts +++ b/test/calendar-utils-dst.spec.ts @@ -1,9 +1,11 @@ import './util/use-london-timezone'; -import { getWeekView } from '../src/calendar-utils'; +import { DEFAULT_TIMEZONE } from './util/use-london-timezone'; +import { getWeekView, getAllDayWeekEvents } from '../src/calendar-utils'; import { adapterFactory as dateFnsAdapterFactory } from '../src/date-adapters/date-fns'; import { adapterFactory as momentAdapterFactory } from '../src/date-adapters/moment'; import { adapterFactory as luxonAdapterFactory } from '../src/date-adapters/luxon'; -import { startOfDay } from 'date-fns'; +import { startOfDay, setHours, setMinutes } from 'date-fns'; +import { register } from 'timezone-mock'; import * as moment from 'moment'; import * as luxon from 'luxon'; @@ -71,3 +73,45 @@ describe('getTimezoneOffset', () => { expect(luxonOffset).toBe(-60); }); }); + +describe('getAllDayWeekEvents', () => { + beforeEach(() => { + // Mock Date/date-fns (uses native Date) + register('US/Eastern'); // Toronto uses Eastern timezone + }); + + afterEach(() => { + register(DEFAULT_TIMEZONE); // Restore default timezone for other tests + }); + + it('should correctly position an all-day event spanning from Friday before the DST change to the next Friday (Toronto) with excluded days [0, 6]', () => { + // View: March 3–15, 2026 so we see both Fridays (DST springs forward in Toronto on March 9) + const viewStart = new Date('2026-03-03'); + const viewEnd = new Date('2026-03-15'); + + // Event spans Fri Mar 7 (before DST) – Fri Mar 14 (after DST), crossing the timezone change weekend + const event = { + title: 'Week spanning DST', + start: setMinutes(setHours(new Date('2026-03-07'), 8), 0), + end: setMinutes(setHours(new Date('2026-03-14'), 8), 0), + allDay: true, + }; + + const result = getAllDayWeekEvents(dateAdapter, { + viewStart, + viewEnd, + excluded: [0, 6], // Exclude Sunday and Saturday + events: [event], + }); + + expect(result).toHaveLength(1); + expect(result[0].row).toHaveLength(1); + // With excluded [0, 6], visible days are Mon 3, Tue 4, Wed 5, Thu 6, Fri 7, Mon 10, Tue 11, Wed 12, Thu 13, Fri 14 + // Event spans Fri 7 → Fri 14 → offset 4 (Fri 7), span 6 (Fri 7, Mon 10, Tue 11, Wed 12, Thu 13, Fri 14) + expect(result[0].row[0].offset).toBe(4); + expect(result[0].row[0].span).toBe(6); + expect(result[0].row[0].startsBeforeWeek).toBe(false); + expect(result[0].row[0].endsAfterWeek).toBe(false); + expect(result[0].row[0].event).toEqual(event); + }); +}); diff --git a/test/util/use-london-timezone.ts b/test/util/use-london-timezone.ts index 54aa1a7..fa5ef6e 100644 --- a/test/util/use-london-timezone.ts +++ b/test/util/use-london-timezone.ts @@ -1,3 +1,4 @@ import { register } from 'timezone-mock'; -register('Europe/London'); +export const DEFAULT_TIMEZONE = 'Europe/London'; +register(DEFAULT_TIMEZONE);