| Name | Size |
|---|---|
| .github/workflows/main.yml | 498B |
| LICENSE | 1072B |
| README.md | 3441B |
| nominaldelta.py | 4564B |
| pyproject.toml | 449B |
| test.py | 15671B |
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:
yearsare converted to 12 monthsweeksare converted to 7 dayshoursare converted to 3600 secondsminutesare converted to 60 secondssecondscan be a float to represent milli- and micoseconds
Notably, NominalDelta avoids to perpetuate some common misconceptions:
yearsare not converted to 365 days because leap years have 366 daysdaysare not converted to 24 hours because of daylight saving time. Also, days with leap seconds have an extra second.
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())