Работа с граничными случаями

Одной из наиболее распространённых проблем при работе с датами становится создание объекта из некорректного значения. Day.js предоставляет метод isValid(), позволяющий определить, удалось ли корректно распарсить дату.

const date = dayjs('2025-02-31');

console.log(date.isValid()); // true

Особенность Day.js заключается в том, что библиотека использует механизм Date из Javascript, который автоматически нормализует значения. В результате 2025-02-31 превращается в 2025-03-03.

Для строгой проверки необходимо использовать плагин customParseFormat.

const customParseFormat = require('dayjs/plugin/customParseFormat');

dayjs.extend(customParseFormat);

const date = dayjs('2025-02-31', 'YYYY-MM-DD', true);

console.log(date.isValid()); // false

Третий аргумент true включает строгий режим проверки.

Проверка пользовательского ввода

При работе с формами важно валидировать строки перед сохранением или отправкой на сервер.

function validateDate(value) {
    const date = dayjs(value, 'DD.MM.YYYY', true);

    return date.isValid();
}

console.log(validateDate('29.02.2024')); // true
console.log(validateDate('29.02.2025')); // false

Високосные годы

Високосные годы регулярно становятся причиной ошибок в вычислениях. Для корректной работы существует плагин isLeapYear.

const isLeapYear = require('dayjs/plugin/isLeapYear');

dayjs.extend(isLeapYear);

console.log(dayjs('2024-01-01').isLeapYear()); // true
console.log(dayjs('2025-01-01').isLeapYear()); // false

Проверка 29 февраля

const date = dayjs('2024-02-29');

console.log(date.isValid()); // true
const invalidDate = dayjs('2025-02-29', 'YYYY-MM-DD', true);

console.log(invalidDate.isValid()); // false

Добавление года к 29 февраля

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

const date = dayjs('2024-02-29');
const result = date.add(1, 'year');

console.log(result.format('YYYY-MM-DD'));

Результат:

2025-02-28

Day.js автоматически приводит дату к последнему корректному дню месяца.

Переход между месяцами

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

const date = dayjs('2025-01-31');
const result = date.add(1, 'month');

console.log(result.format('YYYY-MM-DD'));

Результат:

2025-02-28

Поведение при цепочке операций

const date = dayjs('2025-01-31');

console.log(date.add(1, 'month').format('YYYY-MM-DD'));
console.log(date.add(2, 'month').format('YYYY-MM-DD'));

Результат:

2025-02-28
2025-03-31

Day.js старается сохранить исходный день месяца, если это возможно.

Безопасная работа с концом месяца

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

const lastDay = dayjs().endOf('month');

console.log(lastDay.format('YYYY-MM-DD'));

Проверка последнего дня месяца

function isLastDayOfMonth(date) {
    return date.date() === date.daysInMonth();
}

const date = dayjs('2025-02-28');

console.log(isLastDayOfMonth(date)); // true

Переход через границы суток

При добавлении часов дата может перейти на следующий день.

const date = dayjs('2025-05-10 23:30');
const result = date.add(2, 'hour');

console.log(result.format('YYYY-MM-DD HH:mm'));

Результат:

2025-05-11 01:30

Отрицательные значения

const date = dayjs('2025-05-10 00:15');
const result = date.subtract(30, 'minute');

console.log(result.format('YYYY-MM-DD HH:mm'));

Результат:

2025-05-09 23:45

Работа с часовыми поясами

Часовые пояса — одна из самых сложных тем при работе со временем. Для поддержки таймзон используются плагины utc и timezone.

const utc = require('dayjs/plugin/utc');
const timezone = require('dayjs/plugin/timezone');

dayjs.extend(utc);
dayjs.extend(timezone);

Разница между локальным временем и UTC

const local = dayjs();
const utcDate = dayjs.utc();

console.log(local.format());
console.log(utcDate.format());

Конвертация между часовыми поясами

const date = dayjs.tz('2025-05-10 12:00', 'Europe/Moscow');

console.log(date.tz('Asia/Tokyo').format());

Ошибки при хранении локального времени

Нежелательно хранить даты без указания временной зоны.

Проблемный пример:

const date = '2025-05-10T12:00:00';

Без таймзоны сервер и клиент могут интерпретировать время по-разному.

Безопасный вариант:

const date = '2025-05-10T12:00:00Z';

Или:

const date = dayjs().utc().toISOString();

Переход на летнее и зимнее время

DST (Daylight Saving Time) создаёт множество граничных случаев.

Несуществующее время

В некоторых странах часы переводятся вперёд, и часть времени исчезает.

const date = dayjs.tz(
    '2025-03-30 02:30',
    'Europe/Berlin'
);

console.log(date.format());

Такое время может быть автоматически скорректировано библиотекой.

Дублирующееся время

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

const date = dayjs.tz(
    '2025-10-26 02:30',
    'Europe/Berlin'
);

