Как правильно рассчитать номер недели по стандарту ISO 8601 в 1С?

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

При разработке отчетов и систем планирования в 1С программисты часто сталкиваются с тем, что встроенная функция НеделяГода() выдает результат, не соответствующий ожиданиям бизнес-пользователей или внешних торговых систем. Проблема заключается в том, что алгоритм 1С по умолчанию просто делит количество дней от начала года на семь, тогда как во многих странах, включая Россию (согласно ГОСТ ИСО 8601-2001), используется стандарт ISO 8601. Давайте подробно разберем, в чем разница, и как реализовать корректный расчет в программном коде и запросах.

В чем заключается проблема стандартной функции 1С?

Стандартная функция НеделяГода(Дата) в 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С

Часто номер недели требуется получить непосредственно в запросе для группировки данных. В языке запросов 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 для любой даты совпадает с номером недели четверга той же самой недели. Это позволяет избежать сложных ветвлений ВЫБОР.

  1. Найдем четверг для текущей даты.
  2. Возьмем НеделяГода() от этого четверга.

Посмотрим, как это выглядит в коде:


Функция ISO_Неделя_Хак(Дата)
    // Находим четверг текущей недели: 
    // Номер дня недели (Пн=1...Вс=7). Четверг - это 4.
    Смещение = 4 - ДеньНедели(Дата);
    ДатаЧетверга = Дата + Смещение * 24 * 60 * 60;
    
    // Номер недели четверга всегда соответствует стандарту ISO 8601
    Возврат НеделяГода(ДатаЧетверга);
КонецФункции

Этот метод считается наиболее лаконичным и устойчивым к ошибкам. Он автоматически "закидывает" 1 января в прошлый год, если это необходимо, потому что четверг для 1 января (если это воскресенье) будет относиться к декабрю прошлого года.

Когда в году бывает 53 недели?

Выясним причину появления 53-й недели, так как это часто вызывает вопросы у пользователей. Согласно стандарту, год содержит 53 недели, если:

Во всех остальных случаях в году будет ровно 52 недели. При тестировании ваших функций обязательно проверяйте такие годы (например, 2015, 2020, 2026), чтобы убедиться в корректности расчетов.

Проанализировав все способы, можно сделать вывод: если вам нужен быстрый результат в коде — используйте метод четверга. Если нужно реализовать логику в сложном запросе СКД — используйте правило 4 января с проверкой смещения начала года. Это обеспечит единство данных в 1С и внешних системах (таких как Excel или SQL Server), которые по умолчанию могут использовать стандарт ISO.

← На главную