173

I have two datetime.time values, exit and enter and I want to do something like:

duration = exit - enter

However, I get this error:

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

How do I do this correctly? One possible solution is converting the time variables to datetime variables and then subtruct, but I'm sure you guys must have a better and cleaner way.

FObersteiner
  • 22,500
  • 8
  • 42
  • 72
Chaggster
  • 2,441
  • 3
  • 21
  • 14

11 Answers11

128

Try this:

from datetime import datetime, date

datetime.combine(date.today(), exit) - datetime.combine(date.today(), enter)

combine builds a datetime, that can be subtracted.

bstpierre
  • 30,042
  • 15
  • 70
  • 103
gruszczy
  • 40,948
  • 31
  • 128
  • 181
  • 4
    To @IgnacioVazquez-Abrams point, one could use, say,`datetime(1,1,1,0,0,0)` instead of `date.today()`. – Akavall Mar 07 '14 at 18:54
  • 49
    Don't name a `datetime` `exit`, since `exit` is a built-in function. – orokusaki Sep 30 '15 at 03:28
  • 1
    using python 2.7, I don't have `combine` method in my `datetime` module: `AttributeError: 'module' object has no attribute 'combine'` – mtoloo Jul 28 '16 at 12:32
  • 11
    mtoloo: There is `datetime` module and it has a `datetime` object inside. The object inside has `combine` method. If you are simply importing `datetime` (like this: `import datetime`), then what you need to do later is this `datetime.datetime.combine`. – gruszczy Jul 28 '16 at 15:37
  • or date.min -> datetime.date(1, 1, 1) – Sean Aug 01 '16 at 08:14
  • 9
    There is a very rare case when you call this function at some time like 23:59:59:9999. In that case, the `date.today()` before and the `date.today()` later will return a different value. It would be better to give the value `date.today()` to a variable. – ramwin Mar 22 '17 at 05:44
  • If the purpose is to determine the duration from time X to time Y, with the understanding that "time Y" is *the next time* that the clock shows that time, be careful that the time could pass midnight in between X and Y. If the above calculation gives a negative result, depending on your application, it may be correct to add 24 hours. – Karl Knechtel Sep 08 '22 at 05:46
69

Use:

from datetime import datetime, date

duration = datetime.combine(date.min, end) - datetime.combine(date.min, beginning)

Using date.min is a bit more concise and works even at midnight.

This might not be the case with date.today() that might return unexpected results if the first call happens at 23:59:59 and the next one at 00:00:00.

alexpirine
  • 3,023
  • 1
  • 26
  • 41
54

instead of using time try timedelta:

from datetime import timedelta

t1 = timedelta(hours=7, minutes=36)
t2 = timedelta(hours=11, minutes=32)
t3 = timedelta(hours=13, minutes=7)
t4 = timedelta(hours=21, minutes=0)

arrival = t2 - t1
lunch = (t3 - t2 - timedelta(hours=1))
departure = t4 - t3

print(arrival, lunch, departure)
Edmilson Junior
  • 664
  • 5
  • 6
  • 5
    Thanks. This is very clean and better than all the answers here - https://stackoverflow.com/questions/3096953/how-to-calculate-the-time-interval-between-two-time-strings/3096984 – Pushpak Dagade Mar 24 '18 at 07:02
  • 2
    Note that `timedelta` conceptually represents a span of time, not a time of day. – Karl Knechtel Sep 08 '22 at 05:48
  • Note that`timedelta` conceptually represents a span of time and not a point in time, which makes it the correct thing to use here, as we are implicitly talking about the time that has passed (a delta of time) since the start of the day (a point in time). – Bengt Feb 09 '23 at 17:21
10

You have two datetime.time objects so for that you just create two timedelta using datetime.timedetla and then substract as you do right now using "-" operand. Following is the example way to substract two times without using datetime.

enter = datetime.time(hour=1)  # Example enter time
exit = datetime.time(hour=2)  # Example start time
enter_delta = datetime.timedelta(hours=enter.hour, minutes=enter.minute, seconds=enter.second)
exit_delta = datetime.timedelta(hours=exit.hour, minutes=exit.minute, seconds=exit.second)
difference_delta = exit_delta - enter_delta

difference_delta is your difference which you can use for your reasons.

Hardik Patel
  • 483
  • 4
  • 10
9

The python timedelta library should do what you need. A timedelta is returned when you subtract two datetime instances.

import datetime
dt_started = datetime.datetime.utcnow()

# do some stuff

dt_ended = datetime.datetime.utcnow()
print((dt_ended - dt_started).total_seconds())
Ben
  • 1,620
  • 18
  • 11
7

