Операции с кварталами

Квартальная система в Moment.js реализована как надстройка над календарной логикой месяцев и позволяет оперировать периодами по три месяца: первый квартал (январь–март), второй (апрель–июнь), третий (июль–сентябрь), четвертый (октябрь–декабрь). Работа с кварталами встроена в ядро библиотеки и не требует дополнительных плагинов.

Для получения номера квартала используется метод quarter(). Он возвращает значение от 1 до 4 в зависимости от текущей даты:

moment('2026-02-10').quarter(); // 1
moment('2026-05-10').quarter(); // 2
moment('2026-08-10').quarter(); // 3
moment('2026-11-10').quarter(); // 4

Логика вычисления основана на месяце даты, который делится на интервалы по три месяца с округлением вверх.

Установка квартала

Метод quarter(value) позволяет задавать квартал вручную. Допустимые значения — от 1 до 4. При изменении квартала Moment.js автоматически пересчитывает месяц внутри соответствующего диапазона, сохраняя день месяца, если это возможно.

moment('2026-01-15').quarter(3); // перевод в третий квартал → июль (или ближайший корректный день)
moment('2026-01-15').quarter(4); // перевод в четвертый квартал → октябрь

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

Начало и конец квартала

Для работы с границами кварталов используется метод startOf('quarter') и endOf('quarter').

moment('2026-05-10').startOf('quarter'); // 2026-04-01 00:00:00
moment('2026-05-10').endOf('quarter');   // 2026-06-30 23:59:59.999

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

Границы квартала всегда соответствуют локальному календарю, если не используется UTC-режим:

moment.utc('2026-05-10').startOf('quarter');

Форматирование кварталов

Для вывода квартала в строковом формате используется токен Q в методе format().

moment('2026-05-10').format('Q');   // "2"
moment('2026-05-10').format('[Q]Q'); // "Q2"

Дополнительно применяется порядковый формат Qo, учитывающий локализацию порядковых чисел:

moment('2026-05-10').format('Qo'); // "2nd" (в зависимости от локали)

Форматирование интегрируется с системой локалей Moment.js, где могут определяться собственные правила отображения порядковых суффиксов.

Арифметика кварталов

Методы add() и subtract() поддерживают работу с кварталами через ключ quarter.

moment('2026-01-15').add(1, 'quarter');  // переход во 2 квартал
moment('2026-07-10').subtract(2, 'quarter'); // сдвиг на два квартала назад

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

Внутренне операция сводится к преобразованию:

  • 1 quarter = 3 months
  • сохраняется день месяца с корректировкой переполнения

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

Метод diff() позволяет вычислять разницу между датами в кварталах:

moment('2026-10-01').diff(moment('2026-01-01'), 'quarter'); // 3
moment('2026-07-01').diff(moment('2026-02-01'), 'quarter'); // 1

Расчёт выполняется через приведение дат к месячной шкале и последующее деление на три месяца с округлением вниз.

Точная логика зависит от временной точки: сравнение всегда опирается на начало квартала, если явно не указаны дополнительные параметры округления.

Квартальные границы и нормализация дат

При переходах между кварталами Moment.js учитывает неодинаковую длину месяцев. Например:

moment('2026-01-31').quarter(2);

В апреле нет 31 числа, поэтому дата нормализуется до последнего допустимого дня месяца (30 апреля). Эта особенность критична при массовых вычислениях дат, привязанных к квартальным отчётам.

Использование startOf и endOf в цепочках

Квартальные операции часто комбинируются с другими уровнями времени:

moment('2026-05-10')
  .startOf('quarter')
  .add(10, 'days')
  .format('YYYY-MM-DD');

Или:

moment('2026-05-10')
  .endOf('quarter')
  .subtract(1, 'month');

Каждый шаг пересчитывает внутреннее состояние объекта Moment, включая дату, время и таймзону.

Формирование отчётных периодов

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

const start = moment().quarter(1).startOf('quarter');
const end = moment().quarter(1).endOf('quarter');

Такая конструкция фиксирует границы периода независимо от текущего дня внутри квартала.

Особенности календарной модели

Кварталы в Moment.js основаны на григорианском календаре без учёта финансовых или нестандартных календарных систем. Финансовый год, начинающийся, например, в апреле, не поддерживается напрямую и требует ручной корректировки:

moment().month(3).startOf('month').quarter(1);

В таких случаях квартальная логика переопределяется через смещение начала года.

Связь кварталов с месяцами и неделями

Квартал является производной единицей от месяцев, поэтому любые операции сводятся к месячной арифметике:

  • квартал → 3 месяца
  • квартал → 13 недель (приблизительно)
  • квартал → переменное число дней

Недели не участвуют в расчёте квартала напрямую, но могут использоваться для дополнительной детализации внутри периода:

moment('2026-05-10')
  .startOf('quarter')
  .weeks();

Поведение в UTC-режиме

При использовании moment.utc() квартальная логика сохраняется, но все вычисления выполняются в UTC-календаре:

moment.utc('2026-05-10').quarter(); // 2
moment.utc('2026-05-10').startOf('quarter');

Различия между локальным и UTC-режимом проявляются только в смещениях дат, но не в границах квартала.

Нормализация и пограничные случаи

При установке квартала и выполнении арифметики учитываются следующие особенности:

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

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