Почему функция НеделяГода() в 1С расходится с производственным календарем и как рассчитать неделю по стандарту ISO 8601?

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

При разработке отчетов и обработке календарных данных разработчики часто сталкиваются с ситуацией, когда встроенная функция НеделяГода() возвращает значение, не совпадающее с данными производственного календаря или европейских систем. Например, для даты 01.01.2017 функция платформы 1С возвращает 1, тогда как производственные календари и международные стандарты относят этот день к 52-й неделе 2016 года. Разберем причины такого расхождения и рассмотрим алгоритмы правильного вычисления недели по международному стандарту.

Причины поведения встроенной функции НеделяГода()

Проанализируем, как устроен расчет номеров недель в платформе 1С:

  1. Жесткая привязка к 1 января: Встроенная функция НеделяГода() (а также функция НЕДЕЛЯ() в языке запросов 1С) всегда считает дату 1 января началом первой недели текущего года. Поведение аналогично функции DATEPART(week, ...) в СУБД MS SQL Server.
  2. Искусственное разделение граничных недель: Платформа 1С разбивает неделю на стыке годов на две части. Дни недели до 31 декабря включительно относятся к последней неделе уходящего года (52 или 53), а дни с 1 января — к 1-й неделе нового года, даже если они приходятся на одну и ту же непрерывную семидневку (с понедельника по воскресенье).
  3. Отсутствие встроенных настроек стандарта: У функции НеделяГода() нет параметров для переключения алгоритма на международный стандарт ISO 8601 (в РФ — ГОСТ ИСО 8601-2001).

Правила определения номера недели по стандарту ISO 8601

Посмотрим, как стандарт определяет первую неделю года:

  1. Первой календарной неделей года считается неделя, содержащая первый четверг нового года.
  2. В григорианском календаре это эквивалентно правилу: первая неделя года — это неделя, содержащая дату 4 января.
  3. Неделя всегда начинается с понедельника и заканчивается воскресеньем.
  4. Если 1 января выпадает на пятницу, субботу или воскресенье, эти дни относятся к последней неделе (52-й или 53-й) предыдущего календарного года. Именно это произошло 01.01.2017 (воскресенье).

При использовании стандарта ISO важно помнить: рассчитывается не только номер недели, но и соответствующий расчетный год (ISO Year), который для первых или последних дней января/декабря может отличаться от календарного года даты.

Решение 1: Программный расчет недели по ISO 8601 на встроенном языке 1С

Разберем по шагам алгоритм, который позволяет вычислить правильный номер недели и расчетный год по стандарту ISO 8601. Основная идея заключается в смещении расчетной даты к четвергу текущей семидневки, так как четверг однозначно определяет принадлежность недели конкретному году.

Посмотрим на пример реализации экспортных функций:


// Функция возвращает структуру с номером недели и расчетным годом по стандарту ISO 8601
Функция ПолучитьНеделюИГодISO(Знач ПроверяемаяДата) Экспорт

    ПроверяемаяДата = НачалоДня(ПроверяемаяДата);
    НомерДня = ДеньНедели(ПроверяемаяДата);
    
    // Находим четверг текущей недели (смещение относительно текущего дня)
    ДатаЧетверга = ПроверяемаяДата + (4 - НомерДня) * 86400;
    
    // Год найденного четверга является расчетным ISO-годом
    РасчетныйГод = Год(ДатаЧетверга);
    
    // Находим первый четверг расчетного года (четверг недели, содержащей 4 января)
    Дата4Января = Дата(РасчетныйГод, 1, 4);
    ПервыйЧетверг = Дата4Января + (4 - ДеньНедели(Дата4Января)) * 86400;
    
    // Вычисляем количество полных недель между первым четвергом года и текущим четвергом
    НомерНедели = 1 + (ДатаЧетверга - ПервыйЧетверг) / (7 * 86400);
    
    Результат = Новый Структура;
    Результат.Вставить("НомерНедели", Цел(НомерНедели));
    Результат.Вставить("Год", РасчетныйГод);
    
    Возврат Результат;

КонецФункции

Рассмотрим, как работает данный расчет для даты 01.01.2017:

  1. День недели для 01.01.2017 — 7 (воскресенье).
  2. Четверг этой недели был 29.12.2016.
  3. Расчетный год определяется по четвергу: 2016.
  4. Функция возвращает номер недели 52 и год 2016, что полностью соответствует производственному календарю.

Решение 2: Получение данных из производственного календаря конфигурации

Если в конфигурации используется подсистема «Календарные графики» БСП (Библиотеки Стандартных Подсистем) или типовой регистр сведений ДанныеПервичныхКлассификаторов / КалендарныеГрафики, номер недели можно получать непосредственно из предварительно заполненных данных производственного календаря.

Рассмотрим пример запроса к регистру сведений производственного календаря:


Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |	Календарь.НомерНедели КАК НомерНедели,
    |	Календарь.Год КАК Год
    |ИЗ
    |	РегистрСведений.ДанныеПроизводственногоКалендаря КАК Календарь
    |ГДЕ
    |	Календарь.Дата = &Дата
    |	И Календарь.ПроизводственныйКалендарь = &ПроизводственныйКалендарь";

Запрос.УстановитьПараметр("Дата", НачалоДня(ТекущаяДата()));
Запрос.УстановитьПараметр("ПроизводственныйКалендарь", Справочники.ПроизводственныеКалендари.РоссийскаяФедерация);

Выборка = Запрос.Выполнить().Выбрать();
Если Выборка.Следующий() Тогда
    Сообщить("Номер недели по производственному календарю: " + Строка(Выборка.НомерНедели));
КонецЕсли;

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

← На главную