Как программно рассчитать время ближайшего запуска регламентного задания в 1С?

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

Приветствуем вас, уважаемые коллеги! Сегодня мы детально разберем одну очень интересную и частую задачу, с которой сталкиваются разработчики при создании сложных механизмов автоматизации в среде 1С:Предприятие. Задача звучит следующим образом: как программно определить время ближайшего (следующего) запуска регламентного задания?

На первый взгляд кажется, что платформа 1С должна предоставлять простой и лаконичный метод для получения этой даты напрямую из объекта расписания. Однако на практике разработчики быстро обнаруживают, что у объекта платформы РасписаниеРегламентногоЗадания отсутствует прямой метод получения даты следующего выполнения. Единственный доступный инструмент, который нам предлагает платформа для анализа времени запуска — это метод ТребуетсяВыполнение(), который возвращает лишь логическое «Истина» или «Ложь» для конкретной секунды времени.

В рамках этой статьи мы подробно рассмотрим три эффективных подхода к решению данной задачи: использование готовых механизмов БСП (Библиотека стандартных подсистем), применение математического метода половинного деления (дихотомии) на чистой платформе, а также логику, заложенную в профессиональных инструментах разработчика.

Решение 1: Использование механизмов БСП (Библиотека стандартных подсистем)

Если вы ведете разработку в типовой конфигурации 1С (например, 1С:Бухгалтерия, 1С:ERP Управление предприятием, 1С:Комплексная автоматизация, 1С:Управление торговлей) или используете БСП в своей самописной базе, то вам крупно повезло. В составе библиотеки уже присутствует готовый, оптимизированный и протестированный функционал для решения нашей задачи.

Давайте обратим внимание на общий модуль ОчередьЗаданийСлужебный. В нем реализована экспортная функция ПолучитьЗапланированныйМоментЗапускаЗадания(), которая идеально подходит для расчета даты следующего старта.

Давайте разберем по шагам, как вызвать этот метод из своего кода:


// 1. Получаем расписание нужного регламентного задания
РегЗадание = РегламентныеЗадания.НайтиПредопределенное(Метаданные.РегламентныеЗадания.ОбновлениеИнформационнойБазы);
Расписание = РегЗадание.Расписание;

// 2. Определяем часовой пояс (обычно используется часовой пояс сеанса или ИБ)
ЧасовойПояс = ЗаполнениеСвойствПолнотекстовогоПоиска.ЧасовойПоясОбластиДанных(); 

// 3. Получаем даты предыдущих запусков задания
ДатаНачалаПоследнегоЗапуска = РегЗадание.ПоследнееНачало;
ДатаЗавершенияПоследнегоЗапуска = РегЗадание.ПоследнееОкончание;

// 4. Рассчитываем планируемый момент запуска
СледующийСтартUTC = ОчередьЗаданийСлужебный.ПолучитьЗапланированныйМоментЗапускаЗадания(
    Расписание, 
    ЧасовойПояс, 
    ДатаНачалаПоследнегоЗапуска, 
    ДатаЗавершенияПоследнегоЗапуска
);

// Переводим время обратно из UTC в локальное для вывода пользователю
СледующийСтартЛокальный = МестноеВремя(СледующийСтартUTC, ЧасовойПояс);

Сообщить("Ближайший запуск регламентного задания запланирован на: " + СледующийСтартЛокальный);

Проанализируем ситуацию: как эта функция работает изнутри? Внутри общего модуля БСП вызывается служебный алгоритм СледующаяДатаВыполненияРасписания(). Он математически сопоставляет переданное расписание с историей предыдущих запусков и текущим временем, аккуратно учитывая цикличность, детальные настройки по дням недели, месяцам, интервалам внутри дня и исключениям. Это самый надежный способ, гарантирующий полное соответствие системным алгоритмам планировщика 1С.

Решение 2: Универсальный метод половинного деления (дихотомия) на чистой платформе

Что делать, если ваша конфигурация написана с нуля без использования БСП, или вам по каким-то причинам недоступны ее модули? В таком случае нам на помощь приходит классическая математика, а именно — метод половинного деления (дихотомия).

Как мы уже выяснили, у объекта расписания есть стандартный метод: РасписаниеРегламентногоЗадания.ТребуетсяВыполнение(ДатаПроверки, ДатаНачалаПоследнегоЗапуска, ДатаЗавершенияПоследнегоЗапуска).

Очевидно, что перебирать каждую секунду в цикле, начиная от текущего момента и до бесконечности — это крайне неэффективное решение, которое намертво повесит рабочий сервер (ведь в одном годе содержится более 31 миллиона секунд!). Однако если мы применим алгоритм дихотомии, количество итераций сократится до минимума.

