COMP 141: Project 4

Perpetual Calendar

A perpetual calendar is a calendar designed to be valid for many years, usually designed to look up the day of the week for a given date in the future. In this project, you will design a perpetual calendar by writing a number of functions.

Follow the steps below to implement your project. There are ten steps, but they are all relatively short, so it looks a lot more involved than it actually is.

Getting Started

Step 1: Recall that Python has a number of different data types, including ints, floats, and strings. In this project, we will learn about another data type, called a boolean. Boolean variables can only hold two values, either True or False. Start off by reading this page about how they work and what they're good for: Boolean variables in Python.

Step 2: Review the guidelines for coding style and comments. Now that we are adding functions, every function you write, except main(), must have a comment before it explaining what the function does, what the parameters are, and what the return value is.

Step 3: Create a new, blank Python program. Type the standard comment header at the top, including the pledge.

Step 4 (Determining if a year is a leap year): Write a function called is_leap that takes a parameter called year, which will be an integer. Your function should return True if the year is a leap year, and False if it is not.

The definition line of this function should look like this def is_leap(year):

Hints:

Step 4A: Test your is_leap function. Do this by running your code (Run Menu -> Run Module). The Python shell will open, and you can call your function manually with different year arguments by calling your function from the shell. Here's an example of how you might test it:

>>> is_leap(2000)
True
>>> is_leap(1900)
False
>>> is_leap(2004)
True
>>> is_leap(2005)
False

When your function passes all of these tests, move on to Step 5.

Step 5 (Determining the day of the year): In the next two steps, we will write a function to determine the day of the year that a date falls on. For instance, January 1 is (obviously) the first day of the year. December 31 is the 365th day of the year, but the 366th day in a leap year. If you're curious, here's a website where you can see the days of the year for any year.

The first step in creating this function is to create a helper function (a helper function is a function designed to be called from another function, in order to help it do its job). This helper function will be called magic_month and it is designed to return the number of days that have gone by in a year on the first of any month (assuming it's not a leap year). For instance, on January 1, zero days have gone by in the year so far. On February 1, 31 days have gone by (because January has 31 days). On March 1, 59 days have gone by (the 31 days of January, plus the 28 days of February, assuming it's not a leap year).

Define a function called magic_month that takes a parameter called month, that will be the integer representing a month (1=January, 2=February, and so on). This function should return the number of days gone by since the beginning of the year. Here's a table of what to return:

