К статье
Практический материал

Разработка и Конфигурация · 10 мин

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

Как в 1С получить разницу дат в днях, месяцах, годах: функция РазностьДат в запросе, вычитание дат в коде, типовые ошибки и примеры для разработчика.

Задача вычислить разницу между двумя датами встречается в 1С постоянно: посчитать стаж сотрудника, определить просрочку по договору, рассчитать возраст товара на складе или число дней до окончания гарантии. В зависимости от контекста — код на встроенном языке или запрос — используются разные инструменты, и у каждого есть свои ограничения.

Главное, что нужно понимать сразу: встроенный язык 1С не имеет отдельной функции «разница в месяцах» или «разница в годах». Простое вычитание дат даёт результат в секундах, а всё остальное приходится пересчитывать. В языке запросов, наоборот, есть штатная функция РАЗНОСТЬДАТ, но она работает только внутри запроса и не всегда доступна в произвольных выражениях СКД.

Ниже разобраны оба способа с примерами кода, типичными ошибками и рекомендациями, какой вариант выбрать в конкретной ситуации.

Разница дат встроенным языком: вычитание и секунды

Самый простой способ — вычесть одну дату из другой оператором «минус». Результатом будет число секунд между моментами времени. Это частая ловушка для начинающих: разработчик ожидает дни, а получает большое число вроде 2592000.

Чтобы перевести результат в нужные единицы, используйте деление:

РазницаВСекундах = ДатаОкончания - ДатаНачала;

РазницаВДнях = РазницаВСекундах / 86400; // 86400 = 60 60 24

РазницаВЧасах = РазницаВСекундах / 3600;

Обратите внимание: если даты содержат время (а в 1С тип «Дата» всегда хранит время с точностью до секунды), результат деления будет дробным. Например, разница между 10.03 15:00 и 12.03 09:00 даст 1,75 дня. Если нужны целые календарные дни, предварительно отсечь время функцией НачалоДня():

ДнейМежду = (НачалоДня(ДатаОкончания) - НачалоДня(ДатаНачала)) / 86400;

Такой подход корректно считает дни, но не подходит для месяцев и лет: месяцы имеют разную длину, и делить на 30 или 30,44 — значит получать приблизительный результат, неприемлемый для кадровых и финансовых расчётов.

Функция РАЗНОСТЬДАТ в языке запросов

В языке запросов 1С есть штатная функция, которая решает задачу напрямую. Синтаксис:

РАЗНОСТЬДАТ(ДатаНачала, ДатаОкончания, ЕдиницаИзмерения)

Третий параметр задаёт единицу результата: СЕКУНДА, МИНУТА, ЧАС, ДЕНЬ, МЕСЯЦ, ГОД. Функция возвращает целое число — количество полных единиц между датами.

ВЫБРАТЬ

Сотрудники.Ссылка КАК Сотрудник,

РАЗНОСТЬДАТ(Сотрудники.ДатаРождения, &ТекущаяДата, ГОД) КАК Возраст,

РАЗНОСТЬДАТ(Сотрудники.ДатаПриема, &ТекущаяДата, ДЕНЬ) КАК СтажВДнях

ИЗ

Справочник.Сотрудники КАК Сотрудники

Важная особенность: РАЗНОСТЬДАТ считает именно полные единицы. Между 31.01 и 01.03 разница в месяцах равна 1, хотя прошло 30 дней — чуть меньше «календарного» месяца. Между 15.06.2000 и 14.06.2010 разница в годах — 9, а не 10, потому что десятый год ещё не завершился.

Если дата начала больше даты окончания, функция вернёт отрицательное значение. Это удобно для определения просрочки: отрицательный результат по дате оплаты означает, что срок ещё не наступил.

bsl
РазницаДней = РАЗНОСТЬДАТ(&ДатаНачала, &ДатаОкончания, ДЕНЬ)

Как посчитать месяцы и годы в коде без запроса

Если запрос недоступен или неудобен (например, расчёт идёт в цикле по строкам табличной части), месяцы и годы вычисляют комбинацией стандартных функций работы с датами.

Базовый приём для полных месяцев — сравнение годов и месяцев с поправкой на день:

Функция МесяцевМеждуДатами(Дата1, Дата2) Экспорт

Месяцев = (Год(Дата2) - Год(Дата1)) * 12 + (Месяц(Дата2) - Месяц(Дата1));

Если День(Дата2) < День(Дата1) Тогда

Месяцев = Месяцев - 1;

КонецЕсли;

Возврат Месяцев;

КонецФункции

Для лет логика аналогична: из разницы годов вычитается единица, если месяц-день конечной даты раньше начальной. Именно так считается возраст человека или полный стаж.

Альтернатива — функция ДобавитьМесяц() в цикле: прибавляйте по месяцу к начальной дате, пока не превысите конечную. Способ медленнее, но наглядно показывает границы периодов и удобен, когда нужно не только число, но и перечень месяцев (например, для графика платежей).

Разница дат в СКД и произвольных выражениях

В схеме компоновки данных функция РАЗНОСТЬДАТ доступна только в тексте самого запроса набора данных. В вычисляемых полях и выражениях ресурсов СКД она не работает — там действует ограниченный набор функций языка выражений компоновки.

Практическое решение: вычисляйте разницу дат на уровне запроса набора данных и передавайте в СКД уже готовое числовое поле. Если разница должна зависеть от параметра отчёта (например, «на дату, указанную пользователем»), передайте её параметром в запрос:

РАЗНОСТЬДАТ(Заказы.ДатаОтгрузки, &ПериодОтчета, ДЕНЬ) КАК ДнейПросрочки

