CS115 – Hw 12
How to get a Date!
The Date class
|
This week’s homework will guide you through creating a class named Date which will allow you to do |
|
computations on dates. |
Grab the starting file date_template.py
Save your copy as date.py (and change Hw 11 to Hw 12 if you like).
Take a moment to look over this file as it stands so far…
Notice that in this Date class there are three attributes, also known as data members:
- A data member holding the month (this is self.month)
- A data member holding the day of the month (this is self.day)
- A data member holding the year (this is self.year)
Note that self is used to denote any object value of class Date! Methods are just functions…
Object-oriented programming tends to have some of its own names for familiar things. For
example, method is the “OOP” name for function. In particular, a method is a function whose first input is self!
Note that the Date class has an __init__ method and a __str__ method. As we’ve discussed in class, Python expects to see these special methods in virtually every class. The double underscores before and after these method names indicate that these methods are special methods that Python knows to look for. In the case of __init__, this is the method that Python looks for when making a new Date object. In the case of __str__, this is the method that Python looks for when it needs
to represent the object as a string. Notice the line
s = ‘%02d/%02d/%04d’ % (self.month, self.day, self.year)
in the str method. This constructs a string with the month, day, and the year, formatted very nicely to
have exactly two digits places for the month, two digit places for the day, and four for the year. If you
would like to learn more about fancy string formatting, you can look at the string documentation page.
|
We’ve also defined our own isLeapYear method. There are no double-underscores here, so Python |
|
didn’t “expect” this method, but it certainly doesn’t “object” to it either (sorry for the bad pun – we clearly |
|
have no class!). |
CS115 – Hw 12
Note on “method”
Traditionally, functions called by objects are called methods. There is no really good reason for this. They
are functions—the only thing special about them is that they are defined in a class and they are called
after a dot or period following the name of an object. For example, you might try these:
>>> d = Date(11, 8, 2011) >>> d.isLeapYear() False
>>> d2 = Date(3, 15, 2012) >>> d2.isLeapYear()
True
>>> Date(1, 1, 1900).isLeapYear() False
# no variable needed!
What’s up with self?
One odd thing about the above example is that three different objects of type Date are calling
the same isLeapYear code. How does the isLeapYear method tell the different objects apart?
|
The method does not know the name of the variable that calls it! In fact, in the third example, there |
|
is no variable name! The answer is self. The self variable holds the object that calls the method, |
|
including all of its data members. This is why self is always the first input to all of the methods in the Date class (and in any class that you define!): because self is how the method can access the individual data members in the object that called it. Please notice also—this means that a method always has at least one input argument, namely self. However, this value is passed in implicitly when the method is called. For example, isLeapYear is invoked in the example above as Date(1,1,1900).isLeapYear(), and Python automatically passed self, in this case the object Date(1,1,1900), as the first input to the isLeapYear method. |
Testing your initial Date class
Just to get a feel for how to test your new datatype, try out the following calls:
# create an object named d with the constructor >>> d = Date(11, 9, 2011)
# show d's value >>> d 11/09/2011
# a printing example
>>> print ‘Wednesday is’, d Wednesday is 11/09/2011
copy and equals
• copy(self):
Here is the code for this method:
CS115 – Hw 12
# create another object named d2 # of *the same date*
>>> d2 = Date(11, 9, 2011)
# show its value >>> d2 11/09/2011
# are they the same? >>> d == d2 False
# look at their memory locations
>>> id(d) # return memory address
413488 # your result will be different…
>>> id(d2) # again…
430408 # this should differ from above!
# check if d2 is in a leap year — it’s not! >>> d2.isLeapYear()
False
# yet another object of type Date # next year’s NYDay
>>> d3 = Date(1, 1, 2012)
# check if d3 is in a leap year >>> d3.isLeapYear()
True
|
For this part, you should paste the following two methods (code provided) into your Date class and then |
|
test them. We are providing the code so that you have several more examples of what it is like to define |
|
functions inside a class: |
def copy(self):
”’Returns a new object with the same month, day, year
as the calling object (self).”’
dnew = Date(self.month, self.day, self.year) return dnew
This method returns a newly constructed object of type Date with the same month, day, and year that the calling object has. Remember that the calling object is named self, so the calling object’s month
is self.month, the calling object’s day is self.day, and so on.
• equals(self, d2):
Here is the code for this method:
CS115 – Hw 12
Since you want to create a newly constructed object, you need to call the constructor! This is what you see happening in the copy method.
Try out these examples, which use this year’s New Year’s Day…
>>> d = Date(1, 1, 2011) >>> d2 = d.copy() >>> d 01/01/2011
>>> d2 01/01/2011
>>> id(d) 430568 >>> id(d2) 413488 >>> d == d2 False
# your memory address may differ
# but d2 should be different from d! # thus, this should be false…
def equals(self, d2):
”’Decides if self and d2 represent the same calendar date,
whether or not they are the in the same place in memory.”’ return self.year == d2.year and self.month == d2.month and \
self.day == d2.day
This method should return True if the calling object (named self) and the input (named d2) represent
the same calendar date. If they do not represent the same calendar date, this method should
return False. The examples above show that the same calendar date may be represented at multiple
locations in memory—in that case the == operator returns False. This method can be used to see if two
objects represent the same calendar date, regardless of whether they are at the same location in
memory.
Try these examples (after reloading your file) to get the hang of how this equals method works: >>> d = Date(1, 1, 2011)
>>> d2 = d.copy()
>>> d
01/01/2011
>>> d2 01/01/2011
>>> d == d2
CS115 – Hw 12
False # this should be False!
>>> d.equals(d2)
True # but this should be True!
>>> d.equals(Date(1, 1, 2011)) # no name needed! True
>>> d == Date(1, 1, 2011) # tests memory addresses False # so it should be False
|
Now, the next part of the homework asks you to implement a few methods for the Date class from |
|
scratch. Be sure to add a docstring to each of the methods you write! (Recall that the term method refers to a function that is a member of a user-defined class… .) |
tomorrow
First, add the following method to your Date class (you may have notes that help…):
• tomorrow(self):
This method should NOT RETURN ANYTHING! Rather, it should change the calling object so that it represents one calendar day after the date it originally represented. This means
that self.day will definitely change. What’s more, self.month and self.year might change.
• You may find the list DIM = (0,31,28,31,30,31,30,31,31,30,31,30,31) a useful line to have at the very top of your function… !
That list makes it easy to determine how many days there are in any particular month (self.month). Testing tomorrow
|
To test your tomorrow method, you should try several test cases of your own design. Here are a couple |
|
of randomly chosen ones to get you started: >>> d = Date(12, 31, 2010) >>> d 12/31/2010 >>> d.tomorrow() >>> d |
CS115 – Hw 12
01/01/2011
>>> d = Date(2, 28, 2012) >>> d.tomorrow() >>> d 02/29/2012
>>> d.tomorrow() >>> d 03/01/2012
yesterday
Next, add the following method to your Date class:
• yesterday(self):
Like tomorrow, this method should not return anything. Again, it should change the calling object so that it represents one calendar day before the date it originally represented.
Again, self.day will definitely change, and self.month and self.year might change.
Testing yesterday
To test your yesterday method, you should try several test cases of your own design. Here are the
reverses of the previous tests:
>>> d = Date(1, 1, 2011) >>> d 1/1/2011 >>> d.yesterday()
>>> d 12/31/2010
>>> d = Date(3, 1, 2012) >>> d.yesterday() >>> d 02/29/2012
>>> d.yesterday() >>> d 02/28/2012
addNDays
Next, add the following method to your Date class:
• addNDays(self, N):
This method only needs to handle nonnegative integer inputs N. Like the tomorrow method, this method should not return anything. Rather, it should change the calling object so that it represents N calendar days after the date it originally represented.
Testing addNDays
To test your addNDays method, you should try several test cases of your own design. Here are a couple
CS115 – Hw 12
|
Don’t copy code from the tomorrow method! Instead, consider how you could call the tomorrow method |
|
inside a for loop in order to implement this! In addition, this method should print all of the dates from the starting date to the finishing date, inclusive of both endpoints. Remember that the line print self can be used to print an object from within one of that object’s methods! See below for examples of output. |
to start with –
>>> d = Date(11, 9, 2011) >>> d.addNDays(3) 11/09/2011 11/10/2011
11/11/2011 11/12/2011 >>> d 11/12/2011
>>> d = Date(11, 11, 2011) # the near future >>> d.addNDays(1283)
11/11/2011
11/12/2011
… lots of dates skipped … 5/16/2015
5/17/2015
>>> d
5/17/2015 # graduation! (if not before…)
You can check your own additions with this website: http://www.timeanddate.com/date/dateadd.html. Note that 1752 was a weird year for the United States/colonies’ calendar—especially September! However, your Date class does not need to handle these unusual situations (and shouldn’t do so, so that
we can test things consistently!).
subNDays
CS115 – Hw 12
Next, include the following method in your Date class: • subNDays(self, N):
This method only needs to handle nonnegative integer inputs N. Like the addNDays method, this method should not return anything. Rather, it should change the calling object so that it represents N calendar days before the date it originally represented. You might consider
using yesterday and a for loop to implement this!
Testing subNDays
Try reversing the above test cases!
isBefore
Next, add the following method to your Date class:
• isBefore(self, d2):
This method should return True if the calling object is a calendar date before the input
named d2 (which will always be an object of type Date). If self and d2 represent the same day, this method should return False. Similarly, if self is after d2, this should returnFalse.
Testing isBefore
|
In addition, this method should print all of the dates from the starting date to the finishing date, inclusive |
|
of both endpoints. Again, this mirrors the addNDays method. See below for examples of the output. |
|
It might be worth mentioning that you could pass a string or a float or an integer as the input to |
|
this isBefore method. In this case, your code will likely raise a TypeError. We won’t do this to your |
|
code! Python relies on the user to keep track of the types being used. |
To test your isBefore method, you should try several test cases of your own design. Here are a few to
get you started:
>>> d = Date(11, 11, 2011) >>> d2 = Date(1, 1, 2012) >>> d.isBefore(d2) True
>>> d2.isBefore(d) False >>> d.isBefore(d) False
isAfter
Next, add the following method to your Date class: • isAfter(self, d2):
This method should return True if the calling object is a calendar date after the input
named d2 (which will always be an object of type Date). If self and d2 represent the same day, this method should return False. Similarly, if self is before d2, this should returnFalse.
Testing isAfter
diff
Next, add the following method to your Date class:
• diff(self, d2):
This method should return an integer representing the number of days between self and d2.
You can think of it as returning the integer representing
self – d2
To test your isAfter method, you should try several test cases of your own design. For example, you
CS115 – Hw 12
|
You can emulate your isBefore code here OR you might consider how to use |
|
the isBefore and equals methods to write isAfter. |
might reverse the examples shown above for isBefore.
But dates are more complicated than integers!! So, implementing diff will be more involved than this!
See below for hints…
|
One crucial point: this method should NOT change self NOR should it change d2! You should |
|
create and manipulate copies of self and d2, so that the originals remain unchanged. Also, The sign of the return value is important! Consider these three cases: |
- If self and d2 represent the same calendar date, this method should return 0.
- If self is before d2, this method should return a negative integer equal to the number of days
between the two dates.
- If self is after d2, this method should return a positive integer equal to the number of days
between the two dates.
Two approaches not to use!
- First, don’t try to subtract years, months, and days between two dates…this is way too error- prone.
- By the same token, however, don’t use addNDays or subNDays to implement
your diff method. Checking all of the possible difference amounts will be too slow! Rather, implement diff in the same style as those two methods: namely,
using yesterday and/or tomorrowand loops.How do we do this?
- You will want to use the tomorrow and yesterday methods you’ve already written — inside a while loop!
- The test for the while loop may be something like while day1.isBefore( day2 ): or it may use isAfter…
- Use yesterday or tomorrow within the loop and count the number of times you need to loop before it finishes…
Testing diff
To test your diff method, you should try several test cases. Here are two relatively nearby pairs of dates:
CS115 – Hw 12
- >>> d = Date(11,9,2011)
- >>> d2 = Date(12,16,2011) >>> d2.diff(d)
37>>> d.diff(d2)
-37
>>> d
11/09/2011
>>> d2 # make sure they did not change… 12/16/2011# Here are two that pass over a leap year…
>>> d = Date(11,9,2011) >>> d3 = Date(5, 18, 2012) >>> d3.diff(d)
191
And here are two relatively distant pairs of dates:
>>> d = Date(11, 9, 2011) >>> d.diff(Date(1, 1, 1899)) 41219
>>> d.diff(Date(1, 1, 2101)) -32560
You can check other differences at www.timeanddate.com/date/duration.html . dow
Next, add the following method to your Date class: • dow(self):
This method should return a string that indicates the day of the week (dow) of the object (of type Date) that calls it. That is, this method returns one of the following strings: ‘Monday’, ‘Tuesday’, ‘Wednesday’, ‘Thursday’, ‘Friday’, ‘Saturday’, or ‘Sunday’.
Testing dow
CS115 – Hw 12
|
Hint: How might it help to find the diff from a known date, like Wednesday, November 9, 2011? How |
|
might the mod (%) operator help? |
To test your dow method, you should try several test cases of your own design. Here are a few to get you
started:
>>> d = Date(12, 7, 1941) >>> d.dow() 'Sunday'
>>> Date(10, 28, 1929).dow() ‘Monday’
>>> Date(10, 19, 1987).dow() ‘Monday’
>>> d = Date(1, 1, 2100) >>> d.dow() 'Friday'
# dow is appropriate: crash day! # ditto!
You can check your days of the week at www.timeanddate.com/calendar/ by entering the year you’re interested in.
Congratulations — you now have a Date class whose objects can compute the differences and the days of the week for any calendar dates at all!




