/** * Daily scheduling without a cron dependency: one `setTimeout` to the next * HH:MM occurrence (container-local time via TZ), re-armed after every run. * A run that crosses midnight simply shifts the next one — backups are * hours apart, drift by seconds is irrelevant. */ /** Milliseconds from `now` to the next local-time occurrence of `time` (HH:MM). */ export function msUntilNext(now: Date, time: string): number { const [hours = 0, minutes = 0] = time.split(':').map(Number); const next = new Date(now); next.setHours(hours, minutes, 0, 0); if (next.getTime() <= now.getTime()) next.setDate(next.getDate() + 1); return next.getTime() - now.getTime(); } export function scheduleDaily( time: string, task: () => Promise, log: { info(details: object, message: string): void }, ): { stop(): void } { let timer: NodeJS.Timeout; const arm = (): void => { const delay = msUntilNext(new Date(), time); log.info({ nextRunInMs: delay }, 'next backup run scheduled'); timer = setTimeout(() => { void task().finally(arm); }, delay); }; arm(); return { stop: () => clearTimeout(timer) }; }