Hampton Court Astrological Clock

Handling dates is difficult.

On paper, it doesn't seem to be a complicated task. After all, computers are good with numbers, and what are days, months and years if not small, supposedly easy-to-deal-with numbers?

But behind this deceptively simple façade lie all sorts of nasty traps. The historical baggage of our civilization means that a good programmer needs to deal with tens of different date formats, multiple calendars, leap years, leap seconds, and some dates simply going missing. One might argue that humanity should've hammered out a more unified, common system long ago — but since we're still at least two hundred years away from adopting stardates, we have to keep accounting for each and every edge case.

Fortunately, we coders are a smart bunch. Most languages have a good date library that takes at least some of that burden off our shoulders. Unfortunately, this being The Daily WTF, today's specimen decided to ignore them all and reinvent the wheel, with quite a few jagged edges...

function checkDate( dateName, dateIn, errorFlag )   {
    var valid  = new String("0123456789.");
    var mon31  = new Array("01","03","05","07","08","10","12");
    var mon30  = new Array("04","06","09","11");
    var minYear= 1970;
    var maxYear= 2018;
    var regExp = /(\d.+)(\W)(\d.+)(\W)(\d.+)/;

        // Check whether all characters are valid
    for (a=0; a<dateIn.length; a++) {
        if (valid.indexOf(dateIn.charAt(a))==-1)    {
            if (errorFlag)  {
                alert ("Please provide a valid date in dd.mm.yyyy format.");
            }
            if (dateName == "dateFrom") {
                window.document.Filter.dateFrom.focus();
            }
            if (dateName == "dateUntil"){
                window.document.Filter.dateUntil.focus();
            }
            return false;   
        }
    }
        // Check whether the input pattern is valid (date format dd.mm.yyyy)
    if (dateIn.search(regExp) ==-1) {
        if (errorFlag)  {
            alert ("Please provide a valid date in dd.mm.yyyy format.");
        }
        if (dateName == "dateFrom") {
            window.document.Filter.dateFrom.focus();
        }
        if (dateName == "dateUntil"){
            window.document.Filter.dateUntil.focus();
        }
        return false;
    }
        // Check whether the date is valid.
    regExp.exec(dateIn);
    if (parseInt(RegExp.$5,10) < minYear || parseInt(RegExp.$5,10) > maxYear)   {
        if (errorFlag)  {
            alert ("Please provide a valid year: " + minYear + "-" + maxYear);
        }
        if (dateName == "dateFrom") {
            window.document.Filter.dateFrom.focus();
        }
        if (dateName == "dateUntil"){
            window.document.Filter.dateUntil.focus();
        }
        return false;
    }
    if (parseInt(RegExp.$3,10) < 01 || parseInt(RegExp.$3,10) > 12) {
        if (errorFlag)  {
            alert ("Please provide a valid month.");
        }
        if (dateName == "dateFrom") {
            window.document.Filter.dateFrom.focus();
        }
        if (dateName == "dateUntil"){
            window.document.Filter.dateUntil.focus();
        }
        return false;
    }
    if (parseInt(RegExp.$1,10) < 01 || parseInt(RegExp.$1,10) > 31) {
        if (errorFlag)  {
            alert ("Please provide a valid day.");
        }
        if (dateName == "dateFrom") {
            window.document.Filter.dateFrom.focus();
        }
        if (dateName == "dateUntil"){
            window.document.Filter.dateUntil.focus();
        }
        return false;
    }
    for (a=0; a<mon31.length; a++)  {
        if ( (parseInt(mon31[a],10) == parseInt(RegExp.$3,10)) && (parseInt(RegExp.$1,10) > 31) )   {
            if (errorFlag)  {
                alert ("Please provide a valid day.");
            }
            if (dateName == "dateFrom") {
                window.document.Filter.dateFrom.focus();
            }
            if (dateName == "dateUntil"){
                window.document.Filter.dateUntil.focus();
            }
            return false;
        }
    }
    for (a=0; a<mon30.length; a++)  {
        if ( (parseInt(mon30[a],10) == parseInt(RegExp.$3,10)) && (parseInt(RegExp.$1,10) > 30) )   {
            if (errorFlag)  {
                alert ("Please provide a valid day.");
            }
            if (dateName == "dateFrom") {
                window.document.Filter.dateFrom.focus();
            }
            if (dateName == "dateUntil"){
                window.document.Filter.dateUntil.focus();
            }
            return false;
        }
    }
    var expYear= parseInt(RegExp.$5,10) % 4;
    if ( (parseInt(RegExp.$3,10) == 02) && (parseInt(RegExp.$1,10) > 29) && expYear == 0)   {
        if (errorFlag)  {
            alert ("Please provide a valid day.");
        }
        if (dateName == "dateFrom") {
            window.document.Filter.dateFrom.focus();
        }
        if (dateName == "dateUntil"){
            window.document.Filter.dateUntil.focus();
        }
        return false;
    }
    if ( (parseInt(RegExp.$3,10) == 02) && (parseInt(RegExp.$1,10) > 28) && expYear  > 0)   {
        if (errorFlag)  {
            alert ("Please provide a valid day.");
        }
        if (dateName == "dateFrom") {
            window.document.Filter.dateFrom.focus();
        }
        if (dateName == "dateUntil"){
            window.document.Filter.dateUntil.focus();
        }
        return false;
    }
}

First thing that stands out is the great implementation of the copy-paste programming paradigm. All nine steps of the date validation process use the same error handling code, with the only difference being the error message — and each and every time, the same snippet is repeated to ensure that nobody dares to change the handler without being at least mildly inconvenienced. But don't be fooled — that doesn't mean the actual code is any better.

Another interesting aspect of the code is the attempt at validating the date format with a regular expression. By itself, the regular expression allows such dates as 999.123.20000, 12?34?56 and 15.5March.2099, while rejecting a perfectly valid 1.09.2015. Luckily, all non-digit, non-period characters are rejected in a loop before that, using the ever-so-helpful indexOf function.

Other WTFs include comparing against 01 and 02 (luckily, 1 and 2 in octal are still 1 and 2 in decimal), making mon30 and mon31 arrays of strings only to parseInt() them in code, and the biggest of all: not spending thirty seconds on Google to find moment.js, date.js or any other JavaScript date library that takes care of each of those rules, and more, in no more than two lines of code.

[Advertisement] BuildMaster allows you to create a self-service release management platform that allows different teams to manage their applications. Explore how!