Search code examples
pythondatetimepytz

Incorrect datetime conversion from America/New_York time to UTC and back to America/New_York


I am trying to convert time from America/New York to UTC and then converting it back to the New York time. But I get different results while doing with this with pytz.

I am doing this:

new_date = parser.parse("May 4, 2021")
new_date = new_date.replace(tzinfo=pytz.timezone("America/New_York"))
date = new_date.astimezone(pytz.timezone("UTC"))     

Output:

datetime.datetime(2021, 5, 4, 4, 56, tzinfo=<UTC>)

When I try to reconvert it back to the New York time I get this:

date.astimezone(pytz.timezone("America/New_York"))

I get:

datetime.datetime(2021, 5, 4, 0, 56, tzinfo=<DstTzInfo 'America/New_York' EDT-1 day, 20:00:00 DST>)

My Question is why there is 56 minute difference and what can be done to prevent this?


Solution

  • The 56 min difference originates from the fact that the first entry in the database that pytz accesses, refers to LMT (local mean time):

    import pytz
    t = pytz.timezone("America/New_York")
    print(repr(t))
    # <DstTzInfo 'America/New_York' LMT-1 day, 19:04:00 STD>
    

    You can read more in P. Ganssle's Fastest Footgun blog post or in Weird timezone issue with pytz here on SO.

    tl;dr - Never replace a tzinfo with a timezone object from pytz! Use localize (or astimezone) instead, to adjust the timezone to the year of the datetime object.

    ...But: since you use dateutil already, why don't you use it here as well:

    import dateutil
    
    new_date = dateutil.parser.parse("May 4, 2021")
    # you can safely replace with dateutil's tz objects:
    new_date = new_date.replace(tzinfo=dateutil.tz.gettz("America/New_York"))
    date = new_date.astimezone(dateutil.tz.UTC)   
    
    # date
    # datetime.datetime(2021, 5, 4, 4, 0, tzinfo=tzutc())
    
    date = date.astimezone(dateutil.tz.gettz("America/New_York"))
    # date
    # datetime.datetime(2021, 5, 4, 0, 0, tzinfo=tzfile('US/Eastern'))
    print(date)
    # 2021-05-04 00:00:00-04:00