pythondatetimedst

creating datetime from date string plus hour offset not working across DST boundary


I'm in the early stages of learning Python, and I'm struggling to get DST time changes handled properly. I have CSV data that has a date column and an hour column and I am trying to create a timezone-aware datetime from that. The CSV data is in US Eastern Standard Time (prevailing) but the data itself does not contain this information.

So for November 3, 2024 (the date of the fall time change), we'll have 25 hours. My date field will simply be '2024-11-03' and the hours field will range from 0 to 24.

Using the content from Specify timezone when loading using datetime.strptime, I tried this:

for i in range(25):
    dt = datetime.fromisoformat('2024-11-03').replace(tzinfo=ZoneInfo("America/New_York"))
    dt += timedelta(hours=i)
    print(dt.timestamp() , '->' , dt.strftime('%Y-%m-%d %H:%M:%S %z'))

which results in:

1730606400.0 -> 2024-11-03 00:00:00 -0400
1730610000.0 -> 2024-11-03 01:00:00 -0400
1730617200.0 -> 2024-11-03 02:00:00 -0500
1730620800.0 -> 2024-11-03 03:00:00 -0500
...
1730692800.0 -> 2024-11-03 23:00:00 -0500
1730696400.0 -> 2024-11-04 00:00:00 -0500

Not quite right.

If I do the almost identical code in PHP (which I'm proficient in) I get the expected result:

1730606400 -> 2024-11-03T00:00:00-04:00
1730610000 -> 2024-11-03T01:00:00-04:00
1730613600 -> 2024-11-03T01:00:00-05:00
1730617200 -> 2024-11-03T02:00:00-05:00
...
1730689200 -> 2024-11-03T22:00:00-05:00
1730692800 -> 2024-11-03T23:00:00-05:00

So clearly my method here in Python doesn't like the time change itself. So I tried converting to timestamps and manually adding seconds, but it doesn't work either:

for i in range(25):
    dt = datetime.fromisoformat('2024-11-03').replace(tzinfo=ZoneInfo("America/New_York"))
    dt = dt.fromtimestamp(dt.timestamp() + i*3600).replace(tzinfo=ZoneInfo("America/New_York"))
    print(dt.timestamp() , '->' , dt.strftime('%Y-%m-%d %H:%M:%S %z'))

I also saw this: Converting 25 hours (DST) to datetime, but it didn't seem to solve my issue either.

I'm sure there must be a simple solution here...

EDIT: in addition to Michael Cao's solution, which I accepted, I found another using pytz, in case this helps anyone else:

from datetime import datetime, timedelta
from pytz import timezone

eastern = timezone('America/New_York')
for i in range(25):
    dt = eastern.normalize(eastern.localize(datetime.fromisoformat('2024-11-03')) + timedelta(hours=i))
    print(dt.timestamp() , '->' , dt.strftime('%Y-%m-%d %H:%M:%S %z'))

Solution

  • I found success in converting the datetime to utc first then adding the timedelta then converting back to EST.

    from datetime import datetime, timedelta
    from zoneinfo import ZoneInfo
    
    for i in range(25):
        dt = datetime.fromisoformat('2024-11-03').replace(tzinfo=ZoneInfo("America/New_York"))
        dt = dt.astimezone()
        dt += timedelta(hours=i)
        dt = dt.astimezone(ZoneInfo("America/New_York"))
        print(dt.timestamp() , '->' , dt.strftime('%Y-%m-%d %H:%M:%S %z'))
    

    Output:

    1730606400.0 -> 2024-11-03 00:00:00 -0400
    1730610000.0 -> 2024-11-03 01:00:00 -0400
    1730613600.0 -> 2024-11-03 01:00:00 -0500
    1730617200.0 -> 2024-11-03 02:00:00 -0500
    1730620800.0 -> 2024-11-03 03:00:00 -0500
    ...
    1730692800.0 -> 2024-11-03 23:00:00 -0500