176

I'm writing a function that needs to parse string to a timedelta. The user must enter something like "32m" or "2h32m", or even "4:13" or "5hr34m56s"... Is there a library or something that has this sort of thing already implemented?

1
  • 1
    For people just looking to construct a timedelta object of d days, h hours, m minutes and s seconds using one line (after importing datetime): datetime.timedelta(days = d, hours = h, minutes=m, seconds=s). Commented Apr 18, 2017 at 22:09

13 Answers 13

164

To me the most elegant solution, without having to resort to external libraries such as dateutil or manually parsing the input, is to use datetime's powerful strptime string parsing method.

from datetime import datetime, timedelta
# we specify the input and the format...
t = datetime.strptime("05:20:25","%H:%M:%S")
# ...and use datetime's hour, min and sec properties to build a timedelta
delta = timedelta(hours=t.hour, minutes=t.minute, seconds=t.second)

After this you can use your timedelta object as normally, convert it to seconds to make sure we did the correct thing etc.

print(delta)
assert(5*60*60+20*60+25 == delta.total_seconds())
Sign up to request clarification or add additional context in comments.

8 Comments

Note this approach only works if the timespan is less than 24 hours (datetime.strptime("32:20:25","%H:%M:%S") doesn't work), and you have to know the exact input format.
@verdesmarald So, as of python 3.5, is there an elegant solution without using external libraries and without assuming timespan is less than 24 hours?
I find the need to manually specify the named parameters for the timedelta parameter pretty annoying, but the best I can come up with for avoiding this is: delta = t - datetime.combine(t.date(), time.min), which is...horrible.
A serious problem with this approach is that if you include days then sending %d into strptime, will not enable you to input day 0, as only days of >=1 are valid for a date.
Mention of dateutil is an unnecessary distraction. dateutil.parse.parse doesn't support timedelta objects.
|
126

I had a bit of time on my hands yesterday, so I developed @virhilo's answer into a Python module, adding a few more time expression formats, including all those requested by @priestc.

Source code is on github (MIT License) for anybody that wants it. It's also on PyPI:

pip install pytimeparse

Returns the time as a number of seconds:

>>> from pytimeparse.timeparse import timeparse
>>> timeparse('32m')
1920
>>> timeparse('2h32m')
9120
>>> timeparse('4:13')
253
>>> timeparse('5hr34m56s')
20096
>>> timeparse('1.2 minutes')
72

3 Comments

is there a Java/Scala equivalent?
@luca.giovagnoli In Scala you can use Duration class. Duration can be constructed from strings like '15 seconds', '4 minutes' etc.
As of 2025, last commit on github.com/wroberts/pytimeparse is 6 years old. Is there a similar alternative that is still maintained?
109

For the first format (5hr34m56s), you should parse using regular expressions

Here is re-based solution:

import re
from datetime import timedelta


regex = re.compile(r'((?P<hours>\d+?)hr)?((?P<minutes>\d+?)m)?((?P<seconds>\d+?)s)?')


def parse_time(time_str):
    parts = regex.match(time_str)
    if not parts:
        return
    parts = parts.groupdict()
    time_params = {}
    for name, param in parts.items():
        if param:
            time_params[name] = int(param)
    return timedelta(**time_params)


>>> from parse_time import parse_time
>>> parse_time('12hr')
datetime.timedelta(0, 43200)
>>> parse_time('12hr5m10s')
datetime.timedelta(0, 43510)
>>> parse_time('12hr10s')
datetime.timedelta(0, 43210)
>>> parse_time('10s')
datetime.timedelta(0, 10)
>>> 

4 Comments

I was thinking of some kind of function that could take anything you throw at it and still be able to handle converting to timedelta.
I added re based solution example:)
I don't see how dateutil.parser.parse can parse durations, seems like it always returns a datetime. What am I missing?
dateutil.parser.parse won't parse timedelta objects. It returns a datetime, and it would trigger an exception for strings like '28:32:11.10'.
28

If Pandas is already in your dependencies, it does this pretty well:

>>> import pandas as pd
>>> pd.Timedelta('5hr34m56s')
Timedelta('0 days 05:34:56')

>>> pd.Timedelta('2h32m')
Timedelta('0 days 02:32:00')

>>> pd.Timedelta('5hr34m56s')
Timedelta('0 days 05:34:56')

>>> # It is pretty forgiving:
>>> pd.Timedelta('2 days 24:30:00 10 sec')
Timedelta('3 days 00:30:10')

