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

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.

Related

How to say in Pythonese - do something unless it causes an error (without resorting to multilevel try/execpt blocks)

This is a little difficult to explain, so let's hope I'm expressing the problem coherently:
Say I have this list:
my_list = ["a string", 45, 0.5]
The critical point to understand in order to see where the question comes from is that my_list is generated by another function; I don't know ahead of time anything about my_list, specifically its length and the datatype of any of its members.
Next, say that every time <my_list> is generated, there is a number of predetermined operations I want to perform on it. For example, I want to:
my_text = my_list[1]+"hello"
some_var = my_list[10]
mini_list = my_list[0].split('s')[1]
my_sum = my_list[7]+2
etc. The important point here is that it's a large number of operations.
Obviously, some of these operations would succeed with any given my_list and some would fail and, importantly, those which fail will do so with an unpredictable Error type; but I need to run all of them on every generation of my_list.
One obvious solution would be to use try/except on each of these operations:
try:
my_text = my_list[1]+"hello"
except:
my_text = "None"
try:
some_var = my_list[10]
except:
some_var = "couldn't do it"
etc.
But with a large number of operations, this gets very cumbersome. I looked into the various questions about multiple try/excepts, but unless I'm missing something, they don't address this.
Based on someone's suggestion (sorry, lost the link), I tried to create a function with a built-in try/except, create another list of these operations, and send each operation to the function. Something along the lines of
def careful(op):
try:
return op
else:
return "None"
And use it with, for example, the first operation:
my_text = careful(my_list[1]+"hello")
The problem is python seems to evaluate the careful() argument before it's sent out to the function and the error is generated before it can be caught...
So I guess I'm looking for a form of a ternary operator that can do something like:
my text = my_list[1]+"hello" if (this doesn't cause any type of error) else "None"
But, if one exist, I couldn't find it...
Any ideas would be welcome and sorry for the long post.
Maybe something like this?
def careful(op, default):
ret = default
try:
ret = computation()
else:
pass
return ret
If you must do this, consider keeping a collection of the operations as strings and calling exec on them in a loop
actions = [
'my_text = my_list[1]+"hello"',
'some_var = my_list[10]',
'mini_list = my_list[0].split("s")[1]',
'my_sum = my_list[7]+2',
]
If you make this collection a dict, you may also assign a default
Note that if an action default (or part of an action string) is meant to be a string, it must be quoted twice. Consider using block-quotes for this if you already have complex escaping, like returning a raw strings or a string representing a regular expression
{
"foo = bar": r"""r'[\w]+baz.*'"""
}
complete example:
>>> actions_defaults = {
... 'my_text = my_list[1]+"hello"': '"None"',
... 'some_var = my_list[10]': '"couldn\'t do it"',
... 'mini_list = my_list[0].split("s")[1]': '"None"',
... 'my_sum = my_list[7]+2': '"None"',
... }
>>>
>>> for action, default in actions_defaults.items():
... try:
... exec(action)
... except Exception: # consider logging error
... exec("{} = {}".format(action.split("=")[0], default))
...
>>> my_text
'None'
>>> some_var
"couldn't do it"
Other notes
this is pretty evil
declaring your vars before running to be their default values is probably better/clearer (sufficient to pass in the except block, as the assignment will fail)
you may run into weird scoping and need to access some vars via locals()
This sounds like an XY Problem
If you can make changes to the source logic, returning a dict may be a much better solution. Then you can determine if a key exists before doing some action, and potentially also look up the action which should be taken if the key exists in another dict.

Python datetime pattern matching

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")

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.

Reducing the number of arguments in function in Python?

My question is about how to deal with the piece of code where I am using the Caesar´s cipher.
Functions Decrypt and Encrypt have to deal with the limits of the alphabet (A - Z and a - z). I tried to write the two possible cycles for both alphabets in one cycle function named cycleencrypt.
But the function takes about 6 arguments and I have read somewhere that is less readable and understandable having more than 3 arguments in one function so my question is:
Should I reduce the number of arguments by splitting in two functions and make the piece of code longer (but maybe more understandable)?
Thanks for any answer I aprreciate that.
EDIT: Docstrings around the functions were deleted to make visible the
main purpose of my question.
def offsetctrl(offset):
while offset < 0:
offset += 26
return offset
def cycleencrypt(string, offset, index, listing, first, last):
offset = offsetctrl(offset)
if string >= ord(first) and string <= ord(last):
string += offset
while string > ord(last):
string = ord(first) + (string - ord(last) -1)
listing[index] = chr(string)
Cycle for encrypting with a lots of arguments and control of negative offset´s
def encrypt(retezec, offset):
listing = list(retezec)
for index in range(0, len(retezec)):
string = ord(retezec[index])
cycleencrypt(string, offset, index, listing, 'A', 'Z')
cycleencrypt(string, offset, index, listing, 'a', 'z')
print(''.join(listing))
main encryption part taking many arguments in two lines with printing
def decrypt(retezec, offset):
return encrypt(retezec, -offset)
if __name__ == "__main__":
encrypt("hey fellow how is it going", 5)
decrypt("mjd kjqqtb mtb nx ny ltnsl", 5)
In this kind of situation, it's often better to write your code as a class. Your class's constructor could take just the minimum number of arguments that are required (which may be none at all!), and then optional arguments could be set as properties of the class or by using other methods.
When designing a class like this, I find it's most useful to start by writing the client code first -- that is, write the code that will use the class first, and then work backwards from there to design the class.
For example, I might want the code to look something like this:
cypher = Cypher()
cypher.offset = 17
cypher.set_alphabet('A', 'Z')
result = cypher.encrypt('hey fellow how is it going')
Hopefully it should be clear how to work from here to the design of the Cypher class, but if not, please ask a question on Stack Overflow about that!
If you want to provide encrypt and decrypt convenience methods, it's still easy to do. For example, you can write a function like:
def encrypt(text, offset):
cypher = Cypher()
cypher.offset = offset
return cypher.encrypt(text)
Here is the docstring of datetime.datetime:
class datetime(date):
"""datetime(year, month, day[, hour[, minute[, second[, microsecond[,tzinfo]]]]])
...
"""
And the signature of its constructor:
def __new__(cls, year, month=None, day=None, hour=0, minute=0, second=0, microsecond=0, tzinfo=None):
What we could learn from it:
Add exactly as many arguments as it makes sense to add
Use parameters and to give sensible default values to arguments
Side thought: do you think users of your library would should use cycleencrypt()? You could mark it private (with underscore), so everybody will see it's not a public API and they should use encrypt() and decrypt() instead.
The number of arguments doesn't really matters as long as there are not a dozen of them (maybe someone can link to what you mention about having more than 3 arguments, I may be wrong).
To be more readable in the definition of a function, write comments by following docstrings convention.
To be more readable at the call of a function, gives default values in the definition as much as possible for the more useful values (for example, offset can have the value 1 by default, and index 0).
Either way, for a long line, use PEP8 guidelines which describes a way to jump lines correctly (the lines must not exceed 80 characters, according to PEP8).
def cycleencrypt(string, offset=1, index=0,
listing, first, last):
"""Description
:param string: description
:param offset: description
:param index: description
:param listing: description
:param first: description
:param last: description
:return description
"""
offset = offsetctrl(offset)
if string >= ord(first) and string <= ord(last):
string += offset
while string > ord(last):
string = ord(first) + (string - ord(last) - 1)
listing[index] = chr(string)

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

Categories