From 0b4b6b24e170fcaefd6767d3b632291669193267 Mon Sep 17 00:00:00 2001 From: BOHA Date: Mon, 23 Mar 2026 13:50:30 +0100 Subject: [PATCH] fix: attendance times showing +1 hour due to UTC timezone conversion Times in the database are stored as local time (CET). JavaScript's Date constructor treated them as UTC, then toLocaleTimeString added +1 hour for CET timezone. Fix: extract hours/minutes directly from the datetime string via regex instead of going through Date object. No timezone conversion applied. Co-Authored-By: Claude Opus 4.6 (1M context) --- src/admin/utils/attendanceHelpers.ts | 34 +++++++++++++++++++++------- 1 file changed, 26 insertions(+), 8 deletions(-) diff --git a/src/admin/utils/attendanceHelpers.ts b/src/admin/utils/attendanceHelpers.ts index 9a030b7..25dd2ef 100644 --- a/src/admin/utils/attendanceHelpers.ts +++ b/src/admin/utils/attendanceHelpers.ts @@ -24,15 +24,31 @@ export const formatDate = (dateStr: string | null | undefined): string => { return d.toLocaleDateString('cs-CZ') } +/** Extract time as HH:MM from a datetime string without timezone conversion */ +const extractTime = (datetime: string): string => { + // Try ISO format: "2026-03-23T08:00:00.000Z" or "2026-03-23T08:00:00" + const tMatch = datetime.match(/T(\d{2}):(\d{2})/) + if (tMatch) return `${tMatch[1]}:${tMatch[2]}` + // Try space format: "2026-03-23 08:00:00" + const sMatch = datetime.match(/\s(\d{2}):(\d{2})/) + if (sMatch) return `${sMatch[1]}:${sMatch[2]}` + // Fallback: try parsing time-only "08:00" + const hMatch = datetime.match(/^(\d{2}):(\d{2})/) + if (hMatch) return `${hMatch[1]}:${hMatch[2]}` + return datetime +} + export const formatDatetime = (datetime: string | null | undefined): string => { if (!datetime) return '—' - const d = new Date(datetime) - return `${d.getDate()}.${d.getMonth() + 1}. ${d.toLocaleTimeString('cs-CZ', { hour: '2-digit', minute: '2-digit' })}` + // Extract date part without timezone conversion + const dMatch = datetime.match(/(\d{4})-(\d{2})-(\d{2})/) + const datePart = dMatch ? `${parseInt(dMatch[3])}.${parseInt(dMatch[2])}.` : '' + return `${datePart} ${extractTime(datetime)}` } export const formatTime = (datetime: string | null | undefined): string => { if (!datetime) return '—' - return new Date(datetime).toLocaleTimeString('cs-CZ', { hour: '2-digit', minute: '2-digit' }) + return extractTime(datetime) } export const calculateWorkMinutes = (record: AttendanceRecord): number => { @@ -123,12 +139,14 @@ export const calcFormWorkMinutes = (form: ShiftForm): number => { export const formatTimeOrDatetimePrint = (datetime: string | null | undefined, shiftDate: string): string => { if (!datetime) return '—' - const timeDate = new Date(datetime).toISOString().split('T')[0] - if (timeDate !== shiftDate) { - const d = new Date(datetime) - return `${d.getDate()}.${d.getMonth() + 1}. ${d.toLocaleTimeString('cs-CZ', { hour: '2-digit', minute: '2-digit' })}` + // Extract date from the datetime string directly (no timezone conversion) + const dateMatch = datetime.match(/(\d{4})-(\d{2})-(\d{2})/) + const timeDate = dateMatch ? `${dateMatch[1]}-${dateMatch[2]}-${dateMatch[3]}` : '' + if (timeDate && timeDate !== shiftDate) { + const datePart = `${parseInt(dateMatch![3])}.${parseInt(dateMatch![2])}.` + return `${datePart} ${extractTime(datetime)}` } - return new Date(datetime).toLocaleTimeString('cs-CZ', { hour: '2-digit', minute: '2-digit' }) + return extractTime(datetime) } export const calculateWorkMinutesPrint = (record: AttendanceRecord): number => {