Created
December 10, 2022 17:08
-
-
Save simon04/90ad63486022fd110e5aea58e8ecb411 to your computer and use it in GitHub Desktop.
Python: parses strings as ISO 8601 timedelta (License: CC0)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from datetime import timedelta | |
import re | |
import unittest | |
def parse_timedelta(delta: str) -> timedelta: | |
"""Parses the given string as ISO 8601 timedelta""" | |
# https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/time/Duration.html#parse(java.lang.CharSequence) | |
match = re.compile( | |
r"(?P<sign>[-+]?)P" | |
r"(?:(?P<days>[-+]?[0-9]+)D)?" | |
r"(T" | |
r"(?:(?P<hours>[-+]?[0-9]+)H)?" | |
r"(?:(?P<minutes>[-+]?[0-9]+)M)?" | |
r"(?:(?P<seconds>[-+]?[0-9]+)" | |
r"(?:[.,](?P<microseconds>[0-9]{0,9}))?S)?" | |
r")?", | |
re.IGNORECASE, | |
).match(delta) | |
if not match: | |
raise ValueError(f"{delta} cannot be parsed as timedelta!") | |
sign = -1 if match.group("sign") == "-" else 1 | |
return sign * timedelta( | |
days=float(match.group("days") or "0"), | |
hours=float(match.group("hours") or "0"), | |
minutes=float(match.group("minutes") or "0"), | |
seconds=float( | |
(match.group("seconds") or "0") + "." + (match.group("microseconds") or "0") | |
), | |
) | |
class TestTimedelta(unittest.TestCase): | |
"""Tests parse_timedelta""" | |
def test_parse_timedelta(self) -> None: | |
"""Tests parse_timedelta""" | |
self.assertEqual( | |
parse_timedelta("PT20.345S"), | |
timedelta(seconds=20, microseconds=345000), | |
) | |
self.assertEqual(parse_timedelta("PT15M"), timedelta(minutes=15)) | |
self.assertEqual(parse_timedelta("PT10H"), timedelta(hours=10)) | |
self.assertEqual(parse_timedelta("PT37H"), timedelta(hours=37)) | |
self.assertEqual(parse_timedelta("P2D"), timedelta(days=2)) | |
self.assertEqual( | |
parse_timedelta("P2DT3H4M"), timedelta(days=2, hours=3, minutes=4) | |
) | |
self.assertEqual(parse_timedelta("PT-6H3M"), timedelta(hours=-6, minutes=3)) | |
self.assertEqual(parse_timedelta("-PT6H3M"), timedelta(hours=-6, minutes=-3)) | |
self.assertEqual(parse_timedelta("-PT-6H+3M"), timedelta(hours=6, minutes=-3)) | |
self.assertEqual( | |
parse_timedelta("P3DT5H8M11.21S"), | |
timedelta(days=3, hours=5, minutes=8, seconds=11, microseconds=210000), | |
) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment