CS303E Homework 4

Instructor: Dr. Bill Young
Due Date: Monday, February 12, 2023 at 11:59pm

Assignment: Computing the Day of the Year

There are a number of ways to specify a given day. The most common is the date, but then you'd still have to decide which calendar (Julian, Gregorian, Lunar, Aztec, ...). We won't worry about that question; we'll just assume our standard Gregorian calendar. Given a date in that calendar, our task is to compute the "ordinal date," that is the number of that day within the 365/366 days of the year. This is sometimes called the "current day," "day of the year," or numerous other names. In this assignment you'll compute the ordinal date for any legal date. For example, 12/31/2023 is day 365; however, 12/31/2024 is day 366, because of the leap year.

You will accept from the user a year, month, and day (using three separate input statements. Don't forget to convert the values to integers. You'll validate the inputs and then compute what ordinal day of the year was specified. You can assume that the values entered are integers and that the year provided is legal. However, the month must be in the range 1 to 12 and the day must be appropriate for the month, i.e, in range 1 to the number of days in that month.

To compute the ordinal date for a particular Gregorian date, you'll simply add the day (e.g., 19 in 4/19/2024) to the sum of the days in all of the previous months of the year (e.g., 31 + 29 + 31, since that's the number of days in January, February, and March for 2024). (Consequently, 4/19/2024 is day 110 of the year 2024.) After you compute the day of the year for the specified day, you'll print it out in the format indicated in the sample output below.

In this assignment, you'll be using a lot of if-elif-else statements. It's likely to be somewhat repetitive, but that's deliberate. Later, when we introduce other constructs such as looping, lists, and dictionaries you'll undoubtedly think how much more elegant and compact the solution to this assignment could have been with a richer toolset. But make sure you don't use any of those in this assignment.

Be careful to structure your code so that you exit at the bottom of the code; that is, you can't use an exit, return, or any other way of exiting prematurely. See the discussion of a common error in the Programming Hints section below.

Also, remember that there's code in Slideset 4 to compute whether a given year is a leap year. You are welcome to use any of the several versions you'll find there.

Sample Output

Output of your program should match the following exactly for these inputs. Of course, it should run for arbitrary correct date values.
> python DayOfYear.py
Specify a year: 2024
Specify a month (1-12): 2
Specify a day of the month: 30
Illegal date entered! 
> python DayOfYear.py
Specify a year: 2024
Specify a month (1-12): 1
Specify a day of the month: 31
1/31/2024 is day 31 of the year.
> python DayOfYear.py
Specify a year: 2024
Specify a month (1-12): 2
Specify a day of the month: 29
2/29/2024 is day 60 of the year.
> python DayOfYear.py
Specify a year: 2025
Specify a month (1-12): 2
Specify a day of the month: 29
Illegal date entered! 
> python DayOfYear.py
Specify a year: 2024
Specify a month (1-12): 12
Specify a day of the month: 31
12/31/2024 is day 366 of the year.
> python DayOfYear.py
Specify a year: 2023
Specify a month (1-12): 12
Specify a day of the month: 31
12/31/2023 is day 365 of the year.
> python DayOfYear.py
Specify a year: 2024
Specify a month (1-12): 15
Specify a day of the month: 14
Illegal date entered! 
> 

Turning in the Assignment:

The program should be in a file named DayOfYear.py. Submit the file via Canvas before the deadline shown at the top of this page. Submit it to the assignment weekly-hw4 under the assignments sections by uploading your Python file.

Be sure to test your program before submission. It must also contain a header with the following format:

# File: DayOfYear.py
# Student: 
# UT EID:
# Course Name: CS303E
# 
# Date:
# Description of Program: 

If you submit multiple times to Canvas, it will rename your file name to something like Filename-1.py, Filename-2.py, etc. Don't worry about that; we'll grade the latest version. x

Programming tips

You can assume. When an assignment says that "you can assume" something about the input, that just means that you don't have to check it. If the user enters an input that doesn't meet that assumption, the program can crash or behave badly. That's not your problem. In particular, you can be assured that we won't test your program on any inputs that violate the assumptions.

