Работа с датами в JavaScript неизбежно связана с неопределённостью входных значений: строки приходят из API, числовые метки времени могут быть некорректными, пользовательский ввод часто содержит частично валидные или вовсе ошибочные данные. Библиотека date-fns предоставляет набор инструментов, позволяющих выстраивать предсказуемую и строгую проверку входных данных перед выполнением операций над датами.
Конструктор Date в JavaScript допускает широкий спектр
входных форматов, включая строки, числа и даже частично некорректные
значения. Это приводит к ситуации, когда объект Date
создаётся, но содержит невалидное значение времени.
const d1 = new Date('invalid-date');
console.log(d1); // Invalid Date
console.log(d1 instanceof Date); // true
Ключевая особенность заключается в том, что Invalid Date
остаётся объектом Date, но его числовое представление
возвращает NaN:
console.log(Number(d1)); // NaN
Следовательно, проверка только через instanceof Date не
обеспечивает корректной валидации.
В date-fns центральным инструментом проверки является функция
isValid. Она принимает дату, timestamp или строку,
преобразует входное значение в дату и проверяет её корректность.
import { isValid } from 'date-fns';
isValid(new Date()); // true
isValid(new Date('invalid')); // false
isValid(1699999999999); // true или false в зависимости от значения
Механизм проверки основан на попытке приведения к числовому представлению времени:
Типичная ошибка заключается в использовании только проверки типа:
const value = new Date('bad input');
if (value instanceof Date) {
// всегда true даже для Invalid Date
}
Корректный подход:
import { isValid } from 'date-fns';
if (isValid(value)) {
// безопасное использование
}
Валидация становится более строгой, если проверка выполняется до
создания объекта Date.
function isValidTimestamp(value) {
return typeof value === 'number' && Number.isFinite(value);
}
Использование:
if (isValidTimestamp(input)) {
const date = new Date(input);
}
Строки требуют дополнительной осторожности, так как
Date.parse допускает неоднозначные форматы:
const timestamp = Date.parse('2024-13-40'); // NaN
Функция parse из date-fns позволяет разбирать строки по
заданному шаблону:
import { parse } from 'date-fns';
const result = parse('31-12-2024', 'dd-MM-yyyy', new Date());
Однако даже при использовании шаблона возможны ситуации, когда результат формально создаётся, но не является валидной датой:
const date = parse('31-02-2024', 'dd-MM-yyyy', new Date());
Такой результат требует дополнительной проверки:
import { isValid } from 'date-fns';
const date = parse('31-02-2024', 'dd-MM-yyyy', new Date());
if (!isValid(date)) {
// обработка ошибки
}
Функция parseISO используется для работы с ISO 8601
строками. Она обеспечивает более предсказуемое поведение, чем
Date.parse.
import { parseISO, isValid } from 'date-fns';
const date = parseISO('2024-12-31T10:15:30Z');
if (isValid(date)) {
// корректная дата
}
ISO-формат минимизирует неоднозначность, однако не гарантирует валидность календарных значений:
parseISO('2024-02-30T00:00:00Z'); // Invalid Date
Одной из частых проблем является передача в функции значений, не относящихся к датам:
const input = null;
Без проверки это приводит к:
new Date(null); // 1970-01-01T00:00:00.000Z
Такое поведение может быть логически некорректным, но формально валидным.
Строгая проверка:
function isValidDateInput(value) {
if (value == null) return false;
if (typeof value === 'boolean') return false;
return true;
}
Комбинированный подход:
import { isValid } from 'date-fns';
function safeToDate(value) {
const date = new Date(value);
return isValid(date) ? date : null;
}
Валидация часто включает контроль допустимого диапазона времени.
const MIN_DATE = new Date('2000-01-01');
const MAX_DATE = new Date('2100-01-01');
function isInRange(date) {
return date >= MIN_DATE && date <= MAX_DATE;
}
Комбинация с isValid:
import { isValid } from 'date-fns';
function validate(date) {
return isValid(date) && isInRange(date);
}
Функция toDate используется для унификации входных
типов:
import { toDate } from 'date-fns';
toDate(1699999999999);
toDate(new Date());
Однако toDate не выполняет строгую валидацию, поэтому
требуется дополнительная проверка:
import { toDate, isValid } from 'date-fns';
const date = toDate(input);
if (!isValid(date)) {
// обработка ошибки
}
if (date) {
// неверно: Invalid Date является truthy
}
const time = Number(new Date('bad'));
const date = parse('31-02-2024', 'dd-MM-yyyy', new Date());
// использование без isValid
На практике применяется многоуровневый подход:
parse, parseISO
или toDateisValidПример универсального валидатора:
import { isValid, toDate } from 'date-fns';
function validateDate(value) {
if (value == null) return false;
const date = toDate(value);
if (!isValid(date)) return false;
if (date.getFullYear() < 1970) return false;
return true;
}
При работе с коллекциями требуется проверка каждого элемента:
import { isValid, toDate } from 'date-fns';
function validateDates(array) {
if (!Array.isArray(array)) return false;
return array.every((item) => isValid(toDate(item)));
}
Для оптимизации вычислений применяется стратегия раннего выхода:
function hasInvalidDate(array) {
for (const item of array) {
const date = new Date(item);
if (!isValid(date)) return true;
}
return false;
}
Date сам по себе не гарантирует валидностьinstanceof Date недостаточнаisValid является базовым критерием корректностиparse, parseISO, toDate
требуют последующей проверкиnull, undefined и нечисловых
значений обязательна