8

I am using datetime.fromtimestamp to convert epoch time into local time. I found that datetime.fromtimestamp does a discrete jump of one hour at a certain point of time and I am completely baffled as to why it does that.

(I am also using time.mktime to convert a datetime object into epoch time, as suggested by Raymond Hettinger. I'm not sure whether this is relevant information for this question, so I'm saying this just in case.)

Python 2.7.1+ (r271:86832, Apr 11 2011, 18:05:24) 
[GCC 4.5.2] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import time, datetime
>>> def datetime_to_epoch_time(datetime_):
...     return time.mktime(datetime_.timetuple()) + datetime_.microsecond / 1e6
... 

Picking a specific epoch time:

>>> x = datetime_to_epoch_time(datetime.datetime(2012, 3, 30, 3, 0))

Converting it to a datetime using fromtimestamp:

>>> datetime.datetime.fromtimestamp(x)
datetime.datetime(2012, 3, 30, 3, 0)

We get a time of 3am.

Now let's convert the time that's exactly one second before it:

>>> datetime.datetime.fromtimestamp(x-1)
datetime.datetime(2012, 3, 30, 1, 59, 59) 

We suddenly get 1:59am!

What happened? I know that stuff like that happens around leap days, but since when is March 30th a leap day?

I should note that I've had this happen to me only on Linux and not on Windows. And I think that different Linux computers (in different timezones) have a different time point in which fromtimestamp does the jump.

Community
  • 1
  • 1
Ram Rachum
  • 84,019
  • 84
  • 236
  • 374

2 Answers2

9

fromtimestamp uses the current user's "local environment", as defined by the POSIX C library (see man (3) tzset, and docs of the time module).

If you really want to get a pytz representation of the current user's environment's local-time, the datetime-tz package has a function for auto-detecting it.

However, common wisdom is to work always with UTC, and avoid all DST issues (use local timezone only for final display). Use datetime.fromtimestamp(x, tz=pytz.UTC), or, if you do not have pytz:

>>> datetime.datetime.fromtimestamp(x)
datetime.datetime(2012, 3, 30, 3, 0)
>>> datetime.datetime.utcfromtimestamp(x)
datetime.datetime(2012, 3, 30, 0, 0)
>>> datetime.datetime.utcfromtimestamp(x-1)
datetime.datetime(2012, 3, 29, 23, 59, 59)

P.S. You can also set your process' locale to UTC (but this might not work on non-POSIX OS's):

>>> import os,time
>>> os.environ["TZ"] = "UTC"
>>> time.tzset()
>>> datetime.datetime.fromtimestamp(x)
datetime.datetime(2012, 3, 30, 0, 0)
David Cain
  • 16,484
  • 14
  • 65
  • 75
amitar
  • 91
  • 2
5

Easy. March 30th is presumably a daylight savings time switch in your timezone.

So on that day, time did indeed go from 1:59:59 to 3:00:00

Malcolm Box
  • 3,968
  • 1
  • 26
  • 44
  • I see. So `fromtimestamp` is aware of the time zone I live in and knows which hours to skip. But `datetime` objects aren't aware of this jump: If I subtract the `datetime` object for 3am from the one for 2am, I get a `timedelta` of one hour, where I should be getting zero. – Ram Rachum Mar 28 '12 at 20:36
  • How, then, do I make `datetime` objects aware of my timezone, and make them use the *exact* some timezone as `fromtimestamp`, so they will be completely synchronized? – Ram Rachum Mar 28 '12 at 20:37
  • @RamRachum Pass the `tzinfo` flag when constructing the timestamp. the `pytz` library may be of some help with that. – Amber Mar 28 '12 at 20:46
  • @Amber But how will I know that the `pytz` library is giving me the exact same timezone that `fromtimestamp` is using? If `fromtimestamp` is using a certain timezone that already exists on my computer without installing `pytz`, is it possible for me to use it? – Ram Rachum Mar 28 '12 at 21:00