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

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

В процессе разработки на платформе 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С:

  1. Всегда проверяйте заполненность: Прежде чем выполнять арифметические операции с датами, убедитесь, что переменная не равна Неопределено, используя ЗначениеЗаполнено().
  2. Помните про секунды: Вычитание в коде дает секунды. Делите на 86400 для получения дней.
  3. Используйте НачалоДня(): Это исключит ошибки, связанные с разным временем внутри одной календарной даты.
  4. Для СКД и запросов — только РАЗНОСТЬДАТ: Это стандарт платформы, обеспечивающий производительность и правильность типов.
  5. Обработка NULL в СКД: Всегда оборачивайте поля дат в ЕСТЬNULL(), если в базе данных они могут быть не заполнены.

Соблюдение этих простых правил позволит вам создавать надежные отчеты и алгоритмы расчета, которые не будут "падать" при встрече с некорректными данными в информационной базе.

← На главную