Диапазоны дат для отчетов

Работа с диапазонами дат в интерфейсах отчётности строится вокруг необходимости задать начальную и конечную границу временного интервала, который затем используется для фильтрации данных на сервере или в клиентской логике. В экосистеме JavaScript одной из наиболее удобных библиотек для этих задач выступает Flatpickr, предоставляющая режим выбора диапазона без необходимости писать собственную логику парсинга, валидации и отображения календаря.


Режим range как фундамент отчётных интервалов

Ключевой механизм, позволяющий работать с диапазонами, — это режим:

flatpickr("#reportRange", {
  mode: "range"
});

В этом режиме пользователь последовательно выбирает две даты:

  • первая дата интерпретируется как start date
  • вторая дата — как end date

После выбора библиотека автоматически формирует строку вида:

2026-06-01 to 2026-06-15

или в зависимости от настроек форматирования:

01.06.2026 - 15.06.2026

Особенность внутреннего состояния

Flatpickr хранит диапазон как массив из двух элементов:

[selectedStart, selectedEnd]

Если конечная дата не выбрана, второй элемент остаётся null, что важно учитывать при построении отчётной логики.


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

Отчётные системы почти всегда требуют строгого формата дат, например:

  • ISO 8601: YYYY-MM-DD
  • UNIX timestamp
  • локализованные строки только для UI

Настройка:

flatpickr("#reportRange", {
  mode: "range",
  dateFormat: "Y-m-d"
});

Разделитель диапазона

По умолчанию Flatpickr использует разделитель " to ", но его часто заменяют на более нейтральный:

flatpickr("#reportRange", {
  mode: "range",
  dateFormat: "Y-m-d",
  rangeSeparator: " — "
});

Это важно для систем, где строка диапазона отображается в UI-логах или печатных формах отчётов.


Ограничение допустимого периода отчёта

В реальных системах отчётности диапазон почти всегда ограничивается бизнес-правилами:

  • максимум 7 дней (оперативные отчёты)
  • максимум 30 дней (аналитика)
  • максимум 1 год (архивные выборки)

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

flatpickr("#reportRange", {
  mode: "range",
  minDate: "2026-01-01",
  maxDate: "2026-12-31"
});

Динамическое ограничение относительного периода

const today = new Date();
const thirtyDaysAgo = new Date();
thirtyDaysAgo.setDate(today.getDate() - 30);

flatpickr("#reportRange", {
  mode: "range",
  minDate: thirtyDaysAgo,
  maxDate: today
});

Такой подход используется для предотвращения запросов к слишком объёмным выборкам данных.


Автозаполнение диапазона для отчётных сценариев

В отчётных системах часто требуется заранее заданный период:

Последние 7 дней

const end = new Date();
const start = new Date();
start.setDate(end.getDate() - 7);

flatpickr("#reportRange", {
  mode: "range",
  defaultDate: [start, end]
});

Текущий месяц

const now = new Date();
const start = new Date(now.getFullYear(), now.getMonth(), 1);
const end = new Date(now.getFullYear(), now.getMonth() + 1, 0);

flatpickr("#reportRange", {
  mode: "range",
  defaultDate: [start, end]
});

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

Основная логика отчётности строится вокруг события onChange.

flatpickr("#reportRange", {
  mode: "range",
  dateFormat: "Y-m-d",
  onChange: function(selectedDates) {
    const [start, end] = selectedDates;

    if (!start || !end) return;

    fetchReports({
      from: start.toISOString(),
      to: end.toISOString()
    });
  }
});

Важная особенность

Flatpickr может возвращать:

  • только начальную дату
  • полную пару дат

Поэтому обязательна проверка завершённости диапазона.


Нормализация диапазона перед отправкой на сервер

Разные системы требуют разную нормализацию:

1. Начало и конец дня

function normalizeRange([start, end]) {
  const from = new Date(start);
  from.setHours(0, 0, 0, 0);

  const to = new Date(end);
  to.setHours(23, 59, 59, 999);

  return { from, to };
}

Это критично для отчётов, где учитываются все события за сутки.


2. UTC-нормализация

const fromUTC = new Date(Date.UTC(
  start.getFullYear(),
  start.getMonth(),
  start.getDate()
));

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


Валидация диапазона в отчётной форме

Несмотря на встроенные ограничения Flatpickr, бизнес-валидация часто требуется дополнительно.

