Convert string that was originally a `timedelta` back into a `timedelta` object in Python

Question:

I have strings which were originally produced from timedelta objects like so: print(f'{my_delta}').

I have many of these statements logged (e.g. "12:21:00", "1 day, 0:53:00", "2 days, 9:28:00") and I simply want to parse these logged statements to convert back to timedelta objects. Is this possible with the datetime library?

The strings were literally produced from just printing timedelta objects, but I cannot seem to convert back by using timedelta(my_string). Wondering if there is a standard way of doing this that I am missing.

Asked By: efthimio

||

Answers:

To convert the logged statements back to timedelta objects, you can use the datetime library’s datetime.strptime() function to parse the strings and extract the relevant information. Here’s an example of how you can do it:

    from datetime import timedelta, datetime

def parse_timedelta_string(delta_string):
    # Remove leading/trailing whitespace and split the string by commas and colons
    parts = [part.strip() for part in delta_string.split(',') + delta_string.split(':')]
    
    # Extract the days, hours, minutes, and seconds from the parts
    days = 0
    hours, minutes, seconds = 0, 0, 0
    
    for part in parts:
        if 'day' in part:
            days = int(part.split()[0])
        elif 'hour' in part:
            hours = int(part.split()[0])
        elif 'minute' in part:
            minutes = int(part.split()[0])
        elif 'second' in part:
            seconds = int(part.split()[0])
    
    # Create a timedelta object with the extracted values
    delta = timedelta(days=days, hours=hours, minutes=minutes, seconds=seconds)
    
    return delta

# Example usage
delta_string = "2 days, 9:28:00"
delta = parse_timedelta_string(delta_string)
print(delta)  # Output: 2 days, 9:28:00

In the example above, the parse_timedelta_string() function takes a delta string as input. It splits the string by commas and colons, then iterates over the parts to extract the days, hours, minutes, and seconds. Finally, it creates a timedelta object with the extracted values and returns it.

You can call this function for each logged statement you have to convert them back to timedelta objects.

Answered By: Raziye

This should be amenable to regex, since it seems very regular. It may have days specified at the beginning, or microseconds at the end. But its very… regular:

import re

pattern = r'''
    ((?P<days>-?d{1,}) days?, )?  # maybe there will be day(s)
    (?P<hours>d{1,2})  # hours will be one or 2 digits
    :(?P<minutes>d{2})  # separated by a colon, minutes will be 2 digits
    :(?P<seconds>d{2})  # separated by a colon, seconds will be 2 digits
    (.(?P<microseconds>d{6}))?  # maybe, separated by a period, microseconds will be 6 digits
'''

parse_timedelta_regex = re.compile(pattern, flags=re.VERBOSE)

Note, take care using the re.VERBOSE flag with spaces, they are ignored outside of specific cases unless you escape them. But I do think it is more readable this way.

Examples of how to use this:

>> parse_timedelta_regex.search("1000 days, 0:00:00").groupdict()
{'days': '1000', 'hours': '0', 'minutes': '00', 'seconds': '00', 'microseconds': None}
>>> parse_timedelta_regex.search("1 day, 11:00:13.000130").groupdict()
{'days': '1', 'hours': '11', 'minutes': '00', 'seconds': '13', 'microseconds': '000130'}

You could also just write the function to convert the string to timedelta object like so:

>>> def string_to_timedelta(s):
...     groups = parse_timedelta_regex.search(s).groupdict()
...     args = {k:int(v) for k,v in groups.items() if v}
...     return datetime.timedelta(**args)
...
>>> string_to_timedelta(str(datetime.timedelta(seconds=1337)))
datetime.timedelta(seconds=1337)

Here’s a more rigorous test (still not very rigorous):

>> def test_parse(n):
...     for _ in range(n):
...         seconds = random.random()*100_000_000
...         td = datetime.timedelta(seconds=seconds)
...         parsed = string_to_timedelta(str(td))
...         assert td == parsed
...
>>> test_parse(1000)
>>> test_parse(10_000)
>>> test_parse(100_000)
Answered By: juanpa.arrivillaga

As others have noted emphatically, the datetime library does not seem to support this functionality.

Here’s a one-liner that uses regex:

>>> from datetime import timedelta
>>> import re
>>> 
>>> str_to_dlt = lambda t: timedelta(days=int((m:=re.match(r'((d+)sdays?,s*)?(d{1,2}):(d{2}):(d{2})', t))[2] or 0), hours=int(m[3]), minutes=int(m[4]), seconds=int(m[5]))

Printing the timedelta returns the original string it was supplied:

>>> print(str_to_dlt('5:11:13'))
5:11:13
>>> 
>>> print(str_to_dlt('1 day, 5:11:13'))
1 day, 5:11:13
>>> 
>>> print(str_to_dlt('2 days, 5:11:13'))
2 days, 5:11:13
Answered By: efthimio