Zahra
Zahra

Reputation: 7197

TypeError: unsupported operand type(s) for -: 'datetime.time' and 'datetime.timedelta'

Python datetime does not allow subtracting timedelta from time; I suppose that's because timedelta can span over days and time is only within 24 hrs (hh:mm:ss.ns).

Now the question is if there is a data structure similar to timedelta that allows subtraction directly from time?

I understand that I can convert my datetime.time to datetime.timedelta to enable subtraction from another datetime.timedelta. BUT I'm trying to avoid casting time to timedelta because I'm reading times from a large file and it's expensive to cast every time to timedelta. However, it is a one time operation to simply replace timedelta with some_datastructure (if that sth exist) and subtract directly from time.

example:

t1 = datetime.time(hour=1, minute=1, second=1)
# t1 = datetime.timedelta(hours=t1.hour, minutes=t1.minute, seconds=t1.second)
t_diff = datetime.timedelta(hours=0, minutes=0, seconds=1)
print(t1-t_diff)

**p.s. how many times did I use the word time?!

Upvotes: 2

Views: 3029

Answers (1)

FObersteiner
FObersteiner

Reputation: 25544

a conversion to datetime.datetime instead of datetime.timedelta would be more efficient:

from random import randrange
from datetime import datetime, time, timedelta

# some random times:
N = int(1e6)
ts = [time(randrange(23),randrange(59),randrange(59),randrange(999999)) for _ in range(N)]

# to timedelta could look like
td = [timedelta(0, 0, (t.hour*60*60*1e6+t.minute*60*1e6+t.second*1e6+t.microsecond)) for t in ts]

# %timeit [timedelta(0, 0, (t.hour*60*60*1e6+t.minute*60*1e6+t.second*1e6+t.microsecond)) for t in ts]
# 856 ms ± 903 µs per loop (mean ± std. dev. of 7 runs, 1 loop each)

# to datetime could look like
today = datetime.now().date()
dt = [datetime.combine(today, t) for t in ts]

# %timeit [datetime.combine(today, t) for t in ts]
# 201 ms ± 1.85 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

# now you'd do something with dt
ts_new = [(d + timedelta(seconds=5)).time() for d in dt]

# %timeit [(d + timedelta(seconds=5)).time() for d in dt]
# 586 ms ± 1.68 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

But still - for the 1M element example, the timeits show that we're definitely falling into the critical >1ms range. If you want to go faster, I guess you'll have to use another time format in the first place, such as for example integer (micro)seconds since midnight.

Upvotes: 1

Related Questions