feat: local time (#7)

Co-authored-by: terraputix <terraputix@mailbox.org>
Reviewed-on: useweb/ombrella#7
This commit is contained in:
2026-02-16 11:41:34 +01:00
co-authored by terraputix
parent 9a7e66d5d9
commit 84eefd538b
16 changed files with 948 additions and 701 deletions
@@ -3,8 +3,8 @@
import * as echarts from 'echarts';
import { formatZoned, getRelativeDayLabel } from '$lib/utils/date';
import { buildCurrentTimeSeries, buildDaylightSeries, getThemeColors } from '$lib/utils/echarts';
import { pad } from '$lib/utils/index';
import { ChartContainer, ChartToolbar, EChart } from '$lib/components/charts';
import '$lib/components/charts/echarts.css';
@@ -13,7 +13,6 @@
import {
type FetchedHourly,
type WeatherUnits,
getDayLabel,
getPrecipUnit,
getTempUnit,
getWindDirectionLabel,
@@ -32,7 +31,6 @@
const CHART_GROUP = 'week-meteogram';
const MS_PER_DAY = 24 * 3600 * 1000;
const today = new Date();
let showCharts = $state(false);
let chartComponents: EChart[] = $state([]);
@@ -40,9 +38,17 @@
let chartOptions: Array<Record<string, unknown>> = $state([]);
export function scrollToDay(day: Date): void {
if (chartInstances.length === 0) return;
if (chartInstances.length === 0 || !data) return;
const dayStart = new Date(day.getFullYear(), day.getMonth(), day.getDate()).getTime();
const tz = data.timezone;
const targetDayStr = formatZoned(day, tz, 'yyyy-MM-dd');
const firstHourIdx = data.hourlyDates.findIndex(
(d) => formatZoned(d, tz, 'yyyy-MM-dd') === targetDayStr
);
if (firstHourIdx === -1) return;
const dayStart = data.timestamps[firstHourIdx];
const dayEnd = dayStart + MS_PER_DAY;
const timestamps = data.timestamps;
const rangeStart = timestamps[0];
@@ -90,7 +96,7 @@
$effect(() => {
if (!data) return;
const { hourly, utc_offset_seconds, timestamps, markAreas } = data;
const { hourly, timestamps, markAreas } = data;
const colors = getThemeColors();
const tempUnit = getTempUnit(units);
const precipUnit = getPrecipUnit(units);
@@ -116,7 +122,7 @@
const annotations = (): Array<Record<string, unknown>> => {
const series: Array<Record<string, unknown>> = [];
series.push(buildCurrentTimeSeries({ utcOffsetSeconds: utc_offset_seconds }));
series.push(buildCurrentTimeSeries());
const dl = buildDaylightSeries({ markAreas });
if (dl) series.push(dl);
return series;
@@ -126,7 +132,12 @@
type: 'time',
splitLine: { show: false },
axisLine: { lineStyle: { color: colors.axisLine } },
axisLabel: { color: colors.text, hideOverlap: true, show: showLabel },
axisLabel: {
color: colors.text,
hideOverlap: true,
show: showLabel,
formatter: (value: number) => formatZoned(new Date(value), data.timezone, 'HH:mm')
},
axisTick: { lineStyle: { color: colors.axisLine } }
});
@@ -160,7 +171,14 @@
});
const tooltipBase = (
formatter: (params: Record<string, unknown>[]) => string
formatter: (
params: Array<{
axisValue: number;
seriesName: string;
marker: string;
value: number | number[] | null;
}>
) => string
): Record<string, unknown> => ({
trigger: 'axis',
axisPointer: {
@@ -170,7 +188,13 @@
backgroundColor: colors.tooltipBg,
color: colors.text,
borderColor: colors.tooltipBorder,
borderWidth: 1
borderWidth: 1,
formatter: (params: { axisDimension: string; value: number }) => {
if (params.axisDimension === 'x') {
return formatZoned(new Date(params.value), data.timezone, 'EEE d MMM HH:mm');
}
return params.value.toFixed(1);
}
}
},
backgroundColor: colors.tooltipBg,
@@ -181,7 +205,8 @@
const formatDate = (ts: number): string => {
const date = new Date(ts);
return `<b>${date.toLocaleDateString('en-GB', { weekday: 'short', day: 'numeric', month: 'short' })} ${pad(date.getHours())}:${pad(date.getMinutes())}</b><br/>`;
const dateStr = formatZoned(date, data.timezone, 'EEE d MMM HH:mm');
return `<b>${dateStr}</b><br/>`;
};
const isAnnotation = (name: string): boolean => name === 'Daylight' || name === 'Current Time';
@@ -488,11 +513,11 @@
<div class="detailed-charts" in:fade={{ duration: 200 }}>
<div class="charts-header">
<h3 class="charts-title">
{selectedDay.toLocaleDateString('en-GB', { weekday: 'long' })}
{formatZoned(selectedDay, data.timezone, 'EEEE')}
<small>
{getDayLabel(selectedDay, today) !==
selectedDay.toLocaleDateString('en-GB', { weekday: 'long' })
? ` (${getDayLabel(selectedDay, today)})`
{getRelativeDayLabel(selectedDay, data.timezone) !==
formatZoned(selectedDay, data.timezone, 'EEEE')
? ` (${getRelativeDayLabel(selectedDay, data.timezone)})`
: ''}
</small>
</h3>