Date arithmetic across the BC/AD boundary is a classic off-by-one trap: there is no year 0 in the historical calendar, so 1 BC is followed directly by AD 1. A nice real-world exercise for this is one of the most argued-over numbers in ancient literature, the "seventy weeks" of Daniel 9:24–27.
The numbers in the text
Daniel 9:25 counts "seven weeks, and threescore and two weeks" (7 + 62 = 69 "weeks") from "the going forth of the commandment to restore and to build Jerusalem" until an "anointed one". Most readers take each "week" as seven years, so 69 weeks = 483 years.
The disagreement is about two inputs, which makes it a perfect parameterised function:
- The start date. Three decrees are usually proposed: Cyrus (Ezra 1, 538 BC), Artaxerxes I to Ezra (Ezra 7, usually dated 458/457 BC) and Artaxerxes I to Nehemiah (Nehemiah 2, 445/444 BC).
- The length of a "year". Either an ordinary solar year, or a 360-day "prophetic year", an idea popularised by Sir Robert Anderson in The Coming Prince (1894) and later used by Harold Hoehner (1977).
The code
The trick is to convert to astronomical year numbering, where 1 BC = 0, 2 BC = −1, and so on. Then ordinary subtraction and addition just work.
SOLAR = 365.2422 # mean tropical year in days
WEEKS = 7 + 62 # Daniel 9:25
YEARS = WEEKS * 7 # 483
def bc_to_astro(bc):
return 1 - bc # 1 BC -> 0, 444 BC -> -443
def astro_to_label(y):
y = round(y)
return f"AD {y}" if y >= 1 else f"{1 - y} BC"
starts = {
"Cyrus (Ezra 1), 538 BC": 538,
"Artaxerxes to Ezra (Ezra 7), 457 BC": 457,
"Artaxerxes to Nehemiah (Neh 2), 444 BC": 444,
}
for name, bc in starts.items():
a = bc_to_astro(bc)
solar = a + YEARS
prophetic = a + YEARS * 360 / SOLAR
print(f"{name:<40} solar: {astro_to_label(solar):>7} 360-day: {astro_to_label(prophetic):>7}")
Output:
Cyrus (Ezra 1), 538 BC solar: 55 BC 360-day: 62 BC
Artaxerxes to Ezra (Ezra 7), 457 BC solar: AD 27 360-day: AD 20
Artaxerxes to Nehemiah (Neh 2), 444 BC solar: AD 40 360-day: AD 33
483 years of 360 days is 173,880 days, which is about 476.07 solar years. That is how the "444 BC to AD 33" chronology is built, while the "457 BC to AD 27" reading uses ordinary solar years.
The off-by-one, made visible
If you skip the conversion and just compute 483 - 444, you get 39 and might print "AD 39". The correct answer is AD 40, because the count jumps from 1 BC straight to AD 1. Every BC-to-AD span calculated by naive subtraction is one year too long or too short, depending on which way you apply the error, and published timelines on the web regularly contain exactly this bug.
What the code can't settle
The arithmetic is the easy part. Which decree counts, whether the dates are 458 or 457 BC, whether the 360-day year is justified, and whether the passage was ever meant as a countdown to the first century at all are interpretive questions. Many critical scholars read the "anointed one cut off" as a reference to the high priest Onias III (murdered around 171 BC) and the crisis under Antiochus IV, not to a later figure. A longer walk-through of the competing readings is in this overview of the seventy weeks of Daniel prophecy, and Ancient Biotechnology also has a broader look at biblical prophecies people say came true and how they are evaluated.
For a developer the takeaway is simpler: whenever a date crosses the BC/AD line, convert to astronomical years first, do your maths, and convert back only for display.
Top comments (0)