Search code examples
pythonpython-3.xpython-3.6pytz

Python epoch - datetime conversion bug?


So I have the following code:

import pytz
from datetime import datetime


tz = pytz.timezone('Asia/Singapore')

original_time = tz.localize(datetime.now())
original_epoch = original_time.timestamp()
converted_dt = tz.localize(datetime.utcfromtimestamp(original_epoch))
converted_epoch = converted_dt.timestamp()

print('{}\t\t{}'.format(original_time, original_epoch))
print('{}\t\t{}'.format(converted_dt, converted_epoch))

And it spits out

# Original Time                         Original Epoch
2018-07-16 02:17:41.583510+08:00        1531678661.58351
2018-07-15 18:17:41.583510+08:00        1531649861.58351
# Converted Time                        Converted Epoch

Is this a Python bug or am I simply missing something? And either way, how can I convert datetime to epoch and back with confidence that I am getting back the right time?


Solution

  • tz.localize() does not perform any timezone adjustments to the given datetime; it just sets its tzinfo to the given timezone. For the timestamps you're using, this means that tz.localtime() performs the following:

     datetime.now()                           ->  tz.localize(datetime.now())
     2018-07-16 02:17:41.583510                   2018-07-16 02:17:41.583510+08:00
    
     datetime.utcfromtimestamp(original_epoch) -> tz.localize(datetime.utcfromtimestamp(original_epoch))
     2018-07-15 18:17:41.583510                -> 2018-07-15 18:17:41.583510+08:00
    

    Note that the times don't change; only the timezone offset does. Because the inputs to tz.localize() are two different naïve times, you get two different aware times out.

    The correct way to construct a datetime from a UNIX timestamp and a timezone is to use datetime.fromtimestamp() with two arguments:

    >>> print(datetime.fromtimestamp(1531678661.58351, pytz.timezone('Asia/Singapore')))
    2018-07-16 02:17:41.583510+08:00