Проверка максимальной длины интервала

function validateRange(start, end) {
  const diff = (end - start) / (1000 * 60 * 60 * 24);

  if (diff > 30) {
    throw new Error("Диапазон не может превышать 30 дней");
  }
}

Обработка некорректного выбора

Пользователь может выбрать:

  • одинаковые даты
  • обратный порядок (обрабатывается автоматически)
  • неполный диапазон

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


Работа с визуальным выделением диапазона

В режиме range библиотека автоматически подсвечивает:

  • начальную дату
  • конечную дату
  • промежуточные дни

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

Упрощение визуального режима

flatpickr("#reportRange", {
  mode: "range",
  showMonths: 2
});

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


Разделение UI и логики отчёта

Диапазон дат в Flatpickr не должен напрямую считаться финальным источником истины. Обычно архитектура строится так:

  1. Flatpickr — ввод диапазона
  2. State layer — хранение нормализованных дат
  3. API layer — запрос отчётов

Пример промежуточного состояния:

const state = {
  range: null
};

flatpickr("#reportRange", {
  mode: "range",
  onChange(selectedDates) {
    if (selectedDates.length === 2) {
      state.range = normalizeRange(selectedDates);
    }
  }
});

Интеграция с серверной фильтрацией

Диапазон почти всегда превращается в параметры запроса:

GET /reports?from=2026-06-01&to=2026-06-15

Или POST-запрос:

fetch("/reports", {
  method: "POST",
  body: JSON.stringify({
    from,
    to
  })
});

Важно, что Flatpickr не навязывает формат API — он лишь поставляет структурированные даты.


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

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

flatpickr("#reportRange", {
  mode: "range",
  defaultDate: [savedStart, savedEnd]
});

Если сохраняется только строка:

2026-06-01 to 2026-06-15

её необходимо разобрать:

const [startStr, endStr] = saved.split(" to ");

Поведение при частичном вводе

Если пользователь выбирает только одну дату и закрывает календарь, возможны сценарии:

  • сброс диапазона
  • фиксация как “start-only”
  • ожидание второго клика

В отчётных интерфейсах чаще используется стратегия:

onClose(selectedDates) {
  if (selectedDates.length !== 2) {
    clearReportFilter();
  }
}

Работа с запрещёнными датами

В отчётных системах часто блокируются:

  • выходные
  • праздничные дни
  • периоды закрытой отчётности
flatpickr("#reportRange", {
  mode: "range",
  disable: [
    "2026-01-01",
    "2026-05-09"
  ]
});

Или через функцию:

disable: [
  function(date) {
    return date.getDay() === 0; // воскресенье
  }
]

Оптимизация выбора больших диапазонов

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

  • два месяца в интерфейсе
  • быстрые пресеты
  • ручной ввод

Пример пресетов:

document.querySelector("#lastMonth").oncl ick = () => {
  const end = new Date();
  const start = new Date();
  start.setMonth(start.getMonth() - 1);

  fp.setDate([start, end]);
};

Особенности отображения при локализации

Flatpickr поддерживает локализацию, влияющую на отображение диапазонов:

  • порядок даты
  • названия месяцев
  • разделители
import { Russian } from "flatpickr/dist/l10n/ru.js";

flatpickr("#reportRange", {
  mode: "range",
  locale: Russian
});

Это влияет только на UI, но не на внутренние значения дат.


Стабильность диапазона при смене временных зон

При работе с глобальными системами отчётности критично учитывать:

  • локальное время пользователя
  • серверное время
  • UTC

Flatpickr всегда оперирует локальными Date-объектами, поэтому финальная нормализация должна происходить вне библиотеки.


Поведение при очистке диапазона

flatpickr("#reportRange", {
  mode: "range",
  onChange(selectedDates) {
    if (selectedDates.length === 0) {
      resetReport();
    }
  }
});

Очистка может происходить:

  • кнопкой reset
  • повторным кликом по выбранному диапазону
  • программным сбросом

Связь диапазона с фильтрацией данных

Диапазон дат становится центральным фильтром:

  • продажи за период
  • активность пользователей
  • финансовые транзакции
  • логирование событий

Каждый из этих сценариев требует одинаковой структуры данных:

[startDate, endDate]

с последующей трансформацией в формат, понятный backend-системе.