Case 111
Days between dates
days_between_dates.eml counts the days between two calendar dates by converting each to a day number since a fixed epoch and subtracting.
ok: true — round-trip fixpoint reached (python1 == python2)updated 2026-07-27
EML
eml# Self-authored for the EML case corpus (no external origin). Counts the
# days between two calendar dates by converting each to a day number
# since a fixed epoch and subtracting. Companion to
# examples/day-of-week-zeller/, which answers a different question about
# the same calendar.
#
# The full Gregorian leap rule is three rules, not one: divisible by 4 is
# a leap year, EXCEPT divisible by 100 is not, EXCEPT divisible by 400 is
# after all. The samples are built to exercise each branch separately,
# because an implementation that only knows the first rule still gets most
# dates right and fails silently on the rest:
#
# 2023-02-28 -> 2023-03-01 1 day ordinary year, no 29th
# 2024-02-28 -> 2024-03-01 2 days divisible by 4, leap
# 1900-02-28 -> 1900-03-01 1 day divisible by 100, NOT leap
# 2000-02-28 -> 2000-03-01 2 days divisible by 400, leap after all
#
# The 1900 and 2000 pair is the one that matters: they differ only in the
# century rule, so a "divisible by 4" implementation gets 1900 wrong while
# every other sample still passes.
def is_leap(year):
if year % 400 == 0:
return 1
if year % 100 == 0:
return 0
if year % 4 == 0:
return 1
return 0
def days_in_month(year, month):
lengths^+[31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
if month == 2 and is_leap(year) == 1:
return 29
return lengths[month - 1]
def day_number(year, month, day):
0 => total
1900 => y
while y < year:
if is_leap(y) == 1:
total + 366 => total
else:
total + 365 => total
y + 1 => y
1 => m
while m < month:
total + days_in_month(year, m) => total
m + 1 => m
return total + day
def days_between(a, b):
day_number(b[0], b[1], b[2]) - day_number(a[0], a[1], a[2]) => difference
return difference
def render(date):
return str(date[0]) + "-" + str(date[1]) + "-" + str(date[2])
pairs^+[[[2023, 2, 28], [2023, 3, 1]],
[[2024, 2, 28], [2024, 3, 1]],
[[1900, 2, 28], [1900, 3, 1]],
[[2000, 2, 28], [2000, 3, 1]],
[[2000, 1, 1], [2000, 12, 31]],
[[1999, 12, 31], [2000, 1, 1]]]
expected^+[1, 2, 1, 2, 365, 1]
labels^+["ordinary year",
"divisible by 4: leap",
"divisible by 100: NOT leap",
"divisible by 400: leap after all",
"a full leap year, Jan 1 to Dec 31",
"across a year boundary"]
0 => matches
for i in [0:5]:
pairs[i] => pair
days_between(pair[0], pair[1]) => days
expected[i] => known
render(pair[0]) + " -> " + render(pair[1]) + " = " + str(days) => line
if days == known:
matches + 1 => matches
line + " days (" + labels[i] + ")" => line
else:
line + " days (EXPECTED " + str(known) + ")" => line
line^0
"" => blank
blank^0
str(matches) + " of " + str(len(pairs)) + " match their known day count" => summary
summary^0Python (deterministic transpilation)
pythondef is_leap(year):
if year % 400 == 0:
return 1
if year % 100 == 0:
return 0
if year % 4 == 0:
return 1
return 0
def days_in_month(year, month):
lengths = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
if month == 2 and is_leap(year) == 1:
return 29
return lengths[month - 1]
def day_number(year, month, day):
total = 0
y = 1900
while y < year:
if is_leap(y) == 1:
total = total + 366
else:
total = total + 365
y = y + 1
m = 1
while m < month:
total = total + days_in_month(year, m)
m = m + 1
return total + day
def days_between(a, b):
difference = day_number(b[0], b[1], b[2]) - day_number(a[0], a[1], a[2])
return difference
def render(date):
return str(date[0]) + "-" + str(date[1]) + "-" + str(date[2])
pairs = [[[2023, 2, 28], [2023, 3, 1]], [[2024, 2, 28], [2024, 3, 1]], [[1900, 2, 28], [1900, 3, 1]], [[2000, 2, 28], [2000, 3, 1]], [[2000, 1, 1], [2000, 12, 31]], [[1999, 12, 31], [2000, 1, 1]]]
expected = [1, 2, 1, 2, 365, 1]
labels = ["ordinary year", "divisible by 4: leap", "divisible by 100: NOT leap", "divisible by 400: leap after all", "a full leap year, Jan 1 to Dec 31", "across a year boundary"]
matches = 0
for i in range(0, 6):
pair = pairs[i]
days = days_between(pair[0], pair[1])
known = expected[i]
line = render(pair[0]) + " -> " + render(pair[1]) + " = " + str(days)
if days == known:
matches = matches + 1
line = line + " days (" + labels[i] + ")"
else:
line = line + " days (EXPECTED " + str(known) + ")"
print(line)
blank = ""
print(blank)
summary = str(matches) + " of " + str(len(pairs)) + " match their known day count"
print(summary)stdout (executed)
text2023-2-28 -> 2023-3-1 = 1 days (ordinary year)
2024-2-28 -> 2024-3-1 = 2 days (divisible by 4: leap)
1900-2-28 -> 1900-3-1 = 1 days (divisible by 100: NOT leap)
2000-2-28 -> 2000-3-1 = 2 days (divisible by 400: leap after all)
2000-1-1 -> 2000-12-31 = 365 days (a full leap year, Jan 1 to Dec 31)
1999-12-31 -> 2000-1-1 = 1 days (across a year boundary)
6 of 6 match their known day countTrace event types
eml:run:starteml:defeml:assigneml:calleml:returneml:outputeml:run:done