Python datetime pattern matching - python

I'm trying to identify if a string can be cast as a date, according to a list of different formats. Thus, the whole list has to be looped over. If a match is found, that match should be returned. If all attempts return errors, that error should be returned. I'm not quite sure how to do this, my approach can be seen below.
_DEFAULT_PATTERNS = ["%d.%m.%Y", "%y-%m-%d"]
try:
if format == 'default':
for p in _DEFAULT_PATTERNS:
try:
value = datetime.strptime(value, p).date()
except:
continue
except Exception:
return ERROR
return value

Your first choice would be to use dateutil.parser. If, however, the parser does not meet your needs, here's a version of your code, tidied up:
def parseDate(value):
PATTERNS = ("%d.%m.%Y", "%y-%m-%d")
for p in PATTERNS:
try:
return datetime.strptime(value, p).date()
except ValueError:
continue
return False # No match found
Alternatively, raise an exception if the match is not found (instead of returning False). This will make your function more similar to strptime:
raise ValueError

Try something like this:
from datetime import datetime
_DEFAULT_PATTERNS = ["%d.%m.%Y", "%y-%m-%d"]
def is_castable_to_date(value):
for p in _DEFAULT_PATTERNS:
try:
value = datetime.strptime(value, p).date()
return True
except:
pass
return False
print is_castable_to_date("12-12-12")
print is_castable_to_date("12.12.12")
print is_castable_to_date("12/12/12")

Related

How to pass whole statements as function parameters? python

I have this dict
dic = {'wow': 77, 'yy': 'gt', 'dwe': {'dwdw': {'fefe': 2006}}}
and I have this function
def get_missing_key(data, nest, default_value):
try:
return data + nest
except KeyError as err:
return default_value
and this is how I call it:
get_missing_key(dic, ['dwe']['dwdw']['fefe'], 16)
What I want is that I want the second parameter to get converted to normal python expression and do calculations with it
I want it to be like this
def get_missing_key(data, nest, default_value):
try:
return data['dwe']['dwdw']['fefe']
except KeyError as err:
return default_value
is there a way to achieve this?
But what I have clearly doesn't work, since I can't concatinate a dict with a list
You could use reduce like #kyle-parsons did, or you could manually loop:
lookup = ["dwe", "dwdw", "fefe"]
def find_missing(data, lookup, default):
found = data
for i in lookup:
try:
found = found[i]
except KeyError:
return default
return found
You should pass your keys as a list.
from functools import reduce
def get_missing_key(data, nest, default_value):
try:
reduce(dict.__getitem__, nest, data)
except KeyError:
return default_value
In general, Python eagerly evaluates expressions and there's no way to delay that short of passing in strings of code to be built up and execed, but that's really not a good idea.

Conditional string representation based on variable type

I would like to create a string representation of a datetime object that could contain a None value. So far, I came up with a solution, but I was looking at a better/cleaner way of doing it.
Let's say I have the following two variables:
import datetime as dt
a = None
b = dt.datetime(2017, 11, 30)
def str_format(str):
return '{:%Y-%m-%d}'.format(str)
The following would return a formatted string:
str_format(b)
'2017-11-30'
But the following would return an error:
str_format(a)
TypeError: unsupported format string passed to NoneType.__format__
So far I can up with the following solution:
def str_format(str):
if isinstance(str, type(None)) is False:
return '{:%Y-%m-%d}'.format(str)
else:
return '{}'.format(str)
str_format(a)
'None'
str_format(b)
'2017-11-30'
However, I was looking at a more efficient/cleaner way of writing the function.
Often times these types of things are wrapped in a try/except
def str_format(str):
try:
return '{:%Y-%m-%d}'.format(str)
except TypeError:
# unrecognized type, return blank or whatever you want to return
return ''
The answer on this question explains why you typically use try/except instead of a conditional check fairly well.
your function is overcomplex. None is a singleton, so the pythonic way of testing against it is just is None.
Just do it in one line with a ternary expression:
def str_format(s):
return str(s) if s is None else '{:%Y-%m-%d}'.format(s)
or to return a default date (ex: 1/1/2010) if None is passed:
def str_format(s):
return '{:%Y-%m-%d}'.format(s or dt.datetime(2010, 1, 1))
as a side note don't use str as a variable name as it is the python string type.

More pythonic way of checking if user input exists and consists of integers

