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

Asked By: davidx1

||

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
Answered By: jamylak

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

Answered By: Roman A. Taycher

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))
Answered By: DhruvPathak

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))
Answered By: daryl

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
Answered By: David P

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

gives

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.

Answered By: reducing activity

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])
Answered By: anon
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>")
Answered By: Batjigdrel bataa

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.

Answered By: codehia

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

Answered By: Zvi
Categories: questions Tags: , , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.