repositories / will
will
owned by admin
src/shared/time.ts
Rawexport interface Clock {
now(): Date
}
export const systemClock: Clock = {
now: () => new Date(),
}
/** Deterministic clock for tests and reproducible jobs. */
export function fixedClock(iso: string): Clock {
const at = new Date(iso)
return { now: () => new Date(at.getTime()) }
}
export function nowIso(clock: Clock = systemClock): string {
return clock.now().toISOString()
}
/** UTC hour key like `2026-09-12T03` used for at-most-once heartbeat claims. */
export function utcHourKey(at: Date): string {
return at.toISOString().slice(0, 13)
}
/** ms until the next UTC hour boundary. */
export function msUntilNextHour(at: Date): number {
const next = Date.UTC(
at.getUTCFullYear(),
at.getUTCMonth(),
at.getUTCDate(),
at.getUTCHours() + 1,
)
return next - at.getTime()
}
/** Day key `YYYY-MM-DD` (UTC) used for budget rollover. */
export function utcDayKey(at: Date): string {
return at.toISOString().slice(0, 10)
}
export function addDays(at: Date, days: number): Date {
return new Date(at.getTime() + days * 86_400_000)
}
export function isoToDate(iso: string): Date {
const d = new Date(iso)
if (Number.isNaN(d.getTime())) throw new Error(`invalid iso timestamp: ${iso}`)
return d
}