datetime.datetime.utcnow()
returns a timezone naive datetime, this is counter-intuitive since you are logically dealing with a known timezone. I suspect this was implemented this way for fidelity with the rest of datetime.datetime (which returns timezone naive datetime objects).
The workaround (see below) is to replace the missing tzinfo.
Recommendation:
By default datetime.datetime.utcnow() should return a timezone aware datetime (with tzinfo of UTC of course) or at least offer this behavoir as an option,
e.g.:
datetime.datetime.utcnow(timezone-aware=True)
Workaround:
dt = datetime.utcnow().replace(tzinfo=timezone.utc)
Yes, this is the documented behavior, including a warning against using UTC now in the documentation!
There is some possibility of removing utcnow entirely as an "attractive nuisance", but I suspect that this will lead to much consternation and hand-wringing, and there are some legitimate uses for `utcnow`, so I haven't made it a high priority to have that particular fight...