Parse a duration int/float/string and return a datetime.timedelta. The preferred format for durations in Django is '%d %H:%M:%S.%f'. Also supports ISO 8601 representation.
(value: StrBytesIntFloat)
| 211 | |
| 212 | |
| 213 | def parse_duration(value: StrBytesIntFloat) -> timedelta: |
| 214 | """ |
| 215 | Parse a duration int/float/string and return a datetime.timedelta. |
| 216 | |
| 217 | The preferred format for durations in Django is '%d %H:%M:%S.%f'. |
| 218 | |
| 219 | Also supports ISO 8601 representation. |
| 220 | """ |
| 221 | if isinstance(value, timedelta): |
| 222 | return value |
| 223 | |
| 224 | if isinstance(value, (int, float)): |
| 225 | # below code requires a string |
| 226 | value = f'{value:f}' |
| 227 | elif isinstance(value, bytes): |
| 228 | value = value.decode() |
| 229 | |
| 230 | try: |
| 231 | match = standard_duration_re.match(value) or iso8601_duration_re.match(value) |
| 232 | except TypeError: |
| 233 | raise TypeError('invalid type; expected timedelta, string, bytes, int or float') |
| 234 | |
| 235 | if not match: |
| 236 | raise errors.DurationError() |
| 237 | |
| 238 | kw = match.groupdict() |
| 239 | sign = -1 if kw.pop('sign', '+') == '-' else 1 |
| 240 | if kw.get('microseconds'): |
| 241 | kw['microseconds'] = kw['microseconds'].ljust(6, '0') |
| 242 | |
| 243 | if kw.get('seconds') and kw.get('microseconds') and kw['seconds'].startswith('-'): |
| 244 | kw['microseconds'] = '-' + kw['microseconds'] |
| 245 | |
| 246 | kw_ = {k: float(v) for k, v in kw.items() if v is not None} |
| 247 | |
| 248 | return sign * timedelta(**kw_) |