Lab 02

Lab 2: Computing the Day of the Week

In this lab you will write Python code to print out the current day of the week in Williamstown based on the computer’s internal clock. In doing so, you will gain experience with the following:

Pre-lab: Packing Donut Boxes!

To practice some of the concepts that will show up in this lab, write the solution to this prelab exercise using pencil and paper. Think of pre-labs as a way of exercising your skills in developing and reasoning about algorithms before implementation. Developing this sort of reasoning helps you write code more quickly and with fewer errors.

Problem.

Write a function called min_boxes_needed that takes an integer argument called num_donuts and prints the number of boxes required to pack these donuts. Assume that a donut box can hold a maximum of 12 donuts and that each box is filled to its maximum capacity before starting to pack a new one. Additionally, if there is one box that does not contain the maximum of 12 donuts after packing, your function should also print the number of donuts that are in this box.

A few things to note:

Example outputs from this function are shown below.

>>> min_boxes_needed(72)
Num boxes needed: 6
>>> min_boxes_needed(75)
Num boxes needed: 7
Donuts in final box: 3

Make sure to neatly write this function on a piece of paper with your name on it, and bring this with you to lab. We will come around at the start of the lab session and give you feedback on your solution.

Keeping time()

All computers keep track of time. On many machines, time is represented by the number of seconds that have elapsed since the end of the 1960s in England. The first moments of Thursday, January 1, 1970 had a time less than 1, the first minute had time values less than 60, and the first hour had times less than 3600. The value that represents the current time is a much bigger number.

In Python, we can access this value (called the Universal Coordinated Time, or UTC) through the time() function, which is found in the time module. You can use it in the following way, once you’ve entered interactive Python by typing python3 in the Terminal and pressing enter.

>>> from time import time
>>> time()
1612800680.9091752

The result of calling time is a float value, with the fractional component (digits to the right of the decimal point) representing a fraction of a second. This value is, on most machines, accurate to the millionth of a second. In this lab, we only want to use the integer part of this value, which we can extract using the int() function:

>>> now = int(time())
>>> now
1612800680

In the above example, we’re taking the current time and storing just its whole number portion in a variable called now.

Note: You can quit out of interactive Python by typing exit() or Ctrl + D.

Understanding Remainders and Modulo

Recall that if a and b are integers, then when we compute a // b, the result is always an integer. Similarly, we can compute the remainder, r, of this integer division with the modulo (%) operation: r = a % b.

When a and b are integers, the result of a % b is one of the b possible integer remainders: 0, 1, ..., b-1. This is very convenient if we want to take a large randomly selected integer and reduce it one of a very small range of values.

Our Algorithm

