Search code examples
pythondatetimetimezoneutcpytz

Losing DST information using pytz and UTC conversions


Maybe this is a 4am bug, but I think I'm doing everything right, but it doesn't appear as though DST is translating from the UTC timestamp to the localized datetime.

>>> from datetime import datetime
>>> import pytz
>>> eastern = pytz.timezone("US/Eastern")
>>> utc = pytz.utc
>>> local_now = eastern.localize(datetime.now())
>>> utc_now = local_now.astimezone(utc)
>>> seconds = int(utc_now.strftime("%s"))
>>> utc_then = utc.localize(datetime.fromtimestamp(seconds))
>>> local_then = utc_then.astimezone(eastern)
>>> print utc_now, utc_then
2013-06-16 10:05:27.893005+00:00 2013-06-16 11:05:27+00:00
>>> print local_now, local_then
2013-06-16 06:05:27.893005-04:00 2013-06-16 07:05:27-04:00

Solution

  •              o------------o
                 |            |  DT.datetime.utcfromtimestamp (*)
                 |            |<-----------------------------------o
                 |            |                                    |
                 |  datetime  |                                    |
                 |            |  DT.datetime.fromtimestamp         |
                 |            |<----------------------------o      |
                 |            |                             |      |
                 o------------o                             |      |
                    |   ^                                   |      |
         .timetuple |   |                                   |      |
      .utctimetuple |   | DT.datetime(*tup[:6])             |      |
                    v   |                                   |      |
                 o------------o                          o------------o
                 |            |-- calendar.timegm (*) -->|            |
                 |            |                          |            |
                 |            |---------- time.mktime -->|            |
                 |  timetuple |                          |  timestamp |
                 |            |<-- time.localtime -------|            |
                 |            |                          |            |
                 |            |<-- time.gmtime (*)-------|            |
                 o------------o                          o------------o
    
    (*) Interprets its input as being in UTC and returns output in UTC
    

    As the diagram shows, when you have a datetime in UTC such as utc_now, to get its timestamp, use

    seconds = calendar.timegm(utc_date.utctimetuple())
    

    When you have a timestamp, to get to the datetime in UTC, use

    DT.datetime.utcfromtimestamp(seconds)
    

    import datetime as DT
    import pytz
    import calendar
    eastern = pytz.timezone("US/Eastern")
    utc = pytz.utc
    now = DT.datetime(2013, 6, 16, 10, 0, 0)
    local_now = eastern.localize(now)
    utc_now = local_now.astimezone(utc)
    seconds = calendar.timegm(utc_now.utctimetuple())
    
    print(seconds)
    # 1371391200
    
    utc_then = utc.localize(DT.datetime.utcfromtimestamp(seconds))
    local_then = utc_then.astimezone(eastern)
    
    print utc_now, utc_then
    # 2013-06-16 14:00:00+00:00 2013-06-16 14:00:00+00:00
    print local_now, local_then
    # 2013-06-16 10:00:00-04:00 2013-06-16 10:00:00-04:00
    

    PS. Note that the timetuple() and utctimetuple() methods drop microseconds off the datetime. To convert a datetime to a timestamp in a way that preserves microseconds, use mata's solution.