В процессе разработки на платформе 1С:Предприятие одной из самых частых задач является расчет периодов: количество дней просрочки, определение возраста или интервала между событиями. Несмотря на кажущуюся простоту, новички часто сталкиваются с ошибкой «Несоответствие типов», когда пытаются вычитать пустые значения или некорректно обрабатывают данные в Системе компоновки данных (СКД). Рассмотрим подробно, как правильно делить дату на количество дней, а точнее — как получать разность дат в различных контекстах.
Разберем ситуацию, представленную в вопросе автора. Код (стр.ДатаПлатежа - ТекДата) / (24 * 60 * 60) выдает ошибку, если переменная ТекДата имеет значение Неопределено. В языке 1С дата — это объектный тип, и платформа не умеет производить арифметические операции между полноценной датой и системным значением Неопределено или NULL.
Проанализируем решение этой проблемы. Чтобы избежать аварийного завершения программы, мы должны гарантировать, что оба операнда являются датами. Посмотрим на правильный вариант проверки:
// Безопасный расчет разницы дат
Если ЗначениеЗаполнено(ТекДата) Тогда
РазницаВСекундах = стр.ДатаПлатежа - ТекДата;
КоличествоДней = РазницаВСекундах / (24 * 60 * 60);
Иначе
КоличествоДней = 0; // Или другая логика обработки пустого значения
КонецЕсли;
Если же логика задачи требует, чтобы при отсутствии даты расчет шел от «начала времен», можно использовать пустую дату Дата(1, 1, 1), как советовали участники форума. Однако в 99% случаев бизнес-логики это будет неверным подходом, и лучше использовать явную проверку ЗначениеЗаполнено().
Важно помнить, что в программном коде 1С (на языке выражений платформы) результат вычитания одной даты из другой всегда возвращается в секундах. Для перевода этого значения в дни мы делим результат на 86 400 (количество секунд в сутках: 24 часа * 60 минут * 60 секунд).
Рассмотрим нюанс с временем. Если у дат заполнено время, результат деления может оказаться дробным. Например, разница между "01.01.2023 12:00" и "02.01.2023 09:00" составит меньше суток. Чтобы получить чистое количество календарных дней, проанализируем ситуацию и приведем даты к началу дня:
// Приведение к началу дня для точности расчета в сутках
ДатаНачала = НачалоДня(ТекДата);
ДатаКонца = НачалоДня(стр.ДатаПлатежа);
КоличествоДней = (ДатаКонца - ДатаНачала) / 86400;
Использование НачалоДня() гарантирует, что мы получим целое число при делении, что критически важно для расчета пеней или сроков годности.
В сообщениях темы упоминается, что код переносится в СКД. Выясним причину, по которой обычное вычитание там может работать иначе. В языке запросов и выражениях СКД нельзя просто вычитать даты через оператор "минус". Для этого предназначена специальная функция РАЗНОСТЬДАТ.
Синтаксис функции в СКД:
РАЗНОСТЬДАТ(ДатаНачала, ДатаКонца, "ДЕНЬ")
Преимущества этого метода:
Если в данных СКД встречаются значения NULL, используйте функцию ЕСТЬNULL для подстановки значения по умолчанию, чтобы выражение не вернуло пустой результат:
// Выражение в поле СКД
РАЗНОСТЬДАТ(ЕСТЬNULL(ДатаНачала, ДАТАВРЕМЯ(1,1,1)), &КонецПериода, "ДЕНЬ")
Иногда пользователю требуется узнать порядковый номер дня в году (например, для статистических отчетов). Для этого в 1С существует встроенная функция ДеньГода(). Рассмотрим на примере:
НужнаяДата = ТекущаяДата();
НомерДня = ДеньГода(НужнаяДата);
// Возвращает число от 1 до 365 (или 366 в високосный год)
Внимание: Будьте осторожны при использовании этой функции для расчета разницы дат, если период переходит через границы года. Если вы вычтете ДеньГода('20231231') из ДеньГода('20240101'), вы получите отрицательное число, а не 1 день. Для сквозного расчета всегда используйте прямое вычитание дат или РАЗНОСТЬДАТ.
Проанализируем фрагмент кода из обсуждения, где рассчитываются пени. Здесь важно правильно инициализировать переменные и проверять условия перехода задолженности. Посмотрим, как это реализовать структурно:
ТекДата = Неопределено;
ТекЗадолженность = 0;
Для Каждого стр Из тзДанные Цикл
// Проверяем, что дата определена и есть долг для начисления пеней
Если ТекДата <> Неопределено И ТекЗадолженность > 0 Тогда
// Рассчитываем количество полных дней
СекундПрошло = стр.ДатаПлатежа - ТекДата;
стр.КоличествоДнейПросрочки = СекундПрошло / 86400;
Иначе
стр.КоличествоДнейПросрочки = 0;
КонецЕсли;
// После расчета фиксируем текущую дату для следующей итерации
ТекДата = стр.ДатаПлатежа;
// Обновляем сумму задолженности
ТекЗадолженность = ТекЗадолженность + стр.СуммаПлатежа - стр.СуммаОплаты;
КонецЦикла;
В данном примере мы видим классический алгоритм «нарастающим итогом». Ключевым моментом здесь является условие Если ТекДата <> Неопределено, которое предотвращает ошибку на первой итерации цикла, когда предыдущая дата еще не известна.
Подводя итог, разберем основные правила работы с датами и числами в 1С:
Неопределено, используя ЗначениеЗаполнено().86400 для получения дней.ЕСТЬNULL(), если в базе данных они могут быть не заполнены.Соблюдение этих простых правил позволит вам создавать надежные отчеты и алгоритмы расчета, которые не будут "падать" при встрече с некорректными данными в информационной базе.