datetime.time can not do it - But you could use datetime.datetime.now()

start = datetime.datetime.now()
sleep(10)
end = datetime.datetime.now()
duration = end - start
gies0r
  • 4,723
  • 4
  • 39
  • 50
5

datetime.time does not support this, because it's nigh meaningless to subtract times in this manner. Use a full datetime.datetime if you want to do this.

Ignacio Vazquez-Abrams
  • 776,304
  • 153
  • 1,341
  • 1,358
  • 73
    If you know from your domain that two `datetime.time` objects `a` and `b` are from the same day, and that `b > a`, then the operation `b - a` has perfect meaning. – swalog Feb 05 '14 at 13:51
  • 5
    Even if they aren't the same day, it still makes fine sense. At least as much sense as `arctan(0) = (0, pi, 2pi, ...)`, but we just don't care about any of those values after the first. So, `4:00 - 20:00` is `8:00` - it's also (`32:00`, `56:00`, ... ), but who cares? – naught101 Mar 10 '15 at 23:34
  • 1
    [Unless it's one of those cases that doesn't.](https://www.youtube.com/watch?v=-5wpm-gesOY) – Ignacio Vazquez-Abrams Mar 11 '15 at 09:35
  • 12
    Furthermore, Python doesn't even stay consistent. If a - b is meaningless for two time objects, then how it that a > b or a < b behaves as it does? The comparison is already assuming same day, otherwise it is completely broken. Since the assumption is made for comparison, it is entirely consistent to make the same assumption for difference operations. – Sean Aug 01 '16 at 08:10
  • 2
    Saying it's meaningless is subjective, but I see where you're coming from. Adding two times together seems to be a meaningless representation to me. I think subtracting two time values should return a timedelta. If that timedelta happens to be negative, so be it. What I want to know is why python can't add or subtract a timedelta from a time to return a new time value. – aj.toulan Jan 04 '18 at 05:10
4
import datetime

def diff_times_in_seconds(t1, t2):
    # caveat emptor - assumes t1 & t2 are python times, on the same day and t2 is after t1
    h1, m1, s1 = t1.hour, t1.minute, t1.second
    h2, m2, s2 = t2.hour, t2.minute, t2.second
    t1_secs = s1 + 60 * (m1 + 60*h1)
    t2_secs = s2 + 60 * (m2 + 60*h2)
    return( t2_secs - t1_secs)

# using it
diff_times_in_seconds( datetime.datetime.strptime( "13:23:34", '%H:%M:%S').time(),datetime.datetime.strptime( "14:02:39", '%H:%M:%S').time())
jacanterbury
  • 1,435
  • 1
  • 26
  • 36
3

timedelta accepts negative(-) time values. so it could be simple as below.

Answer (single line)

datetime.timedelta(hours=exit.hour-enter.hour, minutes=exit.minute-enter.minute)

Run test

import datetime

enter = datetime.time(hour=1, minute=30)
exit = datetime.time(hour=2, minute=0)
duration = datetime.timedelta(hours=exit.hour-enter.hour, minutes=exit.minute-enter.minute)

>>> duration
datetime.timedelta(seconds=1800)
kochul
  • 611
  • 6
  • 12
2

I had similar situation as you and I ended up with using external library called arrow.

Here is what it looks like:

>>> import arrow
>>> enter = arrow.get('12:30:45', 'HH:mm:ss')
>>> exit = arrow.now()
>>> duration = exit - enter
>>> duration
datetime.timedelta(736225, 14377, 757451)
Daniel Serodio
  • 4,229
  • 5
  • 37
  • 33
rominf
  • 2,719
  • 3
  • 21
  • 39
  • 1
    Here you are just demonstrating subtracting complete datetime objects, not times-of-day as in the original question – kleptog Oct 29 '19 at 13:27
2
import time
from datetime import datetime

def calcTime(enter,exit):
    format="%H:%M:%S"
    #Parsing the time to str and taking only the hour,minute,second 
    #(without miliseconds)
    enterStr = str(enter).split(".")[0]
    exitStr = str(exit).split(".")[0]
    #Creating enter and exit time objects from str in the format (H:M:S)
    enterTime = datetime.strptime(enterStr, format)
    exitTime = datetime.strptime(exitStr, format)
    return exitTime - enterTime

enter = datetime.today().time()
#Sleeping for 5 seconds before initializing the exit variable
time.sleep(5)
exit = datetime.today().time()
duration = calcTime(enter,exit)
print(f"Duration is {duration} (Hours:Minutes:Seconds)")
#Output: Duration is 0:00:05 (Hours:Minutes:Seconds)

If it is helpful, you can use the calcTime function as shown.

Nir T
  • 21
  • 2