Is there an equivalent to C#'s DateTime.TryParse()
in Python?
I'm referring to the fact that it avoids throwing an exception, not the fact that it guesses the format.
Is there an equivalent to C#'s DateTime.TryParse()
in Python?
I'm referring to the fact that it avoids throwing an exception, not the fact that it guesses the format.
If you don't want the exception, catch the exception.
try:
d = datetime.datetime.strptime(s, "%Y-%m-%d %H:%M:%S")
except ValueError:
d = None
In the zen of Python, explicit is better than implicit. strptime
always returns a datetime parsed in the exact format specified. This makes sense, because you have to define the behavior in case of failure, maybe what you really want is.
except ValueError:
d = datetime.datetime.now()
or
except ValueError:
d = datetime.datetime.fromtimestamp(0)
or
except ValueError:
raise WebFramework.ServerError(404, "Invalid date")
By making it explicit, it's clear to the next person who reads it what the failover behavior is, and that it is what you need it to be.
Or maybe you're confident that the date cannot be invalid; it's coming from a database DATETIME, column, in which case there won't be an exception to catch, and so don't catch it.
We want to try...catch
multiple datetime formats fmt1,fmt2,...,fmtn
and suppress/handle the exceptions (from strptime
) for all those that mismatch (and in particular, avoid needing a yukky n-deep indented ladder of try..catch
clauses). I found two elegant ways, the second is best in general. (This is big problem on real-world data where multiple, mismatched, incomplete, inconsistent and multilanguage/region date formats are often mixed freely in one dataset.)
1) Individually try applying each format and handle each individual strptime()
fail as a return-value of None
, so you can chain fn calls...
To start off, adapting from @OrWeis' answer for compactness:
def try_strptime_single_format(s, fmt):
try:
return datetime.datetime.strptime(s, fmt)
except ValueError:
return None
Now you can invoke as try_strptime(s, fmt1) or try_strptime(s, fmt2) or try_strptime(s, fmt3) ...
But we can improve that to:
2) Apply multiple possible formats (either pass in as an argument or use sensible defaults), iterate over those, catch and handle any errors internally:
Cleaner, simpler and more OO-friendly is to generalize this to make the formats
parameter either a single string or a list, then iterate over that... so your invocation reduces to try_strptime(s, [fmt1, fmt2, fmt3, ...])
def try_strptime(s, fmts=['%d-%b-%y','%m/%d/%Y']):
for fmt in fmts:
try:
return datetime.strptime(s, fmt)
except:
continue
return None # or reraise the ValueError if no format matched, if you prefer
(As a sidebar, note that ...finally
is not the droid we want, since it would execute after each loop pass i.e. on each candidate format, not once at the end of the loop.)
I find implementation 2) is cleaner and better. In particular the function/method can store a list of default formats, which makes it more failsafe and less exception-happy on real-world data. (We could even infer which default formats to apply based on other columns, e.g. first try German date formats on German data, Arabic on Arabic, weblog datetime formats on weblog data etc.)
Here's an equivalent function implementation
import datetime
def try_strptime(s, format):
"""
@param s the string to parse
@param format the format to attempt parsing of the given string
@return the parsed datetime or None on failure to parse
@see datetime.datetime.strptime
"""
try:
date = datetime.datetime.strptime(s, format)
except ValueError:
date = None
return date
No, what you're asking for is not idiomatic Python, and so there generally won't be functions that discard errors like that in the standard library. The relevant standard library modules are documented here:
http://docs.python.org/library/datetime.html
http://docs.python.org/library/time.html
The parsing functions all raise exceptions on invalid input.
However, as the other answers have stated, it wouldn't be terribly difficult to construct one for your application (your question was phrased "in Python" rather than "in the Python standard library" so it's not clear if assistance writing such a function "in Python" is answering the question or not).
Brute force is also an option:
def TryParse(datestring, offset):
nu = datetime.datetime.now()
retval = nu
formats = ["%d-%m-%Y","%Y-%m-%d","%d-%m-%y","%y-%m-%d"]
if datestring == None:
retval = datetime.datetime(nu.year,nu.month,nu.day,0,0,0) - datetime.timedelta(offset,0,0,0,0,0,0)
elif datestring == '':
retval = datetime.datetime(nu.year,nu.month,nu.day,0,0,0) - datetime.timedelta(offset,0,0,0,0,0,0)
else:
succes = False
for aformat in formats:
try:
retval = datetime.datetime.strptime(datestring,aformat)
succes = True
break
except:
pass
if not succes:
retval = datetime.datetime(nu.year,nu.month,nu.day,0,0,0) - datetime.timedelta(offset,0,0,0,0,0,0)
return retval
Use time.strptime
to parse dates from strings.
Documentation: http://docs.python.org/library/time.html#time.strptime
Examples from: http://pleac.sourceforge.net/pleac_python/datesandtimes.html
#-----------------------------
# Parsing Dates and Times from Strings
time.strptime("Tue Jun 16 20:18:03 1981")
# (1981, 6, 16, 20, 18, 3, 1, 167, -1)
time.strptime("16/6/1981", "%d/%m/%Y")
# (1981, 6, 16, 0, 0, 0, 1, 167, -1)
# strptime() can use any of the formatting codes from time.strftime()
# The easiest way to convert this to a datetime seems to be;
now = datetime.datetime(*time.strptime("16/6/1981", "%d/%m/%Y")[0:5])
# the '*' operator unpacks the tuple, producing the argument list.
How about strptime?
http://docs.python.org/library/time.html#time.strptime
It will throw a ValueError if it is unable to parse the string based on the format that is provided.
Edit:
Since the question was edited to include the bit about exceptions after I answered it. I wanted to add a note about that.
As was pointed out in other answers, if you don't want your program to raise an exception, you can simply catch it and handle it:
try:
date = datetime.datetime.strptime(s, "%Y-%m-%d %H:%M:%S")
except ValueError:
date = None
That's a Pythonic way to do what you want.