January (Month #1):     0
February (Month #2):   31
March (Month #3):      59
April (Month #4):      90
May (Month #5):       120
June (Month #6):      151
July (Month #7):      181
August (Month #8):    212
September (Month #9): 243
October (Month #10):  273
November (Month #11): 304
December (Month #12): 334

The definition line of this function should look like this def magic_month(month):

Hint: No need to be clever here. Just use an if-elif-else statement with twelve branches.

Step 5A: Test your magic_month function. Here are some examples:

>>> magic_month(1)
0
>>> magic_month(2)
31
>>> magic_month(7)
181

Step 6 (actually writing the day-of-year function): Write a function called day_of_year that takes three integer parameters: a month (1-12), a day (1-31), and a year (any year is possible). This function should return the day of year (1-366) that this date occurs on.

Here's how you do it. You need to two pieces of information: first, figure out whether the year is a leap year or not (hint---call your is_leap function on the year and capture the return value). Next, get the appropriate magic_month value for the month (hint---call your magic_month function on the month and capture the return value). The formula for finding the day of the year is quite simple:

     The day of the year is the magic_month number plus the day of the month. If this year is a leap year and the month is March or later, add one to this result.

The definition line of this function should look like this def day_of_year(month, day, year):

Example: Suppose we call day_of_year(7, 29, 2019) to calculate the day of the year for July 29, 2019. 2019 is not a leap year, and the magic_month number for July (month #7) is 181. So the day of the year here is 181 + 29, which is 210.

Example 2: Suppose we call day_of_year(4, 11, 2016) to calculate the day of the year for April 11, 2016. 2016 is a leap year, and the magic_month number for April (month #4) is 90. So the day of the year here is 90 + 11 + 1, which is 102.

Step 6A: Test your day_of_year function. Here are some examples:

>>> day_of_year(7, 29, 2019)
210
>>> day_of_year(4, 11, 2016)
102
>>> day_of_year(12, 31, 2100)
365

Step 7: Over the next three steps you will write three more functions that will help you compute the day of the week for any given day in history. In order to do this, we will associate a number with every day of the week. Here's the pattern:

Sunday: 0
Monday: 1
Tuesday: 2
Wednesday: 3
Thursday: 4
Friday: 5
Saturday: 6
The first function we will write, called new_years_day, will calculate the day of the week number (from the table above) for any January 1 in history. Here's how it works. The steps are: Let's do an example. Suppose we want to find the day of the week for New Year's Day 2019.
Begin with 2019.
Add in 2019 // 4, which is 504. 2019 + 504 = 2523.
Subtract 2019 // 100, which is 20. 2523 - 20 = 2503.
Add in 2019 // 400, which is 5. 2503 + 5 = 2508.
2019 is not a leap year (so the number doesn't change).
2508 % 7 is 2, which means 2019 began on a Tuesday (you can verify this on a calendar!).

Write this function called new_years_day, which takes an integer parameter called year, and returns an integer interpreted as a day of the week.

The definition line should be def new_years_day(year):

Step 7A: Test your new_years_day function. Here are some examples:

>>> new_years_day(2019)
2
>>> new_years_day(2020)
3
>>> new_years_day(1900)
1
>>> new_years_day(2021)
5

Step 8: You will now write a function called day_of_week. This function takes three parameters: the month (1-12), the day of the month (1-31), and the year. This function will finally compute the day of the week for any day of any year! Here's how it works. All you have to do is call your day_of_year function, passing in the month, day, and year parameters; and your new_years_day function, passing in the year parameter. If you add those values together, subtract one, divide the result by 7, and take the remainder, you will have your day of the week (as an integer!). Return this integer value.

Here's an example. Let's find the day of the week for July 29, 2019. We already know that day_of_year(7, 29, 2019) returns 210, and new_years_day(2019) returns 2. If we add these together and subtract 1, we get 210 + 2 - 1 = 211. 211 % 7 is 1, which means July 29, 2019 falls on a Monday (check it on a calendar if you want!).

Write this function in Python now. The definition line is def day_of_week(month, day, year):

Step 8A: Test your day_of_week function. Here are some examples:

>>> day_of_week(7, 29, 2019)
1
>>> day_of_week(12, 25, 2020)
5
>>> day_of_week(7, 4, 1900)
3
>>> day_of_week(2, 14, 2000)
1

Step 9: You're almost done. You're going to write one more function, called day_of_week_str, that does the same operation as day_of_week, except the answer is returned as a string (like "Sunday", "Monday", etc). This function takes three parameters: the month (1-12), the day of the month (1-31), and the year. Here's how it works. Simply call day_of_week, passing in the same three parameters you are given here.

Hint: After capturing the return value from day_of_week, use a 7-branching if-elif-else statement to determine the appropriate day of the week string and return it.

The definition line is def day_of_week_str(month, day, year):

Step 9A: Test your day_of_week_str function. Here are some examples:

>>> day_of_week_str(7, 29, 2019)
'Monday'
>>> day_of_week_str(12, 25, 2020)
'Friday'
>>> day_of_week_str(10, 31, 2056)
'Tuesday'

Once you have written these functions, tested them, and they all work, you can move on to Step 10, which is writing the main function.

Remember

Your functions should be calling other functions as follows:

Step 10: Write main()

Write a main function to do the following:

Grading

Example run

Pick a date, any date (such as your birthday!)
What is the month? (1-12)? 7
What is the day? 29
What is the year? 1982
This is day number 210 of the year.
This date falls on a Thursday.
In 2019, this date falls on a Monday.
In 1982, New Year's Day falls on a Friday.

Another example run

Pick a date, any date (such as your birthday!)
What is the month? (1-12)? 2
What is the day? 14
What is the year? 2020
This is day number 45 of the year.
This date falls on a Friday.
In 2019, this date falls on a Thursday.
In 2020, New Year's Day falls on a Wednesday.

Submission

Challenge

Write a function called find_thanksgiving that takes a year as an integer argument. This function should return the day of the month (1--31) that Thanksgiving falls on in the year given. Thanksgiving (in the US, anyway) always falls on the fourth Thursday of November.

>>> find_thanksgiving(2019)
28
>>> find_thanksgiving(2020)
26
>>> find_thanksgiving(2021)
25
>>> find_thanksgiving(2000)
23

After writing this function, go to the end of your main() function and add more code that computes and prints out the day that Thanksgiving falls on in the year supplied by the user at the beginning of the program.