Use local time
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
import express from 'express';
|
||||
import {resolve, dirname} from 'path';
|
||||
import {fileURLToPath} from 'url';
|
||||
import {cfg} from './config.mjs';
|
||||
import {cfg, localDateStr, tzOffsetMinutes} from './config.mjs';
|
||||
import {queryCurrent, queryHourly, queryDaily, getCoords} from './influx.mjs';
|
||||
import {getCelestialCurrent, getCelestialForecast} from './celestial.mjs';
|
||||
import {getSpaceWeather} from './space.mjs';
|
||||
@@ -13,6 +13,16 @@ import {getWeatherCondition} from './openweather.mjs';
|
||||
import {lastForecast, getForecast, forecastTTL} from './forecast.mjs';
|
||||
import {dailyWeather, hourlyWeather} from './openmeteo.mjs';
|
||||
|
||||
// ── Uncaught error handlers ───────────────────────────────────────────────────
|
||||
|
||||
process.on('unhandledRejection', (reason, promise) => {
|
||||
console.error('[unhandledRejection]', promise, '\nReason:', reason)
|
||||
})
|
||||
|
||||
process.on('uncaughtException', (err) => {
|
||||
console.error('[uncaughtException]', err)
|
||||
})
|
||||
|
||||
const app = express();
|
||||
const DIR = dirname(fileURLToPath(import.meta.url));
|
||||
const CLIENT_DIST = resolve(DIR, '../public');
|
||||
@@ -44,14 +54,13 @@ app.get('/api/current', async (req, res) => {
|
||||
const space = getCelestialCurrent(coords.latitude, coords.longitude)
|
||||
const condition = getWeatherCondition(Object.assign(sensors, space))
|
||||
|
||||
// Merge forecast summary fields — sensor readings take priority
|
||||
const forecast = await getForecast();
|
||||
const merged = {
|
||||
...(forecast?.summary ? {
|
||||
precipitation_chance: forecast.summary.precipitation_chance,
|
||||
temperature_max: forecast.summary.temperature_max,
|
||||
temperature_min: forecast.summary.temperature_min,
|
||||
uv_index_max: forecast.summary.uv_index,
|
||||
precipitation_chance: forecast.summary.precipitation_chance,
|
||||
temperature_max: forecast.summary.temperature_max,
|
||||
temperature_min: forecast.summary.temperature_min,
|
||||
uv_index_max: forecast.summary.uv_index,
|
||||
} : {}),
|
||||
...condition,
|
||||
...sensors,
|
||||
@@ -73,7 +82,6 @@ app.get('/api/hourly', async (req, res) => {
|
||||
|
||||
const coords = await getCoords()
|
||||
|
||||
// Past → now: InfluxDB
|
||||
const historyEnd = new Date(Math.min(now, end))
|
||||
historyEnd.setMinutes(0, 0, 0)
|
||||
|
||||
@@ -82,12 +90,10 @@ app.get('/api/hourly', async (req, res) => {
|
||||
])
|
||||
const history = historyResult.status === 'fulfilled' ? historyResult.value : []
|
||||
|
||||
// Now → +24h: cached physics forecast
|
||||
const physics = end > now
|
||||
? lastForecast.slots.filter(s => { const t = new Date(s.time); return t >= now && t <= new Date(Math.min(end, plus24)) })
|
||||
: []
|
||||
|
||||
// +24h → beyond: Open-Meteo
|
||||
let meteo = []
|
||||
if (end > plus24) {
|
||||
const [meteoResult] = await Promise.allSettled([hourlyWeather(coords.latitude, coords.longitude, plus24, end)])
|
||||
@@ -103,22 +109,32 @@ app.get('/api/hourly', async (req, res) => {
|
||||
app.get('/api/daily', async (req, res) => {
|
||||
const { fields } = req.query
|
||||
const now = new Date()
|
||||
now.setHours(0, 0, 0, 0)
|
||||
const next = new Date(now); next.setDate(now.getDate() + 1)
|
||||
const start = req.query.start ? new Date(req.query.start) : now
|
||||
const end = req.query.end ? new Date(req.query.end) : next
|
||||
|
||||
// Use local midnight as "today" boundary
|
||||
const offset = tzOffsetMinutes(now)
|
||||
const localNow = new Date(now.getTime() + offset * 60_000)
|
||||
localNow.setUTCHours(0, 0, 0, 0)
|
||||
const todayLocal = new Date(localNow.getTime() - offset * 60_000) // back to UTC
|
||||
const nextLocal = new Date(todayLocal.getTime() + 24 * 3600_000)
|
||||
|
||||
const start = req.query.start ? new Date(req.query.start) : todayLocal
|
||||
const end = req.query.end ? new Date(req.query.end) : nextLocal
|
||||
|
||||
const coords = await getCoords()
|
||||
const todayStr = now.toISOString().slice(0, 10) // ✅ string for comparison
|
||||
const usePhysics = lastForecast.summary && start <= now && end > now // ✅ only when today is in window
|
||||
|
||||
const todayStr = localDateStr(now)
|
||||
const usePhysics = lastForecast.summary && start <= now && end > now
|
||||
|
||||
const [sensorResult] = await Promise.allSettled([start < now ? queryDaily(start, now) : Promise.resolve([])])
|
||||
const history = sensorResult.status === 'fulfilled' ? sensorResult.value : []
|
||||
|
||||
// ✅ meteo starts from whichever is later: next or start
|
||||
const meteoFrom = new Date(Math.max(next.getTime(), start.getTime()))
|
||||
const [meteoResult] = await Promise.allSettled([end > next ? dailyWeather(coords.latitude, coords.longitude, meteoFrom, end) : Promise.resolve([])])
|
||||
const meteoFrom = new Date(Math.max(nextLocal.getTime(), start.getTime()))
|
||||
const [meteoResult] = await Promise.allSettled([end > nextLocal ? dailyWeather(coords.latitude, coords.longitude, meteoFrom, end) : Promise.resolve([])])
|
||||
const meteo = meteoResult.status === 'fulfilled' ? meteoResult.value : []
|
||||
|
||||
const todaySlot = usePhysics ? [lastForecast.summary] : []
|
||||
const meteoClean = meteo.filter(r => String(r.time).slice(0, 10) !== todayStr) // ✅ safe string compare
|
||||
// Filter meteo using local date string so we never double-count today
|
||||
const meteoClean = meteo.filter(r => localDateStr(new Date(r.time)) !== todayStr)
|
||||
|
||||
const daily = getCelestialForecast(coords.latitude, coords.longitude, [...history, ...todaySlot, ...meteoClean])
|
||||
res.json(filterArr(daily, fields))
|
||||
@@ -175,6 +191,7 @@ app.use((err, req, res, next) => {
|
||||
})
|
||||
|
||||
// ── Start ─────────────────────────────────────────────────────────────────────
|
||||
|
||||
const c = cfg();
|
||||
|
||||
setTimeout(getForecast, 1)
|
||||
|
||||
Reference in New Issue
Block a user