Прибавляем дни, месяцы и годы к дате в 1С 8.3

· 9 мин

Содержание

В 1С нет функции ДобавитьДень — и об это чаще всего спотыкаются. Чтобы прибавить к дате дни, часы или секунды, дату складывают с числом напрямую: Дата + 86400 даёт те же сутки через день, потому что внутри платформы дата — это количество секунд.

А вот чтобы прибавить к дате месяцы и годы, арифметика не годится: в месяце то 28, то 31 день. Для этого есть ровно одна встроенная функция — ДобавитьМесяц. Годы прибавляют через неё же, умножив на 12.

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

Что нужно прибавить к дате?

Дата — это секунды: прибавляем дни, часы и минуты

Любая дата в 1С хранится как число секунд. Поэтому прибавить к дате число дней — значит прибавить столько же суток в секундах. Запоминаются три множителя:

  • 🔹 сутки — 86400 секунд (24 × 60 × 60);
  • 🔹 час — 3600 секунд;
  • 🔹 минута — 60 секунд.
СледующийДень = ТекущаяДата() + 86400;      // +1 день
ЧерезТриЧаса  = ТекущаяДата() + 3 * 3600;    // +3 часа
ЧерезПолчаса  = ТекущаяДата() + 30 * 60;     // +30 минут

// Плановая отгрузка через 5 дней после заказа
ДатаОтгрузки = ДатаЗаказа + 5 * 86400;

Так же работает и вычитание: Дата - 86400 вернёт вчерашний день. Результат — всегда дата, с ним можно сравнивать и складывать дальше.

Не держите «магические» числа в коде россыпью. Заведите переменную СекундВДне = 86400 или вынесите её в константу — через полгода + 5 * 86400 читается хуже, чем + 5 * СекундВДне.

Главная ловушка: прибавление 1 добавляет одну секунду, а не один день. Если написать Дата + 1, сдвинется счётчик секунд. Для суток множитель 86400 обязателен.

Прибавить месяцы и годы: функция ДобавитьМесяц

Для месяцев арифметика секунд не работает : прибавить «месяц» как фиксированное число секунд нельзя, длина месяца плавает. Поэтому платформа даёт отдельную функцию.

ДобавитьМесяц(<Дата>, <ЧислоМесяцев>) сдвигает дату на указанное число месяцев. Второй аргумент может быть отрицательным — тогда дата уходит назад.

ЧерезМесяц = ДобавитьМесяц(ТекущаяДата(),  1);   // +1 месяц
МесяцНазад = ДобавитьМесяц(ТекущаяДата(), -1);   // -1 месяц
ЧерезГод   = ДобавитьМесяц(ТекущаяДата(), 12);   // +1 год
ТриГода    = ДобавитьМесяц(ТекущаяДата(), 36);   // +3 года

Отдельной функции ДобавитьГод в платформе нет. Год — это 12 месяцев, два года — 24 и так далее. Это надёжнее, чем прибавлять 365 * 86400, потому что в високосном году 366 дней.

Функция аккуратно обрабатывает короткие месяцы. Если в исходной дате число больше, чем есть в целевом месяце, берётся последний день месяца:

// 31 января + 1 месяц. В феврале 2026 только 28 дней (год не високосный)
Результат = ДобавитьМесяц('20260131', 1);   // 28.02.2026

Из-за этого операция необратима на краях месяца. ДобавитьМесяц('20260131', 1) даст 28 февраля, а обратный ДобавитьМесяц от 28 февраля вернёт 28 января, а не 31-е. Если нужна именно «та же дата следующего месяца» — проверяйте край отдельно.

Разность дат и сравнение

Вычитание двух дат возвращает число — разницу в секундах. Чтобы получить дни, делим на 86400:

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

Даты сравнивают обычными операторами <, >, =: более поздняя дата «больше». Это удобно в условиях отбора и проверках сроков:

Если ДатаОплаты > ДатаСчёта + 5 * 86400 Тогда
    Сообщить("Оплата с просрочкой больше 5 дней");
