При разработке отчетов и систем планирования в 1С программисты часто сталкиваются с тем, что встроенная функция НеделяГода() выдает результат, не соответствующий ожиданиям бизнес-пользователей или внешних торговых систем. Проблема заключается в том, что алгоритм 1С по умолчанию просто делит количество дней от начала года на семь, тогда как во многих странах, включая Россию (согласно ГОСТ ИСО 8601-2001), используется стандарт ISO 8601. Давайте подробно разберем, в чем разница, и как реализовать корректный расчет в программном коде и запросах.
Стандартная функция НеделяГода(Дата) в 1С считает первой неделей ту, в которой находится 1 января. Однако согласно ISO 8601, первая неделя года — это та неделя, на которую выпадает первый четверг этого года. Это правило приводит к следующим важным следствиям:
Разберем ситуацию на примере: в 2012 году 1 января выпало на воскресенье. Стандартная функция 1С посчитала это первой неделей 2012 года. Но по ISO 8601 первый четверг года — это 5 января, а значит, первая неделя началась только 2 января. 1-е же января 2012 года официально относилось к последней неделе 2011 года.
Проанализируем наиболее точный способ реализации алгоритма через встроенный язык. Самым надежным методом является "правило 4 января". Мы определим начало недели, в которую попадает 4-е число, и от нее будем вести отсчет. Рассмотрим пошагово реализацию такой функции:
Функция НеделяГода_ISO8601(Знач Дата) Экспорт
// 1. Находим 4 января того года, к которому относится переданная дата
ЧетвертоеЯнваря = Дата(Год(Дата), 1, 4);
// 2. Находим начало первой недели года по стандарту ISO
НачалоПервойНедели = НачалоНедели(ЧетвертоеЯнваря);
// 3. Если проверяемая дата меньше начала первой недели,
// значит она относится к последней неделе ПРЕДЫДУЩЕГО года
Если Дата < НачалоПервойНедели Тогда
// Рекурсивно вызываем функцию для конца прошлого года
Возврат НеделяГода_ISO8601(Дата(Год(Дата) - 1, 12, 31));
КонецЕсли;
// 4. Проверяем, не относится ли дата к первой неделе СЛЕДУЮЩЕГО года
// (актуально для последних дней декабря)
ЧетвертоеЯнваряСледующегоГода = Дата(Год(Дата) + 1, 12, 31); // Смещение для проверки следующего цикла
// Более простой способ — проверить, не попадает ли текущая дата в неделю 4-го января след. года
НачалоПервойНеделиСледующегоГода = НачалоНедели(Дата(Год(Дата) + 1, 1, 4));
Если Дата >= НачалоПервойНеделиСледующегоГода Тогда
Возврат 1;
КонецЕсли;
// 5. Вычисляем количество полных недель между началом первой недели и текущей датой
КоличествоНедель = Цел((НачалоНедели(Дата) - НачалоПервойНедели) / (7 * 24 * 60 * 60));
Возврат 1 + КоличествоНедель;
КонецФункции
В этой функции мы используем свойство 1С НачалоНедели(), которое по умолчанию работает с понедельника (если в настройках системы не указано иное). между годами.
Часто номер недели требуется получить непосредственно в запросе для группировки данных. В языке запросов 1С нет прямого аналога функции ISO, поэтому нам придется использовать конструкцию ВЫБОР. Проанализируем логику: если день недели начала года больше четверга (т.е. пятница, суббота или воскресенье), то 1С завышает номер недели на единицу для всех дат года.
Ниже приведен пример реализации логики ISO 8601 в запросе, который учитывает основные смещения. Для анализа структуры сложных пакетов в таких задачах подойдет управляемая консоль запросов, отчетов 3.9.0:
ВЫБРАТЬ
ВЫБОР
// Случай 1: Год начинается с Пн, Вт, Ср или Чт (правильная неделя)
КОГДА ДЕНЬНЕДЕЛИ(НАЧАЛОПЕРИОДА(&Дата, ГОД)) < 5
ТОГДА НЕДЕЛЯ(&Дата)
// Случай 2: Год начинается с Пт, Сб, Вс (неделя смещена)
ИНАЧЕ ВЫБОР
// Если это первая неделя по календарю 1С, она фактически 52-я или 53-я прошлого года
КОГДА НЕДЕЛЯ(&Дата) = 1
ТОГДА ВЫБОР
// Проверяем, была ли в прошлом году 53-я неделя
КОГДА ДЕНЬНЕДЕЛИ(ДОБАВИТЬКДАТЕ(НАЧАЛОПЕРИОДА(&Дата, ГОД), СЕКУНДА, -1)) < 5
ТОГДА НЕДЕЛЯ(ДОБАВИТЬКДАТЕ(НАЧАЛОПЕРИОДА(&Дата, ГОД), СЕКУНДА, -1))
ИНАЧЕ НЕДЕЛЯ(ДОБАВИТЬКДАТЕ(НАЧАЛОПЕРИОДА(&Дата, ГОД), СЕКУНДА, -1)) - 1
КОНЕЦ
// Для остальных недель года просто вычитаем 1
ИНАЧЕ НЕДЕЛЯ(&Дата) - 1
КОНЕЦ
КОНЕЦ КАК НомерНеделиПоISO
Важный нюанс: представленный выше запрос решает задачу определения номера, но помните, что для дат вроде 1 января 2012 года этот запрос вернет "52", но при этом год останется "2012". Выполняя быстрый анализ данных, помните: для корректной аналитики вам также нужно вычислять "ISO-Год" — расчетный год, к которому относится данная неделя.
Рассмотрим более изящный математический хак, который часто используют опытные разработчики. Математически доказано, что номер недели по ISO 8601 для любой даты совпадает с номером недели четверга той же самой недели. Это позволяет избежать сложных ветвлений ВЫБОР.
НеделяГода() от этого четверга.Посмотрим, как это выглядит в коде:
Функция ISO_Неделя_Хак(Дата)
// Находим четверг текущей недели:
// Номер дня недели (Пн=1...Вс=7). Четверг - это 4.
Смещение = 4 - ДеньНедели(Дата);
ДатаЧетверга = Дата + Смещение * 24 * 60 * 60;
// Номер недели четверга всегда соответствует стандарту ISO 8601
Возврат НеделяГода(ДатаЧетверга);
КонецФункции
Этот метод считается наиболее лаконичным и устойчивым к ошибкам. Он автоматически "закидывает" 1 января в прошлый год, если это необходимо, потому что четверг для 1 января (если это воскресенье) будет относиться к декабрю прошлого года.
Выясним причину появления 53-й недели, так как это часто вызывает вопросы у пользователей. Согласно стандарту, год содержит 53 недели, если:
Во всех остальных случаях в году будет ровно 52 недели. При тестировании ваших функций обязательно проверяйте такие годы (например, 2015, 2020, 2026), чтобы убедиться в корректности расчетов.
Проанализировав все способы, можно сделать вывод: если вам нужен быстрый результат в коде — используйте метод четверга. Если нужно реализовать логику в сложном запросе СКД — используйте правило 4 января с проверкой смещения начала года. Это обеспечит единство данных в 1С и внешних системах (таких как Excel или SQL Server), которые по умолчанию могут использовать стандарт ISO.