feat: local time (#7)
Co-authored-by: terraputix <terraputix@mailbox.org> Reviewed-on: useweb/open-meteo-blue#7
This commit was merged in pull request #7.
This commit is contained in:
+27
-17
@@ -39,18 +39,18 @@ export function range(start: number, stop: number, step: number): number[] {
|
||||
/**
|
||||
* Extracts timestamp array (in milliseconds, with UTC offset applied) from a VariablesWithTime block.
|
||||
*/
|
||||
export function getTimestamps(timeBlock: VariablesWithTime, utcOffsetSeconds: number): number[] {
|
||||
export function getTimestamps(timeBlock: VariablesWithTime): number[] {
|
||||
const start = Number(timeBlock.time());
|
||||
const end = Number(timeBlock.timeEnd());
|
||||
const interval = timeBlock.interval();
|
||||
return range(start, end, interval).map((t) => (t + utcOffsetSeconds) * 1000);
|
||||
return range(start, end, interval).map((t) => t * 1000);
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts Date array (with UTC offset applied) from a VariablesWithTime block.
|
||||
*/
|
||||
export function getDates(timeBlock: VariablesWithTime, utcOffsetSeconds: number): Date[] {
|
||||
return getTimestamps(timeBlock, utcOffsetSeconds).map((t) => new Date(t));
|
||||
export function getDates(timeBlock: VariablesWithTime): Date[] {
|
||||
return getTimestamps(timeBlock).map((t) => new Date(t));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -148,6 +148,7 @@ export function unitToDisplayString(unit: Unit): string {
|
||||
export interface WeatherLocation {
|
||||
latitude: number;
|
||||
longitude: number;
|
||||
timezone?: string;
|
||||
}
|
||||
|
||||
export interface WeatherUnitParams {
|
||||
@@ -164,7 +165,6 @@ export interface WeekForecastParams extends WeatherLocation, WeatherUnitParams {
|
||||
model?: string;
|
||||
forecast_days?: number;
|
||||
past_days?: number;
|
||||
timezone?: string;
|
||||
}
|
||||
|
||||
export interface WeekHourlyData {
|
||||
@@ -197,6 +197,7 @@ export interface WeekForecastResult {
|
||||
hourly: WeekHourlyData;
|
||||
daily: WeekDailyData;
|
||||
utcOffsetSeconds: number;
|
||||
timezone: string;
|
||||
hourlyTimestamps: number[];
|
||||
hourlyDates: Date[];
|
||||
dailyDates: Date[];
|
||||
@@ -219,6 +220,7 @@ export interface ModelCompareResult {
|
||||
models: ModelSeriesData[];
|
||||
timestamps: number[];
|
||||
utcOffsetSeconds: number;
|
||||
timezone: string;
|
||||
markAreas: MarkArea[];
|
||||
units: Record<string, string>;
|
||||
/** Flat record compatible with the existing chart utilities (keys like "temperature_2m_icon_seamless") */
|
||||
@@ -246,6 +248,7 @@ export interface EnsembleForecastResult {
|
||||
variables: Record<string, EnsembleVariableData>;
|
||||
timestamps: number[];
|
||||
utcOffsetSeconds: number;
|
||||
timezone: string;
|
||||
markAreas: MarkArea[];
|
||||
/** Flat record compatible with existing chart utilities (keys like "temperature_2m_member00") */
|
||||
hourlyFlat: Record<string, number[]>;
|
||||
@@ -314,12 +317,13 @@ export async function fetchWeekForecast(params: WeekForecastParams): Promise<Wee
|
||||
const responses = await fetchWeatherApi(FORECAST_URL, cleanParams);
|
||||
const response = responses[0];
|
||||
const utcOffsetSeconds = response.utcOffsetSeconds();
|
||||
const timezone = response.timezone() ?? params.timezone ?? 'UTC';
|
||||
|
||||
const hourlyBlock = response.hourly()!;
|
||||
const dailyBlock = response.daily()!;
|
||||
|
||||
// Hourly: variables are in the same order as WEEK_HOURLY_VARS
|
||||
const hourlyTimestamps = getTimestamps(hourlyBlock, utcOffsetSeconds);
|
||||
const hourlyTimestamps = getTimestamps(hourlyBlock);
|
||||
const hourlyDates = hourlyTimestamps.map((t) => new Date(t));
|
||||
|
||||
const hourly: WeekHourlyData = {
|
||||
@@ -336,7 +340,7 @@ export async function fetchWeekForecast(params: WeekForecastParams): Promise<Wee
|
||||
};
|
||||
|
||||
// Daily: variables are in the same order as WEEK_DAILY_VARS
|
||||
const dailyDates = getDates(dailyBlock, utcOffsetSeconds);
|
||||
const dailyDates = getDates(dailyBlock);
|
||||
|
||||
const sunriseVar = dailyBlock.variables(3)!;
|
||||
const sunsetVar = dailyBlock.variables(4)!;
|
||||
@@ -354,12 +358,13 @@ export async function fetchWeekForecast(params: WeekForecastParams): Promise<Wee
|
||||
winddirection_10m_dominant: getValues(dailyBlock.variables(9)!)
|
||||
};
|
||||
|
||||
const markAreas = buildDaylightMarkAreas(daily.sunrise, daily.sunset, utcOffsetSeconds);
|
||||
const markAreas = buildDaylightMarkAreas(daily.sunrise, daily.sunset);
|
||||
|
||||
return {
|
||||
hourly,
|
||||
daily,
|
||||
utcOffsetSeconds,
|
||||
timezone,
|
||||
hourlyTimestamps,
|
||||
hourlyDates,
|
||||
dailyDates,
|
||||
@@ -379,7 +384,7 @@ export async function fetchWeekForecast(params: WeekForecastParams): Promise<Wee
|
||||
export async function fetchModelComparison(
|
||||
params: ModelCompareParams
|
||||
): Promise<ModelCompareResult> {
|
||||
const forecastApiParams: Record<string, string> = {
|
||||
const forecastApiParams: Record<string, string | number | undefined> = {
|
||||
latitude: String(params.latitude),
|
||||
longitude: String(params.longitude),
|
||||
hourly: params.hourlyVariables.join(','),
|
||||
@@ -387,7 +392,8 @@ export async function fetchModelComparison(
|
||||
daily: 'sunrise,sunset',
|
||||
temperature_unit: params.temperature_unit ?? 'celsius',
|
||||
wind_speed_unit: params.wind_speed_unit ?? 'kmh',
|
||||
precipitation_unit: params.precipitation_unit ?? 'mm'
|
||||
precipitation_unit: params.precipitation_unit ?? 'mm',
|
||||
timezone: params.timezone
|
||||
};
|
||||
|
||||
const responses = await fetchWeatherApi(FORECAST_URL, forecastApiParams);
|
||||
@@ -395,10 +401,10 @@ export async function fetchModelComparison(
|
||||
// With multiple models, we get one response per model
|
||||
const firstResponse = responses[0];
|
||||
const utcOffsetSeconds = firstResponse.utcOffsetSeconds();
|
||||
const timezone = firstResponse.timezone() ?? params.timezone ?? 'UTC';
|
||||
|
||||
// Extract timestamps from the first response's hourly block
|
||||
const hourlyBlock = firstResponse.hourly()!;
|
||||
const timestamps = getTimestamps(hourlyBlock, utcOffsetSeconds);
|
||||
const timestamps = getTimestamps(hourlyBlock);
|
||||
|
||||
// Extract sunrise/sunset from the first response's daily block
|
||||
let markAreas: MarkArea[] = [];
|
||||
@@ -408,7 +414,7 @@ export async function fetchModelComparison(
|
||||
const sunsetVar = dailyBlock.variables(1)!;
|
||||
const sunrise = getInt64Values(sunriseVar);
|
||||
const sunset = getInt64Values(sunsetVar);
|
||||
markAreas = buildDaylightMarkAreas(sunrise, sunset, utcOffsetSeconds);
|
||||
markAreas = buildDaylightMarkAreas(sunrise, sunset);
|
||||
}
|
||||
|
||||
// Process each model's response
|
||||
@@ -463,6 +469,7 @@ export async function fetchModelComparison(
|
||||
models,
|
||||
timestamps,
|
||||
utcOffsetSeconds,
|
||||
timezone,
|
||||
markAreas,
|
||||
units,
|
||||
hourlyFlat,
|
||||
@@ -484,7 +491,7 @@ export async function fetchEnsembleForecast(
|
||||
): Promise<EnsembleForecastResult> {
|
||||
const forecastDays = params.forecast_days ?? 14;
|
||||
|
||||
const ensembleParams: Record<string, string> = {
|
||||
const ensembleParams: Record<string, string | number | undefined> = {
|
||||
latitude: String(params.latitude),
|
||||
longitude: String(params.longitude),
|
||||
hourly: params.hourlyVariables.join(','),
|
||||
@@ -492,7 +499,8 @@ export async function fetchEnsembleForecast(
|
||||
forecast_days: String(forecastDays),
|
||||
temperature_unit: params.temperature_unit ?? 'celsius',
|
||||
wind_speed_unit: params.wind_speed_unit ?? 'kmh',
|
||||
precipitation_unit: params.precipitation_unit ?? 'mm'
|
||||
precipitation_unit: params.precipitation_unit ?? 'mm',
|
||||
timezone: params.timezone
|
||||
};
|
||||
|
||||
const dailyParams: Record<string, string> = {
|
||||
@@ -511,9 +519,10 @@ export async function fetchEnsembleForecast(
|
||||
|
||||
const ensembleResponse = ensembleResponses[0];
|
||||
const utcOffsetSeconds = ensembleResponse.utcOffsetSeconds();
|
||||
const timezone = ensembleResponse.timezone() ?? params.timezone ?? 'UTC';
|
||||
|
||||
const hourlyBlock = ensembleResponse.hourly()!;
|
||||
const timestamps = getTimestamps(hourlyBlock, utcOffsetSeconds);
|
||||
const timestamps = getTimestamps(hourlyBlock);
|
||||
const timeLength = timestamps.length;
|
||||
|
||||
// Extract sunrise/sunset for mark areas
|
||||
@@ -524,7 +533,7 @@ export async function fetchEnsembleForecast(
|
||||
if (dailyBlock) {
|
||||
const sunrise = getInt64Values(dailyBlock.variables(0)!);
|
||||
const sunset = getInt64Values(dailyBlock.variables(1)!);
|
||||
markAreas = buildDaylightMarkAreas(sunrise, sunset, dailyResponse.utcOffsetSeconds());
|
||||
markAreas = buildDaylightMarkAreas(sunrise, sunset);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -613,6 +622,7 @@ export async function fetchEnsembleForecast(
|
||||
variables,
|
||||
timestamps,
|
||||
utcOffsetSeconds,
|
||||
timezone,
|
||||
markAreas,
|
||||
hourlyFlat,
|
||||
hourlyUnitsFlat
|
||||
|
||||
Reference in New Issue
Block a user