ProvenPin Install

dateutil relativedelta month-end billing cycle math: working versions (tested 2026-10-16)

Compute calendar billing cycles from an anchor date with dateutil relativedelta, keeping month-end anchors on the real last day, and lock the behavior down offline with pytest.

passed6 passed · 0 failed2026-10-16 12:55 UTC
Status
passed
Last passed
2026-10-16 12:55 UTC
Tests
6 / 6
Language
Python
Runtime
Python 3.13.16
pinned versions2
python-dateutil@2.9.0.post0pytest@9.1.1
run it
uv venv -q .venv && uv pip install -q --python .venv/bin/python -r requirements.txt
.venv/bin/python -m pytest -q
test environment
Python 3.13.16
linux 6.17.0-1022-azure
x64
runner: github-actions 38903329434

The code

Exactly the files that passed. Your agent can pull them with get_recipe("python-dateutil-relativedelta-billing") (install), or see them on GitHub.

requirements.txt
python-dateutil==2.9.0.post0
pytest==9.1.1
billing.py
from calendar import monthrange
from datetime import date

from dateutil.relativedelta import relativedelta


def is_month_end(d: date) -> bool:
    return d.day == monthrange(d.year, d.month)[1]


def month_end(d: date) -> date:
    return d + relativedelta(day=31)


def add_months(d: date, months: int) -> date:
    return d + relativedelta(months=months)


def next_billing_date(anchor: date, n: int) -> date:
    d = anchor + relativedelta(months=n)
    if is_month_end(anchor):
        d = month_end(d)
    return d


def billing_cycle(anchor: date, n: int) -> tuple[date, date]:
    start = next_billing_date(anchor, n)
    end = next_billing_date(anchor, n + 1)
    return start, end
tests/test_billing.py
from datetime import date

from billing import (
    add_months,
    billing_cycle,
    is_month_end,
    month_end,
    next_billing_date,
)


def test_add_months_clamps_to_month_end():
    assert add_months(date(2024, 1, 31), 1) == date(2024, 2, 29)
    assert add_months(date(2023, 1, 31), 1) == date(2023, 2, 28)


def test_day31_means_last_day_not_literal_31():
    assert month_end(date(2024, 2, 10)) == date(2024, 2, 29)
    assert month_end(date(2023, 2, 10)) == date(2023, 2, 28)


def test_iterative_addition_drifts_off_month_end():
    d = date(2024, 1, 31)
    for _ in range(2):
        d = add_months(d, 1)
    assert d == date(2024, 3, 29)
    assert not is_month_end(d)


def test_anchored_billing_keeps_month_end():
    anchor = date(2024, 1, 31)
    assert next_billing_date(anchor, 1) == date(2024, 2, 29)
    assert next_billing_date(anchor, 2) == date(2024, 3, 31)
    assert next_billing_date(anchor, 3) == date(2024, 4, 30)


def test_billing_cycle_bounds_are_half_open():
    start, end = billing_cycle(date(2024, 1, 31), 1)
    assert start == date(2024, 2, 29)
    assert end == date(2024, 3, 31)


def test_non_month_end_anchor_is_preserved():
    anchor = date(2024, 1, 15)
    assert next_billing_date(anchor, 1) == date(2024, 2, 15)
    assert next_billing_date(anchor, 2) == date(2024, 3, 15)
pytest.ini
[pytest]
pythonpath = .

Connect to ProvenPin

Add the free MCP server so your agent can pull this recipe and others like it. Same setup as the docs.

terminal
claude mcp add --transport http provenpin https://mcp.provenpin.com/mcp

Any other MCP client: Streamable HTTP at https://mcp.provenpin.com/mcp. No API key, no signup.