jQuery Unobtrusive Validation of dates in Chrome: US vs GB format

UPDATE 31 Jan 2013: We have encountered this issue after we have updated our MVC3 project to MVC5. A year ago we have moved MVC3 to MVC4. And suddenly Chrome started to insist on incorrect date format, but everywhere else we have set Globalise locale to be en-Gb. The only other alternative was to disable date validations which can also work – I trust server side validation more than I trust JavaScript ;-)


Today I came across the magical problem in Chrome – it ignores locale information for date format. So it always uses US format: “mm/dd/yyyy”. But largest part of the world is not using this messed up month-first approach (wink-wink).

So I spent a large part of the day to figure out how to fix this problem for all browsers. And this is what I have:

function getDateFormat(formatString) {
    var separator = formatString.match(/[.\/\-\s].*?/);
    var parts = formatString.split(/\W+/);
    if (!separator || !parts || parts.length === 0) {
        throw new Error("Invalid date format.");
    }
    return { separator: separator, parts: parts };
}

function MyParseDate(value, format) {
    var parts = value.split(format.separator);

    //NEVER use Date(0), this is locale specific! 
    // * en-GB: 1970-JAN-01
    // * pt-BR: 1969-DEC-31
    //var date = new Date(0);

    //An arbitrary date 1970-DEC-15
    var date = new Date(1970, 11, 15);

    var year = -1;
    var month = -1;
    var day = -1;

    if (parts.length === format.parts.length) {
        for (var i = 0, cnt = format.parts.length; i < cnt; i++) {
            var val = parseInt(parts[i], 10) || 1;

            switch (format.parts[i]) {
                case 'dd':
                case 'd':
                    day = val;
                    break;
                case 'mm':
                case 'm':
                    month = val - 1; // month is zero-based
                    break;
                case 'yyyy':
                    year = val;
                    break;
            }
        }
    }

    // must be in this particular order, otherwise 29th of Feb in leap year would not validate
    date.setFullYear(year);
    date.setMonth(month);
    date.setDate(day);

    console.log("Date validated to " + date.toString());

    // we can't rely on setDay(), setMonth(), setYear() because if value passed in is greater than allowed, it just ticks over
    // to the next month/year. But we can compare if it was ticked over or not. 
    // If numbers provided are the same as resulting number, then date is fine.
    return date.getDate() === day && date.getMonth() === month && date.getFullYear() === year;
}

jQuery.validator.addMethod('date',
    function (value, element, params) {
        if (this.optional(element)) {
            return true;
        }
        try {
            var format = getDateFormat('dd/mm/yyyy');
            var result = MyParseDate(value, format);
            return result;
        } catch (err) {
            console.log(err);
            return false;
        }
    });

Just include this with your javascript.

I’m pretty certain that there is a bug (or a dozen) somewhere in my code. Please feel free to rub it in my face.

I have used code from Robert Gray’s blog post and added validation bit from redphx from discussion on Codeproject