function createTodayRollover(options = {}) { const now = options.now || (() => new Date()); const resolvedTimeZone = options.resolvedTimeZone || (() => Intl.DateTimeFormat().resolvedOptions().timeZone || 'UTC'); function timeZone() { return options.timeZone?.() || resolvedTimeZone(); } function localDate() { if (options.localDate) return options.localDate(); const parts = new Intl.DateTimeFormat('en-CA', { timeZone: timeZone(), year: 'numeric', month: '2-digit', day: '2-digit', }).formatToParts(now()); const value = Object.fromEntries(parts.map(part => [part.type, part.value])); return `${value.year}-${value.month}-${value.day}`; } function reviewState(plan) { if (!Array.isArray(plan?.ids) || !plan.ids.length) return 'empty'; if (!plan.plan_date) return 'legacy'; return plan.plan_date === localDate() ? 'current' : 'stale'; } function operation({ operation_id, base_revision, selected_ids, capacity_minutes, estimates = {} }) { const ids = [...new Set((selected_ids || []).filter(id => typeof id === 'string' && id))]; const selected = new Set(ids); return { operation_id, action: 'rollover', item_id: 'plan', base_revision, plan_date: localDate(), timezone: timeZone(), ids, capacity_minutes, estimates: Object.fromEntries(Object.entries(estimates).filter(([id]) => selected.has(id))), }; } return { localDate, timeZone, reviewState, operation }; } if (typeof module !== 'undefined' && module.exports) module.exports = createTodayRollover;