/** * Date/time helpers for calendar and duty display. */ /** * YYYY-MM-DD in local time (for calendar keys, "today", request range). * @param {Date} d - Date * @returns {string} */ export function localDateString(d) { const y = d.getFullYear(); const m = String(d.getMonth() + 1).padStart(2, "0"); const day = String(d.getDate()).padStart(2, "0"); return y + "-" + m + "-" + day; } /** * True if duty (start_at/end_at UTC) overlaps the local day YYYY-MM-DD. * @param {object} d - Duty with start_at, end_at * @param {string} dateKey - YYYY-MM-DD * @returns {boolean} */ export function dutyOverlapsLocalDay(d, dateKey) { const [y, m, day] = dateKey.split("-").map(Number); const dayStart = new Date(y, m - 1, day, 0, 0, 0, 0); const dayEnd = new Date(y, m - 1, day, 23, 59, 59, 999); const start = new Date(d.start_at); const end = new Date(d.end_at); return end > dayStart && start < dayEnd; } /** * True if duty overlaps any local day in the range [firstKey, lastKey] (inclusive). * @param {object} d - Duty with start_at, end_at * @param {string} firstKey - YYYY-MM-DD (first day of range) * @param {string} lastKey - YYYY-MM-DD (last day of range) * @returns {boolean} */ export function dutyOverlapsLocalRange(d, firstKey, lastKey) { const [y1, m1, day1] = firstKey.split("-").map(Number); const [y2, m2, day2] = lastKey.split("-").map(Number); const rangeStart = new Date(y1, m1 - 1, day1, 0, 0, 0, 0); const rangeEnd = new Date(y2, m2 - 1, day2, 23, 59, 59, 999); const start = new Date(d.start_at); const end = new Date(d.end_at); return end > rangeStart && start < rangeEnd; } /** @param {Date} d - Date */ export function firstDayOfMonth(d) { return new Date(d.getFullYear(), d.getMonth(), 1); } /** @param {Date} d - Date */ export function lastDayOfMonth(d) { return new Date(d.getFullYear(), d.getMonth() + 1, 0); } /** @param {Date} d - Date (returns Monday of that week) */ export function getMonday(d) { const day = d.getDay(); const diff = d.getDate() - day + (day === 0 ? -6 : 1); return new Date(d.getFullYear(), d.getMonth(), diff); } /** * Format UTC date from ISO string as DD.MM for display. * @param {string} isoDateStr - ISO date string * @returns {string} DD.MM */ export function formatDateKey(isoDateStr) { const d = new Date(isoDateStr); const day = String(d.getDate()).padStart(2, "0"); const month = String(d.getMonth() + 1).padStart(2, "0"); return day + "." + month; } /** * Format YYYY-MM-DD as DD.MM for list header. * @param {string} key - YYYY-MM-DD * @returns {string} DD.MM */ export function dateKeyToDDMM(key) { return key.slice(8, 10) + "." + key.slice(5, 7); } /** * Format ISO date as HH:MM in local time. * @param {string} isoStr - ISO date string * @returns {string} HH:MM */ export function formatTimeLocal(isoStr) { const d = new Date(isoStr); return String(d.getHours()).padStart(2, "0") + ":" + String(d.getMinutes()).padStart(2, "0"); } /** * Format ISO string as HH:MM (local). * @param {string} isoStr - ISO date string * @returns {string} HH:MM or "" */ export function formatHHMM(isoStr) { if (!isoStr) return ""; const d = new Date(isoStr); const h = d.getHours(); const m = d.getMinutes(); return (h < 10 ? "0" : "") + h + ":" + (m < 10 ? "0" : "") + m; }