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

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

Как сложить даты в 1С: практическое руководство

Как сложить даты в 1С: функция ДобавитьКДате, ДОБАВИТЬКДАТЕ в запросах, разность дат, типичные ошибки и проверка результата. Практические примеры кода.

Операции с датами — одна из самых частых задач при разработке и доработке конфигураций 1С: рассчитать срок оплаты по договору, определить дату окончания гарантии, сформировать отчёт за период со сдвигом. При этом платформа 1С не поддерживает привычное сложение дат оператором «плюс» — попытка написать Дата1 + Дата2 завершится ошибкой. Вместо этого используются специальные механизмы: функция ДобавитьКДате во встроенном языке, конструкция ДОБАВИТЬКДАТЕ в языке запросов и арифметика дат с секундами.

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

Материал ориентирован на разработчиков и внедренцев, работающих с платформой 1С:Предприятие 8.3. Примеры применимы к типовым и самописным конфигурациям, если в них не переопределена стандартная работа с типом «Дата».

Как устроен тип «Дата» в 1С и почему нельзя складывать даты напрямую

Тип «Дата» в 1С хранит момент времени с точностью до секунды. Физически это число секунд, отсчитанное от начальной точки отсчёта, но платформа не позволяет применять к двум датам арифметические операции сложения — такая операция лишена смысла: сумма «12 марта» и «25 июня» не является датой.

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

// Сдвиг на один час (3600 секунд)

НоваяДата = ИсходнаяДата + 3600;

// Разница между датами в секундах

СекундМежду = ДатаОкончания - ДатаНачала;

Важно помнить: единица измерения при такой арифметике — секунда, а не день. Частая ошибка новичков — написать Дата + 7, ожидая сдвиг на неделю, а получить сдвиг на семь секунд. Для сдвига на день нужно прибавить 86400, но правильнее и надёжнее использовать ДобавитьКДате, о которой ниже.

Сложение дат во встроенном языке: функция ДобавитьКДате

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

Тип интервала задаётся значением перечисления ТипИнтервалаДаты: Секунда, Минута, Час, День, Неделя, Месяц, Квартал, Год, Декада, Полугодие. Количество может быть отрицательным — тогда дата сдвигается назад, что удобно для вычисления даты «за N дней до события».

Процедура РассчитатьСрокОплаты(ДатаДокумента)

// Срок оплаты — через 14 календарных дней

СрокОплаты = ДобавитьКДате(ДатаДокумента, ТипИнтервалаДаты.День, 14);

// Годовщина договора

Годовщина = ДобавитьКДате(ДатаДокумента, ТипИнтервалаДаты.Год, 1);

// Три месяца назад

ДатаНачалаПериода = ДобавитьКДате(ТекущаяДата(), ТипИнтервалаДаты.Месяц, -3);

КонецПроцедуры

Функция корректно обрабатывает переходы через границы месяцев и лет. Например, прибавление одного месяца к 31 января даст последний день февраля, а не несуществующее «31 февраля». Именно поэтому ручная арифметика с секундами для месяцев и лет не подходит: длительность месяца в секундах непостоянна.

Порядок действий при расчёте даты в коде
  1. 1
    Определите исходную дату и убедитесь, что она заполнена (не пустая дата 01.01.0001)
  2. 2
    Выберите тип интервала: День, Месяц, Год и т.д.
  3. 3
    Вызовите ДобавитьКДате с нужным количеством, отрицательным для сдвига назад
  4. 4
    Присвойте результат переменной или реквизиту
  5. 5
    Проверьте результат в отладчике или через Сообщить()

Сложение дат в запросах: ДОБАВИТЬКДАТЕ

Когда дату нужно сдвинуть прямо в запросе — например, отобрать документы, у которых срок оплаты наступил, — используется функция языка запросов ДОБАВИТЬКДАТЕ(Дата, Тип, Количество). Синтаксис похож на встроенный язык, но тип интервала задаётся ключевым словом без перечисления: СЕКУНДА, МИНУТА, ЧАС, ДЕНЬ, НЕДЕЛЯ, МЕСЯЦ, КВАРТАЛ, ГОД, ДЕКАДА, ПОЛУГОДИЕ.

ВЫБРАТЬ

Реализация.Ссылка КАК Документ,

