Previously, we have documented that utcnow
and utcfromtimestamp
should not be used, but we didn’t go so far as to actually deprecate them, and I wrote a whole article about how you shouldn’t use them.
The main reason I had for not deprecating them at the time was that .utcnow()
is faster than .now(datetime.UTC)
, and if you are immediately converting the datetime to a string, like datetime.utcnow().isoformat()
, there’s no danger.
I have come around to the idea that this type of use case is not important enough to leave the attractive nuisances of utcnow()
and utcfromtimestamp()
in place, and we should go ahead and deprecate them.
I’ve opened an issue about doing this, and also prepared a PR, but I wanted to also open a discourse thread for more visibility. I’ll note that in the deprecation PR I remove all of our internal uses of utcnow
and utcfromtimestamp
and found that everyone was using them correctly, but I think this is atypical.
The main downside here is that for the use case of “I want the time in UTC and I immediately format it without %Z
”, the alternative is slower and more unwieldy (benchmarks on 3.11.3):
>>> %timeit datetime.now(UTC).replace(tzinfo=None).isoformat(' ')
2.15 µs ± 19.9 ns per loop (mean ± std. dev. of 7 runs, 100,000 loops each)
>>> %timeit datetime.now(UTC).isoformat(' ')[:-6]
1.61 µs ± 23.7 ns per loop (mean ± std. dev. of 7 runs, 1,000,000 loops each)
>>> %timeit datetime.utcnow().isoformat(' ')
919 ns ± 5.23 ns per loop (mean ± std. dev. of 7 runs, 1,000,000 loops each)
As an example of how this changes the speed in a real-life application, here are the before and after measurements for the change to http.cookiejar.time2isoz
:
>>> t = datetime.now().timestamp()
>>> %timeit cookiejar(None) # Uses datetime.now
1.52 µs ± 16.2 ns per loop (mean ± std. dev. of 7 runs, 1,000,000 loops each)
>>> %timeit cookiejar_utc(None) # Uses datetime.utcnow
1.32 µs ± 6.72 ns per loop (mean ± std. dev. of 7 runs, 1,000,000 loops each)
>>> %timeit cookiejar(t) # Uses datetime.fromtimestamp
1.77 µs ± 24.2 ns per loop (mean ± std. dev. of 7 runs, 1,000,000 loops each)
>>> %timeit cookiejar_utc(t) # Uses datetime.utcfromtimestamp
1.4 µs ± 5.75 ns per loop (mean ± std. dev. of 7 runs, 1,000,000 loops each)
I’m still more or less convinced that this is useful to do, and I’d like to see if anyone complains that it’s a major problem after the deprecation before worrying about these micro-optimizations.