Разработка и Конфигурация · 10 мин
Как рассчитать дату в 1С: функции, примеры и типичные ошибки
Как рассчитать дату в 1С: прибавление и вычитание дней, месяцев и лет, разница между датами, функции ДобавитьМесяц, КонецМесяца, примеры кода и типичные ошибки.
Расчёт дат — одна из самых частых задач в 1С: сроки оплаты по договору, даты отгрузки, периоды отчётов, графики начислений. Встроенный язык 1С поддерживает арифметику дат напрямую, а также предоставляет набор функций для работы с месяцами, кварталами и границами периодов. Понимание разницы между этими инструментами избавляет от ошибок, которые потом всплывают в документах и отчётах.
Главный принцип: дата в 1С хранится с точностью до секунды, и операция «дата плюс число» прибавляет именно секунды, а не дни. Поэтому «прибавить 30» и «прибавить месяц» — это разные результаты, и путать их нельзя. Ниже разобраны все основные способы расчёта дат с примерами кода и проверкой результата.
Как устроен тип «Дата» и почему это важно
Тип «Дата» в 1С содержит и календарную дату, и время с точностью до секунды. Когда вы пишете Дата("20260115"), время равно 00:00:00. Если документ проведён в 14:35:20, его дата хранит и это время — сравнение такой даты с «чистой» календарной датой даст неравенство, хотя визуально день один и тот же.
Арифметика с датами работает в секундах. Выражение Дата + 1 прибавляет одну секунду, а не один день. Чтобы сдвинуть дату на сутки, нужно прибавить 86400 (60 × 60 × 24). Это самая частая ошибка начинающих разработчиков: код ДатаОплаты = ДатаДокумента + 10 компилируется без ошибок, но сдвигает дату на 10 секунд.
Как прибавить или вычесть дни
Для сдвига на целое число дней используется сложение или вычитание с умножением на 86400. Пример расчёта срока оплаты — 14 календарных дней от даты счёта:
ДатаОплаты = ДатаСчета + 14 * 86400;
Просрочено = ТекущаяДата() > ДатаОплаты;
Если нужно сохранить время суток (например, дата отгрузки всегда в 10:00), сначала обрежьте время функцией НачалоДня(), затем прибавьте дни и нужное количество часов:
ДатаОтгрузки = НачалоДня(ДатаЗаказа) + 3 86400 + 10 3600;
Вычитание работает симметрично: ДатаНачала = ДатаОкончания - 30 * 86400 даст дату за 30 суток до указанной. Учтите, что так считаются календарные дни. Для расчёта рабочих дней арифметики недостаточно — нужен производственный календарь, об этом ниже.
Как прибавить месяцы и годы: функция ДобавитьМесяц
Месяцы имеют разную длину, поэтому прибавлять их через секунды нельзя. Для этого существует функция ДобавитьМесяц(Дата, КоличествоМесяцев). Она корректно обрабатывает переходы через год и разную длину месяцев:
ДатаОкончанияДоговора = ДобавитьМесяц(ДатаНачала, 12); // ровно год
СледующийПлатеж = ДобавитьМесяц(ДатаПлатежа, 1);
Особый случай — конец месяца. Если исходная дата 31 января, то ДобавитьМесяц вернёт 28 или 29 февраля (последний день месяца), а не 3 марта. Это логичное поведение для финансовых расчётов, но о нём нужно помнить при тестировании на граничных датах.
Чтобы прибавить годы, умножьте количество на 12: ДобавитьМесяц(Дата, 5 * 12) — плюс пять лет. Отдельной функции «ДобавитьГод» во встроенном языке нет.
Границы периодов: начало и конец дня, месяца, квартала
Для отчётов и запросов постоянно нужны границы периодов. Встроенный язык предоставляет готовые функции, которые избавляют от ручных вычислений:
НачалоДня(Дата),КонецДня(Дата)— 00:00:00 и 23:59:59;НачалоНедели(Дата),КонецНедели(Дата);НачалоМесяца(Дата),КонецМесяца(Дата);НачалоКвартала(Дата),КонецКвартала(Дата);НачалоГода(Дата),КонецГода(Дата).
Типичный пример — формирование периода отчёта за предыдущий месяц:
НачалоПериода = НачалоМесяца(ДобавитьМесяц(ТекущаяДата(), -1));
КонецПериода = КонецМесяца(НачалоПериода);
Функция КонецДня возвращает 23:59:59, поэтому условие ДатаДокумента <= КонецДня(Период) корректно захватывает все документы дня. Если сравнивать с НачалоДня следующего дня через «строго меньше», результат будет тем же — выбирайте тот вариант, который принят в вашей конфигурации, и придерживайтесь его единообразно.
Как посчитать разницу между датами
Вычитание двух дат даёт разницу в секундах. Чтобы получить дни, разделите на 86400:
ДнейПросрочки = (ТекущаяДата() - ДатаОплаты) / 86400;
Результат будет дробным, если время у дат разное. Для целого числа дней используйте Цел() или предварительно приведите обе даты к началу дня — так расчёт не будет «плясать» от времени проведения документа:
ДнейМежду = (НачалоДня(Дата2) - НачалоДня(Дата1)) / 86400;
Разница в месяцах арифметикой не считается из-за разной длины месяцев. Практический способ — вычислить через год и месяц: (Год(Д2) - Год(Д1)) * 12 + Месяц(Д2) - Месяц(Д1), а затем скорректировать результат, если день второй даты меньше дня первой. В запросах для разницы дат есть встроенная функция РАЗНОСТЬДАТ(Дата1, Дата2, ДЕНЬ) — в запросах предпочтительнее использовать именно её.
- 1Определите базовую дату: дата счёта или дата отгрузки
- 2Уточните единицы срока: календарные дни, рабочие дни или месяцы
- 3Для дней прибавьте N * 86400, для месяцев используйте ДобавитьМесяц
- 4Приведите результат к началу или концу дня, если время не важно
- 5Проверьте расчёт на граничных датах: 31 января, 29 февраля, конец года
Расчёт по рабочим дням и в запросах
Если срок задан в рабочих днях, простой арифметики недостаточно — нужно учитывать выходные и праздники. В типовых конфигурациях для этого используется регистр сведений с производственным календарём (например, «ДанныеПроизводственногоКалендаря»). Алгоритм: в цикле прибавлять по одному дню и проверять по календарю, является ли день рабочим, пока не наберётся нужное количество.
В языке запросов 1С доступны свои инструменты: ДОБАВИТЬКДАТЕ(Дата, ДЕНЬ, 5), РАЗНОСТЬДАТ, НАЧАЛОПЕРИОДА и КОНЕЦПЕРИОДА. Они удобны тем, что вычисляются на стороне СУБД и не требуют выгрузки данных в код. Единицы периода указываются ключевыми словами: СЕКУНДА, МИНУТА, ЧАС, ДЕНЬ, НЕДЕЛЯ, МЕСЯЦ, КВАРТАЛ, ГОД.
Сравнение вариантов расчёта
Подходит для дней и недель. Не подходит для месяцев и лет из-за разной длины месяцев.
Единственный корректный способ сдвига на месяцы и годы в коде. Учитывает високосные годы.
Универсальный вариант при выборке данных; выполняется на сервере СУБД, не требует обхода результата.
Обязателен для расчёта в рабочих днях. Требует актуального заполнения календаря в базе.
Типичные ошибки и проверка результата
Первая ошибка — прибавление дней без умножения на 86400: код работает, ошибок не выдаёт, но дата сдвигается на секунды. Вторая — сравнение дат документов с «чистыми» датами без приведения через НачалоДня: документ, проведённый в 15:00, оказывается «больше» границы периода, заданной как начало того же дня.
Третья ошибка — расчёт месяцев через 30 дней: Дата + 30 * 86400 вместо ДобавитьМесяц(Дата, 1). На длинных дистанциях такой код накапливает погрешность и смещает даты платежей. Четвёртая — игнорирование пустой даты: пустая дата в 1С равна 01.01.0001, и любые расчёты с ней дадут бессмысленный результат. Перед вычислениями проверяйте ЗначениеЗаполнено(Дата).
Как проверить результат
Изменилось только число, время сохранилось
Дата изменилась на секунды или сбросилось время
Получено последнее число февраля
Дата ушла в март
Целое число дней при датах без времени
Дробное значение из-за разного времени
Начало 00:00:00, конец 23:59:59
Документы первого или последнего дня не попадают
Перед выкладкой расчёта в рабочую базу протестируйте его на граничных значениях: конец февраля високосного и невисокосного года, 31-е числа, переход через новый год. Это дешёвая проверка, которая отлавливает большинство дефектов расчёта дат до того, как они попадут в документы.
Что важно запомнить
Дни прибавляйте через умножение на 86400, месяцы и годы — только через ДобавитьМесяц, границы периодов берите штатными функциями Начало/Конец, разницу считайте через вычитание с приведением к началу дня или РАЗНОСТЬДАТ в запросах. Всегда проверяйте заполненность даты и тестируйте код на граничных календарных значениях.
Частые вопросы
Как прибавить к дате 10 рабочих дней?
Арифметика дат не знает о выходных. Используйте производственный календарь: в цикле прибавляйте по одному дню и проверяйте по регистру данных календаря, рабочий ли это день. Цикл продолжается, пока не наберётся 10 рабочих дней. В типовых конфигурациях для этого есть готовые функции общих модулей работы с календарями.
Чем отличается ТекущаяДата() от ТекущаяДатаСеанса()?
ТекущаяДата() возвращает время компьютера, на котором выполняется код, и на клиенте может отличаться от серверного. ТекущаяДатаСеанса() возвращает время сервера с учётом часового пояса сеанса. В серверном коде и при записи документов корректнее использовать ТекущаяДатаСеанса(), чтобы избежать расхождений между клиентами в разных часовых поясах.
Как получить последний день месяца?
Функцией КонецМесяца(Дата) — она вернёт дату с временем 23:59:59 последнего дня. Если нужна только календарная дата без времени, оберните результат в НачалоДня(). Функция сама учитывает високосные годы, вручную вычислять 28 или 29 февраля не требуется.
Почему сравнение дат даёт неверный результат, хотя даты визуально совпадают?
Почти всегда причина во времени: дата документа содержит время проведения, а дата-константа — 00:00:00. Приведите обе даты к началу дня перед сравнением: НачалоДня(Дата1) = НачалоДня(Дата2). Также проверьте, не сравниваете ли вы дату со строкой — неявное преобразование типов в 1С в таких сравнениях не выполняется.
Как посчитать возраст или стаж в полных годах?
Вычислите разницу в годах через Год(Дата2) - Год(Дата1), затем уменьшите результат на 1, если месяц и день рождения в текущем году ещё не наступили. Удобное сравнение: если ДобавитьМесяц(ДатаРождения, Годы * 12) больше текущей даты, вычтите единицу. Такой подход корректно работает с 29 февраля.
Можно ли складывать даты с часовыми поясами?
Дата в 1С не хранит часовой пояс — это просто момент времени. Если база работает в нескольких часовых поясах, используйте ТекущаяДатаСеанса() и учитывайте смещение через функции работы с часовыми поясами сеанса. Самостоятельно прибавлять «поправку на пояс» в секундах рискованно: при переходе на другой сервер логика сломается.