nominaldelta

nominal difference of date/datetime
git clone https://git.ce9e.org/nominaldelta.git

NameSize
.github/workflows/main.yml498B
LICENSE1072B
README.md3441B
nominaldelta.py4564B
pyproject.toml449B
test.py15671B

nominaldelta - nominal difference of date/datetime

Python's datetime module is great: It has ordinal and timestamp as absolute values, and date and datetime as nominal ones for the Gregorian calendar. The only issue is timedelta: It is really an absolute delta, so basically the same as date1.toordinal() - date2.toordinal() or dt1.timestamp() - dt2.timestamp().

The third party library python-dateutil adds relativedelta, which is a nominal delta. However, it acts as if every day had 24 hours, which is not quite true.

So this is an attempt to add a proper nominal delta.

Example

>>> from datetime import date
>>> from datetime import datetime
>>> from zoneinfo import ZoneInfo
>>> from nominaldelta import NominalDelta

# adding months does what you would expect
>>> datetime(1970, 1, 15, 13) + NominalDelta(months=1)
datetime(1970, 2, 15, 13)

# for shorter months, result are clipped to the last day of the month
>>> datetime(1970, 1, 30, 13) + NominalDelta(months=1)
datetime(1970, 2, 28, 13)

# daylight saving time is handled correctly
>>> tz = ZoneInfo('Europe/Berlin')
>>> datetime(2019, 3, 31, 3, 1, tzinfo=tz) - NominalDelta(minutes=2)
datetime(2019, 3, 31, 1, 59, tzinfo=tz)

# you can compute top-heavy differences
>>> delta = NominalDelta.diff(date(1970, 1, 1), date.today())
>>> f'{delta.months // 12} years'
'54 years'

Status

This is just an experiment, so I did not publish it to pypi. Feel free to open an issue if you think this should be published.

Usage

class NominalDelta(years, months, weeks, days, hours, minutes, seconds)

All arguments are optional and default to 0.

NominalDelta only stores months, days, and seconds. All other values are converted to one of them:

Notably, NominalDelta avoids to perpetuate some common misconceptions:

The three values are independent from each other. Seconds are never converted to days, and days are never converted to months.

a + NominalDelta()

When a NominalDelta is added to a date or datetime, the months are added first. If the original day does not exist in that month (e.g. there is no 1970-02-30), the last day of that month is used instead. Days are added after that and seconds are added last.

When adding to a date, seconds are ignored.

NominalDelta.diff(a, b, allow_months=True)

Calculate the delta between two date or datetime objects. This will first check how many months can be added without overshooting. Then it will check how many days can be added on top of the month. Last, the remaining difference in seconds is calculated. This approach is called "top heavy" because it prioritizes larger units over smaller ones.

Set allow_months=False if you only want to get days and seconds. If you only want seconds, use the absolute difference instead:

delta = NominalDelta(seconds=dt1.timestamp() - dt2.timestamp())