Вычисления с днями года

Работа с порядковыми днями года в Day.js опирается на отдельный плагин, расширяющий базовую модель библиотеки и добавляющий возможность интерпретировать дату как число от 1 до 365 (или 366 в високосный год). Такая абстракция полезна при расчётах сезонности, планировании периодов внутри года, анализе временных рядов и нормализации дат.

Базовая сборка Day.js не содержит методов для работы с номером дня в году. Для этого используется плагин dayOfYear.

import dayjs from 'dayjs'
import dayOfYear from 'dayjs/plugin/dayOfYear'

dayjs.extend(dayOfYear)

После расширения экземпляры Day.js получают методы:

  • dayjs().dayOfYear() — получение порядкового дня
  • dayjs().dayOfYear(value) — установка дня года

Получение порядкового дня года

Метод dayOfYear() возвращает целое число, соответствующее позиции даты внутри текущего года.

import dayjs from 'dayjs'
import dayOfYear from 'dayjs/plugin/dayOfYear'

dayjs.extend(dayOfYear)

const d = dayjs('2026-05-23')
const ordinal = d.dayOfYear()

console.log(ordinal)

Результат зависит от календарного года и учитывает:

  • количество дней в предыдущих месяцах
  • текущий день месяца
  • високосные годы

В стандартном году диапазон значений: 1–365 В високосном: 1–366


Установка порядкового дня года

Метод dayOfYear(value) позволяет задать дату через её позицию в году. При этом Day.js пересчитывает месяц и день автоматически.

const d = dayjs().dayOfYear(100)

console.log(d.format('YYYY-MM-DD'))

Если указан день, превышающий количество дней в году, значение переносится в следующий год.

dayjs('2026-01-01').dayOfYear(400)

Такой вызов приведёт к переходу за пределы текущего года с корректировкой даты.


Математика внутри года

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

Разница между датами в рамках одного года:

const a = dayjs('2026-02-10').dayOfYear()
const b = dayjs('2026-05-23').dayOfYear()

const diff = b - a

Результат отражает количество дней между датами внутри одного календарного года.

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


Вычисления с переходом через год

Если даты находятся в разных годах, прямое вычитание порядковых дней даёт искажённый результат. Корректное вычисление требует учёта общего количества дней года.

function diffOrdinal(a, b) {
  const start = dayjs(a)
  const end = dayjs(b)

  const startYear = start.year()
  const endYear = end.year()

  if (startYear === endYear) {
    return end.dayOfYear() - start.dayOfYear()
  }

  let total = 0

  total += dayjs(`${startYear}-12-31`).dayOfYear() - start.dayOfYear()

  for (let y = startYear + 1; y < endYear; y++) {
    total += dayjs(`${y}-12-31`).dayOfYear()
  }

  total += end.dayOfYear()

  return total
}

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


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

Day.js корректно учитывает високосность через внутреннюю календарную модель. Високосный год содержит 366 дней, и это напрямую влияет на:

  • максимальное значение dayOfYear()
  • смещение дат при установке значения
  • разницу между датами

Проверка високосного года:

dayjs('2024-01-01').isLeapYear()

Хотя метод isLeapYear() не является частью ядра, он доступен через отдельный плагин isLeapYear.

import isLeapYear from 'dayjs/plugin/isLeapYear'

dayjs.extend(isLeapYear)

Преобразование даты в долю года

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

function yearProgress(date) {
  const d = dayjs(date)
  const yearLength = d.isLeapYear() ? 366 : 365

  return d.dayOfYear() / yearLength
}

Такой подход применяется в аналитике временных рядов, когда требуется сравнение разных лет без сезонных смещений.


Привязка к началу и концу года

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

Начало года:

const start = dayjs('2026-01-01')

Конец года:

const end = dayjs('2026-12-31')

Разница между ними в порядковых днях всегда равна длине года:

end.dayOfYear() - start.dayOfYear() + 1

Сравнение дат через порядковый день

При анализе дат внутри одного года можно использовать упрощённое сравнение:

const a = dayjs('2026-03-01')
const b = dayjs('2026-03-15')

if (a.year() === b.year()) {
  if (a.dayOfYear() < b.dayOfYear()) {
    // a раньше b
  }
}

Такой метод быстрее, чем полное сравнение объектов даты, но требует проверки года.


Нормализация дат внутри года

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

function normalizeToYear(date, year) {
  const d = dayjs(date)
  return dayjs(`${year}-01-01`).dayOfYear(d.dayOfYear())
}

Это позволяет сравнивать сезонные данные между разными годами без учёта абсолютной даты.


Сдвиг даты по порядковому номеру

Day.js позволяет задавать дату через позицию внутри года, что удобно для генерации временных точек.

const generateSeries = (year) => {
  const daysInYear = dayjs(`${year}-12-31`).dayOfYear()

  return Array.from({ length: daysInYear }, (_, i) => {
    return dayjs(`${year}-01-01`).dayOfYear(i + 1)
  })
}

Результат — последовательность всех дней года в виде объектов Day.js.


Использование в аналитических вычислениях

Порядковый день часто служит основой для:

  • построения сезонных индексов
  • расчёта фазовых смещений
  • анализа повторяющихся событий по дням года
  • нормализации временных рядов

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


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

На практике часто встречаются следующие логические ошибки:

  • вычитание dayOfYear() без проверки года
  • игнорирование високосных лет при нормализации
  • использование порядкового дня для межгодовых интервалов без пересчёта
  • попытка интерпретировать dayOfYear как абсолютный timestamp

Корректная работа требует всегда учитывать контекст года и календарной структуры, поскольку порядковый день является относительной, а не глобальной величиной.