Реализация.Дата КАК ДатаОтгрузки,

ДОБАВИТЬКДАТЕ(Реализация.Дата, ДЕНЬ, 14) КАК СрокОплаты

ИЗ

Документ.РеализацияТоваровУслуг КАК Реализация

ГДЕ

ДОБАВИТЬКДАТЕ(Реализация.Дата, ДЕНЬ, 14) <= &ТекущаяДата

Количество в ДОБАВИТЬКДАТЕ должно быть целым числом — литералом или параметром. Передавать в качестве количества поле таблицы нельзя: если интервал индивидуален для каждой строки (например, у каждого договора свой срок оплаты в днях), функцию придётся заменить обходом результата запроса с расчётом через ДобавитьКДате в коде, либо использовать соединение с таблицей интервалов.

В отчётах на СКД функция ДОБАВИТЬКДАТЕ работает в тексте запроса набора данных так же, как в обычном запросе. Для вычисляемых полей СКД она недоступна — там действует свой ограниченный язык выражений, поэтому сдвиг дат лучше выполнять на уровне запроса.

Разность дат: как посчитать интервал между двумя датами

Обратная задача — определить, сколько времени прошло между двумя датами. Во встроенном языке для этого есть функция РазностьДат(Дата1, Дата2, ТипИнтервала): она возвращает количество полных единиц указанного типа между датами. Если вторая дата раньше первой, результат будет отрицательным.

// Сколько дней до срока оплаты

ДнейОсталось = РазностьДат(ТекущаяДата(), СрокОплаты, ТипИнтервалаДаты.День);

// Стаж сотрудника в годах

СтажЛет = РазностьДат(ДатаПриема, ТекущаяДата(), ТипИнтервалаДаты.Год);

В запросах используется РАЗНОСТЬДАТ(Дата1, Дата2, ТИП) с теми же ключевыми словами интервалов. Это удобно для отбора просроченных документов, расчёта длительности операций и аналитики по периодам.

Учитывайте особенность: разность считается в завершённых единицах. Между 31 января и 1 февраля разность в месяцах равна нулю, хотя прошёл день. Если нужна точная длительность, считайте в меньших единицах (днях или секундах) и при необходимости делите самостоятельно.

Арифметика дат с секундами и работа с границами дня

Когда интервалы короткие и точность важна до секунды — журналы обмена, замеры длительности операций, технологические тайм-ауты — применяется прямое сложение даты с числом секунд. Это единственный случай, когда оператор «+» с датой уместен.

// Дедлайн через 15 минут

Дедлайн = ТекущаяДата() + 15 * 60;

// Конец суток заданной даты

КонецСуток = КонецДня(НекаяДата); // 23:59:59

Для работы с границами периодов платформа предоставляет функции НачалоДня, КонецДня, НачалоМесяца, КонецМесяца, НачалоГода, КонецГода и аналогичные для недели, квартала, часа и минуты. Их комбинация с ДобавитьКДате закрывает большинство прикладных задач: например, «последний день следующего месяца» — это КонецМесяца(ДобавитьКДате(Дата, ТипИнтервалаДаты.Месяц, 1)).

Способы сдвига даты

ДобавитьКДате

Универсальный способ для дней, месяцев, лет. Корректно обрабатывает границы месяцев. Только в коде, не в запросе.

ДОБАВИТЬКДАТЕ в запросе

Для отборов и вычисляемых колонок в запросах и СКД. Количество — только константа или параметр.

Дата + секунды

Для коротких интервалов с точностью до секунды. Не подходит для дней и месяцев из-за переменной длительности.

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

Первая группа ошибок связана с пустыми датами. Пустая дата в 1С — это 01.01.0001 00:00:00. Прибавление интервала к ней даст дату первого года, что обычно означает ошибку в данных. Перед расчётом проверяйте заполненность: Если ЗначениеЗаполнено(ДатаДокумента) Тогда ....

Вторая группа — путаница единиц измерения: сдвиг на секунды вместо дней при арифметике с числом, ожидание дробного результата от РазностьДат, использование месячного интервала там, где нужна точность до дня. Третья группа — попытка сложить две даты или передать поле таблицы в третий параметр ДОБАВИТЬКДАТЕ, из-за чего запрос падает с ошибкой синтаксиса.

