date —— примитивы дат
Вычисления с датами — основная алгоритмическая часть date-picker и общее ядро интервального выбора в calendar. У нативного Date есть несколько классических ловушек (двузначный год, переполнение при new Date(2023, 1, 30)), которые этот модуль обрабатывает единообразно.
Соглашения
Заголовок раздела «Соглашения»month всегда начинается с 0, как у нативного Date (0 = январь, 11 = декабрь). Это самый частый источник ошибок при использовании модуля — «март», полученный из UI, нужно передавать как 2.
Сравнивается только часть даты: isSameDay / compareDate не смотрят на часы, минуты, секунды, что соответствует семантике «выбрать конкретный день».
import { getDaysInMonth, getFirstDayOfMonth, isSameDay, isSameMonth, compareDate, isDateDisabled, formatDate, parseDate,} from '@lh-ui/share/core/shared/date'Таблица функций
Заголовок раздела «Таблица функций»| Функция | Сигнатура | Назначение |
|---|---|---|
getDaysInMonth |
(year: number, month: number) => number |
количество дней в месяце (month с 0) |
getFirstDayOfMonth |
(year: number, month: number) => number |
день недели первого дня месяца (0 = воскресенье) |
isSameDay |
(a: Date, b: Date) => boolean |
один и тот же ли день |
isSameMonth |
(a: Date, b: Date) => boolean |
один ли тот же год и месяц |
compareDate |
(a: Date, b: Date) => number |
сравнение части даты, возвращает разность в миллисекундах |
isDateDisabled |
(date, minDate, maxDate, disabledDate) => boolean |
отключена ли дата |
formatDate |
(date: Date, format: string) => string |
форматирование даты |
parseDate |
(str: string, format: string) => Date | null |
разбор строки даты |
Нормализация двузначного года
Заголовок раздела «Нормализация двузначного года»getDaysInMonth и getFirstDayOfMonth внутренне нормализуют year ∈ [0, 99] (+1900). Почему: new Date(99, 0, 1) в JS воспринимается как 1999-01-01 (особое поведение конструктора Date для годов 0–99), и без нормализации день недели первого дня месяца вычисляется неверно. Обычные четырёхзначные годы не затрагиваются.
Порядок проверок в isDateDisabled
Заголовок раздела «Порядок проверок в isDateDisabled»disabledDate?.(date) возвращает истину → trueдата раньше minDate (minDate не null) → trueдата позже maxDate (maxDate не null) → trueиначе → falseПри null для minDate / maxDate / disabledDate соответствующее условие пропускается — «только верхняя граница» и «только пользовательская оценка» работают корректно. Порядок выбран намеренно: пользовательская оценка имеет приоритет, поэтому семантика disabledDate — «независимо от интервала, эти даты недоступны для выбора» (праздничные дни, даты с нулевым остатком).
Токены formatDate / parseDate
Заголовок раздела «Токены formatDate / parseDate»Поддерживаются токены фиксированной ширины: yyyy / MM / dd / HH / mm / ss.
- Любой порядок:
'MM/dd/yyyy','yyyy-MM-dd HH:mm'— любой вариант допустим. - Многократное вхождение заменяется целиком: обе
ddв'yyyy-MM-dd'будут заменены одним и тем же значением. - Год дополняется нулями до 4 знаков:
formatDate(new Date(23, 0, 1), 'yyyy')→'0023'. - Функции симметричны:
parseDate(formatDate(d, fmt), fmt)должно вернуть тот же день (тот же месяц, тот же год; часы, минуты, секунды — в зависимости от токенов).
formatDate(new Date(2026, 9, 12), 'yyyy-MM-dd') // '2026-10-12'formatDate(new Date(2026, 9, 12, 14, 5), 'yyyy-MM-dd HH:mm') // '2026-10-12 14:05'parseDate('2026-10-12', 'yyyy-MM-dd') // Date(2026, 9, 12)parseDate('неверный', 'yyyy-MM-dd') // nullДва случая возврата null из parseDate
Заголовок раздела «Два случая возврата null из parseDate»- Строка не соответствует формату:
parseIntдаётNaN. - Некорректное значение:
2023-02-30будет нативнымDateмолча сдвинуто на 2 марта — функция после конструирования делает обратную проверку, и если обнаруживает сдвиг, признаёт значение недопустимым и возвращаетnull. Это ключ проверки поля ввода в date-picker: когда пользователь вводит несуществующую дату, компонент способен это распознать.
Значения по умолчанию для отсутствующих токенов: год = текущий, месяц = 0, день = 1, часы / минуты / секунды = 0. Поэтому parseDate('10-12', 'MM-dd') даёт «12 октября текущего года 00:00:00».
Примеры использования
Заголовок раздела «Примеры использования»Рендер панели календаря
Заголовок раздела «Рендер панели календаря»// Год и месяц текущего представленияconst year = 2026const month = 9 // с 0 = октябрь
const days = getDaysInMonth(year, month) // 31const firstDay = getFirstDayOfMonth(year, month) // 4 = четверг → впереди 4 пустых ячейкиИнтервальное отключение
Заголовок раздела «Интервальное отключение»const min = new Date(2026, 0, 1)const max = new Date(2026, 11, 31)
isDateDisabled(new Date(2026, 5, 1), min, max, null) // falseisDateDisabled(new Date(2025, 5, 1), min, max, null) // true (раньше min)isDateDisabled(new Date(2026, 5, 1), null, null, (d) => // true (пользовательская оценка) d.getDay() === 0) // воскресенье недоступно для выбораСвязь с компонентами
Заголовок раздела «Связь с компонентами»// внутри компонента date-pickerfunction isDisabled(date: Date) { return isDateDisabled(date, props.minDate, props.maxDate, props.disabledDate)}
function onInput(value: string) { const parsed = parseDate(value, props.format) if (parsed === null) { errorMessage.value = 'Неверный формат даты' return } modelValue.value = parsed}Замечания
Заголовок раздела «Замечания»- month начинается с 0: когда UI-слой показывает «Октябрь», нужно передать
9. Состояние панели date-picker внутренне использует отсчёт с 0 и добавляет+1только при рендере. - Год
>= 10000не поддерживается: это выходит за выразительную способность токенов фиксированной ширины,formatDateвыдаст непредсказуемый результат. На практике это не ограничение (такжеinput[type=date]поддерживает только до 9999 года). compareDateвозвращает разность в миллисекундах: это разностьDate.UTC(...), положительное число означает, чтоaпозже. Сравнивается только часть даты, без времени суток.disabledDateуisDateDisabledвызывается при каждом рендере каждой ячейки: 42 ячейки календаря × каждое изменение представления — не делайте здесь тяжёлых вычислений, кэширование лежит на UI-слое.- Часовые пояса: всё основано на
Dateв локальном часовом поясе, преобразования в UTC не выполняются. Для кросс-поясных приложений унификацию выполняет UI-слой до передачи значений.
@lh-ui/share/core/date → getDaysInMonth( )(填好参数后点运行)Copyright © 2024-2026 FengMo WenXue