При разработке отчетов и обработке календарных данных разработчики часто сталкиваются с ситуацией, когда встроенная функция НеделяГода() возвращает значение, не совпадающее с данными производственного календаря или европейских систем. Например, для даты 01.01.2017 функция платформы 1С возвращает 1, тогда как производственные календари и международные стандарты относят этот день к 52-й неделе 2016 года. Разберем причины такого расхождения и рассмотрим алгоритмы правильного вычисления недели по международному стандарту.
Проанализируем, как устроен расчет номеров недель в платформе 1С:
НеделяГода() (а также функция НЕДЕЛЯ() в языке запросов 1С) всегда считает дату 1 января началом первой недели текущего года. Поведение аналогично функции DATEPART(week, ...) в СУБД MS SQL Server.НеделяГода() нет параметров для переключения алгоритма на международный стандарт ISO 8601 (в РФ — ГОСТ ИСО 8601-2001).Посмотрим, как стандарт определяет первую неделю года:
При использовании стандарта ISO важно помнить: рассчитывается не только номер недели, но и соответствующий расчетный год (ISO Year), который для первых или последних дней января/декабря может отличаться от календарного года даты.
Разберем по шагам алгоритм, который позволяет вычислить правильный номер недели и расчетный год по стандарту ISO 8601. Основная идея заключается в смещении расчетной даты к четвергу текущей семидневки, так как четверг однозначно определяет принадлежность недели конкретному году.
Посмотрим на пример реализации экспортных функций:
// Функция возвращает структуру с номером недели и расчетным годом по стандарту ISO 8601
Функция ПолучитьНеделюИГодISO(Знач ПроверяемаяДата) Экспорт
ПроверяемаяДата = НачалоДня(ПроверяемаяДата);
НомерДня = ДеньНедели(ПроверяемаяДата);
// Находим четверг текущей недели (смещение относительно текущего дня)
ДатаЧетверга = ПроверяемаяДата + (4 - НомерДня) * 86400;
// Год найденного четверга является расчетным ISO-годом
РасчетныйГод = Год(ДатаЧетверга);
// Находим первый четверг расчетного года (четверг недели, содержащей 4 января)
Дата4Января = Дата(РасчетныйГод, 1, 4);
ПервыйЧетверг = Дата4Января + (4 - ДеньНедели(Дата4Января)) * 86400;
// Вычисляем количество полных недель между первым четвергом года и текущим четвергом
НомерНедели = 1 + (ДатаЧетверга - ПервыйЧетверг) / (7 * 86400);
Результат = Новый Структура;
Результат.Вставить("НомерНедели", Цел(НомерНедели));
Результат.Вставить("Год", РасчетныйГод);
Возврат Результат;
КонецФункции
Рассмотрим, как работает данный расчет для даты 01.01.2017:
29.12.2016.2016.Если в конфигурации используется подсистема «Календарные графики» БСП (Библиотеки Стандартных Подсистем) или типовой регистр сведений ДанныеПервичныхКлассификаторов / КалендарныеГрафики, номер недели можно получать непосредственно из предварительно заполненных данных производственного календаря.
Рассмотрим пример запроса к регистру сведений производственного календаря:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Календарь.НомерНедели КАК НомерНедели,
| Календарь.Год КАК Год
|ИЗ
| РегистрСведений.ДанныеПроизводственногоКалендаря КАК Календарь
|ГДЕ
| Календарь.Дата = &Дата
| И Календарь.ПроизводственныйКалендарь = &ПроизводственныйКалендарь";
Запрос.УстановитьПараметр("Дата", НачалоДня(ТекущаяДата()));
Запрос.УстановитьПараметр("ПроизводственныйКалендарь", Справочники.ПроизводственныеКалендари.РоссийскаяФедерация);
Выборка = Запрос.Выполнить().Выбрать();
Если Выборка.Следующий() Тогда
Сообщить("Номер недели по производственному календарю: " + Строка(Выборка.НомерНедели));
КонецЕсли;
Этот подход гарантирует полное соответствие регламентированной отчетности и утвержденному производственному календарю РФ без необходимости дополнительных ручных вычислений.