Типичные ошибки
01
Дата + 7 вместо ДобавитьКДате(..., День, 7) — сдвиг на 7 секунд вместо недели
02
Сложение двух дат оператором «+» — ошибка выполнения, операция не определена
03
Расчёт без проверки пустой даты — результат уходит в год 1
04
Передача поля таблицы как количества в ДОБАВИТЬКДАТЕ — ошибка запроса
05
Ручной подсчёт месяцев через 30 * 86400 — нарушение на границах месяцев

Проверять результат удобнее всего в отладчике: установите точку останова после расчёта и посмотрите значение переменной. Для быстрой проверки без отладки выведите результат через Сообщить() с форматированием: Сообщить(Формат(СрокОплаты, "ДЛФ=D")). Для запросов используйте консоль запросов — она покажет вычисленную колонку до того, как код попадёт в рабочую базу.

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

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

Значение после ДобавитьКДате
Норма

Дата сдвинута ровно на заданный интервал, время суток сохранено

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

Дата в первом веке или сдвиг на секунды вместо дней

Переход через конец месяца
Норма

31.01 + 1 месяц = последний день февраля

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

Несуществующая дата или сдвиг на фиксированные 30 дней

Результат запроса с ДОБАВИТЬКДАТЕ
Норма

Колонка заполнена рассчитанными датами

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

Ошибка синтаксиса или пустая колонка

Практический пример: расчёт срока оплаты по договору

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

Запрос = Новый Запрос;

Запрос.Текст =

"ВЫБРАТЬ

| Реализация.Ссылка КАК Документ,

| Реализация.Дата КАК ДатаОтгрузки,

| Реализация.Договор.СрокОплатыДней КАК СрокДней

|ИЗ

| Документ.РеализацияТоваровУслуг КАК Реализация

|ГДЕ

| Реализация.Проведен";

Выборка = Запрос.Выполнить().Выбрать();

Пока Выборка.Следующий() Цикл

Если Не ЗначениеЗаполнено(Выборка.ДатаОтгрузки) Тогда

Продолжить;

КонецЕсли;

СрокОплаты = ДобавитьКДате(Выборка.ДатаОтгрузки,

ТипИнтервалаДаты.День, Выборка.СрокДней);

Если СрокОплаты < ТекущаяДата() Тогда

Сообщить("Просрочен: " + Выборка.Документ

+ ", срок был " + Формат(СрокОплаты, "ДЛФ=D"));

КонецЕсли;

КонецЦикла;

Если бы срок оплаты был одинаковым для всех документов, расчёт можно было бы перенести в запрос через ДОБАВИТЬКДАТЕ с параметром — это быстрее на больших объёмах, так как отбор выполняется на стороне СУБД без перебора записей в коде.

Итог

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

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

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

Можно ли сложить две даты в 1С оператором «+»?

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

Как прибавить к дате один месяц, если исходная дата — 31 число?

Используйте ДобавитьКДате(Дата, ТипИнтервалаДаты.Месяц, 1). Функция сама скорректирует результат: к 31 января прибавление месяца даст последний день февраля. Ручной расчёт через секунды здесь неприменим, так как длительность месяцев различается.

Почему ДОБАВИТЬКДАТЕ в запросе выдаёт ошибку, когда я передаю поле таблицы как количество?

Третий параметр ДОБАВИТЬКДАТЕ принимает только целочисленный литерал или параметр запроса. Если интервал индивидуален для каждой строки, выполните запрос без сдвига, а дату рассчитайте в коде через ДобавитьКДате при обходе выборки.

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

РазностьДат возвращает только целые завершённые единицы. Для дробного результата вычтите одну дату из другой — получите секунды — и разделите на 86400. Например: (Дата2 - Дата1) / 86400 даст разницу в днях с учётом времени суток.

Чем отличается ТекущаяДата() от ТекущаяДатаСеанса() при расчётах?

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

Как вычесть из даты несколько дней?

Вызовите ДобавитьКДате с отрицательным количеством: ДобавитьКДате(Дата, ТипИнтервалаДаты.День, -10) вернёт дату за десять дней до исходной. Отдельной функции вычитания интервалов в платформе нет — отрицательный параметр полностью закрывает эту задачу.