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

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

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


Сравнение дат и граничные условия

Day.js предоставляет базовые методы сравнения:

  • isBefore
  • isAfter
  • isSame

Каждый из них может учитывать точность сравнения через единицы измерения времени.

dayjs('2024-01-01').isBefore('2024-01-10') // true
dayjs('2024-01-10').isAfter('2024-01-01')  // true
dayjs('2024-01-01').isSame('2024-01-01')   // true

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

dayjs('2024-01-01 23:59').isSame('2024-01-01 00:00', 'day') // true

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


Инклюзивные и эксклюзивные границы

При проверке диапазона используются два подхода:

  • включающие границы (inclusive)
  • исключающие границы (exclusive)

Они формализуются через математическую нотацию:

  • [] — включительно
  • () — исключительно

Проверка принадлежности диапазону: isBetween

Расширение Day.js через plugin isBetween добавляет прямую проверку попадания даты в интервал.

import isBetween from 'dayjs/plugin/isBetween'
dayjs.extend(isBetween)

Базовая проверка:

dayjs('2024-01-10').isBetween('2024-01-01', '2024-01-31')

По умолчанию границы являются исключающими. Третьим параметром задаётся единица измерения, четвёртым — тип включения границ.

dayjs('2024-01-01').isBetween(
  '2024-01-01',
  '2024-01-31',
  'day',
  '[]'
)

Варианты интерпретации границ:

  • () — обе границы исключены
  • [] — обе границы включены
  • [) — левая включена, правая исключена
  • (] — левая исключена, правая включена

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


Ограничение значений: ручной контроль диапазона

В Day.js отсутствует встроенная функция «clamp», однако ограничение даты внутри диапазона реализуется через комбинацию сравнений.

const clampDate = (value, min, max) => {
  const d = dayjs(value)
  if (d.isBefore(min)) return dayjs(min)
  if (d.isAfter(max)) return dayjs(max)
  return d
}

Логика ограничения сводится к трём состояниям:

  1. значение меньше нижней границы
  2. значение больше верхней границы
  3. значение находится внутри диапазона

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


Округление границ диапазона

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

dayjs('2024-01-10').startOf('month') // 2024-01-01 00:00:00
dayjs('2024-01-10').endOf('month')   // 2024-01-31 23:59:59.999

Методы:

  • startOf
  • endOf

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


Нормализация входных значений

Диапазон корректно работает только при одинаковом уровне нормализации дат. Разные форматы входных данных приводят к неоднозначным результатам:

  • строки ISO
  • объекты Date
  • Unix timestamp
  • Day.js объекты

Единый подход заключается в приведении всех значений к Day.js перед сравнением:

const a = dayjs(valueA)
const b = dayjs(valueB)

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


UTC и локальные ограничения диапазонов

При работе с диапазонами критичным фактором становится часовой пояс. Day.js поддерживает UTC-режим через plugin:

import utc from 'dayjs/plugin/utc'
dayjs.extend(utc)

Сравнение в UTC устраняет смещение локального времени:

dayjs.utc('2024-01-01').isBefore(dayjs.utc('2024-01-02'))

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


Полуинтервалы и пограничные случаи

Особую сложность представляют ситуации, где диапазон определяется на уровне суток:

  • начало дня: 00:00:00.000
  • конец дня: 23:59:59.999

Использование startOf('day') и endOf('day') устраняет необходимость ручного вычисления границ:

const start = dayjs('2024-01-10').startOf('day')
const end = dayjs('2024-01-10').endOf('day')

При этом сравнение с использованием isBetween становится однозначным и стабильным.


Диапазоны с разной точностью измерения

Day.js позволяет задавать точность сравнения:

dayjs('2024-01-10 12:00').isBetween(
  '2024-01-10 00:00',
  '2024-01-11 00:00',
  'hour'
)

Изменение единицы измерения влияет на гранулярность проверки:

  • millisecond — абсолютная точность
  • second — игнорируются миллисекунды
  • day — игнорируются часы, минуты и секунды

Выбор единицы измерения фактически определяет модель диапазона.


Типовые ошибки при работе с диапазонами

Часто встречающиеся проблемы связаны не с самим Day.js, а с логикой построения интервалов:

  • смешивание локального времени и UTC
  • отсутствие нормализации входных значений
  • игнорирование включающих/исключающих границ
  • использование isBefore/isAfter вместо isBetween без учёта равенства границ
  • ручное вычисление конца периода без endOf

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