366

I've got a timedelta. I want the days, hours and minutes from that - either as a tuple or a dictionary... I'm not fussed.

I must have done this a dozen times in a dozen languages over the years but Python usually has a simple answer to everything so I thought I'd ask here before busting out some nauseatingly simple (yet verbose) mathematics.

Mr Fooz raises a good point.

I'm dealing with "listings" (a bit like ebay listings) where each one has a duration. I'm trying to find the time left by doing when_added + duration - now

Am I right in saying that wouldn't account for DST? If not, what's the simplest way to add/subtract an hour?

Oli
  • 235,628
  • 64
  • 220
  • 299
  • 8
    If it is just to get it as a *string* in HH:mm:ss format, say *"0:19:37"* from the `timedelta` object *"datetime.timedelta(seconds=1177)"*: simply [use str()](https://stackoverflow.com/questions/14190045/how-to-convert-datetime-timedelta-to-minutes-hours-in-python/43965102#43965102) – Peter Mortensen Oct 24 '18 at 08:24

14 Answers14

503

If you have a datetime.timedelta value td, td.days already gives you the "days" you want. timedelta values keep fraction-of-day as seconds (not directly hours or minutes) so you'll indeed have to perform "nauseatingly simple mathematics", e.g.:

def days_hours_minutes(td):
    return td.days, td.seconds//3600, (td.seconds//60)%60
Kurt Peek
  • 52,165
  • 91
  • 301
  • 526
Alex Martelli
  • 854,459
  • 170
  • 1,222
  • 1,395
  • 4
    Keep in mind that if `td` is negative, `-1 // 3600` is `-1`. – jcrs Oct 15 '18 at 05:23
  • 12
    why // ? instead of / – Mohammed Shareef C Apr 01 '19 at 08:03
  • 31
    `//` is used instead of `/` to get an integer, not a float. `//` is a floor division operator: "division that results into whole number adjusted to the left in the number line", see [here](https://www.programiz.com/python-programming/operators). – Eerik Sven Puudist Apr 01 '19 at 19:51
  • 2
    @EerikSvenPuudist as `jcrs` pointed out, `-1//3600` is `-1`, so why not use `int(td.seconds/3600)` instead to turn `-1` second into `0` hours rather than `1` hour backwards? – simpleuser Sep 05 '19 at 20:19
  • 6
    how about adding the remaining seconds as well ? `days, hours, minutes = x.days, x.seconds // 3600, x.seconds %3600//60` and then `seconds = x.seconds - hours*3600 - minutes*60` – raphael Mar 24 '20 at 18:19
  • 47
    WHY are these not in the standard library? `days` and `seconds` are available but `hours` and `minutes` not? srsly? Some batteries were not included. `` – 3dGrabber Jan 28 '21 at 16:09
  • Keep in mind that you **need** to use days, one way or another, because hours going above 24 resets/wraps back to 0! – Andrew Jun 22 '23 at 21:32
  • Note also that as jcrs pointed out this breaks with `-1`, and that Python's `%` doesn't work like it should for negatives... It does not work the same as C's `fmod()`, like you would expect, and like most calculators are going to do! So I recommend using Python's `math.fmod()`, with `int()` if you need to, instead of `%`... https://docs.python.org/3/library/math.html#math.fmod I've concluded that for my purposes, for the most part, I'd rather just not use `timedelta` at all... – Andrew Jun 23 '23 at 20:29
103

This is a bit more compact, you get the hours, minutes and seconds in two lines.

days = td.days
hours, remainder = divmod(td.seconds, 3600)
minutes, seconds = divmod(remainder, 60)
# If you want to take into account fractions of a second
seconds += td.microseconds / 1e6
ryanjdillon
  • 17,658
  • 9
  • 85
  • 110
mberacochea
  • 1,716
  • 1
  • 12
  • 18
38
days, hours, minutes = td.days, td.seconds // 3600, td.seconds // 60 % 60

As for DST, I think the best thing is to convert both datetime objects to seconds. This way the system calculates DST for you.

>>> m13 = datetime(2010, 3, 13, 8, 0, 0)  # 2010 March 13 8:00 AM
>>> m14 = datetime(2010, 3, 14, 8, 0, 0)  # DST starts on this day, in my time zone
>>> mktime(m14.timetuple()) - mktime(m13.timetuple())     # difference in seconds
82800.0
>>> _/3600                                                # convert to hours
23.0
Dav Clark
  • 1,430
  • 1
  • 13
  • 26
Jason Orendorff
  • 42,793
  • 6
  • 62
  • 96
  • 2
    `mktime()` may fail if the local timezone may have a different utc offset at different times (many do) -- you could use `pytz` module to get access to the tz database in a portable deteministic way. Also, local time may be ambiguous (50% chances of an error) -- you need some additional info to disambiguate e.g., often (not always) [dates in a log file are monotonous](http://stackoverflow.com/a/26221183/4279). See [How can I subtract a day from a python date?](http://stackoverflow.com/q/441147/4279) that may have to deal with similar issues. – jfs May 09 '15 at 10:34
13

For all coming along and searching for an implementation:

The above posts are related to datetime.timedelta, which is sadly not having properties for hours and seconds. So far it was not mentioned, that there is a package, which is having these. You can find it here:

Example - Calculation:

>>> import timedelta

>>> td = timedelta.Timedelta(days=2, hours=2)

# init from datetime.timedelta
>>> td = timedelta.Timedelta(datetime1 - datetime2)

Example - Properties:

>>> td = timedelta.Timedelta(days=2, hours=2)
>>> td.total.seconds
180000
>>> td.total.minutes
3000
>>> td.total.hours
50
>>> td.total.days
2

I hope this could help someone...

SiL3NC3
  • 690
  • 6
  • 29
  • 2
    This would also get around the limitation of timedelta, in that it depends on C. Thus, it cannot handle even moderately large times. It can only deal with day counts that are less than 1 billion, and if you use a value larger than +2G, you get a type conversion error as the underlying type appears to be just a 32-bit signed integer. – jakobengblom2 Sep 09 '21 at 08:57
9

I don't understand

days, hours, minutes = td.days, td.seconds // 3600, td.seconds // 60 % 60

how about this

days, hours, minutes = td.days, td.seconds // 3600, td.seconds % 3600 / 60.0

You get minutes and seconds of a minute as a float.

campbell
  • 147
  • 2
  • 8
7

I used the following:

delta = timedelta()
totalMinute, second = divmod(delta.seconds, 60)
hour, minute = divmod(totalMinute, 60)
print(f"{hour}h{minute:02}m{second:02}s")
Mathieu CAROFF
  • 1,230
  • 13
  • 19
5

Here is a little function I put together to do this right down to microseconds:

def tdToDict(td:datetime.timedelta) -> dict:
    def __t(t, n):
        if t < n: return (t, 0)
        v = t//n
        return (t -  (v * n), v)
    (s, h) = __t(td.seconds, 3600)
    (s, m) = __t(s, 60)    
    (micS, milS) = __t(td.microseconds, 1000)

    return {
         'days': td.days
        ,'hours': h
        ,'minutes': m
        ,'seconds': s
        ,'milliseconds': milS
        ,'microseconds': micS
    }

Here is a version that returns a tuple:

# usage: (_d, _h, _m, _s, _mils, _mics) = tdTuple(td)
def tdTuple(td:datetime.timedelta) -> tuple:
    def _t(t, n):
        if t < n: return (t, 0)
        v = t//n
        return (t -  (v * n), v)
    (s, h) = _t(td.seconds, 3600)
    (s, m) = _t(s, 60)    
    (mics, mils) = _t(td.microseconds, 1000)
    return (td.days, h, m, s, mics, mils)
Timothy C. Quinn
  • 3,739
  • 1
  • 35
  • 47
5

While pandas.Timedelta does not provide these attributes directly, it indeed provide a method called total_seconds, based on which days, hours, and minutes can be easily derived:

import pandas as pd
td = pd.Timedelta("2 days 12:30:00")
minutes = td.total_seconds()/60
hours = minutes/60
days = hours/ 24
print(minutes, hours, days)
Fei Yao
  • 1,502
  • 10
  • 10
  • 1
    This is available even without pandas, in the standard library. https://docs.python.org/3/library/datetime.html#datetime.timedelta.total_seconds – redbeam_ Feb 17 '23 at 20:45
  • Finally the best answer. It is most important for every other answer to mention the total_seconds() property! Otherwise you get weird numbers that are not exactly the seconds. – Slav Kirilov Jul 03 '23 at 12:20
2

I found the easiest way is using str(timedelta). It will return a sting formatted like 3 days, 21:06:40.001000, and you can parse hours and minutes using simple string operations or regular expression.

2

While if you are using python datetime package, you can also code like below:

import datetime
tap_in = datetime.datetime.strptime("04:12", "%H:%M")
tap_out = datetime.datetime.strptime("18:20", "%H:%M")
num_of_hour = (tap_out - tap_in).total_seconds()/3600
num_of_hour # 14.133333333333333
Question-er XDD
  • 640
  • 3
  • 12
  • 27
0

This is another possible approach, though a bit wordier than those already mentioned. It maybe isn't the best approach for this scenario but it is handy to be able to obtain your time duration in a specific unit that isn't stored within the object (weeks, hours, minutes, milliseconds) and without having to remember or calculate conversion factors.

from datetime import timedelta
one_hour = timedelta(hours=1)
one_minute = timedelta(minutes=1)
print(one_hour/one_minute)  # Yields 60.0

I've got a timedelta. I want the days, hours and minutes from that - either as a tuple or a dictionary... I'm not fussed.

in_time_delta = timedelta(days=2, hours=18, minutes=30)
td_d = timedelta(days=1)
td_h = timedelta(hours=1)
td_m = timedelta(minutes=1)
dmh_list = [in_time_delta.days,
            (in_time_delta%td_d)//td_h,
            (in_time_delta%td_h)//td_m]

Which should assign [2, 18, 30] to dmh_list

jslatane
  • 300
  • 5
  • 15
0

If using pandas (at least version >1.0), the Timedelta class has a components attribute that returns a named tuple with all the fields nicely laid out.

e.g.

import pandas as pd
delta = pd.Timestamp("today") - pd.Timestamp("2022-03-01")
print(delta.components)
Adrian
  • 755
  • 9
  • 17
0
timedelta = pd.Timestamp("today") - pd.Timestamp("2022-01-01")
print(timedelta.components)
print(timedelta.components.days)
print(timedelta.components.seconds)

will return something like:

Components(days=281, hours=2, minutes=24, seconds=3, milliseconds=72, microseconds=493, nanoseconds=0) 
281
3
Alfonso_MA
  • 537
  • 5
  • 26
-5

timedeltas have a days and seconds attribute .. you can convert them yourself with ease.

Jochen Ritzel
  • 104,512
  • 31
  • 200
  • 194