Surprisingly, Unix-based operating systems like MacOS keep track of time in an interesting way: they keep a running total of the number of seconds that have elapsed since the start of the year 1970. 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:
if else
) statements to make
decisions in your code.To practice some of the concepts that will show up in this lab, write the solution to this pre-lab 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.
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:
min_boxes_needed
should be around 4-6 total lines of
code (it can be written using more or fewer lines, but that is a
representative number for guidance)min_boxes_needed
should use some of the arithmetic
operators we have seen, e.g. +, -, *, /, //, %
, as well as
some Boolean logic.min_boxes_needed
should only print (and not
explicitly return a value).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.
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
.
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.
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!
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.)
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.
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:
git clone https://evolene.cs.williams.edu/cs134-labs/23xyz3/lab02.git
(where 23xyz3
is again a place-holder for your CS
username.)
Navigate to your newly created lab02 folder in the Terminal:
cd lab02
Explore the contents of the directory using the ls
command in the Terminal.
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.
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.
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.
To help you check the progress of your implementation, we have
provided a file called runtests.py
. As the name suggests,
it is a python program (it ends with the extension .py
)
that runs tests that we have implemented for you. These “tests” are
implemented as a series of functions that each isolate a single
component of your lab assignment. 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:
def utc_day_test4():
= utc_day(25.0) # 25 seconds after midnight on Jan 1, 1970
weekday print('utc_day(25.0) should return: 4')
print('--> your version returned: ' + str(weekday))
Please add this function to your repository’s
runtests.py
script. After doing so, find the block of code
that calls all the other utc_day
tests and add a call to
utc_day_test4()
:
if __name__ == "__main__":
"""This is where we run all the above tests!"""
print("***************")
print("testing utc_day")
print("***************")
print("")
utc_day_test1()
utc_day_test2()
utc_day_test3()# added call to new function utc_day_test4()
If we don’t add this call to the function, it will never be invoked! Our new test wouldn’t be particularly useful if isn’t called…
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… but we’re not going to worry about that today)
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:
345000.0, 0.0) # 23:50, Sun, Jan 4, 1970, in London; should return 0
local_day(345000.0, +1.0) # 00:50, Mon, Jan 5, 1970, in Paris; should return 1
local_day(-5.0) # should return the current weekday in Williamstown local_day(time(),
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 values 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. For example, if an argument is an
float
, there may be arguments that are in fact valid
float
values but that don’t make sense in the context of
the problem at hand. Is -30
a valid offset argument? It is
a float, but there are only 24 hours in a day: there is no time zone
that requires an ajustment larger than 24. So what should the behavior
of the function be in this case? Adding a test case that confirms that
the behavior of your implementation matches what you expect is an
example of a good test.
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:
1) # should return "Monday"
day_of_week(6) # should return "Saturday" day_of_week(
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
.
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:
# import the functions we will need
from time import time
from day import local_day, day_of_week
# get UTC time
= time()
now
# find the number corresponding to the day of the week
= local_day(now, -5)
day_number
# get the name of the weekday and print it
= day_of_week(day_number)
day_name print("It's " + day_name + "!")
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!
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.
When you are finished adding your functions to the script
day.py
, make sure you add
and
commit
your work. In your Terminal, type:
git add day.py runtests.py williamstown.py
git commit -m "Lab 2 completed"
Then you can the push
your work (remembering to start
the VPN
if you’re working off-campus):
git push
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
:
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.
Please edit the README.md
file and enter the names
of any appropriate students on the Collaboration
line. Add,
commit, and push this change.
Near the bottom of the README.md
, there is a
breakdown of the grading expectations that will form the basis of your
lab’s evaluation. Please keep these in mind as you work through your
lab!
Download a .zip archive of your work. Download
your assignment files for submission by going to your lab repository on
Gitlab, selecting the Download source code
icon (a down
arrow), and select zip
. Doing so should download all of
your lab files into a single zip archive as lab02-main.zip
,
and place it inside your Downloads folder (or to whichever folder is set
as your browser’s default download location).
Submit your work. Navigate to the CS134 course on Gradescope. On your Dashboard, select the appropriate Lab Assignment. Drag and Drop your downloaded zip archive of the Lab Assignment from the previous step, and select ‘Upload’.