To convert to datetime.timedelta if you prefer that type:

>>> pd.Timedelta('1 days').to_pytimedelta()
datetime.timedelta(1)

Unfortunately this does not work though:

>>> pd.Timedelta('4:13')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "pandas\_libs\tslibs\timedeltas.pyx", line 1217, in 
pandas._libs.tslibs.timedeltas.Timedelta.__new__
  File "pandas\_libs\tslibs\timedeltas.pyx", line 454, in 
pandas._libs.tslibs.timedeltas.parse_timedelta_string
ValueError: expected hh:mm:ss format

Pandas actually has pretty extensive date and time tools even though that is not its main purpose.

To install Pandas:

# If you use pip
pip install pandas

# If you use conda
conda install pandas

1 Comment

If you are using pd can also use pd.to_timedelta
20

I've modified virhilo's nice answer with a few upgrades:

  • added a assertion that the string is a valid time string
  • replace the "hr" hour-indicator with "h"
  • allow for a "d" - days indicator
  • allow non-integer times (e.g. 3m0.25s is 3 minutes, 0.25 seconds)

.

import re
from datetime import timedelta


regex = re.compile(r'^((?P<days>[\.\d]+?)d)?((?P<hours>[\.\d]+?)h)?((?P<minutes>[\.\d]+?)m)?((?P<seconds>[\.\d]+?)s)?$')


def parse_time(time_str):
    """
    Parse a time string e.g. (2h13m) into a timedelta object.

    Modified from virhilo's answer at https://stackoverflow.com/a/4628148/851699

    :param time_str: A string identifying a duration.  (eg. 2h13m)
    :return datetime.timedelta: A datetime.timedelta object
    """
    parts = regex.match(time_str)
    assert parts is not None, "Could not parse any time information from '{}'.  Examples of valid strings: '8h', '2d8h5m20s', '2m4s'".format(time_str)
    time_params = {name: float(param) for name, param in parts.groupdict().items() if param}
    return timedelta(**time_params)

7 Comments

Great! I added " *" between the elements to also allow "1d 3h 5m"
@MarcelWaldvogel nice, if you copy the text of the new regex I'll add your answer in
@virhilo and Peter: My slight evolution on your code is here: github.com/zeitgitter/zeitgitterd/blob/master/zeitgitter/… . I presume it is OK to use your code. Do you have any preferences for the license? MIT, Apache, GPL, …?
Marcel, can you send me your address so I can sue? JK go ahead any license is fine.
Here is the new Regex; the difference is the " *"s: regex = re.compile(r'^((?P<days>[\.\d]+?)d)? *' r'((?P<hours>[\.\d]+?)h)? *' r'((?P<minutes>[\.\d]+?)m)? *' r'((?P<seconds>[\.\d]+?)s)?$')
|
19

I wanted to input just a time and then add it to various dates so this worked for me:

from datetime import datetime as dtt

time_only = dtt.strptime('15:30', "%H:%M") - dtt.strptime("00:00", "%H:%M")

4 Comments

dtt.strptime(myduration, "%H:%M:%S") - dtt(1900, 1, 1) also works...
got it. I wasn't sure dtt(1900,1,1) would work for every possible OS
best answer not requiring any external library or regex
For extracting timings out of a JSON dump, I used duration = datetime.strptime(value, "%H:%M:%S.%f") - datetime.strptime("0", "%S"). This is how Python saves these timedeltas to JSON (for durations less than one day, so typically anything like how a long a request took to process, etc).
9

Use isodate library to parse ISO 8601 duration string. For example:

isodate.parse_duration('PT1H5M26S')

Also see Is there an easy way to convert ISO 8601 duration to timedelta?

Comments

8

Django comes with the utility function parse_duration(). From the documentation:

Parses a string and returns a datetime.timedelta.

Expects data in the format "DD HH:MM:SS.uuuuuu" or as specified by ISO 8601 (e.g. P4DT1H15M20S which is equivalent to 4 1:15:20) or PostgreSQL's day-time interval format (e.g. 3 days 04:05:06).

1 Comment

For further information: Django's parse_duration() function uses regex match under the hood.
8

if you want to use : as separator, I use this function:

import re
from datetime import timedelta

def timedelta_parse(value):
    """
    convert input string to timedelta
    """
    value = re.sub(r"[^0-9:.]", "", value)
    if not value:
        return

    return timedelta(**{key:float(val)
                        for val, key in zip(value.split(":")[::-1], 
                                            ("seconds", "minutes", "hours", "days"))
               })

