How to construct a timedelta object from a simple string
For the first format (5hr34m56s
), you should parse using regular expressions
Here is re-based solution:
import re
from datetime import timedelta
regex = re.compile(r'((?P<hours>\d+?)hr)?((?P<minutes>\d+?)m)?((?P<seconds>\d+?)s)?')
def parse_time(time_str):
parts = regex.match(time_str)
if not parts:
return
parts = parts.groupdict()
time_params = {}
for name, param in parts.items():
if param:
time_params[name] = int(param)
return timedelta(**time_params)
>>> from parse_time import parse_time
>>> parse_time('12hr')
datetime.timedelta(0, 43200)
>>> parse_time('12hr5m10s')
datetime.timedelta(0, 43510)
>>> parse_time('12hr10s')
datetime.timedelta(0, 43210)
>>> parse_time('10s')
datetime.timedelta(0, 10)
>>>
How to convert a timedelta to a string and back again
The module pytimeparse, which was inspired by How to construct a timedelta object from a simple string, seems to do the heavy lifting by returning the number of seconds. I just put a wrapper around it which returns a timedelta
object with the same number of seconds:
#!/usr/bin/env python3.5
import datetime
import pytimeparse
import unittest
def reconstruct_timedelta(td_string):
seconds = pytimeparse.parse(td_string)
return datetime.timedelta(seconds=seconds)
class TestReconstruction(unittest.TestCase):
def test_reconstruct_timedelta_is_inverse_of_str(self):
td = datetime.timedelta(weeks=300, days=20, hours=3, minutes=4, milliseconds=254, microseconds=984)
td_reconstructed = reconstruct_timedelta(str(td))
self.assertTrue(td == td_reconstructed)
if __name__ == "__main__":
unittest.main()
As you can see from the test, the reconstructed timedelta
object is the same as the original one, even when it is initialized with an arbitrary number if milliseconds
and microseconds
.
How to convert standard timedelta string to timedelta object
I cannot find a better way other than writing a parser myself. The code looks bulky but it is essentially parsing string into a dictionary which is useful not only to creating a timedelta object.
import re
def parse(s):
if 'day' in s:
m = re.match(r'(?P<days>[-\d]+) day[s]*, (?P<hours>\d+):(?P<minutes>\d+):(?P<seconds>\d[\.\d+]*)', s)
else:
m = re.match(r'(?P<hours>\d+):(?P<minutes>\d+):(?P<seconds>\d[\.\d+]*)', s)
return {key: float(val) for key, val in m.groupdict().iteritems()}
Test:
from datetime import timedelta
s1 = '1157 days, 9:46:39'
s2 = '12:00:01.824952'
s3 = '-1 day, 23:59:31.859767'
t1 = parse(s1)
t2 = parse(s2)
t3 = parse(s3)
timedelta(**t1) # datetime.timedelta(1157, 35199)
timedelta(**t2) # datetime.timedelta(0, 43201, 824952)
timedelta(**t3) # datetime.timedelta(-1, 86371, 859767)
Hope this can suit your purpose.
Turn a string back into a datetime timedelta
Use pd.to_timedelta
pd.to_timedelta(df.iloc[:, 0])
0 0 days 00:00:57.416000
1 0 days 00:00:12.036000
2 0 days 16:46:23.127000
3 49 days 00:09:30.813000
4 50 days 00:39:31.306000
5 55 days 12:39:32.269000
6 -1 days +22:03:05.256000
Name: 0, dtype: timedelta64[ns]
Formatting timedelta objects
But I was wondering if I can do it in a single line using any date time function like
strftime
.
As far as I can tell, there isn't a built-in method to timedelta
that does that. If you're doing it often, you can create your own function, e.g.
def strfdelta(tdelta, fmt):
d = {"days": tdelta.days}
d["hours"], rem = divmod(tdelta.seconds, 3600)
d["minutes"], d["seconds"] = divmod(rem, 60)
return fmt.format(**d)
Usage:
>>> print strfdelta(delta_obj, "{days} days {hours}:{minutes}:{seconds}")
1 days 20:18:12
>>> print strfdelta(delta_obj, "{hours} hours and {minutes} to go")
20 hours and 18 to go
If you want to use a string format closer to the one used by strftime
we can employ string.Template
:
from string import Template
class DeltaTemplate(Template):
delimiter = "%"
def strfdelta(tdelta, fmt):
d = {"D": tdelta.days}
d["H"], rem = divmod(tdelta.seconds, 3600)
d["M"], d["S"] = divmod(rem, 60)
t = DeltaTemplate(fmt)
return t.substitute(**d)
Usage:
>>> print strfdelta(delta_obj, "%D days %H:%M:%S")
1 days 20:18:12
>>> print strfdelta(delta_obj, "%H hours and %M to go")
20 hours and 18 to go
The
totalSeconds
value is shown as 13374 instead of 99774. I.e. it's ignoring the "day" value.
Note in the example above that you can use timedelta.days
to get the "day" value.
Alternatively, from Python 2.7 onwards, timedelta has a total_seconds() method which return the total number of seconds contained in the duration.
Python timedelta object - strfdelta and deltafstr functions for conversion of timedelta 🠞 string 🠞 timedelta
After searching for such functions, and not being able to find one that converts back and forth, I wrote the following two functions and include them in a script. This is compatible with Python v2.6.6, which doesn't support some newer features such as timedelta.total_seconds()
:
#!/usr/bin/python
import re
import sys
import datetime
# String from Date/Time Delta:
# Takes a datetime.timedelta object, and converts the internal values
# to a dd:HH:mm:ss:ffffff string, prefixed with "-" if the delta is
# negative
def strfdelta(tdelta):
# Handle Negative time deltas
negativeSymbol = ""
if tdelta < datetime.timedelta(0):
negativeSymbol = "-"
# Convert days to seconds, as individual components could
# possibly both be negative
tdSeconds = (tdelta.seconds) + (tdelta.days * 86400)
# Capture +/- state of seconds for later user with milliseonds calculation
secsNegMultiplier = 1
if tdSeconds < 0:
secsNegMultiplier = -1
# Extract minutes from seconds
tdMinutes, tdSeconds = divmod(abs(tdSeconds), 60)
# Extract hours from minutes
tdHours, tdMinutes = divmod(tdMinutes, 60)
# Extract days from hours
tdDays, tdHours = divmod(tdHours, 24)
# Convert seconds to microseconds, as individual components
# could possibly both be negative
tdMicroseconds = (tdelta.microseconds) + (tdSeconds * 1000000 * secsNegMultiplier)
# Get seconds and microsecond components
tdSeconds, tdMicroseconds = divmod( abs(tdMicroseconds), 1000000)
return "{negSymbol}{days}:{hours:02d}:{minutes:02d}:{seconds:02d}:{microseconds:06d}".format(
negSymbol=negativeSymbol,
days=tdDays,
hours=tdHours,
minutes=tdMinutes,
seconds=tdSeconds,
microseconds=tdMicroseconds)
# Date/Time delta from string
# Example: -1:23:32:59:020030 (negative sign optional)
def deltafstr(stringDelta):
# Regular expression to capture status change events, with groups for date/time,
# instrument ID and state
regex = re.compile("^(-?)(\d{1,6}):([01]?\d|2[0-3]):([0-5][0-9]):([0-5][0-9]):(\d{6})$",re.UNICODE)
matchObj = regex.search(stringDelta)
# If this line doesn't match, return None
if(matchObj is None):
return None;
# Debug - Capture date-time from regular expression
# for g in range(0, 7):
# print "Grp {grp}: ".format(grp=g) + str(matchObj.group(g))
# Get Seconds multiplier (-ve sign at start)
secsNegMultiplier = 1
if matchObj.group(1):
secsNegMultiplier = -1
# Get time components
tdDays = int(matchObj.group(2)) * secsNegMultiplier
tdHours = int(matchObj.group(3)) * secsNegMultiplier
tdMinutes = int(matchObj.group(4)) * secsNegMultiplier
tdSeconds = int(matchObj.group(5)) * secsNegMultiplier
tdMicroseconds = int(matchObj.group(6)) * secsNegMultiplier
# Prepare return timedelta
retTimedelta = datetime.timedelta(
days=tdDays,
hours=tdHours,
minutes=tdMinutes,
seconds=tdSeconds,
microseconds=tdMicroseconds)
return retTimedelta;
Here is some code that tests going back and forward between the two formats. The constructor arguments for the timedelta
object may be changed to test different scenarios:
# Testing (change the constructor for timedelta to test other cases)
firstDelta = datetime.timedelta(seconds=-1,microseconds=999999, days=-1)
print "--------"
print firstDelta
firstDeltaStr = strfdelta(firstDelta)
print "--------"
print firstDeltaStr;
secondDelta = deltafstr(firstDeltaStr)
print "--------"
print secondDelta
secondDeltaStr = strfdelta(secondDelta)
print "--------"
print secondDelta
print "--------"
Convert pandas freq string to timedelta
In many cases, you can use the to_timedelta
function for this. It will convert a string to a timedelta:
In [9]: pd.to_timedelta('30min')
Out[9]: Timedelta('0 days 00:30:00')
In [10]: pd.to_timedelta('2S')
Out[10]: Timedelta('0 days 00:00:02')
However, it seems that pd.to_timedelta('30T') does not work, but this can maybe be regarded as a missing feature.
But, for this one it does work if you first convert it to a frequency object and then supply it to to_timedelta
:
In [19]: from pandas.tseries.frequencies import to_offset
In [20]: pd.to_timedelta(to_offset('30T'))
Out[20]: Timedelta('0 days 00:30:00')
If you start from a freqstr
from a DatetimeIndex, the second approach will always work. But, you can also use freq
instead of freqstr
, which returns a frequency object directly:
In [34]: dtidx = pd.date_range('2012-01-01', periods=5, freq='30min')
In [35]: pd.to_timedelta(dtidx.freq)
Out[35]: Timedelta('0 days 00:30:00')
Best way to convert time string `1'29.30` to time format
You can use pandas.to_datetime
with a custom format '%M\'%S.%f"'
, then keep only the time
part (without the date
part)
import pandas as pd
data = ['1\'29.30"', '1\'29.36"', '1\'29.54"', '1\'29.93"',
'1\'30.62"', '1\'30.80"', '1\'30.83"']
df = pd.DataFrame(data, columns=['time'])
df['time'] = pd.to_datetime(df['time'], format='%M\'%S.%f"').dt.time
Related Topics
Why Are Scripting Languages (E.G. Perl, Python, and Ruby) Not Suitable as Shell Languages
How to Define a Threshold Value to Detect Only Green Colour Objects in an Image with Python Opencv
Combine Several Images Horizontally with Python
How to Remove Non-Ascii Characters But Leave Periods and Spaces
Matplotlib Make Tick Labels Font Size Smaller
Format Output String, Right Alignment
Differences Between 'Input' and 'Raw_Input'
How to Write Output in Same Place on the Console
Selenium Give File Name When Downloading
Evenly Distributing N Points on a Sphere
Which Key/Value Store Is the Most Promising/Stable
Unboundlocalerror with Nested Function Scopes
Case Insensitive Regular Expression Without Re.Compile
Lost Connection to MySQL Server During Query
Get List from Pandas Dataframe Column or Row
Typeerror: Not All Arguments Converted During String Formatting Python