Search code examples
pythontimezonepytz

Convert EST timestamp to GMT taking into account Daylight Savings


I have a timestamp that represents milliseconds since 1970 1432202088224 which translates to Thursday, May 21, 2015 5:54:48 AM EDT. I'd like to write a python function that converts that timestamp to milliseconds in GMT. I can't naively add four hours (3600000 milliseconds) to the existing timestamp because half the year i'll be off by one hour.

I've tried writing a function using datetime and pytz

def convert_mills_GMT(milliseconds):
    converted_raw = datetime.datetime.fromtimestamp(milliseconds/1000.0)
    date_eastern = eastern.localize(converted_raw, is_dst=True)
    date_utc = date_eastern.astimezone(utc)
    return int(date_utc.strftime("%s")) * 1000

using the input of 1432202088224 this function returns 1432220088000 which is Thursday, May 21, 2015 10:54:48 AM EDT when what I want is 9:54 AM. what am I missing?


Solution

  • Don't use .strftime("%s"). It is not supported, and may silently fail. Instead, to convert a UTC datetime to a timestamp use one of the methods shown here depending on your version of Python:

    Python 3.3+:

    timestamp = dt.timestamp()
    

    Python3 (< 3.3):

    epoch = datetime(1970, 1, 1, tzinfo=timezone.utc)
    timestamp = (dt - epoch) / timedelta(seconds=1)
    

    Python 2.7+:

    timestamp = (dt.replace(tzinfo=None) - datetime(1970, 1, 1)).total_seconds()
    

    Python2 (< 2.7):

    def totimestamp(dt, epoch=datetime(1970,1,1)):
        td = dt - epoch
        # return td.total_seconds()
        return (td.microseconds + (td.seconds + td.days * 86400) * 10**6) / 10**6 
    timestamp = totimestamp(dt.replace(tzinfo=None))
    

    Therefore, your convert_mills_GMT should look like

    def convert_mills_GMT(milliseconds, 
                          utc=pytz.utc,
                          eastern=pytz.timezone('US/Eastern')
    ):
        converted_raw = DT.datetime.fromtimestamp(milliseconds/1000.0)
        date_eastern = eastern.localize(converted_raw, is_dst=True)
        date_utc = date_eastern.astimezone(utc)
        timestamp = ...
        return int(timestamp) * 1000
    

    For example, with Python2.7,

    import datetime as DT
    import pytz
    
    def convert_mills_GMT(milliseconds, 
                          utc=pytz.utc,
                          eastern=pytz.timezone('US/Eastern')
    ):
        converted_raw = DT.datetime.fromtimestamp(milliseconds/1000.0)
        date_eastern = eastern.localize(converted_raw, is_dst=True)
        date_utc = date_eastern.astimezone(utc)
        timestamp = ((date_utc.replace(tzinfo=None) - DT.datetime(1970, 1, 1))
                     .total_seconds())
        return int(timestamp) * 1000
    
    print(DT.datetime.utcfromtimestamp(convert_mills_GMT(1432202088224)/1000.0))
    

    prints

    2015-05-21 09:54:48