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) { return time < getWarsawForecastHour(); }