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

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

Задача проверки пересечения двух интервалов дат и нахождения результирующего отрезка регулярно встречается в прикладной разработке на платформе 1С:Предприятие. С ней сталкиваются при контроле пересечения графиков сотрудников, проверке занятости ресурсов, расчете периодических регистров и на собеседованиях. Рассмотрим подробнее, почему попытка решить эту задачу через множественные ветвления Если ... ИначеЕсли часто приводит к ошибкам, и разберем математически корректные способы решения на встроенном языке и в запросах.

Анализ ошибок в логике условий

Проанализируем типичные ошибки, которые возникают при попытке написать проверку «в лоб» через ручное сравнение границ:

  1. Ошибки в именовании переменных и синтаксисе: часто допускаются опечатки в названиях переменных (например, смешивание НачПериода1, НачПерио2 и НачПериод), из-за чего часть логических блоков просто не работает.
  2. Неполнота логических веток: при ручном переборе условий легко упустить варианты взаимного расположения отрезков — полное вложение одного периода в другой, совпадение одной из границ или касание точками.
  3. Ошибки оператора МЕЖДУ в запросах: проверка попадания хотя бы одной из границ второго периода внутрь первого отрезка не определяет случай, когда первый интервал целиком находится внутри второго (тогда ни начало, ни конец второго периода не попадают между границами первого).
  4. Отсутствие валидации корректности интервалов: перед расчетом пересечения необходимо убедиться, что дата начала не превышает дату окончания внутри каждого отдельного периода.

Математический принцип пересечения отрезков

Выясним универсальный математический принцип. Пусть заданы два отрезка времени [A1, A2] и [B1, B2]. Пересечение этих отрезков всегда вычисляется через минимум и максимум без сложных каскадов условий:

  1. Начало пересечения: Макс(A1, B1) — самая поздняя из начальных дат.
  2. Конец пересечения: Мин(A2, B2) — самая ранняя из конечных дат.
  3. Критерий пересечения: Макс(A1, B1) <= Мин(A2, B2). Если вычисленное начало строго больше вычисленного конца, периоды не пересекаются.

Обработка «бесконечностей» в платформе 1С

По условию задачи незаполненная начальная дата означает «-бесконечность», а незаполненная конечная дата — «+бесконечность». Разберем, как платформа 1С обрабатывает граничные значения дат:

  1. Минимальная дата: пустая дата Дата(1, 1, 1) (или '00010101000000') является минимально возможным значением типа Дата в платформе. При вычислении Макс(Дата1, Дата2) пустая дата уступает любой реальной дате.
  2. Максимальная дата: максимально допустимая дата в 1С — Дата(3999, 12, 31, 23, 59, 59) (или '39991231235959'). Если конечная дата не заполнена, ее необходимо явно заменить на максимальную, иначе функция Мин() выберет пустую дату как наименьшую.
  3. Включение границ дня: если пользователь указывает даты без времени, то для корректной работы со временем начальные даты следует приводить к НачалоДня(), а конечные — к КонецДня().

Вариант 1: Оптимальный расчет на встроенном языке

Рассмотрим по шагам реализацию функции на встроенном языке без обращения к СУБД. Этот способ наиболее производителен при работе в памяти:


// Подготовка границ первого периода
Начало1 = ?(ЗначениеЗаполнено(НачПериода1), НачалоДня(НачПериода1), '00010101000000');
Конец1  = ?(ЗначениеЗаполнено(КонПериода1), КонецДня(КонПериода1),   '39991231235959');

// Подготовка границ второго периода
Начало2 = ?(ЗначениеЗаполнено(НачПериода2), НачалоДня(НачПериода2), '00010101000000');
Конец2  = ?(ЗначениеЗаполнено(КонПериода2), КонецДня(КонПериода2),   '39991231235959');

// Проверка корректности переданных периодов
Если Начало1 > Конец1 Или Начало2 > Конец2 Тогда
    ВызватьИсключение "Некорректно заданы границы исходных периодов (дата начала позже даты окончания).";
КонецЕсли;

// Вычисление границ пересечения
НачалоПересечения = Макс(Начало1, Начало2);
КонецПересечения  = Мин(Конец1, Конец2);

Если НачалоПересечения <= КонецПересечения Тогда
    // Если дата равна минимальной или максимальной, можно вывести строковое обозначение бесконечности
    СтрНачало = ?(НачалоПересечения = '00010101000000', "-бесконечность", Формат(НачалоПересечения, "ДФ=dd.MM.yyyy HH:mm:ss"));
    СтрКонец  = ?(КонецПересечения  = '39991231235959', "+бесконечность", Формат(КонецПересечения,  "ДФ=dd.MM.yyyy HH:mm:ss"));
    
    Сообщить("Пересечение есть: с " + СтрНачало + " по " + СтрКонец);
Иначе
    Сообщить("Пересечения нет");
КонецЕсли;

Вариант 2: Реализация через запрос к базе данных

Посмотрим на пример, когда проверку пересечений необходимо встроить в текст запроса (например, при сопоставлении строк табличной части или выборке данных из регистров сведений). Применим условие пересечения A1 <= B2 И A2 >= B1:


Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |    ВЫБОР
    |        КОГДА &Начало1 > &Начало2 ТОГДА &Начало1
    |        ИНАЧЕ &Начало2
    |    КОНЕЦ КАК НачалоПересечения,
    |    ВЫБОР
    |        КОГДА &Конец1 < &Конец2 ТОГДА &Конец1
    |        ИНАЧЕ &Конец2
    |    КОНЕЦ КАК КонецПересечения
    |ГДЕ
    |    &Начало1 <= &Конец2
    |    И &Конец1 >= &Начало2";

Запрос.УстановитьПараметр("Начало1", ?(ЗначениеЗаполнено(НачПериода1), НачалоДня(НачПериода1), '00010101000000'));
Запрос.УстановитьПараметр("Конец1",  ?(ЗначениеЗаполнено(КонПериода1), КонецДня(КонПериода1),   '39991231235959'));
Запрос.УстановитьПараметр("Начало2", ?(ЗначениеЗаполнено(НачПериода2), НачалоДня(НачПериода2), '00010101000000'));
Запрос.УстановитьПараметр("Конец2",  ?(ЗначениеЗаполнено(КонПериода2), КонецДня(КонПериода2),   '39991231235959'));

Выборка = Запрос.Выполнить().Выбрать();
Если Выборка.Следующий() Тогда
    Сообщить("Пересечение найдено: " + Выборка.НачалоПересечения + " - " + Выборка.КонецПересечения);
Иначе
    Сообщить("Пересечения нет");
КонецЕсли;

Резюме: Использование математических функций Макс() и Мин() в сочетании с явной подстановкой максимальной даты '39991231235959' для открытых интервалов позволяет получить компактный, устойчивый к ошибкам и легко читаемый код.

← На главную