Замена плагинов

Плагинная система в Day.js строится вокруг принципа минимального ядра. Базовый пакет содержит только основные операции с датами, а дополнительная функциональность подключается через расширения. При проектировании приложений это приводит к важной задаче: замена или исключение плагинов в пользу нативных возможностей, пользовательских функций или альтернативных подходов, уменьшающих связанность кода.

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


Принцип работы плагинов Day.js

Плагин в Day.js — это функция, расширяющая прототип Day.js дополнительными методами или изменяющая поведение парсинга и форматирования.

Типичный механизм подключения:

import dayjs from 'dayjs'
import relativeTime from 'dayjs/plugin/relativeTime'

dayjs.extend(relativeTime)

После подключения появляются дополнительные методы:

dayjs().fromNow()
dayjs().to(dayjs('2025-01-01'))

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


Замена плагина relativeTime

Плагин relativeTime часто используется для отображения разницы между датами. Его можно заменить вычислением разницы через стандартные методы Day.js без расширений.

Альтернатива без плагина

import dayjs from 'dayjs'

function diffFromNow(date) {
  const now = dayjs()
  const target = dayjs(date)

  const seconds = now.diff(target, 'second')
  const minutes = now.diff(target, 'minute')
  const hours = now.diff(target, 'hour')
  const days = now.diff(target, 'day')

  if (seconds < 60) return `${seconds} секунд назад`
  if (minutes < 60) return `${minutes} минут назад`
  if (hours < 24) return `${hours} часов назад`
  return `${days} дней назад`
}

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


Замена customParseFormat

Плагин customParseFormat используется для парсинга строк нестандартных форматов. Его можно заменить явной нормализацией входных данных до передачи в Day.js.

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

import dayjs from 'dayjs'

function parseCustom(dateString) {
  const [day, month, year] = dateString.split('.')
  return dayjs(`${year}-${month}-${day}`)
}

В более сложных сценариях применяется слой адаптеров, преобразующий любые входные форматы в ISO-совместимый вид.


Замена UTC и timezone-плагинов

Плагины utc и timezone добавляют поддержку временных зон. Их замена возможна через работу с нативным Date и ISO-строками.

Использование UTC через встроенные средства

import dayjs from 'dayjs'

function toUTC(date) {
  return dayjs(date).toISOString()
}

Имитация временных зон без плагина

import dayjs from 'dayjs'

function shiftTimezone(date, offsetHours) {
  return dayjs(date).add(offsetHours, 'hour')
}

Хотя такой подход не покрывает полноценную IANA-логику, он достаточен для прикладных задач, где требуется фиксированное смещение.


Замена duration

Плагин duration предоставляет объект длительности. Его можно заменить структурой на основе разницы и простого объекта-агрегатора.

Реализация без плагина

import dayjs from 'dayjs'

function createDuration(start, end) {
  const d = dayjs(end).diff(dayjs(start), 'millisecond')

  return {
    milliseconds: d,
    seconds: Math.floor(d / 1000),
    minutes: Math.floor(d / 60000),
    hours: Math.floor(d / 3600000),
    days: Math.floor(d / 86400000)
  }
}

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


Замена advancedFormat

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

Альтернативный форматтер

import dayjs from 'dayjs'

function formatAdvanced(date) {
  const d = dayjs(date)

  return {
    isoWeekYear: d.format('GGGG'),
    quarter: Math.ceil((d.month() + 1) / 3),
    dayOfYear: d.dayOfYear ? d.dayOfYear() : null
  }
}

В случае отсутствия метода dayOfYear он вычисляется вручную:

function getDayOfYear(date) {
  const start = dayjs(date).startOf('year')
  return dayjs(date).diff(start, 'day') + 1
}

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

Наиболее устойчивый способ отказа от плагинов заключается в создании промежуточного слоя функций поверх Day.js. Такой слой инкапсулирует всю дополнительную логику.

import dayjs from 'dayjs'

export const DateUtils = {
  format(date, fmt) {
    return dayjs(date).format(fmt)
  },

  diffInHours(a, b) {
    return dayjs(a).diff(dayjs(b), 'hour')
  },

  isExpired(date) {
    return dayjs(date).isBefore(dayjs())
  }
}

Этот подход устраняет необходимость расширять прототип Day.js и снижает риск конфликтов между плагинами.


Замена isLeapYear

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

function isLeapYear(date) {
  const year = dayjs(date).year()
  return (year % 4 === 0 && year % 100 !== 0) || year % 400 === 0
}

Логика полностью независима от Day.js API.


Стратегия отказа от плагинов в крупных проектах

При масштабировании приложения плагины часто становятся источником неявных зависимостей. Замена строится на трёх уровнях:

  1. Перенос логики в утилиты Все расширения прототипа заменяются чистыми функциями.

  2. Использование композиции вместо расширений Day.js используется только как базовый парсер и форматтер.

  3. Фиксация контрактов данных Все операции с датами приводятся к единому формату (ISO 8601), что устраняет необходимость в специализированных плагинах.


Замена chaining-плагинов через функции

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

import dayjs from 'dayjs'

const pipeDate = (date, ...fns) =>
  fns.reduce((acc, fn) => fn(acc), dayjs(date))

const addDays = (n) => (d) => d.add(n, 'day')
const format = (fmt) => (d) => d.format(fmt)

const result = pipeDate('2026-01-01', addDays(5), format('YYYY-MM-DD'))

Такой подход заменяет расширения прототипа функциональной композицией.


Устранение необходимости в plugin.extend

Механизм extend в Day.js может быть полностью исключён при переходе на модульную архитектуру утилит. В этом случае Day.js остаётся неизменяемым ядром, а вся логика переносится в слой доменных функций, что делает систему предсказуемой и облегчает тестирование.