Adjust usage of utcnow() and utcfromtimestamp() for Python 3#440
Conversation
On the transition from Python 2 to Python 3, this subtle adjustment needed when working with date and datetime objects sometimes slips through. - Instead of `datetime.utcnow()`, use `datetime.now(timezone.utc)` - Instead of `utcfromtimestamp()`, use `fromtimestamp(..., tz=timezone.utc)` See also: https://blog.ganssle.io/articles/2019/11/utcnow.html Please note that this patch still reflects the fact that only timezone-unaware (naive) datetime objects are handled yet. In order to compensate that, a number of `.replace(tzinfo=None)` calls have been added along the lines.
| ) | ||
| from .sqlalchemy.tests import test_suite as sqlalchemy_test_suite | ||
| from .sqlalchemy.types import ObjectArray | ||
| from ..testing.util import datetime_now_utc_naive, date_now_utc_naive |
There was a problem hiding this comment.
PyCharm created that import line for me. The import statements above are also relative.
| from datetime import datetime, timezone | ||
|
|
||
|
|
||
| def datetime_now_utc_naive(): |
There was a problem hiding this comment.
why did you choose naive in the naming?
There was a problem hiding this comment.
The documentation where this patch is based upon also uses the same nomenclature "aware" vs. "naive" for refering to "timezone-aware" vs. "timezone-unaware" native Python datetime objects 12.
Date and time objects may be categorized as “aware” or “naive” depending on whether or not they include timezone information.
Footnotes
| >>> from datetime import datetime | ||
| >>> now = datetime.utcnow() | ||
| >>> from datetime import datetime, timezone | ||
| >>> now = datetime.now(timezone.utc).replace(tzinfo=None) |
There was a problem hiding this comment.
I don't understand the change here. Isn't now(timezone.utc).replace(tzinfo=None) the same as utcnow()?
There was a problem hiding this comment.
It is really sad. In situations when the system time zone setting, defined through the TZ environment variable, is expressed in UTC, everything is fine. However, it is not the case if the system time zone is anything other than UTC.
With c1f183e, I've tried to improve the situation to outline the problem, on behalf of some test cases. 5894c2e quotes and references all important information about this topic, from both the Python documentation and articles by other capacities.
There was a problem hiding this comment.
This is not the case.
Return the current UTC date and time, with tzinfo None.
Yes, it is a naive datetime without timezone info attached, but it is using utc.
>>> print(datetime.utcnow(), '\n', datetime.now(tz=timezone.utc).replace(tzinfo=None), '\n', datetime.now())
2022-07-22 18:27:27.055192
2022-07-22 18:27:27.055200
2022-07-22 20:27:27.055213
↪ date +"%Z %z"
CEST +0200
There was a problem hiding this comment.
Also as the articles point out, the problem is the use of the naive datetime (lack of tzinfo) in subsequent operations. If we remove the tzinfo anyways via .replace(tzinfo=None) we don't gain anything by using now(tz=timezone.utc) but only make things more complicated.
And I'm not sure if attaching a timezone is even the correct thing to do, because the timestamp stored in CrateDB also doesn't have one attached. Assuming that timestamp values returned by the server are UTC may not be safe.
There was a problem hiding this comment.
Thank you. You convinced me that this goes too far. I've submitted #441 instead, which solely focuses on mitigating a fluke I regularly occasionally observed when running the test suite on CI after midnight.
| try: | ||
| return datetime.utcfromtimestamp(value / 1e3).date() | ||
| return datetime.fromtimestamp(value / 1e3, tz=timezone.utc) \ | ||
| .replace(tzinfo=None).date() |
There was a problem hiding this comment.
If we throw away the tzinfo why not keep using utcfromtimestamp?
There was a problem hiding this comment.
I think the idea is to be a preparation step to actually allow the user to pass a tzinfo here, later on?
|
|
||
|
|
||
| def datetime_now_utc_naive(): | ||
| return datetime.now(timezone.utc).replace(tzinfo=None) |
There was a problem hiding this comment.
I far from expert in python but I also read this: https://blog.ganssle.io/articles/2019/11/utcnow.html
Those test cases try to outline the situation why 5a94e36 is needed. They specifically exercise some scenarios where the system time zone is expressed in UTC, or not, and shows the corresponding deviances. Other test cases attempt to outline that the new helper functions `datetime_now_utc_naive` and `date_now_utc_naive` do the right thing in all situations, regardless of any corresponding system locale or time zone setting.
5894c2e to
10a2c7b
Compare
Motivation
This patch further sets the stage for #437.
About
On the transition from Python 2 to Python 3, this subtle adjustment sometimes slips through. It is needed for properly working with the
now()variants of native Pythondateanddatetimeobjects.datetime.utcnow(), usedatetime.now(timezone.utc)utcfromtimestamp(), usefromtimestamp(..., tz=timezone.utc)See also: https://blog.ganssle.io/articles/2019/11/utcnow.html
Please note that this patch still reflects the fact that only timezone-unaware (naive) datetime objects are handled yet. In order to compensate that, a number of
.replace(tzinfo=None)calls have been added along the lines.