Pretty sure it's an easy one but I don't get it.
My local TZ is currently GMT+3, and when I take timestamp from
datetime.utcnow().timestamp() it is indeed giving me 3 hours less than
During another process in my flow, I take that utc timestamp and need to turn it into datetime.
When I'm doing
fromtimestamp I get the right utc hour, but when I'm using
utcfromtimestamp I get another 3 hours offset.
The documentation, though, asks me to use
fromtimestamp for local timezone, and
utcfromtimestamp for utc usages.
What am I missing ? is the initial assumption for both funcs is that the timestamp is given in local timezone ?
Thank you :)
The key thing to notice when working with datetime objects and their POSIX timestamps at the same time is that naive datetime objects (the ones without timezone information) are assumed by Python to refer to local time (OS setting). In contrast, a POSIX timestamp (should) always refer to UTC seconds since the epoch. You can unambiguously obtain it by time.time(). In your example, not-so-obvious things happen:
datetime.now().timestamp() - now() gives you a naive datetime object that resembles local time. If you call for the timestamp(), Python converts the datetime to UTC and calculates the timestamp for that.
datetime.utcnow().timestamp() - utcnow() gives you a naive datetime object that resembles UTC. However, if you call timestamp(), Python assumes (since naive) that the datetime is local time - and converts to UTC again before calculating the timestamp! The resulting timestamp is therefore off from UTC by twice your local time's UTC offset...
A code example. Let's make some timestamps. Note that I'm on UTC+2, so offset is -7200 s.
import time from datetime import datetime, timezone ts_ref = time.time() # reference POSIX timestamp ts_utcnow = datetime.utcnow().timestamp() # dt obj UTC but naive - so also assumed local ts_now = datetime.now().timestamp() # dt obj naive, assumed local ts_loc_utc = datetime.now(tz=timezone.utc).timestamp() # dt obj localized to UTC print(int(ts_utcnow - ts_ref)) # -7200 # -> ts_utcnow doesn't refer to UTC! print(int(ts_now - ts_ref)) # 0 # -> correct print(int(ts_loc_utc - ts_ref)) # 0 # -> correct
I hope this clarifies that if you do
datetime.utcfromtimestamp(ts_utcnow), you get double the local time's UTC offset. Python assumes (which I think is pretty sane) that the timestamp refers to UTC - which in fact, it does not.
My suggestion would be to use timezone-aware datetime objects; like
datetime.now(tz=timezone.utc). The dateutil library can also be very helpful when working with datetime and timezones. And if you want to dig deep, have a look at the datetime src code. That could also help clarifying the issue you encountered.