/** * Time formatting utilities for baby sleep tracker */ /** * Convert minutes after a reference time to clock time string * @param minutes - minutes after reference time (can be negative) * @param reference - reference time as HH:MM string * @returns formatted time string like "07:15" */ export function formatMinutesAfter(minutes, reference = "04:00") { if (minutes === null) return "N/A"; const [refHours, refMinutes] = reference.split(":").map(Number); const totalRefMinutes = refHours * 60 + refMinutes; const targetMinutes = totalRefMinutes + minutes; // Handle wrap around midnight const normalizedMinutes = ((targetMinutes % 1440) + 1440) % 1440; const hours = Math.floor(normalizedMinutes / 60); const mins = normalizedMinutes % 60; return `${String(hours).padStart(2, "0")}:${String(mins).padStart(2, "0")}`; } /** * Adjust minutes by the local timezone offset * @param utcMinutes - minutes from UTC midnight * @returns adjusted minutes for local time */ export function adjustForTimezone(utcMinutes) { const offsetInMinutes = new Date().getTimezoneOffset(); return utcMinutes - offsetInMinutes; } /** * Convert a Date to datetime-local input value format (local time) * @param date - Date object * @returns string in "YYYY-MM-DDTHH:MM" format */ export function toLocalDatetimeInputValue(date) { const offset = date.getTimezoneOffset() * 60 * 1000; const localDate = new Date(date.getTime() - offset); return localDate.toISOString().slice(0, 16); } export function formatElapsedTime(utcTimestamp) { let isoString = utcTimestamp; if (utcTimestamp.includes(' ')) { isoString = utcTimestamp.replace(' ', 'T'); } if (!isoString.endsWith('Z')) { isoString += 'Z'; } const then = new Date(isoString); const now = new Date(); const diffMs = now.getTime() - then.getTime(); const diffMins = Math.floor(diffMs / 60000); const hours = Math.floor(diffMins / 60); const mins = diffMins % 60; if (hours > 0) return `${hours}h ${mins}m`; return `${mins}m`; } /** * Clamp a date to day boundaries (start or end of day) * @param date - Date to clamp * @param bound - 'start' for 00:00, 'end' for 23:59:59.999 * @returns new Date clamped to day boundary */ export function clampToDay(date, bound) { const result = new Date(date); if (bound === "start") { result.setHours(0, 0, 0, 0); } else { result.setHours(23, 59, 59, 999); } return result; } /** * Parse HH:MM time string to Date on a given day * @param timeStr - HH:MM format * @param baseDate - base date (defaults to today) * @returns Date object * @throws Error if timeStr is invalid */ export function parseTime(timeStr, baseDate = new Date()) { const [hours, minutes] = timeStr.split(":").map(Number); const result = new Date(baseDate); result.setHours(hours, minutes, 0, 0); return result; } /** * Get date string for N days ago * @param days - number of days ago * @returns ISO date string (YYYY-MM-DD) */ export function getDaysAgo(days) { const date = new Date(); date.setDate(date.getDate() - days); return date.toISOString().split("T")[0]; } /** * Get date string for today * @returns ISO date string (YYYY-MM-DD) */ export function getToday() { return new Date().toISOString().split("T")[0]; } /** * Convert HH:MM time string to total minutes from start of day * @param timeStr - HH:MM format * @returns total minutes */ export function timeToMinutes(timeStr) { if (!timeStr || typeof timeStr !== 'string') return 0; const [hours, minutes] = timeStr.split(":").map(Number); return hours * 60 + minutes; } /** * Get the offset from a reference time in local minutes * @param utcMinutesFromMidnight - minutes from UTC midnight * @param referenceHHMM - reference time as HH:MM string * @returns local offset from reference in minutes */ export function getLocalOffsetFromReference(utcMinutesFromMidnight, referenceHHMM) { const localMinsFromMidnight = adjustForTimezone(utcMinutesFromMidnight); return localMinsFromMidnight - timeToMinutes(referenceHHMM); } /** * Convert total minutes to a human-readable string (e.g., "1h 30m" or "45m") * @param totalMinutes - Total number of minutes * @returns Formatted string */ export function formatMinutes(totalMinutes) { const hours = Math.floor(totalMinutes / 60); const mins = totalMinutes % 60; if (hours > 0) { return `${hours}h ${mins}m`; } return `${mins}m`; }