КонецЕсли;

Пустая дата в 1С — это не ноль, а '00010101' (1 января первого года). Проверяют её сравнением с пустой датой того же типа, а не с нулём:

Если Заказ.ДатаОтгрузки = Дата(1, 1, 1) Тогда
    // дата не заполнена
КонецЕсли;

Складывать число с незаполненной датой бессмысленно — получите дату в районе первого года. Перед арифметикой убедитесь, что дата заполнена.

Полные годы и стаж — не через деление на 365

Частая ошибка — считать возраст или стаж как (Дата2 - Дата1) / 86400 / 365. Из-за високосных лет набегает погрешность, и на длинных интервалах результат «прыгает» на год. Полное число лет считают сдвигом по месяцам и сравнением:

Функция ПолныхЛет(Начало, Конец)
    Лет = 0;
    Пока ДобавитьМесяц(Начало, (Лет + 1) * 12) <= Конец Цикл
        Лет = Лет + 1;
    КонецЦикла;
    Возврат Лет;
КонецФункции;

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

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

Дата в 1С всегда несёт и время. Когда прибавляете к дате число, время сохраняется: '20261108120000' + 86400 даст 9 ноября тоже в 12:00. Часто это мешает — например, при отборе за день.

Для выравнивания времени есть парные функции границ периода. Они не прибавляют число, но почти всегда идут с датовой арифметикой рядом:

ФункцияЧто возвращает
НачалоДня(Дата)те же сутки, 00:00:00
КонецДня(Дата)те же сутки, 23:59:59
НачалоМесяца(Дата)1-е число месяца, 00:00:00
КонецМесяца(Дата)последний день месяца, 23:59:59
НачалоГода / КонецГодаграницы года
НачалоКвартала / КонецКварталаграницы квартала

Типичный отбор «за сегодня» строится так — иначе документы, проведённые днём, в выборку не попадут:

Отбор.Дата.Использование = Истина;
Отбор.Дата.Вид = ВидСравнения.Интервал;
Отбор.Дата.Значение = Новый Диапазон(НачалоДня(Дата), КонецДня(Дата));

Собрать дату из частей удобно конструктором Дата. Он же нужен, когда год, месяц и день приходят из полей формы:

Д  = Дата(2026, 11, 8);              // 08.11.2026 00:00:00
ДВ = Дата(2026, 11, 8, 14, 30, 0);   // с временем 14:30

Разобрать дату на части помогают функции Год(Дата), Месяц(Дата), День(Дата), Час, Минута, Секунда. Так, «первое число следующего месяца» считается без арифметики секунд: НачалоМесяца(ДобавитьМесяц(ТекущаяДата(), 1)).

Границы недели дают НачалоНедели (понедельник 00:00) и КонецНедели (воскресенье 23:59:59). Через них удобно прибавлять к дате недели и находить ближайший день недели, не считая вручную, какой сегодня день:

ЭтотПонедельник  = НачалоНедели(ТекущаяДата());
СледующийПонедельник = НачалоНедели(ТекущаяДата()) + 7 * 86400;  // +1 неделя
ЧерезДвеНедели   = ТекущаяДата() + 14 * 86400;

Комбинация «граница периода + арифметика» закрывает почти любой сдвиг: сначала выровняли дату к началу суток, недели или месяца, потом прибавили нужное число.

Прибавить рабочие дни по производственному календарю

«Через 5 рабочих дней» через + 5 * 86400 не посчитать: арифметика не знает про выходные и праздники. Нужен производственный календарь .

Если достаточно пропустить только субботы и воскресенья, хватит функции ДеньНедели(Дата) — она возвращает число от 1 (понедельник) до 7 (воскресенье):

Дата = ДатаНачала;
Осталось = 5;
Пока Осталось > 0 Цикл
    Дата = Дата + 86400;
    Если ДеньНедели(Дата) <= 5 Тогда   // будни Пн–Пт
        Осталось = Осталось - 1;
    КонецЕсли;
КонецЦикла;

