Calculating the Day of the Week with Zeller’s Congruence in JavaScript

Today I'll write an implementation in JavaScript of Zeller's Congruence, a simple and elegant little formula to carry out the seemingly complex task of calculating the day of the week (Monday, Tuesday etc.) from a date.

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 JavaScript 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.

Zeller's Congruence

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

Zeller's Congruence

h=(q+((13*(m+1))/5)+Y+(Y/4)-(Y/100)+(Y/400))%7

Starting to Code

The project consists of an HTML page, a small JavaScript file containing a function to output text to the page, a graphic, a CSS file and a JavaScript file called zellerscongruence.js which does all the work.

The files can be downloaded as a zip file from the Downloads page, or you can clone or download the Github repo.

Source Code Links

ZIP File
GitHub

This is zellerscongruence.js.

zellerscongruence.js

window.onload = function()
{
    writeToConsole("Zeller's Congruence<br/>", "console");
    writeToConsole("-------------------<br/><br/>", "console");

    showDatesAndDays();
}


function showDatesAndDays()
{
    let ZellerDayNames = ["Sat", "Sun", "Mon", "Tue", "Wed", "Thu", "Fri"];

    let date;
    let h;

    for(let i = 0; i < 20; i++)
    {
        date = new Date(Date.now() * Math.random());

        writeToConsole(date.toDateString() + "<br/>", "console");

        h = zellerGregorian(date);

        writeToConsole("Day according to Zeller's Congruence: " + ZellerDayNames[h] + "<br/><br/>", "console");
    }
}


function zellerGregorian(date)
{
    let h = 0; // day of week, Saturday = 0

    let q = date.getDate(); // day of month
    let m = date.getMonth(); // month, 3 to 14 = March to February
    let Y = 1900 + date.getYear(); // year is 1900-based

    // adjust month to run from 3 to 14 for March to February
    if(m <= 1)
    {
        m+= 13;
    }
    else
    {
        m+= 1;
    }

    // and also adjust year if January or February
    if(date.getMonth() <= 1)
    {
        Y--;
    }

    // Calculate h as per Herr Zeller
    h = (q + Math.floor(((13 * (m + 1)) / 5)) + Y + Math.floor((Y / 4)) - Math.floor((Y / 100)) + Math.floor((Y / 400))) % 7;

    return h;
}

window.onload

Here we simply print out a heading and call the showDatesAndDays function.

showDatesAndDays

This function starts off with an array to map the values generated by the next function, zellerGregorian, to actual day names.

Then within a loop we create 20 random dates which are printed, including the JavaScript calculated days. We then call zellerGregorian and print out the result to compare.

zellerGregorian

This function implements a version of the Zeller Algorithm for the Gregorian Calendar. As it is rather fiddly I have scattered comments around quite liberally.

Trying it out

If you open zellerscongruence.htm in your browser you will see this:

You will be pleased to see that JavaScript and Zeller days are the same for all dates.