Relativedelta seems to be sometimes buggy when counting days / or / month
This is a pretty straightforward code which uses relativedelta.
from datetime import date, timedelta
from dateutil.relativedelta import relativedelta
def _(message: str) -> str:
"""Fake translation function."""
return message
def get_my_tuple(
d1: date | str,
d2: date | str,
include_start: bool = True,
include_end: bool = True,
):
if isinstance(d1, str):
d1 = date.fromisoformat(d1)
if isinstance(d2, str):
d2 = date.fromisoformat(d2)
if d2 < d1:
d1, d2 = d2, d1
if not include_start:
d1 = d1 + timedelta(days=1)
if include_end:
d2 = d2 + timedelta(days=1)
if d1.year == d2.year and d1.month == d2.month:
days = abs(d2.day - d1.day)
return "{} {}".format(days, _("[days]") if days > 1 else _("[day]"))
result = []
r = relativedelta(d2, d1)
if r.years:
result.append(
"{} {}".format(r.years, _("[years]") if r.years > 1 else _("[year]"))
)
if r.months:
result.append(
"{} {}".format(r.months, _("[months]") if r.months > 1 else _("[month]"))
)
if r.days:
result.append(
"{} {}".format(r.days, _("[days]") if r.days > 1 else _("[day]"))
)
if len(result) > 1:
result[-2] = "{} {} {}".format(result[-2], _("[and]"), result[-1])
result.pop()
return ", ".join(result)
When I run my own test, passing some dates, I get something not normal:
test_case("-", "2025-11-23", "2025-12-31")
23/11/2025 -> 31/12/2025: 1 [month] [and] 9 [days]
Whereas it should be 1 [month] [and] 8 [days]!
This is correct: 23/11/2025 -> 21/12/2025: 29 [days]
And add one day, it's not correct: 23/11/2025 -> 22/12/2025: 1 [month]
It should be 30 [days] and only start counting 1 [month] later
What am I doing wrong?