Прибавляем дни, месяцы и годы к дате в 1С 8.3
Содержание
В 1С нет функции ДобавитьДень — и об это чаще всего спотыкаются. Чтобы прибавить к дате дни, часы или секунды, дату складывают с числом напрямую: Дата + 86400 даёт те же сутки через день, потому что внутри платформы дата — это количество секунд.
А вот чтобы прибавить к дате месяцы и годы, арифметика не годится: в месяце то 28, то 31 день. Для этого есть ровно одна встроенная функция — ДобавитьМесяц. Годы прибавляют через неё же, умножив на 12.
Ниже — все рабочие приёмы: прибавить к дате число дней и часов, сдвинуть на месяц и год, посчитать разность, добавить рабочие дни по производственному календарю и сделать это массово в запросе, а не в медленном цикле.
Дата + Число * 86400 (дни), * 3600 (часы), * 60 (минуты). Платформа складывает секунды.ДобавитьМесяц(Дата, Число). Для лет — число месяцев × 12. Конец месяца обрабатывается автоматически.ДОБАВИТЬКДАТЕ(Поле, ДЕНЬ, 5). Это в разы быстрее цикла.Дата — это секунды: прибавляем дни, часы и минуты
Любая дата в 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;
КонецЕсли;
КонецЦикла;
Но праздники и перенесённые дни так не учесть. Для точного результата нужен регистр производственного календаря, который есть в типовых конфигурациях (Бухгалтерия, ЗУП). Алгоритм тот же цикл, только вместо ДеньНедели — проверка дня по данным календаря:
- Запросом получите из регистра календаря отметки рабочих/нерабочих дней на нужный период.
- Идите по дням вперёд, пропуская нерабочие.
- Уменьшайте счётчик на каждом рабочем дне, пока не доберёте нужное число.
В конфигурациях на Библиотеке стандартных подсистем есть готовый общий модуль для работы с календарными графиками — он умеет и прибавлять рабочие дни, и считать их количество между датами. Прежде чем писать свой цикл, посмотрите, подключён ли он: это снимет с вас заботу о праздниках и переносах.
Массовые операции: считайте даты в запросе
Если нужно сдвинуть даты у тысяч документов, цикл по выборке с арифметикой — худший вариант: каждая итерация читает объект и обращается к базе. Такую задачу решает запрос: в языке запросов есть своя датовая арифметика.
ДОБАВИТЬКДАТЕ(<Дата>, <Период>, <Число>) прибавляет к дате число периодов. Период — СЕКУНДА, МИНУТА, ЧАС, ДЕНЬ, НЕДЕЛЯ, МЕСЯЦ, КВАРТАЛ, ГОД. РАЗНОСТЬДАТ считает разницу в выбранных единицах:
ВЫБРАТЬ
Заказ.Ссылка,
ДОБАВИТЬКДАТЕ(Заказ.Дата, ДЕНЬ, 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`?
ДобавитьМесяц, иначе дата «уплывёт».Как прибавить год, если `ДобавитьГод` не находится?
ДобавитьМесяц(Дата, 12). Это корректно отработает и високосный год.Почему отбор «за сегодня» теряет часть документов?
ТекущаяДата() без выравнивания, в выборку не попадут проведённые позже этого момента. Оборачивайте границы в НачалоДня и КонецДня.Как посчитать, сколько дней между двумя датами?
86400: (Дата2 - Дата1) / 86400. В запросе — РАЗНОСТЬДАТ(Дата1, Дата2, ДЕНЬ).