Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does datetime.utcnow().timestamp() return different values for Python and IPython in the same environment and terminal

In IPython:

Python 3.6.2 (default, Sep  4 2017, 13:34:02) 
Type 'copyright', 'credits' or 'license' for more information
IPython 6.2.1 -- An enhanced Interactive Python. Type '?' for help.

In [1]: datetime.utcnow().timestamp()
Out[1]: 1515396403.049774

In Python:

Python 3.6.2 (default, Sep  4 2017, 13:34:02) 
[GCC 4.2.1 Compatible Apple LLVM 8.1.0 (clang-802.0.42)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> datetime.utcnow().timestamp()
1515425220.215077
>>> 

The result from IPython appears to be in my timezone instead of UTC.

Without the call to .timestamp() the values returned by .utcnow() are the same in both terminals. Why can this be the case?

In a regular Python prompt, the values for datetime.utcnow().timestamp() and datetime.utcnow().replace(tzinfo=timezone.utc).timestamp() are different.

Python 3.6.2 (default, Sep  4 2017, 13:34:02) 
[GCC 4.2.1 Compatible Apple LLVM 8.1.0 (clang-802.0.42)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> from datetime import datetime, timezone
>>> datetime.utcnow().replace(tzinfo=timezone.utc).timestamp()
1515548420.130655
>>> datetime.utcnow().timestamp()
1515577235.982597

In IPython, these values are the same.

In IPython now and utcnow return the same. In Python, now returns my local time.

In [4]: datetime.utcnow()
Out[4]: datetime.datetime(2018, 1, 10, 1, 44, 55, 496083)

In [5]: datetime.now()
Out[5]: datetime.datetime(2018, 1, 10, 1, 45, 53, 811077)

Python:

>>> datetime.datetime.now()
datetime.datetime(2018, 1, 9, 17, 46, 29, 754642)
>>> datetime.datetime.utcnow()
datetime.datetime(2018, 1, 10, 1, 46, 32, 865136)
like image 951
Skylar Saveland Avatar asked May 03 '26 22:05

Skylar Saveland


1 Answers

When datetime.timestamp() is called on a timezone-naive instance of datetime, it assumes the value is a local datetime. Therefore, the code datetime.utcnow().timestamp() should never be used, since the non-local timezone-naive datetime.utcnow() violates that assumption.

The "old" correct way to get the UTC timestamp is: datetime.now().timestamp()

A better way of doing it is to avoid the naive datetime, using the timezone-aware version, formerly using: datetime.utcnow().replace(tzinfo=timezone.utc).timestamp() or now, because datetime.utcnow() has been deprecated since Python version 3.12, using: datetime.now(tz=timezone.utc).timestamp()

A fairly easy way to see the effect is with the following code in an environment where the timezone offset is non-zero:

from datetime import datetime, timezone
now1 = datetime.now()
now2 = datetime.utcnow()
now3 = datetime.now(tz=timezone.utc)
now1
now1.timestamp()
now2
now2.timestamp() # WRONG!
now3
now3.timestamp()

This produces output like this:

>>> from datetime import datetime, timezone
>>> now1 = datetime.now()
>>> now2 = datetime.utcnow()
>>> now3 = datetime.now(tz=timezone.utc)
>>> now1
datetime.datetime(2023, 12, 16, 11, 50, 0, 979519)
>>> now1.timestamp()
1702749000.979519
>>> now2
datetime.datetime(2023, 12, 16, 17, 50, 0, 982995)
>>> now2.timestamp() # WRONG!
1702770600.982995
>>> now3
datetime.datetime(2023, 12, 16, 17, 50, 0, 983405, tzinfo=datetime.timezone.utc)
>>> now3.timestamp()
1702749000.983405

As can be seen above, when using the "old" way with the timezone-naive local datetime now1, timestamp() applies the timezone offset, resulting in the UTC timestamp value. Likewise, the "better" way with the timezone-aware now3 datetime, timestamp() produces the same result. Note that without the timezone info, the value of now2 does not appear much different than the value of now1 except that it is 6 hours ahead. Since now2 has already applied the timezone offset once when utcnow() was called, calling timestamp() on the timezone-naive value applies the timezone offset a second time, resulting in a timestamp value where the timezone offset has been applied twice! In the example above, the time zone was 6 hours west of UTC, resulting in a timestamp that was actually 12 hours ahead of local time and therefore, incorrectly, 6 hours (21,600 seconds) ahead of UTC.

like image 73
Rob at TVSeries.com Avatar answered May 05 '26 12:05

Rob at TVSeries.com