Initial commit

This commit is contained in:
2026-04-24 08:02:13 +02:00
commit 867c06295c
76 changed files with 10573 additions and 0 deletions
+118
View File
@@ -0,0 +1,118 @@
/**
* 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: number | null, reference: string = "04:00"): string {
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")}`;
}
/**
* 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: Date): string {
const offset = date.getTimezoneOffset() * 60 * 1000;
const localDate = new Date(date.getTime() - offset);
return localDate.toISOString().slice(0, 16);
}
/**
* Format elapsed time in human-readable format
* @param startTime - start time as ISO string or Date
* @returns formatted string like "2h 35m" or "just now"
*/
export function formatElapsedTime(startTime: string | Date): string {
const start = typeof startTime === "string" ? new Date(startTime) : startTime;
const now = new Date();
const diffMs = now.getTime() - start.getTime();
if (diffMs < 0) return "just now";
const totalMinutes = Math.floor(diffMs / 60000);
const hours = Math.floor(totalMinutes / 60);
const minutes = totalMinutes % 60;
if (hours === 0 && minutes === 0) return "just now";
if (hours === 0) return `${minutes}m`;
if (minutes === 0) return `${hours}h`;
return `${hours}h ${minutes}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: Date, bound: "start" | "end"): Date {
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
*/
export function parseTime(timeStr: string, baseDate: Date = new Date()): Date {
const [hours, minutes] = timeStr.split(":").map(Number);
const result = new Date(baseDate);
result.setHours(hours, minutes, 0, 0);
return result;
}
/**
* Format minutes as hours and minutes string
* @param minutes - total minutes
* @returns formatted string like "12h 30m"
*/
export function formatMinutes(minutes: number): string {
const hours = Math.floor(minutes / 60);
const mins = minutes % 60;
if (hours === 0) return `${mins}m`;
if (mins === 0) return `${hours}h`;
return `${hours}h ${mins}m`;
}
/**
* Get date string for N days ago
* @param days - number of days ago
* @returns ISO date string (YYYY-MM-DD)
*/
export function getDaysAgo(days: number): string {
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(): string {
return new Date().toISOString().split("T")[0];
}