Skip to content

Instantly share code, notes, and snippets.

@simon04
Created December 10, 2022 17:08
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save simon04/90ad63486022fd110e5aea58e8ecb411 to your computer and use it in GitHub Desktop.
Save simon04/90ad63486022fd110e5aea58e8ecb411 to your computer and use it in GitHub Desktop.
Python: parses strings as ISO 8601 timedelta (License: CC0)
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