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 13th month of the year or 32nd 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 29th 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