Table of Contents
Today I came across this issue:
TypeError: can't subtract offset-naive and offset-aware datetimes
I had found a bug in my program where some calculations were not being done correctly because my datetime values were incorrect or more accurately… inconsistent.
I wanted to find the difference between 2 datetime objects. One of the datetime object originates from a datetime string which is passed to a pydantic Model, which is ultimately converted into a datetime object by the pydantic model.
The 2nd datetime object is generated by the function datetime.utcnow()
function. At this point I had thought that I was doing the right thing by using utcnow() to generate a UTC time based datetime object…
Note: In this example we use Python 3.10 & Pydantic
from datetime import datetime
from pydantic import BaseModel
class InputTime(BaseModel):
t: datetime
if __name__ == "__main__":
# One of my Datetime objects is generated via pydantic
model = InputTime(**{'t': '2023-04-04T21:13:44.520654Z'}).dict()
datetime_obj_1 = model['t']
# Get Datetime object of the time now
datetime_obj_2 = datetime.utcnow()
# Work out the Interval
difference = datetime_obj_2 - datetime_obj_1
print(difference)
The result would be:
Traceback (most recent call last):
File "/home/user/PycharmProjects/PythonTestGround/timezones.py", line 17, in <module>
difference = datetime_obj_2 - datetime_obj_1
TypeError: can't subtract offset-naive and offset-aware datetimes
Offset-aware datetime objects = happy days
I was frustrated and was unsure what the problem would be since they were both datetime objects…
Upon much research, I found out that although both were datetime object, datetime_obj_1 had a timezone set (was offset-aware) and datetime_obj_2 did not (was offset-naive).
The solution was to convert datetime_obj_2 to become an offset aware datetime object by assigning a timezone when the datetime object is generated:
datetime.now(timezone.utc)
Full Example:
from datetime import datetime, timezone
from pydantic import BaseModel
class InputTime(BaseModel):
t: datetime
if __name__ == "__main__":
# One of my Datetime objects is generated via pydantic
model = InputTime(**{'t': '2023-04-04T21:13:44.520654Z'}).dict()
datetime_obj_1 = model['t']
# Get Datetime object of the time now
datetime_obj_2 = datetime.now(timezone.utc)
# Work out the Interval
difference = datetime_obj_2 - datetime_obj_1
print(difference)
Resulting in the calculated time difference:
21:43:56.272346
Conclusion
Here’s A few things I learnt today we it comes to handling time in python programs:
- When passing datetime values around you program, pass it around as a datetime object - NOT in their string format, unless you really want to…
- Make sure your datetime object are offset-aware, meaning set the timezone for them
- Stick to one string format, at least within your program. I use ISO8601,
datetime.now(timezone.utc).strftime('%Y-%m-%dT%H:%M:%S.%f%z')
- Only convert your datetime objects to datetime string when that data is being passed out of your program