diff --git a/frontend/src/routes/weather/+page.svelte b/frontend/src/routes/weather/+page.svelte index 0dfc063..497ae0d 100644 --- a/frontend/src/routes/weather/+page.svelte +++ b/frontend/src/routes/weather/+page.svelte @@ -5,6 +5,30 @@ let { data }: { data: PageData } = $props(); const weather = $derived(data.weather); const unitLabel = $derived(weather.unit === 'celsius' ? 'C' : 'F'); + + // The hourly strip covers the next 24h starting from "now" (see weather/client.ts), which + // almost always crosses a day boundary partway through — grouping by calendar day and + // labeling each group is what actually answers "which day is this hour in", rather than + // leaving it to be inferred from the hour-of-day alone (ambiguous for anything after + // midnight, and easy to misread near the boundary either way). + function dayLabel(d: Date): string { + const startOfDay = (x: Date) => new Date(x.getFullYear(), x.getMonth(), x.getDate()).getTime(); + const diffDays = Math.round((startOfDay(d) - startOfDay(new Date())) / 86_400_000); + if (diffDays === 0) return 'Today'; + if (diffDays === 1) return 'Tomorrow'; + return d.toLocaleDateString([], { weekday: 'long' }); + } + + const hourlyGroups = $derived.by(() => { + const groups: { label: string; hours: typeof weather.hourly }[] = []; + for (const hour of weather.hourly) { + const label = dayLabel(new Date(hour.time)); + const last = groups[groups.length - 1]; + if (last && last.label === label) last.hours.push(hour); + else groups.push({ label, hours: [hour] }); + } + return groups; + });