Работа с массивами дат

Массивы дат в Day.js обрабатываются через комбинацию стандартных методов массива JavaScript и возможностей самого Day.js, поскольку библиотека не предоставляет отдельного специализированного API для коллекций дат. Основной подход заключается в преобразовании элементов массива в экземпляры Day.js, выполнении операций сравнения, фильтрации, сортировки и последующего преобразования обратно в нужный формат.


Работа с датами начинается с приведения входных данных к единому типу. Day.js создаёт неизменяемые объекты, поэтому каждая операция возвращает новый экземпляр.

import dayjs from "dayjs";

const dates = [
  "2026-01-01",
  "2026-03-15",
  "2025-12-31"
];

const parsed = dates.map(d => dayjs(d));

После преобразования становится доступен полный набор методов сравнения и манипуляций.


Сортировка массивов дат

Сортировка выполняется через Array.prototype.sort() с использованием методов isBefore и isAfter.

const sorted = parsed.sort((a, b) => {
  if (a.isBefore(b)) return -1;
  if (a.isAfter(b)) return 1;
  return 0;
});

Более компактная форма с числовым сравнением через valueOf():

const sorted = parsed.sort((a, b) => a.valueOf() - b.valueOf());

valueOf() возвращает timestamp, что делает сравнение прямолинейным и эффективным.


Фильтрация дат по диапазону

Фильтрация массивов дат часто строится вокруг проверки попадания в интервал.

const start = dayjs("2026-01-01");
const end = dayjs("2026-02-01");

const filtered = parsed.filter(date =>
  (date.isAfter(start) || date.isSame(start)) &&
  (date.isBefore(end) || date.isSame(end))
);

Для упрощения логики используется метод isBetween, доступный через plugin isBetween.

import isBetween from "dayjs/plugin/isBetween";
dayjs.extend(isBetween);

const filtered = parsed.filter(date =>
  date.isBetween(start, end, null, "[]")
);

Квадратные скобки задают включительность границ диапазона.


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

Массив дат можно свести к экстремумам через сравнение timestamp.

const minDate = parsed.reduce((min, current) =>
  current.isBefore(min) ? current : min
);

const maxDate = parsed.reduce((max, current) =>
  current.isAfter(max) ? current : max
);

Альтернативный вариант через числовые значения:

const minDate = parsed.reduce((min, current) =>
  current.valueOf() < min.valueOf() ? current : min
);

Удаление дубликатов дат

Уникализация выполняется через нормализацию к строковому представлению или timestamp.

const unique = Array.from(
  new Map(parsed.map(d => [d.format("YYYY-MM-DD"), d])).values()
);

Вариант через Set:

const unique = Array.from(
  new Map(parsed.map(d => [d.valueOf(), d])).values()
);

Выбор ключа зависит от требуемой точности (дата или дата-время).


Группировка дат по периоду

Группировка позволяет структурировать массив по дням, месяцам или годам.

Группировка по дню

const groupedByDay = parsed.reduce((acc, date) => {
  const key = date.format("YYYY-MM-DD");

  if (!acc[key]) acc[key] = [];
  acc[key].push(date);

  return acc;
}, {});

Группировка по месяцу

const groupedByMonth = parsed.reduce((acc, date) => {
  const key = date.format("YYYY-MM");

  if (!acc[key]) acc[key] = [];
  acc[key].push(date);

  return acc;
}, {});

Генерация последовательности дат

Массивы дат часто формируются динамически с шагом в один день или другой интервал.

const start = dayjs("2026-01-01");
const end = dayjs("2026-01-10");

const range = [];
let current = start;

while (current.isBefore(end) || current.isSame(end)) {
  range.push(current);
  current = current.add(1, "day");
}

Каждая итерация создаёт новый объект Day.js, сохраняя неизменяемость.


Сравнение массивов дат

Проверка пересечений или различий между наборами дат выполняется через сравнение нормализованных значений.

const a = parsed.map(d => d.format("YYYY-MM-DD"));
const b = otherParsed.map(d => d.format("YYYY-MM-DD"));

const intersection = a.filter(x => b.includes(x));

Для оптимизации используется Set:

const setB = new Set(b);

const intersection = a.filter(x => setB.has(x));

Преобразование массива в числовые интервалы

Работа с временными интервалами часто требует преобразования дат в timestamp.

const timestamps = parsed.map(d => d.valueOf());

const diffs = timestamps.slice(1).map((t, i) =>
  t - timestamps[i]
);

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


Сдвиг всех дат массива

Операции трансформации применяются к каждому элементу массива через map.

const shifted = parsed.map(d => d.add(7, "day"));

Аналогично возможно уменьшение интервала:

const shiftedBack = parsed.map(d => d.subtract(3, "month"));

Нормализация массива дат

Приведение всех дат к единому формату используется перед хранением или сравнением.

const normalized = parsed.map(d => d.startOf("day"));

Или нормализация к UTC:

const utcNormalized = parsed.map(d => d.utc());

Слияние пересекающихся диапазонов

При работе с интервалами дат массивы могут содержать пересекающиеся периоды.

const ranges = [
  [dayjs("2026-01-01"), dayjs("2026-01-05")],
  [dayjs("2026-01-04"), dayjs("2026-01-10")]
];

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

const merged = ranges.sort((a, b) => a[0] - b[0]).reduce((acc, range) => {
  if (!acc.length) return [range];

  const last = acc[acc.length - 1];

  if (range[0].isBefore(last[1])) {
    last[1] = dayjs.max(last[1], range[1]);
  } else {
    acc.push(range);
  }

  return acc;
}, []);

Преобразование массива дат в календарную структуру

Формирование структуры календаря требует распределения дат по неделям.

const weeks = [];
let week = [];

parsed.forEach(date => {
  week.push(date);

  if (date.day() === 0) {
    weeks.push(week);
    week = [];
  }
});

if (week.length) weeks.push(week);

Работа с разреженными массивами дат

При наличии пропусков между датами применяется заполнение промежутков.

const start = parsed[0];
const end = parsed[parsed.length - 1];

const filled = [];
let current = start;

while (current.isBefore(end) || current.isSame(end)) {
  filled.push(current);
  current = current.add(1, "day");
}

Оптимизация обработки массивов дат

Производительность операций зависит от минимизации повторных парсингов и конверсий. Эффективная схема предполагает:

  • однократное преобразование строк в Day.js
  • использование valueOf() для сравнений
  • применение Set для поиска и исключений
  • избегание повторных вызовов format() внутри циклов
const map = new Map(
  parsed.map(d => [d.valueOf(), d])
);

Такая структура снижает стоимость операций поиска и объединения.