Examples:

In [4]: timedelta_parse("1:0:0:1")
Out[4]: datetime.timedelta(days=1, seconds=1)

In [5]: timedelta_parse("123.5")
Out[5]: datetime.timedelta(seconds=123, microseconds=500000)

In [6]: timedelta_parse("1:6:34:9.983")
Out[6]: datetime.timedelta(days=1, seconds=23649, microseconds=983000)

In [8]: timedelta_parse("23:45:00")
Out[8]: datetime.timedelta(seconds=85500)

2 Comments

It don't process microseconds after point
You were right: re.sub should leave dots in the string. I corrected the function, it should work now. thanks!
3

If you use Python 3 then here's updated version for Hari Shankar's solution, which I used:

from datetime import timedelta
import re

regex = re.compile(r'(?P<hours>\d+?)/'
                   r'(?P<minutes>\d+?)/'
                   r'(?P<seconds>\d+?)$')

def parse_time(time_str):
    parts = regex.match(time_str)
    if not parts:
        return
    parts = parts.groupdict()
    print(parts)
    time_params = {}
    for name, param in parts.items():
        if param:
            time_params[name] = int(param)
    return timedelta(**time_params)

Comments

1

Inspired by a few answers here, this is a version that fully covers the constructor of timedelta():

import re
from datetime import timedelta

TIMEDELTA_PATTERN = re.compile(
    r"""
        ^
        \s*
        ((?P<weeks>\d+(\.\d+)?)(w|\s*wks?|\s*weeks?))?
        \s*
        ((?P<days>\d+(\.\d+)?)(d|\s*days?))?
        \s*
        ((?P<hours>\d+(\.\d+)?)(h|\s*hrs?|\s*hours?))?
        \s*
        ((?P<minutes>\d+(\.\d+)?)(m|\s*mins?|\s*minutes?))?
        \s*
        ((?P<seconds>\d+(\.\d+)?)(s|\s*secs?|\s*seconds?))?
        \s*
        ((?P<milliseconds>\d+(\.\d+)?)(ms|\s*millis?|\s*milliseconds?))?
        \s*
        ((?P<microseconds>\d+(\.\d+)?)(us|\s*micros?|\s*microseconds?))?
        \s*
        $
    """,
    flags=re.IGNORECASE | re.VERBOSE,
)


def parse_timedelta(value: str) -> timedelta:
    match = TIMEDELTA_PATTERN.match(value)
    if not match:
        raise ValueError(f"Invalid timedelta: {value!r}")

    params = {u: float(v) for u, v in match.groupdict().items() if v}
    return timedelta(**params)
parse_timedelta('6d 6ms888.9us')                   # 6 days, 0:00:00.006889
parse_timedelta('8 weeks 1 day 1001us')            # 57 days, 0:00:00.001001
parse_timedelta('1w 1d 1h 1m 1s 1ms 1us')          # 8 days, 1:01:01.001001
parse_timedelta('1d \n 1h')                        # 1 day, 1:00:00
parse_timedelta('5 mins')                          # 0:05:00

Comments

0

Consider trying tempora.parse_timedelta (from tempora).

$ pip-run 'tempora>=4.1.1' -- -q
>>> from tempora import parse_timedelta
>>> parse_timedelta("32m")
datetime.timedelta(seconds=1920)
>>> parse_timedelta("2h32m")
datetime.timedelta(seconds=9120)
>>> parse_timedelta("4:13")
datetime.timedelta(seconds=15180)
>>> parse_timedelta("5hr34m56s")
datetime.timedelta(seconds=20096)

2 Comments

Consider adding a link to a download somewhere? Maybe a PyPI page? I assume it exists, but I can't tell from your "answer".
@JürgenA.Erhard Done. I thought it was obvious from pip-run 'tempora>=4.1.1', but I realize many wouldn't know pip-run or know that it accepts the same syntax as pip install. I also filed jaraco/skeleton#77 to consider addressing the general deficiency (docs don't link easily to the project).
0

It doesn't cover all your examples, but a straightforward way to convert a time string in the format HH:MM:SS to a timedelta object is:

from datetime import time, timedelta
t = time.fromisoformat('00:28:00') # Or any other time in the HH:MM:SS format.
dt = timedelta(
    hours=t.hour,
    minutes=t.minute,
    seconds=t.second,
    microseconds=t.microsecond
)

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.