100 lines
4.1 KiB
TypeScript
100 lines
4.1 KiB
TypeScript
import type { Language, TranslationKey } from "@/lib/i18n";
|
|
import { translate } from "@/lib/i18n";
|
|
import type { HourlyForecast } from "@/types/forecast";
|
|
|
|
export function getForecastConditionKey(code: number | null): TranslationKey {
|
|
if (code === 0) return "forecast.condition.clear";
|
|
if (code === 1 || code === 2) return "forecast.condition.partlyCloudy";
|
|
if (code === 3) return "forecast.condition.cloudy";
|
|
if (code === 45 || code === 48) return "forecast.condition.fog";
|
|
if (code !== null && code >= 51 && code <= 57) return "forecast.condition.drizzle";
|
|
if (code !== null && ((code >= 61 && code <= 67) || (code >= 80 && code <= 82))) return "forecast.condition.rain";
|
|
if (code !== null && ((code >= 71 && code <= 77) || code === 85 || code === 86)) return "forecast.condition.snow";
|
|
if (code !== null && code >= 95) return "forecast.condition.thunderstorm";
|
|
return "forecast.condition.unknown";
|
|
}
|
|
|
|
export function getForecastCondition(code: number | null, language: Language) {
|
|
return translate(language, getForecastConditionKey(code));
|
|
}
|
|
|
|
export function formatForecastTemperature(value: number | null, language: Language) {
|
|
if (value === null) return "—";
|
|
return `${new Intl.NumberFormat(language === "pl" ? "pl-PL" : "en-GB", { maximumFractionDigits: 0 }).format(value)}°`;
|
|
}
|
|
|
|
export function formatForecastRainfall(value: number | null, language: Language) {
|
|
if (value === null) return "—";
|
|
return `${new Intl.NumberFormat(language === "pl" ? "pl-PL" : "en-GB", { maximumFractionDigits: 1 }).format(value)} mm`;
|
|
}
|
|
|
|
export function formatForecastWind(value: number | null, language: Language) {
|
|
if (value === null) return "—";
|
|
return `${new Intl.NumberFormat(language === "pl" ? "pl-PL" : "en-GB", {
|
|
minimumFractionDigits: 1,
|
|
maximumFractionDigits: 1,
|
|
}).format(value)} m/s`;
|
|
}
|
|
|
|
function getWarsawForecastHour(date = new Date()) {
|
|
const parts = new Intl.DateTimeFormat("en-CA", {
|
|
timeZone: "Europe/Warsaw",
|
|
year: "numeric",
|
|
month: "2-digit",
|
|
day: "2-digit",
|
|
hour: "2-digit",
|
|
hourCycle: "h23",
|
|
}).formatToParts(date);
|
|
const getPart = (type: Intl.DateTimeFormatPartTypes) => parts.find((part) => part.type === type)?.value ?? "";
|
|
|
|
return `${getPart("year")}-${getPart("month")}-${getPart("day")}T${getPart("hour")}:00`;
|
|
}
|
|
|
|
function parseForecastHour(time: string) {
|
|
const match = time.match(/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2})/);
|
|
if (!match) return null;
|
|
const [, year, month, day, hour, minute] = match;
|
|
const timestamp = Date.UTC(Number(year), Number(month) - 1, Number(day), Number(hour), Number(minute));
|
|
return Number.isFinite(timestamp) ? timestamp : null;
|
|
}
|
|
|
|
function getForecastHourOfDay(time: string) {
|
|
const match = time.match(/T(\d{2}):/);
|
|
return match ? Number(match[1]) : null;
|
|
}
|
|
|
|
export function getUpcomingHourlyForecast(hours: HourlyForecast[], limit = 24) {
|
|
const currentHour = getWarsawForecastHour();
|
|
const currentTimestamp = parseForecastHour(currentHour);
|
|
const upcomingHours = currentTimestamp === null
|
|
? hours.filter((hour) => hour.time >= currentHour)
|
|
: hours.filter((hour) => {
|
|
const hourTimestamp = parseForecastHour(hour.time);
|
|
return hourTimestamp === null ? hour.time >= currentHour : hourTimestamp >= currentTimestamp;
|
|
});
|
|
|
|
if (upcomingHours.length) return upcomingHours.slice(0, limit);
|
|
|
|
const currentHourOfDay = getForecastHourOfDay(currentHour);
|
|
const fallbackHours = currentHourOfDay === null
|
|
? hours
|
|
: hours.filter((hour) => {
|
|
const forecastHourOfDay = getForecastHourOfDay(hour.time);
|
|
return forecastHourOfDay === null || forecastHourOfDay >= currentHourOfDay;
|
|
});
|
|
|
|
return (fallbackHours.length ? fallbackHours : hours).slice(0, limit);
|
|
}
|
|
|
|
export function getHourlyForecastForDay(hours: HourlyForecast[], date: string) {
|
|
return hours.filter((hour) => hour.time.startsWith(`${date}T`));
|
|
}
|
|
|
|
export function isForecastHourPast(time: string) {
|
|
const currentHour = getWarsawForecastHour();
|
|
const hourTimestamp = parseForecastHour(time);
|
|
const currentTimestamp = parseForecastHour(currentHour);
|
|
if (hourTimestamp === null || currentTimestamp === null) return time < currentHour;
|
|
return hourTimestamp < currentTimestamp;
|
|
}
|