I have a GUI where I ask the user to enter some values. When the user submits the data I do some validation:
first I check if the user has entered a value for each input
then I check if each of the inputted values are integers
Trying not to repeat myself to much I came up with this, but the second part of the validation looks more like a hack. Is there a more pythonic way of rewriting this, short of spelling everything out like in the first part of the validation?
errors = []
# 1) check if values exist
if not self.startInput.GetValue():
errors.append("Please provide a start")
if not self.stopInput.GetValue():
errors.append("Please provide a stop")
if not self.valueInput.GetValue():
errors.append("Please provide a value")
# 2) check if values are integers
try:
self.start = int(self.startInput.GetValue())
self.stop = int(self.stopInput.GetValue())
self.value = int(self.valueInput.GetValue())
except ValueError as err:
tb = traceback.format_exc()
func = re.search('self\.(.*) =', tb).groups()[0]
errors.append("Value for {0} needs to be an integer.".format(func))
if errors:
raise RuntimeError('\n'.join(errors))
Since you're checking for integers, and not floats, you can simply do:
if self.start.GetValue().strip().isdigit():
pass
isdigit() returns False for both cases where the input is an empty string, and when the input contains non-digits.
If you want to send specific errors for incorrect assignment:
startValue = self.start.GetValue().strip()
if not startValue:
errors.append("Please provide a start.")
if not startValue.isdigit():
errors.append("Value of start must be an integer.")
If you have these inputs in a dictionary called inputs you can do:
errors = []
for inputname, inputval in self.inputs.items():
if not inputval:
errors.append("Please provide a {}".format(inputname))
try:
setattr(self, inputname, int(inputval.GetValue()))
except:
errors.append("Value for {0} needs to be an integer.".format(inputname))
if errors:
raise RuntimeError('\n'.join(errors))
I think the try: ... except is perfectly Pythonic. I would have uses an helper function instead of searching through an error message get_int_of_name(name, value, error) which return an int and update error if needed:
def get_int_of_name(name, value, error):
try:
res = int(value)
except ValueError:
error.append("...")
return 0
else:
return res

String checking on multiple types

I have a variable containing a string (extracted from a XML feed). The string value can be of type integer, date or string. I need to convert it from string to given data type. I am doing it this way but it is a little bit ugly so I am asking if there is a better technique. If I would checking for more types, I will end with very nested try - except blocks.
def normalize_availability(self, value):
"""
Normalize the availability date.
"""
try:
val = int(value)
except ValueError:
try:
val = datetime.datetime.strptime(value, '%Y-%m-%d')
except (ValueError, TypeError):
# Here could be another try - except block if more types needed
val = value
Thanks!
Use a handy helper function.
def tryconvert(value, default, *types):
"""Converts value to one of the given types. The first type that succeeds is
used, so the types should be specified from most-picky to least-picky (e.g.
int before float). The default is returned if all types fail to convert
the value. The types needn't actually be types (any callable that takes a
single argument and returns a value will work)."""
value = value.strip()
for t in types:
try:
return t(value)
except (ValueError, TypeError):
pass
return default
Then write a function for parsing the date/time:
def parsedatetime(value, format="%Y-%m-%d")
return datetime.datetime.striptime(value, format)
Now put 'em together:
value = tryconvert(value, None, parsedatetime, int)
The right way would be to know from the xml what type each should be. This would prevent things that happen to be numeric strings from ending up as an int, etc. But assuming that isn't possible.
for int type I prefer
if value.isdigit():
val = int(value)
for the date, the only other way I can think of would be splitting it and looking at the parts, which would be messier then just letting the strptime raise an exception.
def normalize_availability(value):
"""
Normalize the availability date.
"""
val = value
try:
val = datetime.datetime.strptime(value, '%Y-%m-%d')
except (ValueError):
if value.strip(" -+").isdigit():
val = int(value)
return val

Is there a Python equivalent to C#'s DateTime.TryParse()?

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.)
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).
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
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.
I agree tryparse is very useful function on c#. Unfortunately no equivalent direct function of that in python (may be i am not aware). I believe you want to check a string is whether date or not without worrying about date format. My recommendation is go for pandas to_datetime function:
def is_valid_date(str_to_validate: str) -> bool:
try:
if pd.to_datetime(str_to_validate):
return True
else:
return False
except ValueError:
return False
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.

Categories