Если запрос изменить нельзя (типовой отчёт без снятия с поддержки), остаётся вариант с вычисляемым полем через функции общего модуля: в выражении СКД можно вызвать функцию из общего модуля с признаком «Сервер», которая выполнит нужный расчёт. Учтите, что такой вызов выполняется для каждой строки и может замедлить отчёт на больших выборках.

Сравнение вариантов

Вычитание дат в коде

Подходит для дней, часов, секунд. Просто и быстро, но не даёт корректных месяцев и лет.

РАЗНОСТЬДАТ в запросе

Оптимально для отчётов и выборок: любые единицы, выполняется на сервере СУБД. Недоступна вне запроса.

Собственная функция с Год(), Месяц(), ДобавитьМесяц()

Для расчётов в коде, где нужны полные месяцы и годы без запроса. Требует аккуратной обработки дней.

Вызов общего модуля из СКД

Запасной вариант для типовых отчётов. Медленнее и требует серверного общего модуля.

Типичные ошибки и как их проверить

Первая ошибка — забыть про время в составе даты. Две даты одного календарного дня, но с разным временем, дают разницу меньше суток, и сравнение «равно 1 день» не срабатывает. Приводите даты к началу дня перед сравнением и вычитанием.

Вторая — перепутанный порядок аргументов РАЗНОСТЬДАТ. Первым идёт начальная дата, вторым конечная. При обратном порядке вы получите отрицательные числа, что в отчёте о просрочке перевернёт картину: должники станут «добросовестными».

Третья — ожидание дробного результата от РАЗНОСТЬДАТ. Функция всегда возвращает целое число полных единиц. Если нужна большая точность, выбирайте более мелкую единицу и делите сами.

Типичные ошибки
01
Деление секунд на 86400 без отсечения времени — получается дробное число дней и ложные срабатывания условий
02
Обратный порядок аргументов в РАЗНОСТЬДАТ — отрицательные значения и неверные признаки просрочки
03
Ожидание дробного результата от РАЗНОСТЬДАТ — функция возвращает только целые полные единицы
04
Расчёт месяцев делением дней на 30 — накапливающаяся ошибка на длинных периодах

Для проверки результата используйте контрольные пары дат с известным ответом: 01.01–01.01 (0 дней), 31.01–01.02 (1 день, 0 месяцев), 15.03.2000–14.03.2010 (9 полных лет). Если функция проходит эти точки, она с высокой вероятностью корректна.

Контрольные точки

Как проверить результат

Разница 01.01–01.01
Норма

0 дней, 0 месяцев, 0 лет

Тревожный признак

1 день или любое ненулевое значение

Разница 31.01–01.03 в месяцах
Норма

1 полный месяц

Тревожный признак

2 месяца или дробное значение

Порядок дат при просрочке
Норма

Просроченные документы дают положительное число дней

Тревожный признак

Знак результата противоположен ожидаемому

Выбор способа под задачу

Для отчётов и любых выборок из базы однозначно выбирайте РАЗНОСТЬДАТ в запросе: расчёт выполняется на стороне СУБД, не требует перебора строк и работает быстро даже на больших таблицах.

Для проверок в коде («прошло ли больше 14 дней», «не просрочен ли документ») достаточно вычитания дат с приведением к началу дня. Это читаемо и не требует запроса.

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

Маршрут решения

Как выбрать действие

Нужна разница в отчёте или выборкеИспользуйте РАЗНОСТЬДАТ в тексте запроса
Нужна простая проверка в коде на дни или часыВычтите даты и разделите на 86400 или 3600, предварительно применив НачалоДня()
Нужны полные месяцы или годы без запросаНапишите функцию через Год(), Месяц(), День() или цикл с ДобавитьМесяц()
Типовой отчёт СКД без права изменения запросаДобавьте вычисляемое поле с вызовом серверного общего модуля

Частые вопросы

Почему вычитание дат даёт огромное число вместо дней?

Потому что результат вычитания — секунды. Число 86400 означает ровно одни сутки. Разделите результат на 86400, чтобы получить дни, и на 3600 — чтобы получить часы.

Можно ли использовать РАЗНОСТЬДАТ вне запроса?

Нет, это функция языка запросов. В коде на встроенном языке используйте вычитание дат, функции Год(), Месяц(), День() и ДобавитьМесяц() либо выполните небольшой запрос, если это оправдано.

Как посчитать возраст сотрудника в полных годах?

В запросе: РАЗНОСТЬДАТ(ДатаРождения, &ТекущаяДата, ГОД) — функция вернёт число полных лет. В коде: разность годов минус единица, если месяц и день рождения в текущем году ещё не наступили.

Что вернёт РАЗНОСТЬДАТ, если даты равны?

Ноль в любой единице измерения. Это удобно для проверок: условие «разность больше нуля» сразу отсекает текущий день.

Как учесть время при сравнении дат?

Либо сравнивайте даты напрямую с точностью до секунды, либо осознанно отсекайте время через НачалоДня() или КонецДня(). Смешивание подходов в одном алгоритме — частый источник ошибок «на границе суток».

Есть ли в 1С аналог DATEDIF из Excel?

Отдельной встроенной функции нет. Ближайший аналог — РАЗНОСТЬДАТ в запросах. Для кода эквивалент реализуют самостоятельно через комбинацию Год(), Месяц(), День() и ДобавитьМесяц().

Итог

Что важно запомнить

Вычитание дат в коде даёт секунды — делите на 86400 для дней и отсекайте время через НачалоДня(). В запросах применяйте РАЗНОСТЬДАТ с единицей ДЕНЬ, МЕСЯЦ или ГОД: она возвращает целые полные единицы. Для месяцев и лет в коде пишите отдельную функцию и проверяйте её на контрольных парах дат.