How do I validate a date string format in python? - python

I have a python method which accepts a date input as a string.
How do I add a validation to make sure the date string being passed to the method is in the ffg. format:
'YYYY-MM-DD'
if it's not, method should raise some sort of error

>>> import datetime
>>> def validate(date_text):
try:
datetime.date.fromisoformat(date_text)
except ValueError:
raise ValueError("Incorrect data format, should be YYYY-MM-DD")
>>> validate('2003-12-23')
>>> validate('2003-12-32')
Traceback (most recent call last):
File "<pyshell#20>", line 1, in <module>
validate('2003-12-32')
File "<pyshell#18>", line 5, in validate
raise ValueError("Incorrect data format, should be YYYY-MM-DD")
ValueError: Incorrect data format, should be YYYY-MM-DD
Note that datetime.date.fromisoformat() obviously works only when date is in ISO format. If you need to check date in some other format, use datetime.datetime.strptime().

The Python dateutil library is designed for this (and more). It will automatically convert this to a datetime object for you and raise a ValueError if it can't.
As an example:
>>> from dateutil.parser import parse
>>> parse("2003-09-25")
datetime.datetime(2003, 9, 25, 0, 0)
This raises a ValueError if the date is not formatted correctly:
>>> parse("2003-09-251")
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/Users/jacinda/envs/dod-backend-dev/lib/python2.7/site-packages/dateutil/parser.py", line 720, in parse
return DEFAULTPARSER.parse(timestr, **kwargs)
File "/Users/jacinda/envs/dod-backend-dev/lib/python2.7/site-packages/dateutil/parser.py", line 317, in parse
ret = default.replace(**repl)
ValueError: day is out of range for month
dateutil is also extremely useful if you start needing to parse other formats in the future, as it can handle most known formats intelligently and allows you to modify your specification: dateutil parsing examples.
It also handles timezones if you need that.
Update based on comments: parse also accepts the keyword argument dayfirst which controls whether the day or month is expected to come first if a date is ambiguous. This defaults to False. E.g.
>>> parse('11/12/2001')
>>> datetime.datetime(2001, 11, 12, 0, 0) # Nov 12
>>> parse('11/12/2001', dayfirst=True)
>>> datetime.datetime(2001, 12, 11, 0, 0) # Dec 11

I think the full validate function should look like this:
from datetime import datetime
def validate(date_text):
try:
if date_text != datetime.strptime(date_text, "%Y-%m-%d").strftime('%Y-%m-%d'):
raise ValueError
return True
except ValueError:
return False
Executing just
datetime.strptime(date_text, "%Y-%m-%d")
is not enough because strptime method doesn't check that month and day of the month are zero-padded decimal numbers. For example
datetime.strptime("2016-5-3", '%Y-%m-%d')
will be executed without errors.

from datetime import datetime
datetime.strptime(date_string, "%Y-%m-%d")
..this raises a ValueError if it receives an incompatible format.
..if you're dealing with dates and times a lot (in the sense of datetime objects, as opposed to unix timestamp floats), it's a good idea to look into the pytz module, and for storage/db, store everything in UTC.

From mere curiosity, I timed the two rivalling answers posted above.
And I had the following results:
dateutil.parser (valid str): 4.6732222699938575
dateutil.parser (invalid str): 1.7270505399937974
datetime.strptime (valid): 0.7822393209935399
datetime.strptime (invalid): 0.4394566189876059
And here's the code I used (Python 3.6)
from dateutil import parser as date_parser
from datetime import datetime
from timeit import timeit
def is_date_parsing(date_str):
try:
return bool(date_parser.parse(date_str))
except ValueError:
return False
def is_date_matching(date_str):
try:
return bool(datetime.strptime(date_str, '%Y-%m-%d'))
except ValueError:
return False
if __name__ == '__main__':
print("dateutil.parser (valid date):", end=' ')
print(timeit("is_date_parsing('2021-01-26')",
setup="from __main__ import is_date_parsing",
number=100000))
print("dateutil.parser (invalid date):", end=' ')
print(timeit("is_date_parsing('meh')",
setup="from __main__ import is_date_parsing",
number=100000))
print("datetime.strptime (valid date):", end=' ')
print(timeit("is_date_matching('2021-01-26')",
setup="from __main__ import is_date_matching",
number=100000))
print("datetime.strptime (invalid date):", end=' ')
print(timeit("is_date_matching('meh')",
setup="from __main__ import is_date_matching",
number=100000))

