95 lines
2.8 KiB
JavaScript
95 lines
2.8 KiB
JavaScript
// Hilfsfunktionen für Zeit-/Datumsberechnungen rund um das Arbeitszeitkonto.
|
||
|
||
const WEEKDAY_NAMES = ['So', 'Mo', 'Di', 'Mi', 'Do', 'Fr', 'Sa'];
|
||
|
||
/** "08:30" -> 510 (Minuten seit Mitternacht) */
|
||
function timeToMinutes(value) {
|
||
const [h, m] = String(value).split(':').map(Number);
|
||
return h * 60 + m;
|
||
}
|
||
|
||
/** 95 -> "1:35" (immer positiv, ohne Vorzeichen) */
|
||
function minutesToHM(totalMinutes) {
|
||
const abs = Math.abs(Math.round(totalMinutes));
|
||
const h = Math.floor(abs / 60);
|
||
const m = abs % 60;
|
||
return `${h}:${String(m).padStart(2, '0')}`;
|
||
}
|
||
|
||
/** 95 -> "+1:35", -30 -> "-0:30" */
|
||
function formatBalance(minutes) {
|
||
const sign = minutes < 0 ? '−' : '+';
|
||
return `${sign}${minutesToHM(minutes)}`;
|
||
}
|
||
|
||
/** Prüft, ob ein Datum (YYYY-MM-DD) laut Einstellungen ein Arbeitstag ist. */
|
||
function isWorkDay(dateStr, workDays) {
|
||
const day = new Date(`${dateStr}T00:00:00`).getDay();
|
||
return Array.isArray(workDays) && workDays.includes(day);
|
||
}
|
||
|
||
/**
|
||
* Summiert alle Einzelpausen.
|
||
* @param {Array<{start: string, end: string}>} breaks
|
||
* @returns {number} Gesamte Pausenzeit in Minuten
|
||
*/
|
||
function computeTotalBreakMinutes(breaks) {
|
||
if (!Array.isArray(breaks)) return 0;
|
||
return breaks.reduce((total, b) => {
|
||
if (!b || !b.start || !b.end) return total;
|
||
const duration = timeToMinutes(b.end) - timeToMinutes(b.start);
|
||
return total + Math.max(0, duration);
|
||
}, 0);
|
||
}
|
||
|
||
/**
|
||
* Berechnet die tatsächlich geleisteten Netto-Minuten.
|
||
* @param {string} startTime z. B. "08:00"
|
||
* @param {string} endTime z. B. "17:00"
|
||
* @param {Array<{start: string, end: string}>} breaks Pausenzeiten
|
||
* @returns {number}
|
||
*/
|
||
function computeWorkedMinutes(startTime, endTime, breaks) {
|
||
const gross = timeToMinutes(endTime) - timeToMinutes(startTime);
|
||
const totalBreak = computeTotalBreakMinutes(breaks);
|
||
return gross - totalBreak;
|
||
}
|
||
|
||
/** Saldo (Über-/Minusstunden) eines einzelnen Tages in Minuten. */
|
||
function computeBalanceMinutes(workedMinutes, dateStr, settings) {
|
||
const targetMinutes = isWorkDay(dateStr, settings.workDays)
|
||
? settings.targetHoursPerDay * 60
|
||
: 0;
|
||
return workedMinutes - targetMinutes;
|
||
}
|
||
|
||
/** "2026-06-23" -> "Di, 23.06.2026" */
|
||
function formatDateDisplay(dateStr) {
|
||
const date = new Date(`${dateStr}T00:00:00`);
|
||
return new Intl.DateTimeFormat('de-DE', {
|
||
weekday: 'short',
|
||
day: '2-digit',
|
||
month: '2-digit',
|
||
year: 'numeric',
|
||
}).format(date);
|
||
}
|
||
|
||
/** "2026-06" -> "Juni 2026" */
|
||
function formatMonthDisplay(yearMonth) {
|
||
const date = new Date(`${yearMonth}-01T00:00:00`);
|
||
return new Intl.DateTimeFormat('de-DE', { month: 'long', year: 'numeric' }).format(date);
|
||
}
|
||
|
||
module.exports = {
|
||
WEEKDAY_NAMES,
|
||
timeToMinutes,
|
||
minutesToHM,
|
||
formatBalance,
|
||
isWorkDay,
|
||
computeTotalBreakMinutes,
|
||
computeWorkedMinutes,
|
||
computeBalanceMinutes,
|
||
formatDateDisplay,
|
||
formatMonthDisplay,
|
||
};
|