38

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

kenorb
  • 155,785
  • 88
  • 678
  • 743
davidx1
  • 3,525
  • 9
  • 38
  • 65
  • Related: [How do I validate a date string format in python?](http://stackoverflow.com/q/16870663) – kenorb Jul 27 '15 at 20:35

11 Answers11

39

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

Roman A. Taycher
  • 18,619
  • 19
  • 86
  • 141
34

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))
DhruvPathak
  • 42,059
  • 16
  • 116
  • 175
11

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])
Sash Sinha
  • 18,743
  • 3
  • 23
  • 40
anon
  • 111
  • 1
  • 2
7

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.

codehia
  • 124
  • 2
  • 4
  • 16
6

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

Community
  • 1
  • 1
reducing activity
  • 1,985
  • 2
  • 36
  • 64
6

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
jamylak
  • 128,818
  • 30
  • 231
  • 230
  • But wouldn't that give me an error message? and cause everything to kinda crash? I was hoping if there is a function which for exampel, returns 1 if it's valid date, and returns 0 if it's invalid. Then I can prompt the user to re-enter the date into the webpage. – davidx1 Apr 03 '12 at 06:13
  • 1
    Or you could `try...except` and catch the error. Then you can do what you want, pass the error silently if you chose to. – jamylak Apr 03 '12 at 06:15
3

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
David P
  • 71
  • 3
1

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))
daryl
  • 1,190
  • 10
  • 19
0
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>")
  • better provide some explanation – NIMISHAN Sep 14 '18 at 05:07
  • While this might answer the authors question, it lacks some explaining words and links to documentation. Raw code snippets are not very helpful without some phrases around it. You may also find [how to write a good answer](https://stackoverflow.com/help/how-to-answer) very helpful. Please edit your answer. – hellow Sep 14 '18 at 06:10
0

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

Zvi
  • 2,354
  • 2
  • 26
  • 37
0

Personally, I mostly use deteutil.parser which simplifies the code while parsing a date. With this snippet, you can also catch invalid dates:

import dateutil.parser
input_date = '2022-06-30'

try:
    result_date = dateutil.parser.parse(input_date, dayfirst=True).strftime('%Y-%m-%d')
except dateutil.parser._parser.ParserError as ex:
    print(ex) # handle the exception from here on

Where the strftime('%Y-%m-%d') part fixes the output format. Without the method, i.e. result_date = dateutil.parser.parse(input_date, dayfirst=True) only, it returns a datetime obect. The optional dayfirst=True leaves out ambiguous dates like 3/6/2022 to be parsed as 3rd of June (not 6th of March).

Let's try on some inputs with different formats and even putting some garbage:

multiple_inputs = ['2022-06-30', '30/6/2022', '30 06 2022', '3/6/2022', 
                   '2022-06-31', '2022-15-30', '', 'garbage']
for input_date in multiple_inputs:
    try:
        result_date = dateutil.parser.parse(input_date, dayfirst=True).strftime('%Y-%m-%d')
        print(f'Valid date: {result_date}')
    except dateutil.parser._parser.ParserError as ex:
        print(ex)

Which gives:

Valid date: 2022-06-30
Valid date: 2022-06-30
Valid date: 2022-06-30
Valid date: 2022-06-03
day is out of range for month: 2022-06-31
month must be in 1..12: 2022-15-30
String does not contain a date: 
Unknown string format: garbage