Fabio Manganiello on Nostr: What a mess with #Python’s utcnow deprecation.datetime.datetime.utcnow() has been ...
What a mess with #Python’s utcnow deprecation.datetime.datetime.utcnow()
has been deprecated since Python 3.12. And I see why. I’ve always wondered why that method for some reason returned a timezone-naive datetime. Like if I’m asking for UTC, can’t you just set tzinfo to UTC, before I accidentally do a comparison with a datetime.datetime.now() (which is also timezone-naive) a couple of lines down and suddenly end up comparing a timestamp in L.A. with one in London?
The officially suggested alternative is to go for a datetime.datetime.now(datetime.UTC) instead, so explicitly set the UTC timezone if you need a monotonous datetime object.
It’s a sensible implementation that should already have been implemented years ago.
Except that datetime.UTC is a macro introduced only in Python 3.11. On older versions:<code>>>> import datetime
>>> datetime.UTC
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: module 'datetime' has no attribute 'UTC'</code>
So the officially supported solution actually only works with Python versions released since October 2022. They could have at least suggested datetime.tzinfo.utc, as that one at least has been around for a while.
But the biggest issue is how badly it breaks back-compatibility with everything that has been written (and stored) before.
Take this code for example:<code>import datetime as dt
...
token = SessionToken(...)
token.expires_at = (
dt.datetime.utcnow() + dt.timedelta(days=365)
)
db.save(token)
...
if token.expires_at < dt.datetime.utcnow():
raise ExpiredSession(...)</code>
You’ve been running this code for a while, you’ve created a bunch of users, and since you used utcnow all those timestamps have been stored as offset-naive UTC.
Now you modify all the references of utcnow with now(dt.UTC). What happens?
Well, if your code was running with timestamps that are generated on the fly, everything should go ok. It’s a tz-aware to tz-aware comparison. But what happens when you load from the db your token that was saved with the previous, tz-naive utcnow implementation? Well, your code breaks all of a sudden:<code>TypeError: can't compare offset-naive and offset-aware datetimes</code>
I’m not sure if everyone within the community is already aware of the consequences of the new implementation, and that migrating to Python >= 3.12 should be considered a breaking change - especially if your code deals with persisted datetime objects.
Published at
2024-05-31 20:23:52Event JSON
{
"id": "0cf74875522336cb22dd66dfddce85ea3faca5c470f26d6a06dda08b8bb79baf",
"pubkey": "8179879e743ecc0b539b67420e7dc29a1f097751a00fa1c74d3cea319465223b",
"created_at": 1717187032,
"kind": 1,
"tags": [
[
"t",
"python"
],
[
"proxy",
"https://manganiello.social/objects/3804bc99-b824-48e2-9cd2-1399a59e08bc",
"activitypub"
],
[
"L",
"pink.momostr"
],
[
"l",
"pink.momostr.activitypub:https://manganiello.social/objects/3804bc99-b824-48e2-9cd2-1399a59e08bc",
"pink.momostr"
]
],
"content": "What a mess with #Python’s utcnow deprecation.datetime.datetime.utcnow()\n\n has been deprecated since Python 3.12. And I see why. I’ve always wondered why that method for some reason returned a timezone-naive datetime. Like if I’m asking for UTC, can’t you just set tzinfo to UTC, before I accidentally do a comparison with a datetime.datetime.now() (which is also timezone-naive) a couple of lines down and suddenly end up comparing a timestamp in L.A. with one in London?\n\nThe officially suggested alternative is to go for a datetime.datetime.now(datetime.UTC) instead, so explicitly set the UTC timezone if you need a monotonous datetime object.\n\nIt’s a sensible implementation that should already have been implemented years ago.\n\nExcept that datetime.UTC is a macro introduced only in Python 3.11. On older versions:\u003ccode\u003e\u0026gt;\u0026gt;\u0026gt; import datetime\n\u0026gt;\u0026gt;\u0026gt; datetime.UTC\nTraceback (most recent call last):\n File \"\u0026lt;stdin\u0026gt;\", line 1, in \u0026lt;module\u0026gt;\nAttributeError: module 'datetime' has no attribute 'UTC'\u003c/code\u003e\n\nSo the officially supported solution actually only works with Python versions released since October 2022. They could have at least suggested datetime.tzinfo.utc, as that one at least has been around for a while.\n\nBut the biggest issue is how badly it breaks back-compatibility with everything that has been written (and stored) before.\n\nTake this code for example:\u003ccode\u003eimport datetime as dt\n\n...\n\ntoken = SessionToken(...)\ntoken.expires_at = (\n dt.datetime.utcnow() + dt.timedelta(days=365)\n)\n\ndb.save(token)\n\n...\n\nif token.expires_at \u0026lt; dt.datetime.utcnow():\n raise ExpiredSession(...)\u003c/code\u003e\n\nYou’ve been running this code for a while, you’ve created a bunch of users, and since you used utcnow all those timestamps have been stored as offset-naive UTC.\n\nNow you modify all the references of utcnow with now(dt.UTC). What happens?\n\nWell, if your code was running with timestamps that are generated on the fly, everything should go ok. It’s a tz-aware to tz-aware comparison. But what happens when you load from the db your token that was saved with the previous, tz-naive utcnow implementation? Well, your code breaks all of a sudden:\u003ccode\u003eTypeError: can't compare offset-naive and offset-aware datetimes\u003c/code\u003e\n\nI’m not sure if everyone within the community is already aware of the consequences of the new implementation, and that migrating to Python \u003e= 3.12 should be considered a breaking change - especially if your code deals with persisted datetime objects.",
"sig": "bb44e9a3eacdb868e8de4bf7356afbee145b64933758ec56a4da0fe34291192e2caca2d08ed350a66ea55ec1eafeed22ec3bc0d647375ab7b76d7f630f0ae655"
}