Вычитание времени: sub и производные функции

Библиотека date-fns предоставляет большой набор функций для работы с датами без изменения исходного объекта Date. Все операции возвращают новую дату, что делает код предсказуемым и безопасным.

Для вычитания времени используются:

  • sub
  • subDays
  • subMonths
  • subYears
  • subHours
  • subMinutes
  • subSeconds
  • subWeeks
  • subQuarters
  • subBusinessDays

Главная идея — уменьшение даты на определённое количество временных единиц.


Общая функция sub

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

Сигнатура

sub(date, duration)

Параметры

Параметр Описание
date Исходная дата
duration Объект с единицами времени

Поддерживаемые поля duration

{
  years,
  months,
  weeks,
  days,
  hours,
  minutes,
  seconds
}

Базовый пример

import { sub } from 'date-fns'

const date = new Date(2026, 5, 15, 18, 30)

const result = sub(date, {
  years: 1,
  months: 2,
  days: 10,
  hours: 5
})

console.log(result)

Что происходит

Исходная дата:

15 июня 2026 18:30

После вычитания:

5 апреля 2025 13:30

Порядок вычитания

sub применяет изменения последовательно:

  1. Годы
  2. Месяцы
  3. Недели
  4. Дни
  5. Часы
  6. Минуты
  7. Секунды

Это важно при переходах между месяцами и високосными годами.


Неизменяемость даты

Функции библиотеки не изменяют оригинальный объект.

Пример

import { subDays } from 'date-fns'

const original = new Date(2026, 0, 10)

const changed = subDays(original, 5)

console.log(original)
console.log(changed)

Результат

original -> 10 января
changed  -> 5 января

Исходная дата остаётся прежней.


Вычитание дней: subDays

Сигнатура

subDays(date, amount)

Пример

import { subDays } from 'date-fns'

const date = new Date(2026, 7, 20)

const result = subDays(date, 7)

console.log(result)

Результат

13 августа 2026

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

Отрицательное число превращает вычитание в прибавление.

subDays(date, -5)

Аналогично:

addDays(date, 5)

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

import { subDays } from 'date-fns'

const date = new Date(2026, 2, 3)

const result = subDays(date, 10)

console.log(result)

Результат

21 февраля 2026

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


Вычитание месяцев: subMonths

Сигнатура

subMonths(date, amount)

Простой пример

import { subMonths } from 'date-fns'

const date = new Date(2026, 8, 10)

const result = subMonths(date, 3)

console.log(result)

Результат

10 июня 2026

Особенности конца месяца

Одна из самых важных особенностей работы с датами — месяцы имеют разное количество дней.

Пример

import { subMonths } from 'date-fns'

const date = new Date(2026, 2, 31)

const result = subMonths(date, 1)

console.log(result)

Что получится

28 февраля 2026

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


Работа с високосным годом

import { subMonths } from 'date-fns'

const date = new Date(2024, 2, 31)

const result = subMonths(date, 1)

console.log(result)

Результат

29 февраля 2024

Вычитание лет: subYears

Сигнатура

subYears(date, amount)

Пример

import { subYears } from 'date-fns'

const date = new Date(2030, 5, 15)

const result = subYears(date, 10)

console.log(result)

Результат

15 июня 2020

Високосные даты

Проблемный случай

import { subYears } from 'date-fns'

const date = new Date(2024, 1, 29)

const result = subYears(date, 1)

console.log(result)

Результат

28 февраля 2023

Так как 29 февраля в 2023 году не существует, дата автоматически корректируется.


Вычитание недель: subWeeks

Сигнатура

subWeeks(date, amount)

Пример

import { subWeeks } from 'date-fns'

const date = new Date(2026, 10, 20)

const result = subWeeks(date, 2)

console.log(result)

Результат

6 ноября 2026

Эквивалентность

subWeeks(date, 1)

То же самое, что:

subDays(date, 7)

Вычитание часов: subHours

Сигнатура

subHours(date, amount)

Пример

import { subHours } from 'date-fns'

const date = new Date(2026, 3, 10, 15, 0)

const result = subHours(date, 8)

console.log(result)

Результат

10 апреля 2026 07:00

Переход через сутки

import { subHours } from 'date-fns'

const date = new Date(2026, 3, 10, 3, 0)

const result = subHours(date, 5)

console.log(result)

Результат

9 апреля 2026 22:00

Вычитание минут: subMinutes

Сигнатура

subMinutes(date, amount)

Пример

import { subMinutes } from 'date-fns'

const date = new Date(2026, 0, 1, 12, 30)

const result = subMinutes(date, 45)

console.log(result)

Результат

11:45

Вычитание секунд: subSeconds

Сигнатура

subSeconds(date, amount)

Пример

import { subSeconds } from 'date-fns'

const date = new Date(2026, 0, 1, 0, 0, 30)

const result = subSeconds(date, 45)

console.log(result)

Результат

31 декабря 2025 23:59:45

Вычитание кварталов: subQuarters

Назначение

Квартал — это 3 месяца.

Функция удобна для:

  • финансовых систем;
  • отчётности;
  • аналитики;
  • бухгалтерии.

Сигнатура

subQuarters(date, amount)

Пример

