Some people have an extraordinary talent to compute (in their heads) the day of the week that any past date fell on. For example, if you tell them that you were born on October 13, 1995, theyll be able to tell you that you were born on a Friday!
In this problem, you will create a Date class, from which you will be able to create Date objects that represent a day, month, and year. You will add functionality to this class that will enable Date objects to find the day of the week to which they correspond.
Getting started
Begin by downloading the file ps11pr1.py and opening it in IDLE. We have given you the following methods to start:
Your tasks
Below you will add several new methods to the Date class. Be sure to thoroughly test your methods for all cases to ensure that they are correct.
0. Read over the starter code that weve given you. Make sure that you understand how the various methods work.
Then, try the following interactions in the Python Shell to experiment with the __init__, __repr__, and is_leap_year methods:
# Create a Date object named d1 using the constructor.
>>> d1 = Date(4, 5, 2015)
# An example of using print to invoke the __str__ method.
>>> print('d1 has a value of', d1)
d1 has a value of 04/05/2015
# Check if d1 is in a leap year -- it is not!
>>> d1.is_leap_year()
False
# Create another object named d2
>>> d2 = Date(1, 1, 2020)
# Check if d2 is in a leap year.
>>> d2.is_leap_year()
True
Next, try the following examples in the Python Shell to illustrate why we will need to override the __eq__ method to change the meaning of the == operator:
>>> d1 = Date(1, 1, 2014)
>>> d2 = d1
>>> d3 = d1.copy()
>>> id(d1)
430542 # Your memory address may differ.
>>> id(d2)
430542 # d2 is a reference to the same Date that d1 references.
>>> id(d3)
413488 # d3 is a reference to a different Date in memory.
# The == operator tests whether memory addresses are equal.
>>> d1 == d2
True # Shallow copy -- d1 and d2 have the same memory address.
>>> d1 == d3
False # Deep copy -- d1 and d3 have different memory addresses.
1. Write a method tomorrow(self) that changes the called object so that it represents one calendar day after the date that it originally represented.
Notes:
days_in_month = [0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
You can then use this list to quickly determine the number of days in a month. For example, days_in_month[1] is 31 to represent that January (month 1) has 31 days. You can use self.month to index this list to find the number of days in the month that is represented by a Date object.
If you use this approach, be sure to take into account that the days_in_month list is not accurate for Date objects that represent February during leap years. However, you can use an if statement to account for this case when necessary.
Examples:
>>> d = Date(12, 31, 2013)
>>> print(d)
12/31/2013
>>> d.tomorrow()
>>> print(d)
01/01/2014
>>> d = Date(2, 28, 2016)
>>> d.tomorrow()
>>> print(d)
02/29/2016
>>> d.tomorrow()
>>> print(d)
03/01/2016
2. Write a method add_n_days(self, n) that changes the calling object so that it represents n calendar days after the date it originally represented. Additionally, the method should print all of the dates from the starting date to the finishing date, inclusive of both endpoints.
Notes:
Examples:
>>> d = Date(11, 10, 2014)
>>> d.add_n_days(3)
11/10/2014
11/11/2014
11/12/2014
11/13/2014
>>> print(d)
11/13/2014
>>> d = Date(11, 10, 2014)
>>> d.add_n_days(0)
11/10/2014
>>> print(d)
11/10/2014
3. Write a method __eq__(self, other) that returns True if the called object (self) and the argument (other) represent the same calendar date (i.e., if the have the same values for their day, month, and year attributes). Otherwise, this method should return False.
Recall from lecture that the name __eq__ is a special method name that allows us to override the == operatorreplacing the default version of the operator with our own version. In other words, when the == operator is used with Date objects, our new __eq__ method will be invoked!
This method will allow us to use the == operator to see if two Date objects actually represent the same date by testing whether their days, months, and years are the same, instead of testing whether their memory addresses are the same.
After implementing your __eq__ method, try re-executing the following sequence of statements from Task 0:
>>> d1 = Date(1, 1, 2014)
>>> d2 = d1
>>> d3 = d1.copy()
>>> id(d1)
430542 # Your memory address may differ.
>>> id(d2)
430542 # d2 is a reference to the same Date that d1 references.
>>> id(d3)
413488 # d3 is a reference to a different Date in memory.
# The new == operator tests whether the internal date is the same.
>>> d1 == d2
True # Both refer to the same object, so their internal
# data is also the same.
>>> d1 == d3
True # These variables refer to different objects, but
# their internal data is the same!
Notice that we now get True when we evaluate d1 == d3. Thats because the new __eq__ method compares the internals of the objects to which d1 and d3 refer, rather than comparing the memory addresses of the objects.
4. Write a method is_before(self, other) that returns True if the called object represents a calendar date that occurs before the calendar date that is represented by other. If self and other represent the same day, or if self occurs after other, the method should return False.
Notes:
Examples:
>>> ny = Date(1, 1, 2015)
>>> d = Date(11, 10, 2014)
>>> ny.is_before(d)
False
>>> d.is_before(ny)
True
>>> d.is_before(d)
False
5. Write a method is_after(self, other) that returns True if the calling object represents a calendar date that occurs after the calendar date that is represented by other. If self and other represent the same day, or if self occurs before other, the method should return False.
Notes:
6. Write a method diff(self, other) that returns an integer that represents the number of days between self and other.
Notes:
Suggested Approach:
Examples:
>>> d1 = Date(11, 10, 2014)
>>> d2 = Date(12, 19, 2014)
>>> d2.diff(d1)
39
>>> d1.diff(d2)
-39
>>> print(d1) # Make sure the original objects did not change.
11/10/2014
>>> print(d2)
12/19/2014
# Here are two that pass over a leap year.
>>> d3 = Date(12, 1, 2015)
>>> d4 = Date(3, 15, 2016)
>>> d4.diff(d3)
105
7. Write a method day_of_week(self) that returns a string that indicates the day of the week of the Date object that calls it. In other words, the method should return one of the following strings: 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'.
Suggested Approach:
day_of_week_names = ['Monday', 'Tuesday', 'Wednesday', 'Thursday',
'Friday', 'Saturday', 'Sunday']
Examples:
>>> d = Date(11, 10, 2014)
>>> d.day_of_week()
'Monday'
>>> Date(1, 1, 2100).day_of_week()
'Friday'
In this problem, you will continue to create client code using Date objects.
Getting started
Begin by downloading the file ps11pr2.py and opening it in IDLE.
We have included an import statement at the top of ps11pr2.py that imports the Date class from your ps11pr1.py file. Therefore, you will be able to create Date objects and call the methods you defined in Problem 1.
Your tasks
1. Write a function named get_age_on(birthday, other) that accepts two Date objects as parameters: one to represent a persons birthday, and one to represent an arbitrary date. The function should then return the persons age on that date as an integer.
Notes:
Example:
>>> birthday = Date(6, 29, 1994)
>>> d1 = Date(2, 10, 2014)
>>> get_age_on(birthday, d1)
19
>>> d2 = Date(11, 10, 2014)
>>> get_age_on(birthday, d2)
20
2. Write a function nye_counts(start, end) that counts how many times New Years Eve (December 31st) falls on each day of the week between the years start and end. Your function should produce a count for each day of the week, starting with Sunday, and return these counts in a list.
For example, in the three years from 2014 to 2016, New Years Eve falls once on Wednesday, once on Thursday, and once on Saturday, but does not fall on any other days of the week. Thus, your function would return a list with the following contents:
>>> counts = nye_counts(2014, 2016)
[0, 0, 0, 1, 1, 0, 1]
In ps11pr2.py, we have given you code at the start of the function that initializes the list for you. You need to complete the rest of the funtion.
You should use a for loop to iterate over the years in the range given by the parameters start and end. Your loop header should look something like this:
for year in _____________:
where you fill in the blank with an appropriate expression.
The body of the loop should:
Make sure that you return it and do not print it.
Heres another example you can use for testing:
>>> counts = nye_counts(2014, 2113)
>>> counts
[14, 14, 13, 15, 15, 14, 15]