"use strict"; // Day-number kernel: Howard Hinnant, "chrono-Compatible Low-Level Date Algorithms", // https://howardhinnant.github.io/date_algorithms.html (days_from_civil / civil_from_days). // Verified against 1970-01-01 -> day-number 0 and 2000-01-01 -> day-number 10957 (30 years // including 7 intervening leap years: 1972, 76, 80, 84, 88, 92, 96). No host clock, no // Date.now(), no time zone database. function daysFromCivil(y, m, d) { y = m <= 2 ? y - 1 : y; const era = Math.floor((y >= 0 ? y : y - 399) / 400); const yoe = y - era * 400; const doy = Math.floor((153 * (m + (m > 2 ? -3 : 9)) + 2) / 5) + d - 1; const doe = yoe * 365 + Math.floor(yoe / 4) - Math.floor(yoe / 100) + doy; return era * 146097 + doe - 719468; } function civilFromDays(z) { z += 719468; const era = Math.floor((z >= 0 ? z : z - 146096) / 146097); const doe = z - era * 146097; const yoe = Math.floor((doe - Math.floor(doe / 1460) + Math.floor(doe / 36524) - Math.floor(doe / 146096)) / 365); const y = yoe + era * 400; const doy = doe - (365 * yoe + Math.floor(yoe / 4) - Math.floor(yoe / 100)); const mp = Math.floor((5 * doy + 2) / 153); const d = doy - Math.floor((153 * mp + 2) / 5) + 1; const m = mp + (mp < 10 ? 3 : -9); const yy = y + (m <= 2 ? 1 : 0); return { year: yy, month: m, day: d }; } function isLeapGregorian(y) { return (y % 4 === 0 && y % 100 !== 0) || (y % 400 === 0); } function daysInMonth(y, m) { const dim = [31, isLeapGregorian(y) ? 29 : 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]; return dim[m - 1]; } function parseISODate(s, label) { const m = /^(-?\d{4})-(\d{2})-(\d{2})$/.exec(String(s).trim()); if (!m) throw new Error((label || "date") + " must be an ISO 8601 date YYYY-MM-DD, got: " + s); const year = parseInt(m[1], 10), month = parseInt(m[2], 10), day = parseInt(m[3], 10); if (month < 1 || month > 12) throw new Error((label || "date") + " month out of range 1-12"); if (day < 1 || day > daysInMonth(year, month)) throw new Error((label || "date") + " day out of range for that month"); return { year, month, day }; } function pad2(n) { return String(n).padStart(2, "0"); } function toISODate(y, m, d) { const ys = y < 0 ? "-" + String(-y).padStart(4, "0") : String(y).padStart(4, "0"); return ys + "-" + pad2(m) + "-" + pad2(d); } module.exports = { slug: "add-days-to-date", title: "Add days to a date", deck_category: "dates", description: "Adds (or subtracts, for a negative count) a whole number of days to a calendar date.", inputs: [ { name: "date", type: "string", format: "date", required: true, description: "ISO 8601 starting date, YYYY-MM-DD" }, { name: "days", type: "integer", required: true, description: "Number of days to add; negative subtracts days" } ], outputs: [ { name: "result_date", format: "date", description: "The resulting ISO 8601 date" } ], steps: [ "Parse the date as a proleptic Gregorian civil date.", "Convert it to a day-number using the Hinnant days_from_civil algorithm (cited above).", "Add the signed integer days to the day-number.", "Convert the resulting day-number back to a civil (year, month, day) using the inverse civil_from_days algorithm.", "Format the result as an ISO 8601 date." ], constants: [], sources: [ { kind: "url", value: "https://howardhinnant.github.io/date_algorithms.html", title: "chrono-Compatible Low-Level Date Algorithms", publisher: "Howard Hinnant", retrieved_utc: "2026-09-30T19:41:35Z", note: "days_from_civil / civil_from_days, verified round-trip against 1970-01-01 and 2000-01-01" } ], disclaimer: null, questionText(inputs) { const n = inputs.days; return "What date is " + Math.abs(n) + (n >= 0 ? " days after " : " days before ") + inputs.date + "?"; }, run(inputs) { const d = parseISODate(inputs.date, "date"); if (!Number.isInteger(inputs.days)) throw new Error("days must be an integer"); const z = daysFromCivil(d.year, d.month, d.day) + inputs.days; const r = civilFromDays(z); const answer = toISODate(r.year, r.month, r.day); return { answer, values: { result_date: answer } }; }, examples: [ { name: "worked example", inputs: { date: "2024-01-01", days: 31 }, expected: { answer: "2024-02-01" }, check: "January has 31 days, so 31 days after Jan 1 lands on Feb 1" }, { name: "test vector: leap day backward", inputs: { date: "2024-03-01", days: -1 }, expected: { answer: "2024-02-29" }, check: "2024 is a leap year, so the day before March 1, 2024 is Feb 29, 2024" }, { name: "test vector: year rollover", inputs: { date: "2023-12-31", days: 1 }, expected: { answer: "2024-01-01" }, check: "the day after Dec 31 is always Jan 1 of the next year" } ] };