Давайте разберем этот алгоритм по шагам:

  1. Определяем начальные временные границы поиска. Например, нижняя граница (Низ) — это текущая дата и время, а верхняя граница (Верх) — текущая дата плюс один год (максимальный обозримый интервал ожидания запуска).
  2. Проверяем верхнюю границу методом ТребуетсяВыполнение. Если в течение года выполнение не требуется вообще, значит, задание либо выключено, либо настроено на запуск в необозримом будущем. Выходим из расчета.
  3. Делим наш временной отрезок пополам: находим среднюю точку (Среднее).
  4. Вызываем метод ТребуетсяВыполнение для средней точки. Этот метод проверяет, требуется ли выполнение задания хотя бы один раз на временном интервале от начала времен до указанной даты.
  5. Если метод вернул Истина, это означает, что ближайшая дата запуска лежит в левой (более ранней) половине нашего отрезка. Мы смещаем верхнюю границу (Верх) в точку Среднее.
  6. Если метод вернул Ложь, это означает, что в левой половине запусков не планируется, и ближайший старт лежит в правой (более поздней) половине отрезка. Мы смещаем нижнюю границу (Низ) в точку Среднее.
  7. Повторяем деление до тех пор, пока разница между верхней и нижней границей не сократится до 1 секунды. Эта секунда и будет точным временем ближайшего старта.

Ниже представлен готовый программный код функции, которую вы можете использовать в любом общем модуле вашей конфигурации:


&НаСервере
Функция ПолучитьБлижайшийСтартРасписанияДихотомией(Знач Расписание, Знач ДатаНачалаПоследнегоЗапуска = Неопределено, Знач ДатаЗавершенияПоследнегоЗапуска = Неопределено) Экспорт
    
    ТекущееВремя = ТекущаяДата();
    
    // Задаем интервал поиска (например, 1 год)
    Низ = ТекущееВремя;
    Верх = ТекущееВремя + (365 * 24 * 60 * 60); 
    
    // Первичная проверка на то, планируется ли запуск в принципе в течение года
    Если Не Расписание.ТребуетсяВыполнение(Верх, ДатаНачалаПоследнегоЗапуска, ДатаЗавершенияПоследнегоЗапуска) Тогда
        Возврат '00010101000000'; // Возвращаем пустую дату, если запусков не предвидится
    КонецЕсли;
    
    // Основной цикл половинного деления отрезка времени
    Пока Верх - Низ > 1 Цикл
        
        Среднее = Низ + Цел((Верх - Низ) / 2);
        
        // Метод ТребуетсяВыполнение проверяет необходимость запуска до указанного момента времени включительно
        Если Расписание.ТребуетсяВыполнение(Среднее, ДатаНачалаПоследнегоЗапуска, ДатаЗавершенияПоследнегоЗапуска) Тогда
            // Искомая точка находится в левом интервале
            Верх = Среднее;
        Иначе
            // Искомая точка находится в правом интервале
            Низ = Среднее;
        КонецЕсли;
        
    КонецЦикла;
    
    Возврат  Верх;
    
КонецФункции

Проанализируем производительность этого алгоритма: для точного поиска одной секунды внутри целого года (31 536 000 секунд) компьютеру потребуется совершить всего 25–26 итераций цикла! Это занимает доли миллисекунды процессорного времени, что делает данный метод невероятно быстрым и абсолютно независимым от сторонних библиотек.

Решение 3: Логика расчета в "Инструментах разработчика" (ИР)

В популярном профессиональном инструменте «Консоль задач» из пакета «Инструменты разработчика» (ИР) колонка «Следующее начало» вычисляется динамически на лету. Автор инструмента реализовал продвинутый парсер объектов расписания.

Если вы хотите реализовать максимально точное отображение времени запуска в своих интерфейсах (например, в самописной консоли администрирования), вам необходимо дополнительно учитывать следующие факторы:

Сводная таблица преимуществ каждого метода

Давайте сопоставим рассмотренные подходы, чтобы вам было проще определиться с выбором архитектурного решения:

  1. Метод БСП: Идеален для баз на типовых конфигурациях. Не требует написания собственного кода, поддерживается и обновляется фирмой 1С.
  2. Метод Дихотомии: Идеален для самописных конфигураций без БСП. Работает мгновенно, не нагружает сервер, прост в интеграции и отладке.
  3. Метод ИР: Отличное решение для интерактивных административных панелей, где важна не только сухая математика расписания, но и текущий статус блокировок информационной базы.

Теперь в вашем арсенале есть проверенные инструменты для программного управления планированием задач. Выбирайте наиболее подходящий под вашу архитектуру метод и делайте ваши решения на платформе 1С еще более интеллектуальными и удобными для пользователей!

← На главную