In python, how to check if a date is valid?
Question:
I am building a kind of calender web app
I have set up the following form in HTML
<form action='/event' method='post'>
Year ("yyyy"): <input type='text' name='year' />
Month ("mm"): <input type='text' name='month' />
Day ("dd"): <input type='text' name='day' />
Hour ("hh"): <input type='text' name='hour' />
Description: <input type='text' name='info' />
<input type='submit' name='submit' value='Submit'/>
</form>
The input from the user is then submited in the a cherrypy server
I am wondering, is there a way to check if the date entered by the user is a valid date?
Obviously I could write a whole lot of if statements, but are there any built in function that can check this?
Thanks
Answers:
Use datetime
eg.
>>> from datetime import datetime
>>> print datetime(2008,12,2)
2008-12-02 00:00:00
>>> print datetime(2008,13,2)
Traceback (most recent call last):
File "<pyshell#4>", line 1, in <module>
print datetime(2008,13,2)
ValueError: month must be in 1..12
You could try doing
import datetime
datetime.datetime(year=year,month=month,day=day,hour=hour)
that will eliminate somethings like months >12 , hours > 23, non-existent leapdays (month=2 has max of 28 on non leap years, 29 otherwise, other months have max of 30 or 31 days)(throws ValueError exception on error)
Also you could try to compare it with some sanity upper/lower bounds.
ex.:
datetime.date(year=2000, month=1,day=1) < datetime.datetime(year=year,month=month,day=day,hour=hour) <= datetime.datetime.now()
The relevant upper and lower sanity bounds depend on your needs.
edit: remember that this does not handle certain datetimes things which may not be valid for your application(min birthday, holidays, outside hours of operation, ect.)
You can try using datetime and handle the exceptions to decide valid/invalid date :
Example : http://codepad.org/XRSYeIJJ
import datetime
correctDate = None
try:
newDate = datetime.datetime(2008,11,42)
correctDate = True
except ValueError:
correctDate = False
print(str(correctDate))
So, here’s my hacky solution to correct for invalid dates provided. This assumes the user is submitting from a generic html form that provides days 1-31 as options. The primary issue being users providing a day that does not exist for the month (ex 31 September)
def sane_date(year, month, day):
# Calculate the last date of the given month
nextmonth = datetime.date(year, month, 1) + datetime.timedelta(days=35)
lastday = nextmonth.replace(day=1) - datetime.timedelta(days=1)
return datetime.date(year, month, min(day, lastday.day))
class tests(unittest.TestCase):
def test_sane_date(self):
""" Test our sane_date() method"""
self.assertEquals(sane_date(2000,9,31), datetime.date(2000,9,30))
self.assertEquals(sane_date(2000,2,31), datetime.date(2000,2,29))
self.assertEquals(sane_date(2000,1,15), datetime.date(2000,1,15))
Here is a solution using time.
import time
def is_date_valid(year, month, day):
this_date = '%d/%d/%d' % (month, day, year)
try:
time.strptime(this_date, '%m/%d/%Y')
except ValueError:
return False
else:
return True
You can try using datetime and handle the exceptions to decide valid/invalid date:
import datetime
def check_date(year, month, day):
correctDate = None
try:
newDate = datetime.datetime(year, month, day)
correctDate = True
except ValueError:
correctDate = False
return correctDate
#handles obvious problems
print(str(check_date(2008,11,42)))
#handles leap days
print(str(check_date(2016,2,29)))
print(str(check_date(2017,2,29)))
#handles also standard month length
print(str(check_date(2016,3,31)))
print(str(check_date(2016,4,31)))
False
True
False
True
False
This is improvement of an answer by DhruvPathak and makes more sense as an edit but it was rejected as “This edit was intended to address the author of the post and makes no sense as an edit. It should have been written as a comment or an answer.“
The question assumes that the solution without libraries involves “a whole lot of if statements”, but it does not:
def is_valid_date(year, month, day):
day_count_for_month = [0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
if year%4==0 and (year%100 != 0 or year%400==0):
day_count_for_month[2] = 29
return (1 <= month <= 12 and 1 <= day <= day_count_for_month[month])
y = int(input("Year: "))
m = int(input("Month: "))
d = int(input("Day: "))
if 0 <= y and 0 < m < 13 and 0 < d < 32: #Check whether date is under limit.
if y % 4 == 0: # Every 4 year "Leap" year occures so checking...
if m == 2: # In "Leap" year February has 29 days
if d < 30:
print("<Correct>")
else:
print("<Wrong>")
elif m == 2: # But if it's not "Leap" year February will have 28 days
if d < 29:
print("<Correct>")
else:
print("<Wrong>")
elif y % 4 != 0 and m != 2: # Otherwise print "Correct"
print("<Correct>")
else:
print("<Wrong>")
You can try using dateutil.parser
module for easier date parsing:
from dateutil.parser import parse
def is_valid_date(date):
if date:
try:
parse(date)
return True
except:
return False
return False
Hope this helps.
Building on @codehia answer the following allows to check also the format of the date, and split a string into year, month, day – all the above assumed one has year, month, day already.
from dateutil.parser import parse
import string
p=print
space_punct_dict = dict((ord(punct), ' ') for punct in string.punctuation)
def is_valid_date_p(date):
if date:
try:
date = date.translate(space_punct_dict)
new_date = str(parse(date))[:10]
year = new_date[:4]
month = new_date[5:7]
day = new_date[8:]
p(year, month, day)
return True, year, month, day
except:
p('invalid:', date)
return False
return False
year, month, day = 2021, 6, 1
is_valid_date_p(f'{month}/{day}/{year}')
is_valid_date_p(f'{month}.{day}.{year}')
is_valid_date_p(f'{month},{day},{year}')
is_valid_date_p(f'{month}/{day}/{year}')
is_valid_date_p(f'{month}-{day}-{year}')
is_valid_date_p(f'{month} {day} {year}')
p()
is_valid_date_p('12/1/20')
is_valid_date_p('12/31/20')
p()
is_valid_date_p('31/12/20')
is_valid_date_p('30/6/2020')
is_valid_date_p('2020/30/6')
output:
2021 06 01
2021 06 01
2021 06 01
2021 06 01
2021 06 01
2021 06 01
2020 12 01
2020 12 31
2020 12 31
2020 06 30
invalid: 2020 30 6
I am building a kind of calender web app
I have set up the following form in HTML
<form action='/event' method='post'>
Year ("yyyy"): <input type='text' name='year' />
Month ("mm"): <input type='text' name='month' />
Day ("dd"): <input type='text' name='day' />
Hour ("hh"): <input type='text' name='hour' />
Description: <input type='text' name='info' />
<input type='submit' name='submit' value='Submit'/>
</form>
The input from the user is then submited in the a cherrypy server
I am wondering, is there a way to check if the date entered by the user is a valid date?
Obviously I could write a whole lot of if statements, but are there any built in function that can check this?
Thanks
Use datetime
eg.
>>> from datetime import datetime
>>> print datetime(2008,12,2)
2008-12-02 00:00:00
>>> print datetime(2008,13,2)
Traceback (most recent call last):
File "<pyshell#4>", line 1, in <module>
print datetime(2008,13,2)
ValueError: month must be in 1..12
You could try doing
import datetime
datetime.datetime(year=year,month=month,day=day,hour=hour)
that will eliminate somethings like months >12 , hours > 23, non-existent leapdays (month=2 has max of 28 on non leap years, 29 otherwise, other months have max of 30 or 31 days)(throws ValueError exception on error)
Also you could try to compare it with some sanity upper/lower bounds.
ex.:
datetime.date(year=2000, month=1,day=1) < datetime.datetime(year=year,month=month,day=day,hour=hour) <= datetime.datetime.now()
The relevant upper and lower sanity bounds depend on your needs.
edit: remember that this does not handle certain datetimes things which may not be valid for your application(min birthday, holidays, outside hours of operation, ect.)
You can try using datetime and handle the exceptions to decide valid/invalid date :
Example : http://codepad.org/XRSYeIJJ
import datetime
correctDate = None
try:
newDate = datetime.datetime(2008,11,42)
correctDate = True
except ValueError:
correctDate = False
print(str(correctDate))
So, here’s my hacky solution to correct for invalid dates provided. This assumes the user is submitting from a generic html form that provides days 1-31 as options. The primary issue being users providing a day that does not exist for the month (ex 31 September)
def sane_date(year, month, day):
# Calculate the last date of the given month
nextmonth = datetime.date(year, month, 1) + datetime.timedelta(days=35)
lastday = nextmonth.replace(day=1) - datetime.timedelta(days=1)
return datetime.date(year, month, min(day, lastday.day))
class tests(unittest.TestCase):
def test_sane_date(self):
""" Test our sane_date() method"""
self.assertEquals(sane_date(2000,9,31), datetime.date(2000,9,30))
self.assertEquals(sane_date(2000,2,31), datetime.date(2000,2,29))
self.assertEquals(sane_date(2000,1,15), datetime.date(2000,1,15))
Here is a solution using time.
import time def is_date_valid(year, month, day): this_date = '%d/%d/%d' % (month, day, year) try: time.strptime(this_date, '%m/%d/%Y') except ValueError: return False else: return True
You can try using datetime and handle the exceptions to decide valid/invalid date:
import datetime
def check_date(year, month, day):
correctDate = None
try:
newDate = datetime.datetime(year, month, day)
correctDate = True
except ValueError:
correctDate = False
return correctDate
#handles obvious problems
print(str(check_date(2008,11,42)))
#handles leap days
print(str(check_date(2016,2,29)))
print(str(check_date(2017,2,29)))
#handles also standard month length
print(str(check_date(2016,3,31)))
print(str(check_date(2016,4,31)))
False
True
False
True
False
This is improvement of an answer by DhruvPathak and makes more sense as an edit but it was rejected as “This edit was intended to address the author of the post and makes no sense as an edit. It should have been written as a comment or an answer.“
The question assumes that the solution without libraries involves “a whole lot of if statements”, but it does not:
def is_valid_date(year, month, day):
day_count_for_month = [0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
if year%4==0 and (year%100 != 0 or year%400==0):
day_count_for_month[2] = 29
return (1 <= month <= 12 and 1 <= day <= day_count_for_month[month])
y = int(input("Year: "))
m = int(input("Month: "))
d = int(input("Day: "))
if 0 <= y and 0 < m < 13 and 0 < d < 32: #Check whether date is under limit.
if y % 4 == 0: # Every 4 year "Leap" year occures so checking...
if m == 2: # In "Leap" year February has 29 days
if d < 30:
print("<Correct>")
else:
print("<Wrong>")
elif m == 2: # But if it's not "Leap" year February will have 28 days
if d < 29:
print("<Correct>")
else:
print("<Wrong>")
elif y % 4 != 0 and m != 2: # Otherwise print "Correct"
print("<Correct>")
else:
print("<Wrong>")
You can try using dateutil.parser
module for easier date parsing:
from dateutil.parser import parse
def is_valid_date(date):
if date:
try:
parse(date)
return True
except:
return False
return False
Hope this helps.
Building on @codehia answer the following allows to check also the format of the date, and split a string into year, month, day – all the above assumed one has year, month, day already.
from dateutil.parser import parse
import string
p=print
space_punct_dict = dict((ord(punct), ' ') for punct in string.punctuation)
def is_valid_date_p(date):
if date:
try:
date = date.translate(space_punct_dict)
new_date = str(parse(date))[:10]
year = new_date[:4]
month = new_date[5:7]
day = new_date[8:]
p(year, month, day)
return True, year, month, day
except:
p('invalid:', date)
return False
return False
year, month, day = 2021, 6, 1
is_valid_date_p(f'{month}/{day}/{year}')
is_valid_date_p(f'{month}.{day}.{year}')
is_valid_date_p(f'{month},{day},{year}')
is_valid_date_p(f'{month}/{day}/{year}')
is_valid_date_p(f'{month}-{day}-{year}')
is_valid_date_p(f'{month} {day} {year}')
p()
is_valid_date_p('12/1/20')
is_valid_date_p('12/31/20')
p()
is_valid_date_p('31/12/20')
is_valid_date_p('30/6/2020')
is_valid_date_p('2020/30/6')
output:
2021 06 01
2021 06 01
2021 06 01
2021 06 01
2021 06 01
2021 06 01
2020 12 01
2020 12 31
2020 12 31
2020 06 30
invalid: 2020 30 6