import { importSpielplan } from '../utils/spielplan-import.js' const TIME_ZONE = 'Europe/Berlin' const RUN_HOUR = 7 const RUN_MINUTE = 0 const MAX_TIMEOUT = 2_147_483_647 let timer = null let running = false function getTimeParts(date) { const parts = new Intl.DateTimeFormat('en-CA', { timeZone: TIME_ZONE, year: 'numeric', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit', second: '2-digit', hour12: false }).formatToParts(date) return Object.fromEntries(parts.map((part) => [part.type, part.value])) } function getTimeZoneOffset(date) { const parts = getTimeParts(date) const zonedAsUtc = Date.UTC( Number(parts.year), Number(parts.month) - 1, Number(parts.day), Number(parts.hour), Number(parts.minute), Number(parts.second) ) return zonedAsUtc - date.getTime() } function zonedDateToUtc(year, month, day, hour, minute) { const utcGuess = new Date(Date.UTC(year, month - 1, day, hour, minute, 0)) const offset = getTimeZoneOffset(utcGuess) return new Date(utcGuess.getTime() - offset) } function nextRunAt(now = new Date()) { const parts = getTimeParts(now) let year = Number(parts.year) let month = Number(parts.month) let day = Number(parts.day) let candidate = zonedDateToUtc(year, month, day, RUN_HOUR, RUN_MINUTE) if (candidate <= now) { const nextDay = zonedDateToUtc(year, month, day + 1, 12, 0) const nextParts = getTimeParts(nextDay) year = Number(nextParts.year) month = Number(nextParts.month) day = Number(nextParts.day) candidate = zonedDateToUtc(year, month, day, RUN_HOUR, RUN_MINUTE) } return candidate } async function runImport(reason) { if (running) return running = true try { const result = await importSpielplan() console.log(`[spielplan-import] ${reason}: ${result.matchCount} Spiele importiert (${result.source.season.dateStart} bis ${result.source.season.dateEnd})`) } catch (error) { console.error('[spielplan-import] Import fehlgeschlagen:', error) } finally { running = false } } function scheduleNext() { const runAt = nextRunAt() const delay = Math.min(Math.max(runAt.getTime() - Date.now(), 1_000), MAX_TIMEOUT) timer = setTimeout(async () => { await runImport('taeglicher Lauf') scheduleNext() }, delay) timer.unref?.() console.log(`[spielplan-import] Naechster Lauf: ${runAt.toISOString()} (${TIME_ZONE} ${String(RUN_HOUR).padStart(2, '0')}:${String(RUN_MINUTE).padStart(2, '0')})`) } export default defineNitroPlugin((nitroApp) => { if (process.env.SPIELPLAN_IMPORT_DISABLED === 'true') { console.log('[spielplan-import] Scheduler deaktiviert') return } scheduleNext() if (process.env.SPIELPLAN_IMPORT_RUN_ON_START === 'true') { runImport('Startlauf') } nitroApp.hooks.hookOnce('close', () => { if (timer) clearTimeout(timer) }) })