import { subQuarters } from 'date-fns'

const date = new Date(2026, 9, 1)

const result = subQuarters(date, 2)

console.log(result)

Результат

1 апреля 2026

Вычитание рабочих дней: subBusinessDays

Назначение

subBusinessDays исключает:

  • субботу;
  • воскресенье.

Сигнатура

subBusinessDays(date, amount)

Пример

import { subBusinessDays } from 'date-fns'

const date = new Date(2026, 4, 18)

const result = subBusinessDays(date, 5)

console.log(result)

Как считается результат

Если исходная дата — понедельник:

18 мая 2026

то 5 рабочих дней назад:

11 мая 2026

Выходные автоматически пропускаются.


Особенности subBusinessDays

Функция:

  • не учитывает государственные праздники;
  • считает только субботу и воскресенье выходными;
  • полезна для CRM, ERP и банковских систем.

Комбинирование операций

Последовательное вычитание

import {
  subDays,
  subHours,
  subMinutes
} from 'date-fns'

const date = new Date()

const result = subMinutes(
  subHours(
    subDays(date, 2),
    5
  ),
  30
)

console.log(result)

Использование sub вместо цепочки

Тот же пример проще записать через sub.

import { sub } from 'date-fns'

const result = sub(new Date(), {
  days: 2,
  hours: 5,
  minutes: 30
})

Работа с timestamp

Функции принимают обычный объект Date.


Преобразование timestamp

import { subDays } from 'date-fns'

const timestamp = Date.now()

const date = new Date(timestamp)

const result = subDays(date, 30)

console.log(result)

Использование вместе с format

После вычитания дату часто форматируют.

Пример

import {
  subDays,
  format
} from 'date-fns'

const result = subDays(new Date(), 7)

console.log(
  format(result, 'dd.MM.yyyy')
)

Типичные сценарии использования

Получение даты неделю назад

const weekAgo = subWeeks(new Date(), 1)

Получение предыдущего месяца

const prevMonth = subMonths(new Date(), 1)

Получение даты 15 минут назад

const fifteenMinutesAgo =
  subMinutes(new Date(), 15)

Вычисление срока действия

const expiresAt = subDays(deadline, 3)

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

Ошибка с месяцами

Проблема

31 марта - 1 месяц

Не существует:

31 февраля

Реальный результат

28 февраля

Ошибка с часовыми поясами

date-fns работает поверх стандартного Date.

Это означает:

  • влияние локального timezone;
  • переходы летнего времени;
  • различия серверного и клиентского времени.

Пример проблемы DST

subHours(date, 24)

Иногда это не эквивалентно:

subDays(date, 1)

при переходе летнего времени.


Производительность

Функции библиотеки:

  • маленькие;
  • независимые;
  • tree-shakable;
  • хорошо оптимизированы.

Правильный импорт

Хорошо

import { subDays } from 'date-fns'

Менее эффективно

import * as dateFns from 'date-fns'

Сравнение sub и native Date

Native Date

const date = new Date()

date.setDate(date.getDate() - 5)

Недостатки

  • мутирует объект;
  • менее читаемый код;
  • сложнее комбинировать операции;
  • больше вероятность ошибок.

date-fns

const result = subDays(new Date(), 5)

Преимущества

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

Внутренний принцип работы

Большинство функций:

  1. Клонируют дату;
  2. Изменяют нужную часть;
  3. Возвращают новый объект.

Упрощённая модель subDays

function subDays(date, amount) {
  const copy = new Date(date)
  
  copy.setDate(
    copy.getDate() - amount
  )
  
  return copy
}

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


Практический пример: фильтрация записей

Получение записей за последние 30 дней

import {
  subDays,
  isAfter
} from 'date-fns'

const limitDate = subDays(new Date(), 30)

const filtered = posts.filter(post =>
  isAfter(post.createdAt, limitDate)
)

Практический пример: очистка токенов

import {
  subHours,
  isBefore
} from 'date-fns'

const expiredDate = subHours(
  new Date(),
  24
)

const expiredTokens =
  tokens.filter(token =>
    isBefore(token.createdAt, expiredDate)
  )

Практический пример: аналитика

Определение предыдущего квартала

import {
  subQuarters,
  startOfQuarter,
  endOfQuarter
} from 'date-fns'

const previousQuarter =
  subQuarters(new Date(), 1)

const start =
  startOfQuarter(previousQuarter)

const end =
  endOfQuarter(previousQuarter)

Когда использовать sub, а когда специализированные функции

Использование sub

Подходит, когда:

  • нужно вычитать несколько единиц;
  • важна компактность;
  • есть сложная логика смещения.
sub(date, {
  months: 1,
  days: 5,
  hours: 3
})

Использование subDays/subMonths

Подходит, когда:

  • операция одна;
  • важна читаемость;
  • нужен простой код.
subDays(date, 7)

Краткая таблица функций

Функция Назначение
sub Универсальное вычитание
subDays Вычитание дней
subWeeks Вычитание недель
subMonths Вычитание месяцев
subYears Вычитание лет
subHours Вычитание часов
subMinutes Вычитание минут
subSeconds Вычитание секунд
subQuarters Вычитание кварталов
subBusinessDays Вычитание рабочих дней