Related

Converting time string to total seconds [duplicate]

I'm writing a function that needs to parse string to a timedelta. The user must enter something like "32m" or "2h32m", or even "4:13" or "5hr34m56s"... Is there a library or something that has this sort of thing already implemented?
To me the most elegant solution, without having to resort to external libraries such as dateutil or manually parsing the input, is to use datetime's powerful strptime string parsing method.
from datetime import datetime, timedelta
# we specify the input and the format...
t = datetime.strptime("05:20:25","%H:%M:%S")
# ...and use datetime's hour, min and sec properties to build a timedelta
delta = timedelta(hours=t.hour, minutes=t.minute, seconds=t.second)
After this you can use your timedelta object as normally, convert it to seconds to make sure we did the correct thing etc.
print(delta)
assert(5*60*60+20*60+25 == delta.total_seconds())
I had a bit of time on my hands yesterday, so I developed #virhilo's answer into a Python module, adding a few more time expression formats, including all those requested by #priestc.
Source code is on github (MIT License) for anybody that wants it. It's also on PyPI:
pip install pytimeparse
Returns the time as a number of seconds:
>>> from pytimeparse.timeparse import timeparse
>>> timeparse('32m')
1920
>>> timeparse('2h32m')
9120
>>> timeparse('4:13')
253
>>> timeparse('5hr34m56s')
20096
>>> timeparse('1.2 minutes')
72
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)
>>>
I wanted to input just a time and then add it to various dates so this worked for me:
from datetime import datetime as dtt
time_only = dtt.strptime('15:30', "%H:%M") - dtt.strptime("00:00", "%H:%M")
I've modified virhilo's nice answer with a few upgrades:
added a assertion that the string is a valid time string
replace the "hr" hour-indicator with "h"
allow for a "d" - days indicator
allow non-integer times (e.g. 3m0.25s is 3 minutes, 0.25 seconds)
.
import re
from datetime import timedelta
regex = re.compile(r'^((?P<days>[\.\d]+?)d)?((?P<hours>[\.\d]+?)h)?((?P<minutes>[\.\d]+?)m)?((?P<seconds>[\.\d]+?)s)?$')
def parse_time(time_str):
"""
Parse a time string e.g. (2h13m) into a timedelta object.
Modified from virhilo's answer at https://stackoverflow.com/a/4628148/851699
:param time_str: A string identifying a duration. (eg. 2h13m)
:return datetime.timedelta: A datetime.timedelta object
"""
parts = regex.match(time_str)
assert parts is not None, "Could not parse any time information from '{}'. Examples of valid strings: '8h', '2d8h5m20s', '2m4s'".format(time_str)
time_params = {name: float(param) for name, param in parts.groupdict().items() if param}
return timedelta(**time_params)
If Pandas is already in your dependencies, it does this pretty well:
>>> import pandas as pd
>>> pd.Timedelta('5hr34m56s')
Timedelta('0 days 05:34:56')
>>> pd.Timedelta('2h32m')
Timedelta('0 days 02:32:00')
>>> pd.Timedelta('5hr34m56s')
Timedelta('0 days 05:34:56')
>>> # It is pretty forgiving:
>>> pd.Timedelta('2 days 24:30:00 10 sec')
Timedelta('3 days 00:30:10')
To convert to datetime.timedelta if you prefer that type:
>>> pd.Timedelta('1 days').to_pytimedelta()
datetime.timedelta(1)
Unfortunately this does not work though:
>>> pd.Timedelta('4:13')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "pandas\_libs\tslibs\timedeltas.pyx", line 1217, in
pandas._libs.tslibs.timedeltas.Timedelta.__new__
File "pandas\_libs\tslibs\timedeltas.pyx", line 454, in
pandas._libs.tslibs.timedeltas.parse_timedelta_string
ValueError: expected hh:mm:ss format
Pandas actually has pretty extensive date and time tools even though that is not its main purpose.
To install Pandas:
# If you use pip
pip install pandas
# If you use conda
conda install pandas
Django comes with the utility function parse_duration(). From the documentation:
Parses a string and returns a datetime.timedelta.
Expects data in the format "DD HH:MM:SS.uuuuuu" or as specified by ISO 8601 (e.g. P4DT1H15M20S which is equivalent to 4 1:15:20) or PostgreSQL's day-time interval format (e.g. 3 days 04:05:06).
if you want to use : as separator, I use this function:
import re
from datetime import timedelta
def timedelta_parse(value):
"""
convert input string to timedelta
"""
value = re.sub(r"[^0-9:.]", "", value)
if not value:
return
return timedelta(**{key:float(val)
for val, key in zip(value.split(":")[::-1],
("seconds", "minutes", "hours", "days"))
})
Examples:
In [4]: timedelta_parse("1:0:0:1")
Out[4]: datetime.timedelta(days=1, seconds=1)
In [5]: timedelta_parse("123.5")
Out[5]: datetime.timedelta(seconds=123, microseconds=500000)
In [6]: timedelta_parse("1:6:34:9.983")
Out[6]: datetime.timedelta(days=1, seconds=23649, microseconds=983000)
In [8]: timedelta_parse("23:45:00")
Out[8]: datetime.timedelta(seconds=85500)
Use isodate library to parse ISO 8601 duration string. For example:
isodate.parse_duration('PT1H5M26S')
Also see Is there an easy way to convert ISO 8601 duration to timedelta?
If you use Python 3 then here's updated version for Hari Shankar's solution, which I used:
from datetime import timedelta
import re
regex = re.compile(r'(?P<hours>\d+?)/'
r'(?P<minutes>\d+?)/'
r'(?P<seconds>\d+?)$')
def parse_time(time_str):
parts = regex.match(time_str)
if not parts:
return
parts = parts.groupdict()
print(parts)
time_params = {}
for name, param in parts.items():
if param:
time_params[name] = int(param)
return timedelta(**time_params)
Consider trying tempora.parse_timedelta.
$ pip-run 'tempora>=4.1.1'
Collecting tempora>=4.1.1
Downloading tempora-4.1.1-py3-none-any.whl (15 kB)
Collecting jaraco.functools>=1.20
Using cached jaraco.functools-3.3.0-py3-none-any.whl (6.8 kB)
Collecting pytz
Using cached pytz-2021.1-py2.py3-none-any.whl (510 kB)
Collecting more-itertools
Using cached more_itertools-8.8.0-py3-none-any.whl (48 kB)
Installing collected packages: more-itertools, pytz, jaraco.functools, tempora
Successfully installed jaraco.functools-3.3.0 more-itertools-8.8.0 pytz-2021.1 tempora-4.1.1
Python 3.9.2 (v3.9.2:1a79785e3e, Feb 19 2021, 09:06:10)
[Clang 6.0 (clang-600.0.57)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> from tempora import parse_timedelta
>>> parse_timedelta("32m")
datetime.timedelta(seconds=1920)
>>> parse_timedelta("2h32m")
datetime.timedelta(seconds=9120)
>>> parse_timedelta("4:13")
datetime.timedelta(seconds=15180)
>>> parse_timedelta("5hr34m56s")
datetime.timedelta(seconds=20096)

How to convert time string with very precise time measurements to date objects using strptime()? [duplicate]

I am able to parse strings containing date/time with time.strptime
>>> import time
>>> time.strptime('30/03/09 16:31:32', '%d/%m/%y %H:%M:%S')
(2009, 3, 30, 16, 31, 32, 0, 89, -1)
How can I parse a time string that contains milliseconds?
>>> time.strptime('30/03/09 16:31:32.123', '%d/%m/%y %H:%M:%S')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/usr/lib/python2.5/_strptime.py", line 333, in strptime
data_string[found.end():])
ValueError: unconverted data remains: .123
Python 2.6 added a new strftime/strptime macro %f. The docs are a bit misleading as they only mention microseconds, but %f actually parses any decimal fraction of seconds with up to 6 digits, meaning it also works for milliseconds or even centiseconds or deciseconds.
time.strptime('30/03/09 16:31:32.123', '%d/%m/%y %H:%M:%S.%f')
However, time.struct_time doesn't actually store milliseconds/microseconds. You're better off using datetime, like this:
>>> from datetime import datetime
>>> a = datetime.strptime('30/03/09 16:31:32.123', '%d/%m/%y %H:%M:%S.%f')
>>> a.microsecond
123000
As you can see, .123 is correctly interpreted as 123 000 microseconds.
I know this is an older question but I'm still using Python 2.4.3 and I needed to find a better way of converting the string of data to a datetime.
The solution if datetime doesn't support %f and without needing a try/except is:
(dt, mSecs) = row[5].strip().split(".")
dt = datetime.datetime(*time.strptime(dt, "%Y-%m-%d %H:%M:%S")[0:6])
mSeconds = datetime.timedelta(microseconds = int(mSecs))
fullDateTime = dt + mSeconds
This works for the input string "2010-10-06 09:42:52.266000"
To give the code that nstehr's answer refers to (from its source):
def timeparse(t, format):
"""Parse a time string that might contain fractions of a second.
Fractional seconds are supported using a fragile, miserable hack.
Given a time string like '02:03:04.234234' and a format string of
'%H:%M:%S', time.strptime() will raise a ValueError with this
message: 'unconverted data remains: .234234'. If %S is in the
format string and the ValueError matches as above, a datetime
object will be created from the part that matches and the
microseconds in the time string.
"""
try:
return datetime.datetime(*time.strptime(t, format)[0:6]).time()
except ValueError, msg:
if "%S" in format:
msg = str(msg)
mat = re.match(r"unconverted data remains:"
" \.([0-9]{1,6})$", msg)
if mat is not None:
# fractional seconds are present - this is the style
# used by datetime's isoformat() method
frac = "." + mat.group(1)
t = t[:-len(frac)]
t = datetime.datetime(*time.strptime(t, format)[0:6])
microsecond = int(float(frac)*1e6)
return t.replace(microsecond=microsecond)
else:
mat = re.match(r"unconverted data remains:"
" \,([0-9]{3,3})$", msg)
if mat is not None:
# fractional seconds are present - this is the style
# used by the logging module
frac = "." + mat.group(1)
t = t[:-len(frac)]
t = datetime.datetime(*time.strptime(t, format)[0:6])
microsecond = int(float(frac)*1e6)
return t.replace(microsecond=microsecond)
raise
DNS answer above is actually incorrect. The SO is asking about milliseconds but the answer is for microseconds. Unfortunately, Python`s doesn't have a directive for milliseconds, just microseconds (see doc), but you can workaround it by appending three zeros at the end of the string and parsing the string as microseconds, something like:
datetime.strptime(time_str + '000', '%d/%m/%y %H:%M:%S.%f')
where time_str is formatted like 30/03/09 16:31:32.123.
Hope this helps.
My first thought was to try passing it '30/03/09 16:31:32.123' (with a period instead of a colon between the seconds and the milliseconds.) But that didn't work. A quick glance at the docs indicates that fractional seconds are ignored in any case...
Ah, version differences. This was reported as a bug and now in 2.6+ you can use "%S.%f" to parse it.
from python mailing lists: parsing millisecond thread. There is a function posted there that seems to get the job done, although as mentioned in the author's comments it is kind of a hack. It uses regular expressions to handle the exception that gets raised, and then does some calculations.
You could also try do the regular expressions and calculations up front, before passing it to strptime.
For python 2 i did this
print ( time.strftime("%H:%M:%S", time.localtime(time.time())) + "." + str(time.time()).split(".",1)[1])
it prints time "%H:%M:%S" , splits the time.time() to two substrings (before and after the .) xxxxxxx.xx and since .xx are my milliseconds i add the second substring to my "%H:%M:%S"
hope that makes sense :)
Example output:
13:31:21.72
Blink 01
13:31:21.81
END OF BLINK 01
13:31:26.3
Blink 01
13:31:26.39
END OF BLINK 01
13:31:34.65
Starting Lane 01

Python convert string to datetime for comparison to datetime object

I have a string lfile with a datetime in it (type(lfile) gives <type 'str'>) and a Python datetime object wfile. Here is the code:
import os, datetime
lfile = '2005-08-22_11:05:45.000000000'
time_w = os.path.getmtime('{}\\{}.py' .format('C:\Temp_Readouts\RtFyar','TempReads.csv'))
wfile = datetime.datetime.fromtimestamp(time_w)
wfile contains this 2006-11-30 19:08:06.531328 and repr(wfile) gives:
datetime.datetime(2006, 11, 30, 19, 8, 6, 531328)
Problem:
I need to:
convert lfile into a Python datetime object
compare lfile to wfile and determine which datetime is more recent
For 1.:
I am only able to get a partial solution using strptime as per here. Here is what I tried:
lfile = datetime.datetime.strptime(linx_file_dtime, '%Y-%m-%d_%H:%M:%S')
The output is:
`ValueError: unconverted data remains: .000`
Question 1
It seems that strptime() cannot handle the nano seconds. How do I tell strptime() to ignore the last 3 zeros?
For 2.:
When I use type(wfile) I get <type 'datetime.datetime'>. If both wfile and lfile are Python datetime objects (i.e. if step 1. is successful), then would this work?:
if wtime < ltime:
print 'Linux file created after Windows file'
else:
print 'Windows file created after Linux file'
Question 2
Or is there some other way in which Python can compare datetime objects to determine which of the two occurred after the other?
Question 1
Python handles microseconds, not nano seconds. You can strip the last three characters of the time to convert it to microseconds and then add .%f to the end:
lfile = datetime.datetime.strptime(linx_file_dtime[:-3], '%Y-%m-%d_%H:%M:%S.%f')
Question 2
Yes, comparison works:
if wtime < ltime:
...
That's right, strptime() does not handle nanoseconds. The accepted answer in the question that you linked to offers an option: strip off the last 3 digits and then parse with .%f appended to the format string.
Another option is to use dateutil.parser.parse():
>>> from dateutil.parser import parse
>>> parse('2005-08-22_11:05:45.123456789', fuzzy=True)
datetime.datetime(2005, 8, 22, 11, 5, 45, 123456)
fuzzy=True is required to overlook the unsupported underscore between date and time components. Because datetime objects do not support nanoseconds, the last 3 digits vanish, leaving microsecond accuracy.

How to construct a timedelta object from a simple string

I'm writing a function that needs to parse string to a timedelta. The user must enter something like "32m" or "2h32m", or even "4:13" or "5hr34m56s"... Is there a library or something that has this sort of thing already implemented?
To me the most elegant solution, without having to resort to external libraries such as dateutil or manually parsing the input, is to use datetime's powerful strptime string parsing method.
from datetime import datetime, timedelta
# we specify the input and the format...
t = datetime.strptime("05:20:25","%H:%M:%S")
# ...and use datetime's hour, min and sec properties to build a timedelta
delta = timedelta(hours=t.hour, minutes=t.minute, seconds=t.second)
After this you can use your timedelta object as normally, convert it to seconds to make sure we did the correct thing etc.
print(delta)
assert(5*60*60+20*60+25 == delta.total_seconds())
I had a bit of time on my hands yesterday, so I developed #virhilo's answer into a Python module, adding a few more time expression formats, including all those requested by #priestc.
Source code is on github (MIT License) for anybody that wants it. It's also on PyPI:
pip install pytimeparse
Returns the time as a number of seconds:
>>> from pytimeparse.timeparse import timeparse
>>> timeparse('32m')
1920
>>> timeparse('2h32m')
9120
>>> timeparse('4:13')
253
>>> timeparse('5hr34m56s')
20096
>>> timeparse('1.2 minutes')
72
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)
>>>
I wanted to input just a time and then add it to various dates so this worked for me:
from datetime import datetime as dtt
time_only = dtt.strptime('15:30', "%H:%M") - dtt.strptime("00:00", "%H:%M")
I've modified virhilo's nice answer with a few upgrades:
added a assertion that the string is a valid time string
replace the "hr" hour-indicator with "h"
allow for a "d" - days indicator
allow non-integer times (e.g. 3m0.25s is 3 minutes, 0.25 seconds)
.
import re
from datetime import timedelta
regex = re.compile(r'^((?P<days>[\.\d]+?)d)?((?P<hours>[\.\d]+?)h)?((?P<minutes>[\.\d]+?)m)?((?P<seconds>[\.\d]+?)s)?$')
def parse_time(time_str):
"""
Parse a time string e.g. (2h13m) into a timedelta object.
Modified from virhilo's answer at https://stackoverflow.com/a/4628148/851699
:param time_str: A string identifying a duration. (eg. 2h13m)
:return datetime.timedelta: A datetime.timedelta object
"""
parts = regex.match(time_str)
assert parts is not None, "Could not parse any time information from '{}'. Examples of valid strings: '8h', '2d8h5m20s', '2m4s'".format(time_str)
time_params = {name: float(param) for name, param in parts.groupdict().items() if param}
return timedelta(**time_params)
If Pandas is already in your dependencies, it does this pretty well:
>>> import pandas as pd
>>> pd.Timedelta('5hr34m56s')
Timedelta('0 days 05:34:56')
>>> pd.Timedelta('2h32m')
Timedelta('0 days 02:32:00')
>>> pd.Timedelta('5hr34m56s')
Timedelta('0 days 05:34:56')
>>> # It is pretty forgiving:
>>> pd.Timedelta('2 days 24:30:00 10 sec')
Timedelta('3 days 00:30:10')
To convert to datetime.timedelta if you prefer that type:
>>> pd.Timedelta('1 days').to_pytimedelta()
datetime.timedelta(1)
Unfortunately this does not work though:
>>> pd.Timedelta('4:13')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "pandas\_libs\tslibs\timedeltas.pyx", line 1217, in
pandas._libs.tslibs.timedeltas.Timedelta.__new__
File "pandas\_libs\tslibs\timedeltas.pyx", line 454, in
pandas._libs.tslibs.timedeltas.parse_timedelta_string
ValueError: expected hh:mm:ss format
Pandas actually has pretty extensive date and time tools even though that is not its main purpose.
To install Pandas:
# If you use pip
pip install pandas
# If you use conda
conda install pandas
Django comes with the utility function parse_duration(). From the documentation:
Parses a string and returns a datetime.timedelta.
Expects data in the format "DD HH:MM:SS.uuuuuu" or as specified by ISO 8601 (e.g. P4DT1H15M20S which is equivalent to 4 1:15:20) or PostgreSQL's day-time interval format (e.g. 3 days 04:05:06).
if you want to use : as separator, I use this function:
import re
from datetime import timedelta
def timedelta_parse(value):
"""
convert input string to timedelta
"""
value = re.sub(r"[^0-9:.]", "", value)
if not value:
return
return timedelta(**{key:float(val)
for val, key in zip(value.split(":")[::-1],
("seconds", "minutes", "hours", "days"))
})
Examples:
In [4]: timedelta_parse("1:0:0:1")
Out[4]: datetime.timedelta(days=1, seconds=1)
In [5]: timedelta_parse("123.5")
Out[5]: datetime.timedelta(seconds=123, microseconds=500000)
In [6]: timedelta_parse("1:6:34:9.983")
Out[6]: datetime.timedelta(days=1, seconds=23649, microseconds=983000)
In [8]: timedelta_parse("23:45:00")
Out[8]: datetime.timedelta(seconds=85500)
Use isodate library to parse ISO 8601 duration string. For example:
isodate.parse_duration('PT1H5M26S')
Also see Is there an easy way to convert ISO 8601 duration to timedelta?
If you use Python 3 then here's updated version for Hari Shankar's solution, which I used:
from datetime import timedelta
import re
regex = re.compile(r'(?P<hours>\d+?)/'
r'(?P<minutes>\d+?)/'
r'(?P<seconds>\d+?)$')
def parse_time(time_str):
parts = regex.match(time_str)
if not parts:
return
parts = parts.groupdict()
print(parts)
time_params = {}
for name, param in parts.items():
if param:
time_params[name] = int(param)
return timedelta(**time_params)
Consider trying tempora.parse_timedelta.
$ pip-run 'tempora>=4.1.1'
Collecting tempora>=4.1.1
Downloading tempora-4.1.1-py3-none-any.whl (15 kB)
Collecting jaraco.functools>=1.20
Using cached jaraco.functools-3.3.0-py3-none-any.whl (6.8 kB)
Collecting pytz
Using cached pytz-2021.1-py2.py3-none-any.whl (510 kB)
Collecting more-itertools
Using cached more_itertools-8.8.0-py3-none-any.whl (48 kB)
Installing collected packages: more-itertools, pytz, jaraco.functools, tempora
Successfully installed jaraco.functools-3.3.0 more-itertools-8.8.0 pytz-2021.1 tempora-4.1.1
Python 3.9.2 (v3.9.2:1a79785e3e, Feb 19 2021, 09:06:10)
[Clang 6.0 (clang-600.0.57)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> from tempora import parse_timedelta
>>> parse_timedelta("32m")
datetime.timedelta(seconds=1920)
>>> parse_timedelta("2h32m")
datetime.timedelta(seconds=9120)
>>> parse_timedelta("4:13")
datetime.timedelta(seconds=15180)
>>> parse_timedelta("5hr34m56s")
datetime.timedelta(seconds=20096)

Handling the different results from parsedatetime

I'm trying to learn python after spending the last 15 or so years working only in Perl and only occasionally.
I can't understand how to handle the two different kinds of results from the parse method of Calendar.parse() from parsedatetime
Given this script:
#!/usr/bin/python
import parsedatetime.parsedatetime as pdt
import parsedatetime.parsedatetime_consts as pdc
import sys
import os
# create an instance of Constants class so we can override some of the defaults
c = pdc.Constants()
# create an instance of the Calendar class and pass in our Constants # object instead of letting it create a default
p = pdt.Calendar(c)
while True:
reply = raw_input('Enter text:')
if reply == 'stop':
break
else:
result = p.parse(reply)
print result
print
And this sample run:
Enter text:tomorrow
(time.struct_time(tm_year=2009, tm_mon=11, tm_mday=28, tm_hour=9, tm_min=0, tm_sec=0, tm_wday=5, tm_yday=332, tm_isdst=-1), 1)
Enter text:11/28
((2009, 11, 28, 14, 42, 55, 4, 331, 0), 1)
I can't figure out how to get the output such that I can consisently use result like so:
print result[0].tm_mon, result[0].tm_mday
That won't work in the case where the input is "11/28" because the output is just a tuple and not a struct_time.
Probably a simple thing.. but not for this newbie. From my perspective the output of Calendar.parse() is unpredictable and hard to use. Any help appreciated. Tia.
I know this is an old question but I ran into this yesterday and the answer here is incomplete (it will fail in the case that parse() returns a datetime).
From the parsedatetime docs:
parse() returns a tuple ( result, type ) where type specifies one of:
0 = not parsed at all
1 = parsed as a date (of type struct_time)
2 = parsed as a time (of type struct_time)
3 = parsed as a datetime (of type datetime.datetime)
Which is a little weird and maybe not the clearest way to do it, but it works and is pretty useful.
Here's a little chunk of code that will convert whatever it returns to a proper python datetime:
import parsedatetime.parsedatetime as pdt
def datetimeFromString( s ):
c = pdt.Calendar()
result, what = c.parse( s )
dt = None
# what was returned (see http://code-bear.com/code/parsedatetime/docs/)
# 0 = failed to parse
# 1 = date (with current time, as a struct_time)
# 2 = time (with current date, as a struct_time)
# 3 = datetime
if what in (1,2):
# result is struct_time
dt = datetime.datetime( *result[:6] )
elif what == 3:
# result is a datetime
dt = result
if dt is None:
# Failed to parse
raise ValueError, ("Don't understand date '"+s+"'")
return dt
Use x = time.struct_time(result[0]) and you'll get a struct_time (so that you can check x.tm_mon and x.tm_mday) no matter whether that result[0] is a struct_time itself, or just a 9-tuple (I've never heard of parsedatetime so I don't know why it's inconsistent in its return type, but with this simple approach you can neutralize that inconsistency).

Categories