Решил поделиться накопленными JS-велосипедами форматно-логического контроля вводимых пользователем данных о паспорте РФ. Список проверок:

Проверка серии и номера

// проверка серии паспорта
function isPassSerValid(val4check) {
    if (val4check.match(/^\d{4}$/)) {
        return true;
    }
    else { return false; }
}

// проверка номера паспорта
function isPassNumValid(val4check) {
    if (val4check.match(/^\d{6}$/)) {
        return true;
    }
    else { return false; }
}

Проверка даты на соответствие формату

// проверка даты по формату ДД.ММ.ГГГГ
function isDateCorrect(val4check) {
    // сначала по регулярному выражению
    if (!val4check.match(/^\d\d.\d\d.\d\d\d\d$/)) { return false; }
    // год, месяц, день
    year = parseInt(val4check.substr(6, 4), 10);
    month = parseInt(val4check.substr(3, 2), 10);
    day = parseInt(val4check.substr(0, 2), 10);

    // 31 день
    if (day > 31) { return false; }
    // 12 месяцев
    if (month > 12) { return false; }

    // 30 дней
    if (month == 4
        || month == 6
        || month == 9
        || month == 11)
    { if (day > 30) return false; }

    // февраль
    if (month == 2) {
        if (new Date(year, 1, 29).getMonth() === 1) { if (day > 29) { return false; } }
        else { if (day > 28) { return false; } }
    }

    // это кошерная дата
    return true;
}

Проверка срока действия паспорта

Пока писал эту функцию, сначала допустил ошибку - не учитывал наступление дня рождения у чувака в текущем году, из-за чего она выдавала преждевременное истечение срока действия паспорта. И выяснилось, что такая же ошибка присутствует в проверке паспорта на сайте ФМС (подробнее в этой записи).

// @passportDate - дата выдачи паспорта в формате ДД.ММ.ГГГГ
// @dudeDate - дата рождения чувака в формате ДД.ММ.ГГГГ
function checkPassportDate(passportDate, dudeDate)
{
    var dob = new Date(dudeDate.replace(/(\d{2}).(\d{2}).(\d{4})/, "$3-$2-$1"));
    var pssprtDate = new Date(passportDate.replace(/(\d{2}).(\d{2}).(\d{4})/, "$3-$2-$1"));

    var pDate20 = new Date(dob);
    pDate20.setFullYear(pDate20.getFullYear() + 20);
    var pDate45 = new Date(dob);
    pDate45.setFullYear(pDate45.getFullYear() + 45);

    var ageDude = parseInt(yearsDiff(new Date(dudeDate.replace(/(\d{2}).(\d{2}).(\d{4})/, "$3-$2-$1"))));

    // первая смена паспорта
    if (ageDude >= 20 && ageDude < 45) {
        if (pssprtDate < pDate20) { return false; }
    }

    // вторая смена паспорта
    if (ageDude >= 45) {
        if (pssprtDate < pDate45) { return false; }
    }

    return true;
}

// разница в годах между текущей датой и @dt
function yearsDiff(dt)
{
    if (dt > new Date()) { return 0; }

    var crntDate = new Date();

    var yearDiff = parseInt(crntDate.getFullYear() - dt.getFullYear());
    
    // прошёл уже текущий год или ещё нет
    var dat4check = new Date(dt);
    dat4check.setFullYear(crntDate.getFullYear());
    if (dat4check > crntDate) { yearDiff--; }

    if (yearDiff <= 0) { return 0; }

    if (yearDiff === 1)
    {
        var monthDiff = parseInt(crntDate.getMonth() - dt.getMonth());
        if (monthDiff >= 0)
        {
            if (monthDiff == 0) {
                var dayDiff = parseInt(crntDate.getDate() - dt.getDate());
                if (dayDiff > 0) { return yearDiff; }
                else { return 0; }
            }
            else {
                return crntDate.getFullYear() - dt.getFullYear();
            }
        }
        else { return 0; }
    }
    else { return yearDiff; }
}