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

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


I found isodate library to do exactly what I want

isodate.parse_duration('PT1H5M26S')
  • You can read the source code for the function here


Here's a solution without a new package, but only works if you're dealing with a max duration expressed in days. That limitation makes sense though, because as others have pointed out (1):

Given that the timedelta has more than "a month's" worth of days, howwould you describe it using the ISO8601 duration notation withoutreferencing a specific point in time? Conversely, given your example,"P3Y6M4DT12H30M5S", how would you convert that into a timedeltawithout knowing which exact years and months this duration refers to?Timedelta objects are very precise beasts, which is almost certainlywhy they don't support "years" and "months" args in theirconstructors.

import datetimedef get_isosplit(s, split):    if split in s:        n, s = s.split(split)    else:        n = 0    return n, sdef parse_isoduration(s):            # Remove prefix    s = s.split('P')[-1]        # Step through letter dividers    days, s = get_isosplit(s, 'D')    _, s = get_isosplit(s, 'T')    hours, s = get_isosplit(s, 'H')    minutes, s = get_isosplit(s, 'M')    seconds, s = get_isosplit(s, 'S')    # Convert all to seconds    dt = datetime.timedelta(days=int(days), hours=int(hours), minutes=int(minutes), seconds=int(seconds))    return int(dt.total_seconds())
> parse_isoduration("PT1H5M26S")3926


Great question, obviously the "right" solution depends on your expectations for the input (a more reliable data source doesn't need as much input validation).

My approach to parse an ISO8601 duration timestamp only checks that the "PT" prefix is present and will not assume integer values for any of the units:

from datetime import timedeltadef parse_isoduration(isostring, as_dict=False):    """    Parse the ISO8601 duration string as hours, minutes, seconds    """    separators = {        "PT": None,        "W": "weeks",        "D": "days",        "H": "hours",        "M": "minutes",        "S": "seconds",    }    duration_vals = {}    for sep, unit in separators.items():        partitioned = isostring.partition(sep)        if partitioned[1] == sep:            # Matched this unit            isostring = partitioned[2]            if sep == "PT":                continue # Successful prefix match            dur_str = partitioned[0]            dur_val = float(dur_str) if "." in dur_str else int(dur_str)            duration_vals.update({unit: dur_val})        else:            if sep == "PT":                raise ValueError("Missing PT prefix")            else:                # No match for this unit: it's absent                duration_vals.update({unit: 0})    if as_dict:        return duration_vals    else:        return tuple(duration_vals.values())dur_isostr = "PT3H2M59.989333S"dur_tuple = parse_isoduration(dur_isostr)dur_dict = parse_isoduration(dur_isostr, as_dict=True)td = timedelta(**dur_dict)s = td.total_seconds()

>>> dur_tuple(0, 0, 3, 2, 59.989333)>>> dur_dict{'weeks': 0, 'days': 0, 'hours': 3, 'minutes': 2, 'seconds': 59.989333}>>> tddatetime.timedelta(seconds=10979, microseconds=989333)>>> s10979.989333