При разработке отчетов и обработок в платформе 1С:Предприятие 7.7 нередко возникает задача генерации последовательных временных интервалов (например, ежемесячных периодов начисления, графиков платежей или аренды). Однако разработчики периодически сталкиваются с тем, что встроенные механизмы сдвига дат ведут себя неожиданно либо функция вовсе не срабатывает. Разберем по шагам, как устроена арифметика дат, почему стандартная функция может не работать и как правильно строить непрерывные цепочки периодов.
Для изменения даты на заданное количество месяцев в платформе 1С 7.7 предусмотрена встроенная функция ДобавитьМесяц(). Функция принимает два параметра: исходную дату и количество месяцев для добавления (положительное число для движения вперед или отрицательное для движения назад во времени).
Посмотрим на базовый пример вызова функции:
ДатаНачала = '28.02.2017';
ДатаОкончания = ДобавитьМесяц(ДатаНачала, 1);
Сообщить("Результат: " + ДатаОкончания); // Вернет 28.03.2017
Если при вызове функции результат оказывается пустым или возникает ошибка времени выполнения, проанализируем основные причины:
Дата. Если значение получено из строкового поля ввода на форме диалога, из файла или текстовой таблицы, оно может иметь строковый тип ("28.02.2017" вместо '28.02.2017'). В 1С 7.7 неявное приведение строки к дате внутри функций не выполняется. Для проверки типа используем функцию ТипЗначенияСтр(), а для преобразования — конструктор Дата().'00.00.0000', результат работы функции также вернет пустую дату. Проверить это можно вызовом ПустоеЗначение().Дата). Это может приводить к конфликту идентификаторов и непредсказуемому поведению модуля. Используем осмысленные имена: ТекДата, ДатаНачала, ИсходнаяДата.Рассмотрим пример правильной проверки и преобразования типа данных перед вызовом функции:
Если ТипЗначенияСтр(ВходноеЗначение) = "Строка" Тогда
ТекДата = Дата(ВходноеЗначение);
Иначе
ТекДата = ВходноеЗначение;
КонецЕсли;
Если ПустоеЗначение(ТекДата) = 0 Тогда
СледДата = ДобавитьМесяц(ТекДата, 1);
КонецЕсли;
При работе с функцией ДобавитьМесяц() важно понимать правила усечения дней в месяцах с разным количеством суток:
ДобавитьМесяц('31.01.2017', 1) вернет 28.02.2017.ДобавитьМесяц('28.02.2017', -1)), мы получим 28.01.2017, а не исходное 31 января.КонМесяца().Посмотрим на пример логики сохранения конца месяца:
Если ТекДата = КонМесяца(ТекДата) Тогда
НоваяДата = КонМесяца(ДобавитьМесяц(ТекДата, 1));
Иначе
НоваяДата = ДобавитьМесяц(ТекДата, 1);
КонецЕсли;
В платформе 1С 7.7 сложение даты с целым числом выполняет смещение на указанное количество дней (например, ТекДата + 1 возвращает следующий день). Объединив операцию сдвига в днях и функцию ДобавитьМесяц(), мы можем легко строить последовательные интервалы любого вида.
Вариант 1: Формирование цепочки интервалов со сдвигом от даты окончания
Разберем пример построения интервалов вида 28.02.2017 - 28.03.2017, 29.03.2017 - 29.04.2017:
ДатаНачИнтервала = '28.02.2017';
КоличествоПериодов = 3;
Для Счетчик = 1 По КоличествоПериодов Цикл
ДатаКонИнтервала = ДобавитьМесяц(ДатаНачИнтервала, 1);
Сообщить("Период: " + ДатаНачИнтервала + " - " + ДатаКонИнтервала);
// Начало следующего интервала смещается на 1 день от конца текущего
ДатаНачИнтервала = ДатаКонИнтервала + 1;
КонецЦикла;
Вариант 2: Формирование классических непрерывных месячных интервалов («день в день минус один день»)
Если требуется сформировать календарные интервалы без пересечений (например, с 15-го числа по 14-е число следующего месяца):
ДатаНачИнтервала = '01.02.2017';
КоличествоПериодов = 3;
Для Счетчик = 1 По КоличествоПериодов Цикл
// Вычисляем конец интервала как дату через месяц минус один день
ДатаКонИнтервала = ДобавитьМесяц(ДатаНачИнтервала, 1) - 1;
Сообщить("Интервал: " + ДатаНачИнтервала + " - " + ДатаКонИнтервала);
// Начало следующего периода — следующий день после окончания
ДатаНачИнтервала = ДатаКонИнтервала + 1;
КонецЦикла;
Таким образом, сочетание функций ДобавитьМесяц(), КонМесяца() и стандартной арифметики дат в днях позволяет реализовать любой алгоритм разбиения временной шкалы на периоды.