Но праздники и перенесённые дни так не учесть. Для точного результата нужен регистр производственного календаря, который есть в типовых конфигурациях (Бухгалтерия, ЗУП). Алгоритм тот же цикл, только вместо ДеньНедели — проверка дня по данным календаря:

  1. Запросом получите из регистра календаря отметки рабочих/нерабочих дней на нужный период.
  2. Идите по дням вперёд, пропуская нерабочие.
  3. Уменьшайте счётчик на каждом рабочем дне, пока не доберёте нужное число.

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

Массовые операции: считайте даты в запросе

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

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

ВЫБРАТЬ
    Заказ.Ссылка,
    ДОБАВИТЬКДАТЕ(Заказ.Дата, ДЕНЬ, 5)        КАК ПлановаяОтгрузка,
    РАЗНОСТЬДАТ(Заказ.Дата, &Сегодня, ДЕНЬ)   КАК ДнейПрошло
ИЗ
    Документ.ЗаказКлиента КАК Заказ
ГДЕ
    Заказ.Проведён

Одним запросом вы получаете уже сдвинутые даты по всем строкам — платформа считает это на стороне СУБД. Для группировки по периодам в том же запросе пригодятся НАЧАЛОПЕРИОДА и КОНЕЦПЕРИОДА (например, НАЧАЛОПЕРИОДА(Заказ.Дата, МЕСЯЦ)).

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

Правило простое: если дат больше сотни-двух — считайте их в запросе, а не в цикле по объектам. Разница в скорости — на порядок.

Даты в обменах: XML, JSON и часовой пояс

При выгрузке во внешнюю систему дату нельзя отдавать как число секунд — нужен стандартный строковый формат. Для этого есть XMLСтрока и обратная XMLЗначение, работающие по ISO 8601:

Стр = XMLСтрока(ТекущаяДата());          // 2026-11-08T00:00:00
Д   = XMLЗначение(Тип("Дата"), "2026-11-08T00:00:00");

Для JSON-обменов при записи через ЗаписатьJSON формат дат задаётся параметрами записи — выгружайте в ISO, тогда приёмная сторона разберёт дату однозначно.

Отдельный вопрос — часовой пояс. Даты в 1С хранятся без привязки к зоне, а внешняя система часто ждёт время в UTC. Переводят парой функций:

  • ТекущаяУниверсальнаяДата() — текущее время по UTC;
  • УниверсальнаяДата(МестноеВремя) — из времени сеанса в UTC;
  • МестноеВремя(УниверсальнаяДата) — обратно, из UTC в зону сеанса.

Прибавляя к дате число перед выгрузкой, держите в голове, в какой зоне считаете. Сдвиг на сутки в местном времени и в UTC в дни перехода на зимнее/летнее время у части систем даст расхождение — фиксируйте зону явно и переводите один раз на границе обмена.

Частые ошибки при сложении дат

Почему `Дата + 1` не сдвигает день?
Потому что 1 — это одна секунда. Дата внутри хранится в секундах, поэтому для суток множитель 86400, для часа — 3600, для минуты — 60.
Можно ли прибавить месяц через `+ 30 * 86400`?
Нет. 30 дней — это не месяц: в месяце бывает 28, 29, 30 или 31 день. Для сдвига на месяцы и годы используйте ДобавитьМесяц, иначе дата «уплывёт».
Как прибавить год, если `ДобавитьГод` не находится?
Такой функции нет. Прибавьте 12 месяцев: ДобавитьМесяц(Дата, 12). Это корректно отработает и високосный год.
Почему отбор «за сегодня» теряет часть документов?
Документы несут время. Если сравнивать с ТекущаяДата() без выравнивания, в выборку не попадут проведённые позже этого момента. Оборачивайте границы в НачалоДня и КонецДня.
Как посчитать, сколько дней между двумя датами?
Вычтите одну из другой и разделите на 86400: (Дата2 - Дата1) / 86400. В запросе — РАЗНОСТЬДАТ(Дата1, Дата2, ДЕНЬ).