Работа с датами в интерфейсах ввода почти всегда требует разделения двух задач: форматирования и валидации. Библиотека Cleave.js решает первую задачу — структурирование ввода, автоматическую расстановку разделителей и ограничение допустимых символов. Однако корректность самой даты (существует ли 31 февраля, попадает ли дата в допустимый диапазон, соответствует ли логике приложения) требует дополнительной обработки.
Cleave.js предоставляет инструменты для создания предсказуемого шаблона ввода даты, но не является полноценным валидатором календарных значений. Поэтому проверка корректности даты строится на комбинации встроенных возможностей и внешней логики.
Первый уровень контроля достигается через жёсткое определение структуры ввода:
const cleave = new Cleave(input, {
date: true,
delimiter: '.',
datePattern: ['d', 'm', 'Y']
});
В этом режиме ввод автоматически разбивается на блоки:
Такая структура уже снижает количество некорректных значений,
поскольку пользователь физически ограничен форматом. Однако Cleave.js не
запрещает логически некорректные комбинации вроде
39.19.2025 или 00.00.0000.
Поведение блоков можно уточнить через конфигурацию:
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.2024Cleave.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.012.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/YYYYYYYY-MM-DDDD-MM-YYYYПример настройки:
new Cleave(input, {
date: true,
datePattern: ['Y', 'm', 'd'],
delimiter: '-'
});
При смене порядка блоков важно учитывать, что логика парсинга и валидации должна строго соответствовать выбранному формату, иначе проверка корректности даты будет давать ложные результаты.
Корректная архитектура обычно разделяет систему на три уровня:
Cleave.js
Парсер даты
Валидатор
Такая схема снижает связанность логики и упрощает поддержку сложных сценариев, где одна и та же дата может иметь разные ограничения в зависимости от контекста (например, дата рождения и дата документа).