Prev - #20 Leap Year | Table of Contents | Next - #22 Rock, Paper, Scissors

isValidDate(2005, 3, 29) → True

isValidDate(2005, 13, 32) → False

You can represent a date with three integers for the year, month,
and day, but this doesn’t mean that any integers represent a valid date. After
all, there is no 13^{th} month of the year or 32^{nd} day of
any month. This exercise has you check if a year/month/day combination is
valid, given that different months have different numbers of days. You’ll use
the solution you wrote for Exercise #20, “Leap Year” as part of the solution
for this exercise, so finish Exercise #20 before attempting this one.

Exercise Description

Write an `isValidDate()`

function with
parameters `year`

, `month`

,
and `day`

. The function should return True if the integers provided for these parameters
represent a valid date. Otherwise, the function returns `False`

.
Months are represented by the integers `1`

(for
January) to `12`

(for December) and days are
represented by integers `1`

up to `28`

,
`29`

, `30`

, or 31 depending on the month and year. Your solution should
import your *leapyear.py*
program from Exercise #20 for its `isLeapYear()`

function, as February 29^{th} is a valid date on leap years.

September, April, June, and November have 30 days. The rest have 31, except February which has 28 days. On leap years, February has 29 days.

These Python `assert`

statements stop the
program if their condition is `False`

. Copy them to
the bottom of your solution program. Your solution is correct if the following assert statements’ conditions are all `True`

:

assert isValidDate(1999, 12, 31) == True

assert isValidDate(2000, 2, 29) == True

assert isValidDate(2001, 2, 29) == False

assert isValidDate(2029, 13, 1) == False

assert isValidDate(1000000, 1, 1) == True

assert isValidDate(2015, 4, 31) == False

assert isValidDate(1970, 5, 99) == False

assert isValidDate(1981, 0, 3) == False

assert isValidDate(1666, 4, 0) == False

import datetime

d = datetime.date(1970, 1, 1)

oneDay = datetime.timedelta(days=1)

for i in range(1000000):

assert isValidDate(d.year, d.month, d.day) == True

d += oneDay

Try to write a solution based on the information in this description. If you still have trouble solving this exercise, read the Solution Design and Special Cases and Gotchas sections for additional hints.

Prerequisite concepts: `import`

statements, Boolean operators, chaining operators, `elif`

statements

Solution Design

Any integer is a valid value for the `year`

parameter, but we do need to pass `year`

to our leapyear.isLeapYear() function if `month`

is set to `2`

(February). Be sure your program has an import leapyear instruction and that the leapyear.py
file is in the same folder as your solution’s file.

Any `month`

values outside of 1 to `12`

would be an invalid
date. In Python, you can chain together operators as a shortcut: the expression
`1 <= month <= 12`

is the same as the expression
`(1 <= month) and (month <= 12)`

. You can use
this to determine if your `month`

and day parameters are within a valid range.

The number of days in a month depends on the month:

·
If `month`

is `9`

,
`4`

, `6`

, or 11 (September, April, June, and November, respectively)
the maximum value for `day`

is `30`

.

·
If `month`

is `2`

(February), the maximum value for `day`

is 28 (or `29`

if leapyear.isLeapYear(year) returns `True`

).

·
Otherwise, the maximum value for day is `31`

.

Like the solution for Exercise #20, “Leap Year,” this solution is
a series of `if`

, `elif`

, or else statements.

Special Cases and Gotchas

To simplify your code as much as possible, think about the cases
that can cause the function to return as soon as possible. For example, if month is outside of the `1`

to 12 range, the function returns `False`

.
There’s no other set of circumstances you need to check; the function can
immediately return `False`

. If it doesn’t return, you
can assume that month contains a valid value for the rest of the function.

The other immediate check is if `leapyear.isLeapYear(year)`

returns `True`

and `month`

is
`2`

and day is `29`

, the
function can immediately return `True`

. If the
function hasn’t returned `True`

for the leap day, you
can assume that leap years are irrelevant for the rest of the code in the
function.

Now try to write a solution based on the information in the previous sections. If you still have trouble solving this exercise, read the Solution Template section for additional hints.

Solution Template

Try to first write a solution from scratch. But if you have difficulty, you can use the following partial program as a starting place. Copy the following code from https://invpy.com/validatedate-template.py and paste it into your code editor. Replace the underscores with code to make a working program:

# Import the leapyear module for its isLeapYear() function:

____ leapyear

def isValidDate(year, month, day):

# If month is outside the bounds of 1 to 12, return False:

if not (1 ____ month ____ 12):

return ____

# After this point, you can assume the month is valid.

# If the year is a leap year and the date is Feb 29th, it is valid:

if leapyear.isLeapYear(____) and ____ == 2 and ____ == 29:

return ____

# After this point, you can assume the year is not a leap year.

# Check for invalid dates in 31-day months:

if month in (1, 3, 5, 7, 8, 10, 12) and not (1 <= ____ <= 31):

return ____

# Check for invalid dates in 30-day months:

elif ____ in (4, 6, 9, 11) and not (1 <= day <= 30):

return ____

# Check for invalid dates in February:

elif month == ____ and not (1 <= ____ <= 28):

return ____

# Date passes all checks and is valid, so return True:

return ____

The complete solution for this exercise is given in Appendix A and https://invpy.com/validatedate.py. You can view each step of this program as it runs under a debugger at https://invpy.com/validatedate-debug/.

Further Reading

Python’s `datetime`

module has several
features for dealing with dates and calendar data. You can learn more about it
in Chapter 17 of Automate
the Boring Stuff with Python at https://automatetheboringstuff.com/2e/chapter17/.

Prev - #20 Leap Year | Table of Contents | Next - #22 Rock, Paper, Scissors