<!-- canonical: efficientnewlanguage.org/ai/examples/111-days-between-dates | ai_layer_version: 0.1.0 | updated: 2026-07-27 -->

# Example 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.

## 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^0
```

## Python (deterministic transpilation)

```python
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):
    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)

```text
2023-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 count
```

## Round-trip

`ok: true` — round-trip fixpoint reached (python1 == python2)

## Trace event types

eml:run:start · eml:def · eml:assign · eml:call · eml:return · eml:output · eml:run:done
