"use strict"; // Julian Day Number from a proleptic Gregorian civil date, via Howard Hinnant's // "chrono-Compatible Low-Level Date Algorithms", https://howardhinnant.github.io/date_algorithms.html // (days_from_civil), offset by the JDN of the Hinnant epoch (1970-01-01 = JDN 2440588). This // is arithmetically equivalent to the Fliegel and Van Flandern (1968) integer JDN algorithm // ("A Machine Algorithm for Processing Calendar Dates", Communications of the ACM 11(10):657). // Verified against the well-known fact that 2000-01-01 is JDN 2451545 (the J2000.0 epoch date). // 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; } const EPOCH_JDN = 2440588; function toJDN(y, m, d) { return daysFromCivil(y, m, d) + EPOCH_JDN; } 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 }; } module.exports = { slug: "julian-day-number", title: "Julian Day Number of a civil date", deck_category: "dates", description: "Converts a proleptic Gregorian civil date to its Julian Day Number (JDN).", inputs: [ { name: "date", type: "string", format: "date", required: true, description: "ISO 8601 date, YYYY-MM-DD" } ], outputs: [ { name: "jdn", description: "Julian Day Number, an integer day count with no calendar gaps" } ], steps: [ "Parse the date as a proleptic Gregorian civil date.", "Convert it to a day-number using Hinnant's days_from_civil algorithm, which is arithmetically equivalent to the Fliegel and Van Flandern (1968) integer JDN formula.", "Add 2440588 (the JDN of 1970-01-01, the algorithm's epoch) to obtain the Julian Day Number." ], constants: [ { name: "epoch_jdn_1970_01_01", value: 2440588, unit: "JDN", source: 0 } ], 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; verified against the well-known fact that 2000-01-01 is JDN 2451545" }, { kind: "doi", value: "10.1145/364096.364097", title: "A Machine Algorithm for Processing Calendar Dates", publisher: "Fliegel, H. F.; Van Flandern, T. C., Communications of the ACM 11(10):657 (1968)", retrieved_utc: "2026-09-30T19:41:35Z", note: "the original integer civil-date-to-JDN algorithm this method is arithmetically equivalent to" } ], disclaimer: null, questionText(inputs) { return "What is the Julian Day Number of " + inputs.date + "?"; }, run(inputs) { const d = parseISODate(inputs.date, "date"); const jdn = toJDN(d.year, d.month, d.day); return { answer: String(jdn), values: { jdn } }; }, examples: [ { name: "worked example", inputs: { date: "2000-01-01" }, expected: { answer: "2451545" }, check: "2451545 is the well-known Julian Day Number for 2000-01-01 (the J2000.0 epoch date)" }, { name: "test vector: Unix epoch", inputs: { date: "1970-01-01" }, expected: { answer: "2440588" }, check: "2440588 is the well-known Julian Day Number for the Unix epoch date, 1970-01-01" }, { name: "test vector: recent date", inputs: { date: "2024-01-01" }, expected: { answer: "2460311" }, check: "computed independently as 2451545 + 8766 elapsed days between 2000-01-01 and 2024-01-01 (24 years of 365 days plus 6 intervening leap days: 2000, 04, 08, 12, 16, 20)" } ] };