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 timedelta
def 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}
>>> td
datetime.timedelta(seconds=10979, microseconds=989333)
>>> s
10979.989333