Armed with this knowledge, let’s apply this to our time value to solve our problem. Suppose, for example, we compute the remainder (%) when a time, now, is divided by 60. The result is a value between 0 and 59 representing the number of seconds past the current minute. Similarly, when we divide a time value by 60 using integer division (//), the result is the number of minutes since the end of the 1960s (in England).

Continuing the example from above (where now = 1612800680), we see now must have been captured at 20 seconds past the minute, and 26,880,011 minutes since the end of the 1960s (in England):

>>> secs = now % 60
>>> secs
20
>>> mins = now // 60
>>> mins
26880011

You might now see that it’s possible to keep dividing to compute the number of hours and whole days that have elapsed since the end of the 1960s (in England). The remainder at each step is the portion of the day that can be accounted for by the hours, minutes, and seconds since midnight, although we don’t really need all of those values. Further, once we know the number of days, we can easily continue dividing to compute the number of whole weeks that have passed, and the remainder is simply the number of whole days since “the beginning of the week”.

For example, once we know that 18,666 days have passed, it’s then possible to figure out how many whole weeks have passed (2666), and the remainder (4) is the number of whole days that have passed after “the beginning of the week.” Note that since January 1, 1970 fell on a Thursday, this method treats Thursday as “weekday 0”, Friday as “weekday 1”, etc. Since the remainder is 4 in our example, it suggests the weekday associated with now is Monday.

Because we would like to have the logical start of the week (“weekday 0”) happen on Sunday instead of Thursday, we can adjust the time value, measured as the number of seconds since Thursday, January 1, 1970, to be, instead, the number of seconds from Sunday, January 4. If we make this correction, then the final remainder, as computed above, would be 1 instead of 4. Again, that suggests it’s a Monday (“weekday 1” in this adjusted system). Using this algorithm, you should be able to solve today’s lab problem!

Getting Started in Lab

  1. Open the Terminal and go to your cs134 directory using the cd (change directory) command you learned during Lab 1. (If you are working at a new or different computer, you may want to create a new cs134 directory if one does not already exist.)

  2. Now we must retrieve the files we need to complete this week’s lab. Navigate to https://evolene.cs.williams.edu in your browser and log in using your CS credentials. Under Projects, you should see a repository named cs134-labs/23xyz3/lab02 (where 23xyz3 is your CS username). This repository contains your starter files for this week’s lab.

  3. Clone the repository: find the blue button that is a drop-down menu that says Clone. Click on it and click on the “clipboard” icon (Copy URL) option next to the option to Clone with HTTPS.

    Return to the Terminal and type git clone followed by the URL of the lab project you just copied (you can paste on Windows by pressing Ctrl-V and on a Mac by pressing Command-V). This should look like the following:

    (where 23xyz3 is again a place-holder for your CS username.)

  4. Navigate to your newly created lab02 folder in the Terminal:

  5. Explore the contents of the directory using the ls command in the Terminal.

  6. Open VS Code. Then go to File menu option and choose Open Folder. Navigate to your lab02 directory and click Open. You should see the starter files of today’s lab, including day.py, on the left pane of VS Code.

Today’s Tasks

We would like you to write several functions that will, eventually, allow us to determine today’s day-of-week as described above. Please write your code as a script in the file called day.py, which we have included as part of the starter code in the lab02 folder.

  1. In the file day.py, write a function called utc_day. This function takes a float called time_value as its only argument, where time_value is the number of seconds since the end of the 1960s (in England). Remember your function will first need to convert time_value to an int. The value returned by the function call utc_day(time_value) should be the number (as an int) of the day of the week corresponding to time_value, where Sunday is 0, Monday is 1, etc. Refer to the algorithm above for help.

    You can test your function by running runtests.py in the Terminal:

    python3 runtests.py

    If your function is working correctly, you should see the following output:

    ***************
    testing utc_day
    ***************
    
    utc_day(1612800680.0) should return: 1
    ------------> your version returned: 1
    utc_day(345600.0) should return: 1
    --------> your version returned: 1
    utc_day(345599.0) should return: 0
    --------> your version returned: 0
    
    *****************
    testing local_day
    *****************
    
    local_day(345000.0, 0) should return: 0
    -------------> your version returned: None
    local_day(345000.0, +1) should return: 1
    --------------> your version returned: None
    
    *******************
    testing day_of_week
    *******************
    
    day_of_week(1) should return: Monday
    -----> your version returned: None
    day_of_week(6) should return: Saturday
    -----> your version returned: None

    Notice that all the expected outputs for utc_day match the actual outputs of the utc_day function, so we’re good! Also notice that since we haven’t yet written the functions local_day and day_of_week (which are the next two functions you need to write), those outputs are currently None.

    It is usually a good idea to create more test cases and add them to runtests.py to confirm that your code for utc_day works in a variety of settings. For instance, we might want to make sure it works for the first day after the end of the 1960s (which was a Thursday). The following function implements such a test:

    After adding the function to the runtests.py script, find the block of code that calls all the other utc_day tests and add a call to utc_day_test4():

  2. Write a function local_day that takes two float arguments: time_value and offset. It should return the current day of the week (as an int) for a timezone that is offset hours ahead of UTC. In Williamstown, the offset is -5. That means the actual time is 5 hours, or 18000 seconds, earlier than that reported by time(). To help it compute the current day of the week, local_day should call the function utc_day (the function that you just wrote in the previous step).

    Here are some well-known locales and their UTC offsets:

    Locale UTC Offset Locale UTC Offset
    Chatham Island 12.75 Reykjavik 0
    Aukland 12 Cape Verde -1
    Solomon Islands 11 South Georgia Island -2
    Vladivostok 10 São Paulo -3
    Tokyo 9 Corner Brook -3.5
    Hong Kong 8 Santiago -3
    Jakarta 7 Kalamazoo -5
    Rangoon 6.5 Easter Island -5
    India 5.30 Phoenix -7
    Karachi 5 San Francisco -8
    Abu Dhabi 4 Anchorage -9
    Nairobi 3 Honolulu -10
    Cairo 2 Midway Atoll -11
    Paris 1 Baker Island -12

    Positive offsets are an indication that a new day begins that much earlier in these locations—all east of England—while negative offsets indicate a later end to the day—all to the west. (Some of these may be different when Daylight Savings Time applies…)

    The offset is a float because some timezones involve corrections that are partial hours. Remember to convert hours to seconds before adjusting your time_value.

    For example, your function local_day should behave as follows:

    Again, you can test your code by running python3 runtests.py. You should create additional tests to make sure that your code works on a variety of different cases. Thinking about cases that are “interesting” is a skill that you will develop through practice, and reasoning about so-called “edge cases” will come in handy when debugging your code.

  3. Write a function, day_of_week, that takes a single int argument called day and has a return value of type string. If day is between 0 and 6 (inclusive), the returned string must be the name of the corresponding day. You should use conditional (if-elif-else) statements to accomplish this.

    For example, your function day_of_week should behave as follows:

    Again, you can test your code by running python3 runtests.py. You should create additional tests to make sure that your code works on a variety of different cases. Specifically, your function should explicitly return a value in all cases and that value’s type must always be string.

  4. You are now ready to use your functions to print the current day in Williamstown. Open the file williamstown.py and replace its contents with the following code:

    To run the Python program williamstown.py as a script, in the Terminal type python3 williamstown.py. If it is Monday, then you should get the following output (% represents the prompt in your Terminal).

    % python3 williamstown.py
    It's Monday!

    This should work when run at any time of the day!

  5. Thoroughly test your code before you turn it in. In particular, each of the little tests in runtests.py should produce the indicated answers. You might think about what kinds of mistakes one could make in writing the above functions, and write additional tests that prove to yourself you didn’t make those mistakes. This is an important skill we will continue to develop as the semester progresses.

Submit Your Work

  1. When you are finished adding your functions to the script day.py, make sure you add and commit your work. In your Terminal, type:

    Then you can the push your work (remembering to start the VPN if you’re working off-campus):

  2. You can, if you wish, check that your work is up-to-date on https://evolene.cs.williams.edu. Another way to check that you have committed and pushed all your changes is through the Terminal. In the Terminal in your lab02 directory, type git status:

    It should show your changes (probably in green) that have not been committed, and files (probably in red, if any), that have not been added. If you have successfully committed and pushed all your work, it should say so.

  3. Please edit the README.md file and enter the names of any appropriate students on the Collaboration line. Add, commit, and push this change.

  4. Gradesheet.txt gives a breakdown of the rubric that will be used to evaluate your lab submission. When your assignment is graded, this file will be updated to contain actual feedback as well.