Going from knowing the date and time to the time stamp and back gaining one hour?

I am always confused about datetimes

and timezone conversion in Python, but now I am experiencing rather strange behavior. I suspect (strongly) that this is due to the Daylight Savings Time saving feature, but I don't know for sure, and I definitely don't know how to properly handle it.

Here's what happens: if I create an instance datetime

that knows about its timezone, I create its epoch

UTC timestamp and I instantiate again datetime

from that timestamp, I seem to be typing one hour:

>>> import pytz
>>> import datetime
>>> import time
>>>
>>> naive = datetime.datetime.now()
>>> print "Naive 'now' %s" % naive
Naive 'now' 2014-08-21 11:19:13.019046

>>> eastern_tz = pytz.timezone('US/Eastern')
>>> now_eastern = eastern_tz.localize(naive)
>>> print "Now (eastern) is %s" % now_eastern
Now (eastern) is 2014-08-21 11:19:13.019046-04:00

>>> print "Now (utc) is %s" % now_eastern.astimezone(pytz.utc)
Now (utc) is 2014-08-21 15:19:13.019046+00:00
# This one is correct

>>> now_eastern_utc_timetuple = now_eastern.utctimetuple()
>>> print "Now (eastern) as timetuple %s" % now_eastern_utc_timetuple
Now (eastern) as timetuple time.struct_time(tm_year=2014, tm_mon=8, tm_mday=21, \
                   tm_hour=15, tm_min=19, tm_sec=13, tm_wday=3, \
                   tm_yday=233, tm_isdst=0)
# Shouldn't this be isdst=1 ? ----------^^^

>>> now_epoch = time.mktime(now_eastern_utc_timetuple)
>>> print "now epoch (UTC) %s" % now_epoch
now epoch (UTC) 1408652353.0
# I'm pretty sure this is +3600 in advance 

>>> print "Converted back: %s" % datetime.datetime.fromtimestamp(now_epoch)
Converted back: 2014-08-21 16:19:13

      

I checked the time using epochconverter.com and I'm pretty sure the timestamp created with utctimetuple

adds one hour. As I said, I'm pretty sure this is due to Daylight Time being misunderstood, because if I try to use a date when daylight saver is not in use (like December) it works fine.

>>> naive = datetime.datetime.strptime('2012/12/12 10:00', '%Y/%m/%d %H:%M')
>>> print "Naive 'now' %s" % naive
Naive 'now' 2012-12-12 10:00:00

>>> eastern_tz = pytz.timezone('US/Eastern')
>>> now_eastern = eastern_tz.localize(naive)
>>> print "Now (eastern) is %s" % now_eastern
Now (eastern) is 2012-12-12 10:00:00-05:00

>>> print "Now (utc) is %s" % now_eastern.astimezone(pytz.utc)
Now (utc) is 2012-12-12 15:00:00+00:00

>>> now_eastern_utc_timetuple = now_eastern.utctimetuple()
>>> print "Now (eastern) as timetuple %s" % now_eastern_utc_timetuple
Now (eastern) as timetuple time.struct_time(tm_year=2012, tm_mon=12, tm_mday=12,\
                   tm_hour=15, tm_min=0, tm_sec=0, tm_wday=2, \
                   tm_yday=347, tm_isdst=0)

>>> now_epoch = time.mktime(now_eastern_utc_timetuple)
>>> print "now epoch (UTC) %s" % now_epoch
now epoch (UTC) 1355342400.0

>>> print "Converted back: %s" % datetime.datetime.fromtimestamp(now_epoch)
Converted back: 2012-12-12 15:00:00

      

I use:

  • Mac Os X 10.9.4
  • Python 2.7
  • pytz 2012j

So the question is, how do we handle such a transformation properly? Is this problem DST related?

Thanks in advance.

+3


source to share


1 answer


Should it be isdst = 1?

Not. You are asking for a datetime object to give you a UTC tuple. UTC has no DST, so tm_isdst

it is always zero.

now_eastern.astimezone(pytz.utc)

and now_eastern.utctimetuple()

produce the same time. They are correct if now_eastern

correct.



time.mktime(now_eastern_utc_timetuple)

is incorrect because it mktime()

expects local time but now_eastern_utc_timetuple

is in UTC. You can use calendar.timegm(now_eastern_utc_timetuple)

instead or better now_eastern.timestamp()

, see Converting datetime.date to UTC timestamp in Python .

I would use: now_eastern = eastern_tz.localize(naive, is_dst=None)

(assuming that naive

is time 'US/Eastern'

, that is, if your local time zone 'US/Eastern'

, you can use tzlocal.get_localzone()

to automatically create a local time zone) <- raise an exception instead of returning an incorrect answer or better now_eastern = datetime.now(eastern)

.

0


source







All Articles