Валидация корректности введенной даты

Работа с датами в интерфейсах ввода почти всегда требует разделения двух задач: форматирования и валидации. Библиотека Cleave.js решает первую задачу — структурирование ввода, автоматическую расстановку разделителей и ограничение допустимых символов. Однако корректность самой даты (существует ли 31 февраля, попадает ли дата в допустимый диапазон, соответствует ли логике приложения) требует дополнительной обработки.

Cleave.js предоставляет инструменты для создания предсказуемого шаблона ввода даты, но не является полноценным валидатором календарных значений. Поэтому проверка корректности даты строится на комбинации встроенных возможностей и внешней логики.


Формирование структуры даты как основа валидации

Первый уровень контроля достигается через жёсткое определение структуры ввода:

const cleave = new Cleave(input, {
    date: true,
    delimiter: '.',
    datePattern: ['d', 'm', 'Y']
});

В этом режиме ввод автоматически разбивается на блоки:

  • день (2 символа)
  • месяц (2 символа)
  • год (4 символа)

Такая структура уже снижает количество некорректных значений, поскольку пользователь физически ограничен форматом. Однако Cleave.js не запрещает логически некорректные комбинации вроде 39.19.2025 или 00.00.0000.


Ограничение диапазонов через datePattern и блоки

Поведение блоков можно уточнить через конфигурацию:

const cleave = new Cleave(input, {
    date: true,
    datePattern: ['d', 'm', 'Y'],
    delimiter: '.'
});

Каждый блок работает как независимая сущность, ограниченная количеством символов. Это важно для первичной фильтрации ошибок ввода, но не для семантической проверки.

Пример проблемы:

  • пользователь вводит 31.02.2024
  • формат корректен
  • значение даты не существует

Разделение форматирования и семантической проверки

После того как Cleave.js сформировал строку, её необходимо преобразовать в объект даты и проверить на валидность.

Типовой подход:

function parseDate(value) {
    const [day, month, year] = value.split('.').map(Number);
    return new Date(year, month - 1, day);
}

Однако стандартный Date в JavaScript обладает особенностью автоматической нормализации:

new Date(2024, 1, 31) // 2 марта 2024

Это означает, что простое создание объекта даты не является проверкой корректности.


Проверка «реальности» даты

Корректная валидация требует сравнения исходных значений с результатом нормализации:

function isValidDate(value) {
    const [day, month, year] = value.split('.').map(Number);

    const date = new Date(year, month - 1, day);

    return (
        date.getFullYear() === year &&
        date.getMonth() === month - 1 &&
        date.getDate() === day
    );
}

Этот подход позволяет выявлять:

  • несуществующие дни месяца
  • переполнение месяца
  • некорректные комбинации вроде 00.12.2024

Интеграция проверки через onValueChanged

Cleave.js позволяет отслеживать изменения ввода:

const cleave = new Cleave(input, {
    date: true,
    datePattern: ['d', 'm', 'Y'],
    delimiter: '.',
    onValueChanged: function (e) {
        const value = e.target.value;

        if (value.length === 10) {
            const valid = isValidDate(value);

            if (!valid) {
                input.classList.add('error');
            } else {
                input.classList.remove('error');
            }
        }
    }
});

Здесь важно учитывать момент частичного ввода. До завершения строки проверка может давать ложные результаты, поэтому валидация обычно активируется только при полном заполнении.


Обработка незавершённых значений

В процессе ввода строка может находиться в промежуточном состоянии:

  • 1.
  • 12.0
  • 12.03.2

Такие значения нельзя проверять как полноценную дату. Обычно применяется правило минимальной длины:

if (value.length < 10) {
    return;
}

или более гибко:

const isComplete = /^\d{2}\.\d{2}\.\d{4}$/.test(value);

Учет диапазонов дат (минимум и максимум)

В прикладных сценариях требуется ограничение диапазона допустимых дат:

const minDate = new Date(2000, 0, 1);
const maxDate = new Date(2025, 11, 31);

Проверка:

function isInRange(date) {
    return date >= minDate && date <= maxDate;
}

Интеграция с базовой проверкой:

function validateDate(value) {
    const [day, month, year] = value.split('.').map(Number);
    const date = new Date(year, month - 1, day);

    const isReal =
        date.getFullYear() === year &&
        date.getMonth() === month - 1 &&
        date.getDate() === day;

    return isReal && date >= minDate && date <= maxDate;
}

Работа с високосными годами

Особое внимание требуется к февралю. Ошибка часто проявляется в виде:

  • 29.02.2023 (некорректно)
  • 29.02.2024 (корректно)

Проверка високосного года:

function isLeapYear(year) {
    return (year % 4 === 0 && year % 100 !== 0) || year % 400 === 0;
}

Хотя при использовании метода сравнения через Date эта логика уже косвенно учитывается, отдельная функция полезна для более явных сообщений об ошибках.


Сценарий с пользовательской маской и строгой валидацией

При необходимости можно усилить контроль за вводом:

const cleave = new Cleave(input, {
    date: true,
    datePattern: ['d', 'm', 'Y'],
    delimiter: '.',
    numericOnly: true
});

Дальнейшая проверка полностью переносится в пользовательскую логику, что позволяет:

  • блокировать отправку формы
  • отображать ошибки по полям
  • нормализовать дату перед сохранением

Синхронизация с формой и отправкой данных

Обычно финальная проверка выполняется при отправке формы:

form.addEventListener('submit', function (e) {
    const value = input.value;

    if (!validateDate(value)) {
        e.preventDefault();
        input.classList.add('error');
    }
});

Такой подход гарантирует, что даже если промежуточная проверка была пропущена, некорректные данные не попадут в систему.


Обработка нестандартных форматов отображения

Хотя Cleave.js работает с фиксированным форматом, в реальных приложениях часто требуется адаптация под локальные стандарты:

  • DD/MM/YYYY
  • YYYY-MM-DD
  • DD-MM-YYYY

Пример настройки:

new Cleave(input, {
    date: true,
    datePattern: ['Y', 'm', 'd'],
    delimiter: '-'
});

При смене порядка блоков важно учитывать, что логика парсинга и валидации должна строго соответствовать выбранному формату, иначе проверка корректности даты будет давать ложные результаты.


Разделение ответственности между слоями обработки

Корректная архитектура обычно разделяет систему на три уровня:

  1. Cleave.js

    • форматирование ввода
    • ограничение символов
    • визуальная структура
  2. Парсер даты

    • преобразование строки в компоненты
    • нормализация значений
  3. Валидатор

    • проверка календарной корректности
    • проверка диапазонов
    • бизнес-ограничения

Такая схема снижает связанность логики и упрощает поддержку сложных сценариев, где одна и та же дата может иметь разные ограничения в зависимости от контекста (например, дата рождения и дата документа).