Calculating the Day of the Week with Zeller’s Congruence in Python
A implementation in Python of Zeller's Congruence, a simple and elegant little formula to calculate the day of the week from a date.
![](https://substackcdn.com/image/fetch/w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F218fbca8-1b89-4425-9891-8d5c0b9790ce_600x600.jpeg)
Christian Zeller and His Clever Little Formula
The formula was developed by German mathematician Christian Zeller (24 June 1822, a Monday, to 31 May 1899, a Wednesday). I have always been impressed that a task which you might think highly complex and solvable only by some sort of brute-force iteration can actually be reduced to a short and elegant formula. Of course you wouldn't use the code in this post in production applications as Python provides the functionality already, but I hope you agree it is a worthwhile programming exercise.
There are several versions of the formula, including two sub-versions of each, one for the Gregorian calendar and one for the Julian. This is the Gregorian version of the formula I will be implementing, in an image stolen from Wikipedia.
Note that the symbols like an elongated letter L and it's mirror-image twin denote "floor", ie the result of the term is rounded down to the nearest integer. The full Wikipedia article is here and is worth at least skimming.
Rewritten in a more computer-friendly way we get:
h=(q+((13*(m+1))/5)+Y+(Y/4)-(Y/100)+(Y/400))%7
Starting to Code
Create a new folder and within it create the following empty files. You can also download the source code as a zip or clone/download from Github if you prefer.
zeller.py
main.py
Open zeller.py and type or copy/paste the imports and first function.
zeller.py part 1
import datetime
import random
import calendar
import math
def showdatesanddays():
"""
Creates a selection of random dates and runs the
Zeller Algorithm on them,
printing out the date, and then the day according
to Python and Zeller.
"""
# Zeller gives a value 0 to 6 representing Saturday to Friday
zellerdays = ["Saturday", "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday"]
for i in range(0, 19):
d = datetime.date.fromtimestamp(random.randint(1, 1000000000))
print("Date: " + str(d))
print("Python: " + calendar.day_name[d.weekday()])
print("Zeller: " + zellerdays[zellergregorian(d)])
print("------------------")
The showdatesanddays function firstly creates a list of day names starting with Saturday. These correspond with the values returned by Zeller's Algorithm. We need to do this as "Python Weeks" start on a Monday.
After that we enter a for loop which generates a number of random dates using the fromtimestamp and randint functions. The fromtimestamp function creates a date from the number of seconds from 1/1/1970, and using 1 billion as the upper limit of randint will create dates between 1/1/1970 and 9/9/2001, which is adequate to demonstrate the algorithm.
After that we just need to print out the date and the corresponding day name using Python's calendar.day_name list. Finally we print out the day name again from the zellerdays list, using a call to our zellergregorian function as the list index.
We can now implement that function so enter or paste it into zeller.py.
zeller.py part 2
def zellergregorian(d):
"""
Runs the Zeller algorithm on the given date
and returns the day index 0 to 6 for Saturday to Friday.
"""
q = d.day
m = d.month
Y = d.year
# adjust month to run from 3 to 14 from March to February
if m <= 2:
m+= 12
# and also adjust year if January or February
if d.month <= 2:
Y -= 1
h = (q + math.floor((13 * (m + 1)) / 5) + Y + math.floor(Y / 4) - math.floor(Y / 100) + math.floor(Y / 400)) % 7
return h
In zellergregorian we first declare three variables for day, month and year, setting them to the values from the date function argument. Of course we could just use the values in the date directly but I wanted to use variable names in the formula which matched those used by Herr Zeller. (I don't know why day is called "q"!)
We then need to adjust month and year if the month of our date is January or February as the algorithm uses years running from March to February, indexed 3 to 14.
Finally we calculate the day index - if you examine this line carefully you will see it implements the algorithm given above exactly. Note the use of the math.floor function to round down various terms to the nearest integer.
Now open main.py and after a line to print the heading we call showdatesanddays.
import zeller
def main():
"""
Zeller's Congruence calculates the day of the
week from the given date.
"""
print("-----------------------------------")
print("| codedrome.com |")
print("| Zeller's Congruence: |")
print("| Calculating the Day of the Week |")
print("-----------------------------------")
zeller.showdatesanddays()
if __name__ == "__main__":
main()
Now we can run the program with this command...
python3 main.py
...which will give us this output.
You will be pleased to see that Python and Zeller days are the same for all dates. Yours will be different as the dates are generated randomly. (Actually there’s a diminutive chance the might be the same!)