ISO-недели

В большинстве стран календарная неделя начинается с понедельника, а первая неделя года определяется не просто наличием 1 января. Для стандартизации был создан международный стандарт ISO 8601.

Согласно ISO 8601:

  • неделя начинается с понедельника;
  • первая ISO-неделя года — неделя, содержащая первый четверг года;
  • каждая ISO-неделя имеет номер от 1 до 52 или 53.

Moment.js предоставляет отдельный набор методов для работы именно с ISO-неделями, поскольку обычные календарные недели могут отличаться в зависимости от локали.


Получение номера ISO-недели

Метод isoWeek() возвращает номер ISO-недели.

const date = moment('2025-01-02');

console.log(date.isoWeek());

Результат:

1

Разница между week() и isoWeek()

Moment.js содержит два похожих метода:

Метод Основа
week() локальные настройки
isoWeek() стандарт ISO 8601

Пример:

const date = moment('2022-01-01');

console.log(date.week());
console.log(date.isoWeek());

В разных локалях week() может вернуть различные значения, тогда как isoWeek() всегда следует ISO-стандарту.


Установка ISO-недели

Метод isoWeek(number) позволяет изменить неделю даты.

const date = moment('2025-01-10');

date.isoWeek(15);

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

Moment.js автоматически пересчитает дату в пределах соответствующего года.


Получение ISO-года

ISO-календарь использует собственное понятие года. Иногда последние дни декабря относятся уже к первой ISO-неделе следующего года.

Метод isoWeekYear() возвращает ISO-год.

const date = moment('2020-12-31');

console.log(date.isoWeekYear());

Результат:

2020

Однако:

const date = moment('2021-01-01');

console.log(date.isoWeekYear());

Результат:

2020

Хотя календарный год уже 2021, ISO-календарь всё ещё относит дату к предыдущему ISO-году.


Установка ISO-года

const date = moment();

date.isoWeekYear(2030);

console.log(date.format());

Получение ISO-дня недели

Метод isoWeekday() работает по ISO-стандарту:

День Значение
Понедельник 1
Вторник 2
Среда 3
Четверг 4
Пятница 5
Суббота 6
Воскресенье 7

Пример:

const date = moment('2025-05-19');

console.log(date.isoWeekday());

Результат:

1

Отличие day() от isoWeekday()

Метод day() использует американскую систему:

День day()
Воскресенье 0
Понедельник 1
Суббота 6

ISO-подход делает понедельник первым днём недели.

Сравнение:

const date = moment('2025-05-18');

console.log(date.day());
console.log(date.isoWeekday());

Результат:

0
7

Установка ISO-дня недели

const date = moment('2025-05-21');

date.isoWeekday(1);

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

Дата будет изменена на понедельник текущей ISO-недели.


Начало ISO-недели

Для получения начала ISO-недели используется startOf('isoWeek').

const date = moment('2025-05-21');

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

Результат:

2025-05-19

Конец ISO-недели

const date = moment('2025-05-21');

console.log(
    date.endOf('isoWeek').format('YYYY-MM-DD HH:mm:ss')
);

Результат:

2025-05-25 23:59:59

Форматирование ISO-недели

Moment.js поддерживает специальные токены форматирования.

Токен Значение
W номер ISO-недели
WW ISO-неделя с ведущим нулём
GGGG ISO-год

Пример:

const date = moment('2025-01-05');

console.log(
    date.format('GGGG-[W]WW')
);

Результат:

2025-W01

Практика работы с ISO-календарём

Генерация диапазона ISO-недели

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

console.log(start.format());
console.log(end.format());

Проверка принадлежности к одной ISO-неделе

const a = moment('2025-05-19');
const b = moment('2025-05-25');

console.log(
    a.isoWeek() === b.isoWeek() &&
    a.isoWeekYear() === b.isoWeekYear()
);

Получение всех дней ISO-недели

const start = moment().startOf('isoWeek');

const days = [];

for (let i = 0; i < 7; i++) {
    days.push(
        start.clone().add(i, 'days').format('YYYY-MM-DD')
    );
}

console.log(days);

Особенности 53-й недели

Некоторые годы содержат 53 ISO-недели.

Проверка:

const date = moment('2020-12-31');

console.log(date.isoWeek());

Результат:

53

53-я неделя появляется, если:

  • год начинается в четверг;
  • либо високосный год начинается в среду.

Переход между ISO-годами

Особенность ISO-календаря особенно заметна на границе лет.

Пример:

Дата ISO-год ISO-неделя
2020-12-31 2020 53
2021-01-01 2020 53
2021-01-04 2021 1

Проверка:

const dates = [
    '2020-12-31',
    '2021-01-01',
    '2021-01-04'
];

dates.forEach(d => {
    const m = moment(d);

    console.log(
        d,
        m.isoWeekYear(),
        m.isoWeek()
    );
});

Использование ISO-недель в бизнес-логике

ISO-недели широко применяются:

  • в ERP-системах;
  • в бухгалтерии;
  • в производственном планировании;
  • в системах учёта рабочего времени;
  • в аналитике;
  • в международной отчётности.

Пример формирования идентификатора недели:

const id = moment().format('GGGG-[W]WW');

console.log(id);

Результат:

2025-W21

Создание даты из ISO-недели

Moment.js позволяет создавать дату, используя ISO-год, ISO-неделю и день недели.

const date = moment()
    .isoWeekYear(2025)
    .isoWeek(10)
    .isoWeekday(1);

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

Работа с ISO-неделями и UTC

ISO-методы полностью совместимы с UTC.

const date = moment.utc('2025-01-01');

console.log(date.isoWeek());
console.log(date.isoWeekYear());

Типичные ошибки

Использование week() вместо isoWeek()

moment().week();

Такой код зависит от локали и может давать неожиданные результаты.

Для международных систем предпочтителен ISO-подход:

moment().isoWeek();

Игнорирование ISO-года

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

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

a.isoWeek() === b.isoWeek()

Правильно:

a.isoWeek() === b.isoWeek() &&
a.isoWeekYear() === b.isoWeekYear()

Ошибки при форматировании

Неверно:

moment().format('YYYY-WW')

YYYY — обычный календарный год.

Для ISO-календаря необходимо использовать GGGG.

Правильно:

moment().format('GGGG-[W]WW')

Сравнение ISO-недель

Проверка текущей ISO-недели

const currentWeek = moment().isoWeek();

console.log(currentWeek);

Проверка предыдущей недели

const previousWeek = moment()
    .subtract(1, 'week')
    .isoWeek();

console.log(previousWeek);

Разница между датами в ISO-неделях

const a = moment('2025-01-01');
const b = moment('2025-03-01');

console.log(
    b.diff(a, 'weeks')
);

Локализация и ISO-недели

Даже при изменении локали ISO-методы остаются неизменными.

moment.locale('ru');

const date = moment();

console.log(date.isoWeek());
console.log(date.isoWeekday());

ISO-календарь всегда:

  • начинает неделю с понедельника;
  • использует международный стандарт нумерации недель.

Краткая сводка ISO-методов

Метод Назначение
isoWeek() получение/установка ISO-недели
isoWeekYear() получение/установка ISO-года
isoWeekday() получение/установка ISO-дня
startOf('isoWeek') начало ISO-недели
endOf('isoWeek') конец ISO-недели
format('GGGG-[W]WW') ISO-форматирование недели