From af23aa369cedb1e7b3f4bf8661772848c0e23bd0 Mon Sep 17 00:00:00 2001 From: Felix Schlusche Date: Thu, 23 Oct 2025 19:36:55 +0200 Subject: [PATCH] fix: improve holiday name retrieval by comparing year, month, and day directly to avoid timezone issues refactor: change exported variables to local scope in state management for better encapsulation --- public/js/holidays.js | 9 ++++-- public/js/main.js | 48 +++++++++++++---------------- public/js/state.js | 70 +++++++++++++++++++++---------------------- 3 files changed, 63 insertions(+), 64 deletions(-) diff --git a/public/js/holidays.js b/public/js/holidays.js index 6669149..be0b3fa 100644 --- a/public/js/holidays.js +++ b/public/js/holidays.js @@ -129,11 +129,16 @@ function getPublicHolidays(year, bundesland) { */ function getHolidayName(date) { const year = date.getFullYear(); + const month = date.getMonth(); + const day = date.getDate(); + const holidays = getPublicHolidays(year, currentBundesland); - const dateStr = date.toISOString().split('T')[0]; + // Compare year, month, and day directly (avoid timezone issues) const holiday = holidays.find(h => { - return h.date.toISOString().split('T')[0] === dateStr; + return h.date.getFullYear() === year && + h.date.getMonth() === month && + h.date.getDate() === day; }); return holiday ? holiday.name : null; diff --git a/public/js/main.js b/public/js/main.js index 085f344..abccf5b 100644 --- a/public/js/main.js +++ b/public/js/main.js @@ -1,38 +1,18 @@ // ============================================ -// STATE & VARIABLES +// STATE & VARIABLES (additional to state.js) // ============================================ -let currentEditingId = null; -let datePicker = null; -let startTimePicker = null; -let endTimePicker = null; -let filterFromPicker = null; -let filterToPicker = null; let manualStartTimePicker = null; -// Timer state -let timerInterval = null; -let timerStartTime = null; -let timerPausedDuration = 0; // Total paused time in seconds -let isPaused = false; -let pauseTimeout = null; +// Additional timer state (beyond state.js) let pauseStartElapsed = 0; // Elapsed time when pause started (to freeze display) let pauseEndTime = 0; // Timestamp when pause will end (for countdown) let timerStartTimeString = ''; // Start time as string (HH:MM) for display -let currentEntryId = null; // ID of today's entry being timed - -// Current month display state -let displayYear = new Date().getFullYear(); -let displayMonth = new Date().getMonth(); // 0-11 // Current view state let currentView = 'monthly'; // 'monthly' or 'filter' let currentFilterFrom = null; let currentFilterTo = null; -// Bulk edit state -let bulkEditMode = false; -let selectedEntries = new Set(); - // Settings state let currentBundesland = 'BW'; // Default: Baden-Württemberg let totalVacationDays = 30; // Default vacation days per year @@ -2109,7 +2089,10 @@ async function bulkExportPDF() { // Count workdays based on selected entries only selectedDates.forEach(dateISO => { - const dateObj = new Date(dateISO); + // Parse date correctly to avoid timezone issues + const [year, month, day] = dateISO.split('-').map(Number); + const dateObj = new Date(year, month - 1, day); + const isVacation = vacationDaysSet.has(dateISO); const isFlextime = flextimeDaysSet.has(dateISO); const isWeekendHoliday = isWeekendOrHoliday(dateObj); @@ -2975,8 +2958,19 @@ async function handleExportPDF() { // Calculate statistics const today = new Date(); + today.setHours(23, 59, 59, 999); // Set to end of day to include today - // Count workdays (excluding weekends and holidays, only up to today) + // Find the last entry date to calculate workdays up to (parse correctly to avoid timezone issues) + let lastEntryDate = today; + if (entries.length > 0) { + const sortedEntries = entries.sort((a, b) => b.date.localeCompare(a.date)); + const lastDateStr = sortedEntries[0].date; // "2025-10-22" + const [year, month, day] = lastDateStr.split('-').map(Number); + lastEntryDate = new Date(year, month - 1, day, 23, 59, 59, 999); + } + + // Count workdays (excluding weekends and holidays, up to last entry or today, whichever is later) + const countUntil = lastEntryDate > today ? lastEntryDate : today; let workdaysPassed = 0; let totalWorkdaysInMonth = 0; @@ -3008,13 +3002,13 @@ async function handleExportPDF() { if (!isWeekendHoliday && !isVacation) { // Normal workday (excluding vacation days) totalWorkdaysInMonth++; - if (dateObj <= today) { + if (dateObj <= countUntil) { workdaysPassed++; } } else if (isFlextime && isWeekendHoliday) { // Flextime on weekend/holiday counts as additional workday totalWorkdaysInMonth++; - if (new Date(dateISO) <= today) { + if (new Date(dateISO) <= countUntil) { workdaysPassed++; } } @@ -3037,7 +3031,7 @@ async function handleExportPDF() { entries.forEach(entry => { const entryDate = new Date(entry.date); - if (entryDate <= today) { + if (entryDate <= countUntil) { if (!entry.entryType || entry.entryType === 'work') { totalNetHours += entry.netHours; workEntriesCount++; diff --git a/public/js/state.js b/public/js/state.js index 05ab0ae..f15eac1 100644 --- a/public/js/state.js +++ b/public/js/state.js @@ -3,49 +3,49 @@ */ // Modal state -export let currentEditingId = null; -export let datePicker = null; -export let startTimePicker = null; -export let endTimePicker = null; -export let filterFromPicker = null; -export let filterToPicker = null; +let currentEditingId = null; +let datePicker = null; +let startTimePicker = null; +let endTimePicker = null; +let filterFromPicker = null; +let filterToPicker = null; // Timer state -export let timerInterval = null; -export let timerStartTime = null; -export let timerPausedDuration = 0; // Total paused time in seconds -export let isPaused = false; -export let pauseTimeout = null; -export let currentEntryId = null; // ID of today's entry being timed +let timerInterval = null; +let timerStartTime = null; +let timerPausedDuration = 0; // Total paused time in seconds +let isPaused = false; +let pauseTimeout = null; +let currentEntryId = null; // ID of today's entry being timed // Current month display state -export let displayYear = new Date().getFullYear(); -export let displayMonth = new Date().getMonth(); // 0-11 +let displayYear = new Date().getFullYear(); +let displayMonth = new Date().getMonth(); // 0-11 // Bulk edit state -export let bulkEditMode = false; -export let selectedEntries = new Set(); +let bulkEditMode = false; +let selectedEntries = new Set(); // Setters for state mutations -export function setCurrentEditingId(id) { currentEditingId = id; } -export function setDatePicker(picker) { datePicker = picker; } -export function setStartTimePicker(picker) { startTimePicker = picker; } -export function setEndTimePicker(picker) { endTimePicker = picker; } -export function setFilterFromPicker(picker) { filterFromPicker = picker; } -export function setFilterToPicker(picker) { filterToPicker = picker; } +function setCurrentEditingId(id) { currentEditingId = id; } +function setDatePicker(picker) { datePicker = picker; } +function setStartTimePicker(picker) { startTimePicker = picker; } +function setEndTimePicker(picker) { endTimePicker = picker; } +function setFilterFromPicker(picker) { filterFromPicker = picker; } +function setFilterToPicker(picker) { filterToPicker = picker; } -export function setTimerInterval(interval) { timerInterval = interval; } -export function setTimerStartTime(time) { timerStartTime = time; } -export function setTimerPausedDuration(duration) { timerPausedDuration = duration; } -export function setIsPaused(paused) { isPaused = paused; } -export function setPauseTimeout(timeout) { pauseTimeout = timeout; } -export function setCurrentEntryId(id) { currentEntryId = id; } +function setTimerInterval(interval) { timerInterval = interval; } +function setTimerStartTime(time) { timerStartTime = time; } +function setTimerPausedDuration(duration) { timerPausedDuration = duration; } +function setIsPaused(paused) { isPaused = paused; } +function setPauseTimeout(timeout) { pauseTimeout = timeout; } +function setCurrentEntryId(id) { currentEntryId = id; } -export function setDisplayYear(year) { displayYear = year; } -export function setDisplayMonth(month) { displayMonth = month; } +function setDisplayYear(year) { displayYear = year; } +function setDisplayMonth(month) { displayMonth = month; } -export function setBulkEditMode(mode) { bulkEditMode = mode; } -export function clearSelectedEntries() { selectedEntries.clear(); } -export function addSelectedEntry(id) { selectedEntries.add(id); } -export function removeSelectedEntry(id) { selectedEntries.delete(id); } -export function hasSelectedEntry(id) { return selectedEntries.has(id); } +function setBulkEditMode(mode) { bulkEditMode = mode; } +function clearSelectedEntries() { selectedEntries.clear(); } +function addSelectedEntry(id) { selectedEntries.add(id); } +function removeSelectedEntry(id) { selectedEntries.delete(id); } +function hasSelectedEntry(id) { return selectedEntries.has(id); }