/** * `new Date("YYYY-MM-DD")` parses the string as UTC midnight, but a later * `.setHours()` call always applies in the server's local timezone. On a * non-UTC server (e.g. IST, UTC+5:30) that mismatch silently shifts the * boundary by the offset - excluding same-day data for hours after local * midnight. Parsing the components directly keeps everything local. */ function parseLocalDate(dateStr: string): Date { const [y, m, d] = dateStr.split('-').map(Number) return new Date(y, m - 1, d) } // A full ISO timestamp (recency-window presets like "last 15 minutes" send one) // carries its own time-of-day and should be used exactly as-is, not snapped to a // day boundary the way a bare "YYYY-MM-DD" is. function isExactInstant(dateStr: string): boolean { return dateStr.includes('T') } export function startOfDay(dateStr: string | null | undefined, fallback: Date): Date { if (dateStr && isExactInstant(dateStr)) return new Date(dateStr) const d = dateStr ? parseLocalDate(dateStr) : fallback d.setHours(0, 0, 0, 0) return d } export function endOfDay(dateStr: string | null | undefined, fallback: Date): Date { if (dateStr && isExactInstant(dateStr)) return new Date(dateStr) const d = dateStr ? parseLocalDate(dateStr) : fallback d.setHours(23, 59, 59, 999) return d }