Search code examples
pythondatetimetimezonetimedelta

Calculating the difference between two datetimes?


from datetime import datetime
import pytz

cur_time = datetime.now()
cur_time = pytz.utc.localize(cur_time)

rr_name = 'xx_20211005141746.txt'
rr_time =  re.search('_(.*).txt', rr_name).group(1)
rr_time =  datetime.strptime(rr_time, '%Y%m%d%H%M%S')

tzone = "Europe/London"
hb_time = pytz.utc.localize(rr_time).astimezone(pytz.timezone(tzone))
                    
diff = cur_time - hb_time

l_heared = round((diff.total_seconds() / 60), 2) 

The difference between cur_time and rr_time is around 6 minutes, which is 360 seconds. How comes delta gives me back 77.65 seconds...

Please note cur_time and rr_name are native datetimes


Solution

  • You'll have to set the correct time zone for date/time stored in the filename directly, don't set it to UTC first and then convert:

    import re
    from datetime import datetime
    from zoneinfo import ZoneInfo # Python 3.9+ ; there is backports.zoneinfo for older versions
    # import pytz
    
    now = datetime.now(ZoneInfo('UTC'))
    # now = datetime.now(pytz.UTC)
    print(now.astimezone(ZoneInfo('Europe/London')))
    # 2021-10-05 14:58:43.957950+01:00
    
    rr_name = 'xx_20211005141746.txt' # time zone Europe/London in filename ...
    rr_time =  re.search('_(.*).txt', rr_name).group(1)
    rr_time =  datetime.strptime(rr_time, '%Y%m%d%H%M%S').replace(tzinfo=ZoneInfo('Europe/London'))
    # rr_time = pytz.timezone('Europe/London').localize(datetime.strptime(rr_time, '%Y%m%d%H%M%S'))
    print(rr_time)
    # 2021-10-05 14:17:46+01:00
    
    l_heared = round(((now-rr_time).total_seconds() / 60), 2) 
    print(l_heared, "minutes ago")
    # 40.97 minutes ago
    

    when this will fail

    actually, since the filename's timestamp does not provide a UTC offset, this will fail for a DST transition summer -> winter time because the wall clock would show 1 AM for two hours! Ex:

    from datetime import datetime, timedelta
    from zoneinfo import ZoneInfo
    
    tz = ZoneInfo("Europe/London")
    tzutc = ZoneInfo("UTC")
    
    dt = [datetime(2021, 10, 30, 23, tzinfo=tzutc) + timedelta(hours=i) for i in range(4)]
    dt = [d.astimezone(tz) for d in dt]
    
    for d in dt: print(d)
    # 2021-10-31 00:00:00+01:00
    # 2021-10-31 01:00:00+01:00
    # 2021-10-31 01:00:00+00:00 # DST change, 1 AM appears for two hours!
    # 2021-10-31 02:00:00+00:00
    
    t_ref = datetime(2021, 10, 31, 3, tzinfo=tzutc)
    for t in dt: print(f"{t} -> age: {t_ref-t}")
    # 2021-10-31 00:00:00+01:00 -> age: 4:00:00
    # 2021-10-31 01:00:00+01:00 -> age: 3:00:00
    # 2021-10-31 01:00:00+00:00 -> age: 2:00:00 # only correct because input is aware datetime with correct UTC offset
    # 2021-10-31 02:00:00+00:00 -> age: 1:00:00
    
    # ---------- DST winter -> summer works fine though: ----------
    
    dt = [datetime(2021, 3, 28, tzinfo=tzutc) + timedelta(hours=i) for i in range(4)]
    dt = [d.astimezone(tz) for d in dt]
    
    for d in dt: print(d)
    # 2021-03-28 00:00:00+00:00
    # 2021-03-28 02:00:00+01:00 # DST change, 1 AM is skipped
    # 2021-03-28 03:00:00+01:00
    # 2021-03-28 04:00:00+01:00
    
    t_ref = datetime(2021, 3, 28, 3, tzinfo=tzutc)
    for t in dt: print(f"{t} -> age: {t_ref-t}")
    # 2021-03-28 00:00:00+00:00 -> age: 3:00:00
    # 2021-03-28 02:00:00+01:00 -> age: 2:00:00 # this time no ambiguity
    # 2021-03-28 03:00:00+01:00 -> age: 1:00:00
    # 2021-03-28 04:00:00+01:00 -> age: 0:00:00