Here we are talking about the datetime
object of the Python datetime
package.
The datetime
object is not good or bad, it is complex. In combination with the datetime.tz
module
almost all of your date and time related needs can be solved. But at a cost!
- datetime object has a fat state of 9 fields (year, month, ..., tzinfo, fold).
- You must know the state to have a correct mental model what your code does.
- But it is hard or impossible to have a mental model of datetime locally.
- A typical datetime operation
- stuffs something into a datetime
- transforms the datetime, mutating it
- gets something out of it
- Btw the
time
object is almost as percky asdatetime
- Use
datetime
only locally - Make wrapper objects conveying different types of date and time
- Have at least two new types according the bitemporal model
- transaction time
- valid time
- For the valid time, make it impossible to express invalid 😜 times.
- Do all time arithmetics and conversions locally.
At transaction time 2024-11-19T09:19:54.226175+01:00
we plan the electrical power of 20MW to be delivered in the hour (valid time) starting at 2024-11-19T13:00+01:00
.
from datetime import datetime, timedelta, time, tzinfo
from dateutil import tz
BERLIN = tz.gettz('Europe/Berlin')
# Stuff something in
now = datetime.now()
# Transform
now = now.astimezone(BERLIN)
# Get something out
now.time() > time(9, 0)
# Btw a better solution would be
datetime.now().time() > time(10, 0, tzinfo=BERLIN)
def foo(d1: datetime, d2: datetime):
""" There is no way to locally argue that the assertion is true """
# Difference in wall clock time
delta_wall_clock = (d2 - d1).total_seconds()
delta_absolute = d2.timestamp() - d1.timestamp()
assert delta_wall_clock == delta_absolute
foo(datetime(2024, 10, 27, 1, tzinfo=BERLIN), datetime(2024, 10, 27, 3, tzinfo=BERLIN))
# -> AssertionError