Reputation: 928
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.
Upvotes: 0
Views: 112
Reputation: 928
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
Upvotes: 1
Reputation: 96267
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)
Upvotes: 1