Problem 1: A Date class

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:

  • The __init__(self, new_month, new_day, new_year) method, which is the constructor for Date objects. In other words, this is the method that Python uses when making a new Date object. It defines the attributes that compose a Date object (month, day, and year) and accepts parameters to set an objects attributes to some initial values.
  • The __str__(self) method, which returns a string representation of a Date object. This method will be called when an object of type Date is printed. This method formats the month, day, and year that represent a Date object into a string of the form 'mm/dd/yyyy' and returns it.
  • The is_leap_year(self) method, which returns True if the called object is in a leap year, and False otherwise. In other words, when we create a Date object and call its is_leap_year method, the method will return whether that specific Date object falls in a leap year.
  • The copy(self) method, which returns a newly-constructed object of type Date with the same month, day, and year that the called object has. This allows us to create deep copies of Date objects.

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:

  • This method should not return anything. Instead, it should change the value of one or more variables inside the called object.
  • Since we are advancing the Date object by one day, self.day will change. Depending on what day it is, self.month and self.year may also change.
  • You may find it helpful to use the following list by declaring it on the first line of the method:
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:

  • Dont copy code from the tomorrow method. Instead, consider how you could call the tomorrow method in a loop to implement this method.
  • This method should work for any nonnegative integer n.
  • If n is 0, only the starting date should be printed.

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:

  • This method is similar to the __eq__ method that you have written in that you will need to compare the years, months, and days to determine whether the calling object comes before other.

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:

  • There are two ways of writing this method. You can either emulate your code for is_before OR you can think about how you could call __eq__ (==) and is_before to make writing this method very simple.

6. Write a method diff(self, other) that returns an integer that represents the number of days between self and other.

Notes:

  • This method should not change self nor should it change other during its execution.
  • The sign of the return value is important! In particular:
    • If self and other represent the same calendar date, this method should return 0.
    • If self is before other, this method should return a negative integer equal to the number of days between the two dates.
    • If self is after other, this method should return a positive integer equal to the number of days between the two dates.

Suggested Approach:

  • Since this method should not change the original objects, you should first create true copies of self and other.
  • Then, use is_before or is_after to figure out which date comes first.
  • You can use the tomorrow method that you have already written in a similar way to how you used it in the add_n_days method to count up from one date to another. However, unlike in that method, in diff it is not clear how many times you need to call tomorrow to get an appropriate count from one date to the other. What kind of loop is well-suited for this kind of problem?
  • Once you know how many days separate the two values, you can again use is_before or is_after to figure out whether the returned result should be positive or negative.
  • You should not try to subtract years, months, and days between the two dates. This technique is too prone to mistakes.
  • You should also not try to use add_n_days to implement your diff method. Checking all of the possible difference amounts will be too slow!

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:

  • Try using the diff method from a known date. For example, how could it help to know the number of days between the called object and a Date object representing Monday, November 10, 2014? How might the modulus (%) operator help?
  • It will be useful to copy and paste the following list to the first line of your method:
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'

Problem 2: more Date clients

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:

  • You can assume that the other parameter will represent a date on or after the birthday date.
  • It may be helpful to construct a new Date object that represents the persons birthday in the year of other. That way, you can determine whether the persons birthday has already passed in the year of other, and use that information to calculate the age.

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:

  • Create a Date object to represent New Years Eve for the current value of year.
  • Call the day_of_week method for that Date object to determine the day of the week on which New Years Eve falls in that year.

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]
Academic Honesty!
It is not our intention to break the school's academic policy. Posted solutions are meant to be used as a reference and should not be submitted as is. We are not held liable for any misuse of the solutions. Please see the frequently asked questions page for further questions and inquiries.
Kindly complete the form. Please provide a valid email address and we will get back to you within 24 hours. Payment is through PayPal, Buy me a Coffee or Cryptocurrency. We are a nonprofit organization however we need funds to keep this organization operating and to be able to complete our research and development projects.