console.log(date.format());

В подобных ситуациях рекомендуется хранить даты в UTC и конвертировать только при отображении.

Сравнение дат

Ошибки часто возникают из-за сравнения объектов вместо временных меток.

Неправильно:

const a = dayjs();
const b = dayjs();

console.log(a === b); // false

Правильно:

console.log(a.isSame(b));

Сравнение по единице времени

const a = dayjs('2025-05-10 10:00');
const b = dayjs('2025-05-10 22:00');

console.log(a.isSame(b, 'day')); // true

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

Для этого используется плагин isBetween.

const isBetween = require('dayjs/plugin/isBetween');

dayjs.extend(isBetween);

const date = dayjs('2025-05-15');

console.log(
    date.isBetween(
        '2025-05-01',
        '2025-05-31'
    )
);

Мутация объектов

В отличие от Date, объекты Day.js являются неизменяемыми.

const date = dayjs();
const updated = date.add(1, 'day');

console.log(date.format());
console.log(updated.format());

Исходный объект не изменяется.

Ошибка ожидания мутации

let date = dayjs();

date.add(1, 'day');

console.log(date.format());

Результат останется прежним, потому что необходимо сохранить новое значение.

Правильный вариант:

date = date.add(1, 'day');

Работа с Unix Timestamp

Секунды и миллисекунды

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

Unix Timestamp в Javascript использует миллисекунды:

console.log(Date.now());

Многие API возвращают секунды:

const timestamp = 1746870000;

Для корректного преобразования:

const date = dayjs.unix(timestamp);

console.log(date.format());

Ошибка масштаба

const timestamp = 1746870000;

console.log(dayjs(timestamp).format());

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

Работа с null и undefined

Неявное поведение

console.log(dayjs(null).format());
console.log(dayjs(undefined).format());

undefined создаёт текущую дату, а null — невалидную.

Безопасная проверка

function parseDate(value) {
    if (!value) {
        return null;
    }

    const date = dayjs(value);

    return date.isValid() ? date : null;
}

Парсинг неоднозначных форматов

Строки вида:

01/02/2025

могут означать:

  • 1 февраля
  • 2 января

Использование строгого формата

const date = dayjs(
    '01/02/2025',
    'DD/MM/YYYY',
    true
);

console.log(date.format('YYYY-MM-DD'));

Работа с большими диапазонами дат

Переполнение дат

const date = dayjs('9999-12-31');

console.log(
    date.add(1, 'day').format()
);

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

const result = date.add(1, 'day');

console.log(result.isValid());

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

const start1 = dayjs('2025-05-01');
const end1 = dayjs('2025-05-10');

const start2 = dayjs('2025-05-05');
const end2 = dayjs('2025-05-15');

const hasIntersection =
    start1.isBefore(end2) &&
    end1.isAfter(start2);

console.log(hasIntersection);

Разница между startOf() и ручным обнулением

Неправильно:

const date = dayjs()
    .hour(0)
    .minute(0)
    .second(0);

Правильно:

const date = dayjs().startOf('day');

startOf() учитывает все единицы времени, включая миллисекунды.

Работа с миллисекундами

Потеря точности

const date = dayjs('2025-05-10T12:00:00.123');

console.log(date.millisecond());

Сравнение с миллисекундами

const a = dayjs('2025-05-10T12:00:00.123');
const b = dayjs('2025-05-10T12:00:00.124');

console.log(a.isSame(b));

Результат:

false

Игнорирование миллисекунд

console.log(a.isSame(b, 'second'));

Результат:

true

Безопасная сериализация дат

JSON и даты

const date = dayjs();

console.log(JSON.stringify({ date }));

Day.js автоматически сериализует объект в ISO-формат.

Явное преобразование

Наиболее безопасный вариант:

const payload = {
    createdAt: dayjs().toISOString()
};

Проверка временных интервалов

Отрицательная разница

const start = dayjs('2025-05-10');
const end = dayjs('2025-05-01');

console.log(end.diff(start, 'day'));

Результат:

-9

Абсолютная разница

const diff = Math.abs(
    end.diff(start, 'day')
);

console.log(diff);

Особенности метода diff()

Неполные месяцы

const start = dayjs('2025-01-31');
const end = dayjs('2025-02-28');

console.log(end.diff(start, 'month'));

Результат:

1

Однако:

console.log(end.diff(start, 'day'));

Результат:

28

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

Защита от ошибок временной зоны на сервере

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

  • хранить время в UTC;
  • передавать даты в ISO 8601;
  • отображать локальное время только на клиенте;
  • избегать хранения строк без таймзоны;
  • не использовать локальное серверное время как источник истины.

Пример безопасной схемы:

const createdAt = dayjs.utc().toISOString();

Отображение:

const localDate = dayjs.utc(createdAt).local();