Files
zeitkonto/src/utils/time.js
T
2026-07-02 22:07:04 +02:00

95 lines
2.8 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// 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,
};