Как программно разбить произвольный период на календарные недели в 1С?

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

При решении прикладных задач в 1С часто возникает необходимость представить отчетный период (например, месяц или квартал) в виде последовательности календарных недель. Рассмотрим, как реализовать этот алгоритм, чтобы правильно выделить границы интервалов, учитывая начало и конец исходного периода.

Использование функций платформы для работы с датами

Для корректного определения границ недели в 1С существуют встроенные функции НачалоНедели() и КонецНедели(). Использование этих методов предпочтительнее ручного расчета через секунды (86400 в сутках), так как платформа автоматически учитывает календарные особенности и привязку к началу недели (понедельнику).

Решение через циклическую обработку периода

Разберем один из самых понятных подходов — использование цикла Пока для последовательного перебора интервалов. Такой метод позволяет гибко управлять границами и корректно «обрезать» первый и последний интервалы, чтобы они не выходили за рамки установленного пользователем периода.

Для реализации логики нам потребуется инициализировать начальную и конечную даты и в цикле наращивать их до тех пор, пока мы не достигнем конца целевого периода.


// Пример алгоритма для разбиения периода
НачалоТекущейНедели = Объект.НачалоПериода;
// Вычисляем конец первой неполной недели
КонецТекущейНедели = НачалоНедели(НачалоТекущейНедели) + 6*24*60*60; 
КонецТекущейНедели = Мин(КонецТекущейНедели, Объект.КонецПериода);

Пока НачалоТекущейНедели <= Объект.КонецПериода Цикл
    // Вывод или запись интервала
    Сообщить("" + НачалоТекущейНедели + " - " + КонецТекущейНедели);
    
    // Переход к следующей неделе
    НачалоТекущейНедели = КонецТекущейНедели + 1;
    КонецТекущейНедели = Мин(НачалоТекущейНедели + 6*24*60*60, Объект.КонецПериода);
    
    Если НачалоТекущейНедели > Объект.КонецПериода Тогда
        Прервать;
    КонецЕсли;
КонецЦикла;

Почему важно использовать встроенные функции?

Многие начинающие разработчики пытаются считать недели, просто прибавляя 7 * 86400 к дате. Давайте выясним, почему это приводит к ошибкам:

  1. Фиксированная структура: Неделя всегда начинается в понедельник. Если ваша дата выпадает на среду, добавление 7 дней не перенесет вас в понедельник следующей недели.
  2. Граничные условия: Если период начинается в четверг, первый интервал должен охватывать только четверг, пятницу, субботу и воскресенье. Стандартный математический подход через секунды «выкинет» эти дни или сдвинет всю сетку.
  3. Читаемость кода: Использование НачалоНедели() делает ваш код самодокументированным и понятным для других разработчиков.

Использование универсальной функции для разных периодов

Если в вашем отчете пользователь может выбирать детализацию (день, неделя, месяц), удобно вынести логику в отдельную функцию, возвращающую ТаблицаЗначений. Рассмотрим структуру такой функции:


Функция ПолучитьТаблицуИнтервалов(ДатаНачала, ДатаОкончания, УровеньДетализации)
    ТЗ = Новый ТаблицаЗначений;
    ТЗ.Колонки.Добавить("ДатаНачала");
    ТЗ.Колонки.Добавить("ДатаОкончания");
    
    ТекущаяДата = ДатаНачала;
    Пока ТекущаяДата <= ДатаОкончания Цикл
        Строка = ТЗ.Добавить();
        Строка.ДатаНачала = ТекущаяДата;
        
        Если УровеньДетализации = "Неделя" Тогда
            Строка.ДатаОкончания = Мин(КонецНедели(ТекущаяДата), ДатаОкончания);
            ТекущаяДата = КонецНедели(ТекущаяДата) + 1;
        КонецЕсли;
    КонецЦикла;
    Возврат ТЗ;
КонецФункции

Рекомендация по использованию запросов

В случаях, когда данные находятся в базе данных (например, обороты товаров или документов), правильнее использовать возможности языка запросов. Вместо того чтобы перебирать даты в коде, можно сгруппировать данные по периоду недели:

ВЫБРАТЬ
НАЧАЛОПЕРИОДА(ТаблицаДанных.Дата, НЕДЕЛЯ) КАК НачалоНедели,
МИНИМУМ(ТаблицаДанных.Дата) КАК РеальноеНачало,
МАКСИМУМ(ТаблицаДанных.Дата) КАК РеальноеОкончание
ИЗ ТаблицаДанных ГДЕ ...
СГРУППИРОВАТЬ ПО НАЧАЛОПЕРИОДА(ТаблицаДанных.Дата, НЕДЕЛЯ)

Этот подход работает быстрее и позволяет платформе самой оптимизировать вычисление границ, что особенно актуально для больших объемов данных. Всегда анализируйте, требует ли задача сложной обработки в коде или её можно решить декларативно с помощью конструктора запросов.

← На главную