For many of the assignments this semester, we'll make assumptions about the inputs mainly because you don't yet have the skills necessary to check for certain errors.

But if you get a programming job, you should always validate the inputs as much as possible. In general, it's bad programming practice to allow bad inputs to crash your program; it means that your program is not robust. Later we'll insist that you "validate" most inputs, meaning to assure that they do meet specifications. For example, you could ensure that the value entered for month (remember it's a string) is a legal value by:

    # Check whether the value entered is a string representing one of
    # the numbers from 1 through 12.
    if monthEntered != '1' and monthEntered != '2' and ... monthEntered != '12:
       < handle the error here >
    else:
       # Input is legal, so turn it into an integer.
       month = int( monthEntered )
But if we say that you can assume that it's (a string representing) an integer, then the test becomes much easier:
     month = int( monthEntered )
     if (month < 1 or month > 12):
        < handle the error here>
     else:
        ...
There are more compact and more robust ways to do this, but this would work. Note that we're not checking for all possible errors: spurious input like "abc", etc. Production code should really never crash due to erroneous user input.

Using symbolic constants: If you are using the same literal data multiple times, it's a good idea to define it as a constant. For example, this program uses the number of days in each of the 12 months. Your code wouldn't be very readable if you peppered it with constants like 30 and 31. Another way is to define symbolic (or named) constants near the top of your program and use them throughout your code:

JAN_DAYS = 31
# Must have defined isLeapYear earlier for this to work
FEB_DAYS = ( 29 if isLeapYear else 28 )
...
DEC_DAYS = 31
Then, you might have the following code snippet:
elif month == 4:
    if day > APR_DAYS:
       ...
    else:
        dayNumberInYear = JAN_DAYS + FEB_DAYS + MAR_DAYS + day
By convention, put your symbolic constants near the top of your program and use uppercase names. (From Python's perspective, they're just variables and you could re-assign them; the uppercase convention reminds you not to do that.)

Another place to use symbolic constants is for messages output by your program. If you define them once at the start of your program, you won't risk mistyping them later (as often happens):

BAD_DATE_MESSAGE = "Illegal date entered!"
Then, instead of
   print( "Illegal date entered!" )
you'd type
   print( BAD_DATE_MESSAGE )
That way, if you decide later to change the message you just have to fix it in one place. Strictly speaking, there's no need to do this for a message you're only printing once, but it's probably still a pretty good idea. That way, you have all of the messages collected in one place and it's easy to change one or more without searching through the code.

A common error: An error I frequently see in this assignment is forgetting that an if statement is just another statement. After you execute it, you continue with the following code, unless you explicitly tell your program not to. Students sometimes structure their code for this assignment as follows:

   if date_is_bad:
      print( error_message )
   do some other stuff
In this program, if the date is bad you want to print the error message and stop. But if you structure the code as above, you'll print the error message and then go on to do the other stuff.

There are at least two ways to solve this problem. The first is to put all of your code into a function main() as described in slideset 1. If your code is in a function, you can use return to exit (but not otherwise). This would look like the following:

def main():
  ...
  if date_is_bad:
     print( error_message )
     return
  do some other stuff

The return statement will terminate execution of the main() function and you won't do the other stuff, which is what you really want.

Alternatively, remember that an if statement can have an else clause. Use it so that you don't do the other stuff unless the date isn't bad. That would look like this:

   if date_is_bad:
      print( error_message )
   else:
      do some other stuff

Another Common Error: Often novice programmers try something like:

   if month == 1, 2:
      ...
That doesn't work. It's interpreted by Python as:
   if month == (1, 2):
      ...
which is comparing the month to a tuple of values, which is almost certainly False. (We'll cover tuples later in the semester.)

Similarly,

if month == 1 or 2:
won't work. This is interpreted by Python as:
if (month == 1) or 2:
Any non-zero integer is treated as True in a Boolean context. So that test is always True, which is not what you wanted. What you almost certainly meant was:
   if month == 1 or month == 2:
      ...
(Think about why you'd use and instead of or here.)