From 396e7b2b6b2ded8f2c281caa037b072d15ecb0cf Mon Sep 17 00:00:00 2001 From: Gregor Vostrak Date: Tue, 12 May 2026 17:25:46 +0200 Subject: [PATCH] fix DST boundary issue in timesheets --- resources/js/utils/timesheet/cellMath.ts | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/resources/js/utils/timesheet/cellMath.ts b/resources/js/utils/timesheet/cellMath.ts index 71f78361..92771531 100644 --- a/resources/js/utils/timesheet/cellMath.ts +++ b/resources/js/utils/timesheet/cellMath.ts @@ -26,6 +26,15 @@ interface Interval { end: Dayjs; } +function localDayBounds(date: string, tz: string): { dayStart: Dayjs; dayEnd: Dayjs } { + const dayjs = getDayJsInstance(); + const localDayStart = dayjs.tz(`${date} 00:00:00`, tz); + return { + dayStart: localDayStart.utc(), + dayEnd: localDayStart.add(1, 'day').utc(), + }; +} + /** * Collect entries that intersect the day `[dayStart, dayEnd)`, clipped * to those bounds. Running entries use `nowDayjs` as their end. @@ -76,8 +85,7 @@ export function findFreeWindowOnDay( if (requiredSeconds <= 0) return null; const dayjs = getDayJsInstance(); - const dayStart = dayjs.tz(`${date} 00:00:00`, tz).utc(); - const dayEnd = dayStart.add(1, 'day'); + const { dayStart, dayEnd } = localDayBounds(date, tz); if (requiredSeconds > dayEnd.diff(dayStart, 'second')) return null; @@ -149,8 +157,7 @@ export function freeGapSecondsAfter( now?: string | Dayjs ): number { const dayjs = getDayJsInstance(); - const dayStart = dayjs.tz(`${date} 00:00:00`, tz).utc(); - const dayEnd = dayStart.add(1, 'day'); + const { dayStart, dayEnd } = localDayBounds(date, tz); const cursorDjs = dayjs.utc(cursor); if (cursorDjs.isSameOrAfter(dayEnd)) return 0;