pythondatetimetimezonepython-datetimepytz

In python (pytz), how can I add a "day" to a datetime in a DST-aware fashion? (Sometimes the result should be 23 or 25 hours in in the future)


I'm doing some datetime math in python with the pytz library (although I'm open to using other libraries if necessary). I have an iterator that needs to increase by one day for each iteration of the loop. The problem comes when transitioning from November 3rd to November 4th in the Eastern timezone, which crosses the daylight saving boundary (there are 25 hours between the start of November 3rd and the start of November 5th, instead of the usual 24). Whenever I add a "day" that crosses the boundary, I get a time that is 24 hours in the future, instead of the expected 25.

This is what I've tried:

import datetime  
import pytz  
ET = pytz.timezone("US/Eastern")  
first_day = ET.localize(datetime.datetime(2024, 11, 3))  
next_day = first_day + datetime.timedelta(days=1)  
  
first_day.isoformat()  # '2024-11-03T00:00:00-04:00'  
next_day.isoformat()  # '2024-11-04T00:00:00-04:00'  
  
assert next_day == ET.localize(datetime.datetime(2024, 11, 4))  # This fails!! 
# I want next_day to be '2024-11-04T00:00:00-05:00' or '2024-11-04T01:00:00-04:00'

I also tried throwing a normalize() in there, but that didn't produce the right result either:

ET.normalize(next_day).isoformat()  # '2024-11-03T23:00:00-05:00'

(That's one hour earlier than my desired output)

I suppose I could make a copy of my start_day that increments the day field, but then I'd have to be aware of month and year boundaries, which doesn't seem ideal to me.


Solution

  • It looks like you want "wall time", which is the same "wall clock" time the next day, regardless of daylight savings time transitions. I would use the built-in zoneinfo module. You may need to install the "1st party" tzdata module if using Windows to have up-to-date time zone information (pip install tzdata):

    import datetime as dt
    import zoneinfo as zi
    
    ET = zi.ZoneInfo('US/Eastern')
    first_day = dt.datetime(2024, 11, 3, tzinfo=ET)  # time zone aware
    next_day = first_day + dt.timedelta(days=1)      # "wall clock" math in time zone
    
    print(first_day)
    print(next_day)
    
    assert next_day == dt.datetime(2024, 11, 4, tzinfo=ET)
    

    Output:

    2024-11-03 00:00:00-04:00
    2024-11-04 00:00:00-05:00
    

    Note that if instead you want exactly 24 hours later regardless of DST transitions, do the time delta math in UTC:

    import datetime as dt
    import zoneinfo as zi
    
    ET = zi.ZoneInfo("US/Eastern")  
    first_day = dt.datetime(2024, 11, 3, tzinfo=ET)  # time zone aware
    
    # Convert to UTC, do math, convert back to desired time zone
    next_day = (first_day.astimezone(dt.UTC) + dt.timedelta(days=1)).astimezone(ET)
    
    print(first_day)
    print(next_day)
    

    Output:

    2024-11-03 00:00:00-04:00
    2024-11-03 23:00:00-05:00