Ограничения диапазонов дат

Базовая модель работы с датами и масками

Cleave.js представляет собой библиотеку для форматирования пользовательского ввода в реальном времени. В контексте дат она не выполняет полноценную валидацию календарных правил, а работает как слой маскирования и структурирования ввода.

При работе с диапазонами дат ключевая особенность заключается в том, что Cleave.js:

  • форматирует отдельные даты по заданному паттерну;
  • не хранит семантику диапазона как сущности;
  • не проверяет логическую корректность связи между двумя датами.

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


Форматы представления диапазона дат

Наиболее распространённые форматы диапазонов:

  • YYYY-MM-DD - YYYY-MM-DD
  • DD.MM.YYYY — DD.MM.YYYY
  • MM/DD/YYYY to MM/DD/YYYY

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

Пример базовой конфигурации для одной даты:

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

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

  • один input с ручной обработкой сегментов;
  • два независимых input с синхронизацией.

Ограничения диапазона на уровне двух полей

Наиболее устойчивый подход — разделение диапазона на две даты:

const startDate = new Cleave('#start', {
  date: true,
  datePattern: ['Y', 'm', 'd']
});

const endDate = new Cleave('#end', {
  date: true,
  datePattern: ['Y', 'm', 'd']
});

Далее ввод ограничивается логикой приложения:

  • минимальная дата конца = дата начала;
  • максимальная дата начала = дата конца (если требуется симметрия).

Проверка выполняется отдельно:

function validateRange(start, end) {
  const s = new Date(start);
  const e = new Date(end);

  return s <= e;
}

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


Ограничение минимальной и максимальной даты

Cleave.js не предоставляет встроенных параметров minDate и maxDate. Поэтому ограничения реализуются через:

  • блокировку ввода символов;
  • автоматическое исправление значения;
  • откат некорректного ввода;
  • обработку событий onChange.

Пример внешнего ограничения:

const MIN = new Date('2024-01-01');
const MAX = new Date('2026-12-31');

function clampDate(value) {
  const d = new Date(value);

  if (d < MIN) return MIN;
  if (d > MAX) return MAX;

  return d;
}

Контроль логики диапазона внутри одного поля

При использовании одного input диапазон приходится парсить вручную. Типичная структура:

2025-01-10 - 2025-02-15

Разделение выполняется через delimiter:

function parseRange(value) {
  const [start, end] = value.split(' - ');
  return { start, end };
}

После этого применяется логика валидации:

  • корректность обеих дат;
  • проверка порядка;
  • обработка неполных значений.

Проблема частичного ввода

При вводе диапазона пользователь может находиться в состоянии:

  • введена только первая дата;
  • вторая дата неполная;
  • разделитель присутствует, но данные отсутствуют.

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

Пример проверки состояния:

function isRangeComplete(value) {
  const parts = value.split(' - ');
  return parts.length === 2 && parts[0].length === 10 && parts[1].length === 10;
}

Обработка инверсии диапазона

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

Варианты обработки:

  1. автоматический swap;
  2. блокировка ввода;
  3. визуальная ошибка без изменения значения.

Автоматическая коррекция:

function normalizeRange(start, end) {
  const s = new Date(start);
  const e = new Date(end);

  if (s > e) {
    return { start: end, end: start };
  }

  return { start, end };
}

Ограничения на уровне календарной корректности

Даже при корректном формате Cleave.js не учитывает:

  • количество дней в месяце;
  • високосные годы;
  • смещение временных зон;
  • календарные аномалии.

Например, ввод 2025-02-30 может быть пропущен как строка, но не является реальной датой.

Поэтому используется дополнительная валидация:

function isValidDate(d) {
  const date = new Date(d);
  return !isNaN(date.getTime());
}

Синхронизация двух полей диапазона

При работе с двумя input часто требуется динамическое ограничение второго поля на основе первого.

Пример логики:

  • после выбора startDate обновляется min для endDate;
  • после изменения endDate обновляется max для startDate.
function syncRange(start, end) {
  const startDate = new Date(start);

  end.min = startDate;
}

В реальной реализации DOM-значения синхронизируются через события input или change.


Ограничения пользовательского ввода

Дополнительные ограничения часто накладываются через:

  • блокировку недопустимых символов;
  • автозамену разделителей;
  • фиксированную длину сегментов;
  • принудительное добавление ведущих нулей.

Cleave.js уже выполняет часть этих задач, но для диапазонов важно учитывать:

  • разделитель диапазона не должен конфликтовать с разделителем даты;
  • ввод не должен ломать позиционную структуру маски.

Обработка пустых и полу-заполненных значений

Особое внимание требуется для случаев:

  • ""
  • "2025-"
  • "2025-01-"
  • "2025-01-10 - "

Такие состояния должны трактоваться как промежуточные, а не ошибочные.

Типичная стратегия:

  • не валидировать до завершения ввода;
  • различать draft и committed состояние.

Работа с кастомными разделителями диапазона

Выбор разделителя влияет на устойчивость парсинга:

  • " - " — наиболее читаемый вариант;
  • " to " — семантически выраженный;
  • ".." — компактный, но менее очевидный.

Важно учитывать конфликт с форматами даты:

  • - используется внутри ISO формата;
  • поэтому для диапазона часто применяется " - " с пробелами.

Интеграция с внешней валидацией

Cleave.js следует рассматривать как слой представления. Для строгих ограничений используются библиотеки:

  • Day.js;
  • date-fns;
  • Luxon.

Пример проверки через date-fns:

import { isBefore } from 'date-fns';

function validate(start, end) {
  return isBefore(new Date(start), new Date(end));
}

Итоговая архитектура ограничения диапазонов

Типичная схема работы:

  1. Cleave.js форматирует ввод;
  2. приложение разбивает диапазон на части;
  3. выполняется парсинг дат;
  4. применяется бизнес-валидация;
  5. при необходимости данные корректируются или отклоняются.

Такая архитектура обеспечивает:

  • устойчивость к частичному вводу;
  • независимость от ограничений библиотеки;
  • контроль над логикой диапазонов;
  • предсказуемое поведение интерфейса.