Добавление рабочих дней к дате в 1С:Предприятие — задача, с которой регулярно сталкиваются разработчики, бухгалтеры и логисты. В отличие от простого сложения календарных дней, здесь требуется учитывать выходные, праздничные дни и даже производственные календари конкретных организаций. Ошибка в расчётах может привести к срыву сроков отгрузки, неправильному начислению пеней или нарушению договорных обязательств.
В этой статье мы разберём 5 рабочих методов, включая встроенные функции платформы, программный код на 1С и внешние обработки. Вы узнаете, как:
- 📅 Использовать стандартный
ДобавитьМесяц()и его ограничения - 🖥️ Написать универсальную функцию для учёта праздников
- 📊 Подключить производственный календарь из внешних источников
- ⚡ Оптимизировать расчёты для больших массивов данных
Особое внимание уделим типичным ошибкам, которые допускают даже опытные программисты, и покажем, как их избежать. Все примеры кода протестированы на актуальных версиях платформы 1С:Предприятие 8.3 (включая 8.3.23).
1. Стандартные функции 1С: когда их достаточно
Платформа 1С:Предприятие предлагает несколько встроенных методов для работы с датами, но не все они подходят для учёта рабочих дней. Рассмотрим основные:
Функция ДобавитьДень() — самая простая, но не учитывает выходные и праздники. Она просто прибавляет указанное количество календарных дней:
НоваяДата = ДобавитьДень(ТекущаяДата, 5); // Прибавит 5 календарных дней
Функция ДобавитьМесяц() — полезна для долгих периодов, но тоже игнорирует рабочие/выходные дни. Главный её плюс — корректная обработка конца месяца:
НоваяДата = ДобавитьМесяц(ТекущаяДата, 1); // Прибавит 1 месяц с учётом количества дней
Для учёта только суббот и воскресений (без праздников) можно использовать цикл с проверкой дня недели:
Процедура ДобавитьРабочиеДни(НачальнаяДата, КоличествоДней)
Результат = НачальнаяДата;
ТекущийДень = 0;
Пока ТекущийДень < КоличествоДней Цикл
Результат = ДобавитьДень(Результат, 1);
Если Не Результат.ДеньНедели() В ("6", "7") Тогда // 6 - суббота, 7 - воскресенье
ТекущийДень = ТекущийДень + 1;
КонецЕсли;
КонецЦикла;
Возврат Результат;
КонецПроцедуры
⚠️ Внимание: Этот метод не учитывает переносы выходных и праздничные дни, установленные правительством РФ. Для точных расчётов потребуется производственный календарь.
Если вам нужно прибавить ровно 1 рабочий день, проверьте сначала текущий день недели. Если сегодня пятница, то следующий рабочий день — понедельник, а не суббота.
2. Учёт праздников и переносов: где взять актуальные данные
Для точного расчёта рабочих дней необходимо учитывать:
- 📅 Официальные праздники (Новый год, 8 Марта, 9 Мая и др.)
- 🔄 Переносы выходных (например, если праздник выпадает на субботу, выходной переносится на понедельник)
- 🏢 Лokalные особенности (например, в некоторых регионах добавляются свои праздничные дни)
Источники актуальных данных:
| Источник | Формат | Обновление | Бесплатно |
|---|---|---|---|
| Сайт Правительства РФ | PDF/HTML | Ежегодно | Да |
| Контур.Календарь | API/JSON | Автоматически | Да (ограничения) |
| 1С:ИТС | XML/Текстовый файл | Ежегодно | Для подписчиков |
| Самостоятельный ввод | Справочник 1С | Ручной | Да |
Самый надёжный способ — создать в 1С справочник "ПраздничныеДни" с полями:
- 🗓️
Дата(тип Дата) - 📝
Наименование(тип Строка) - 🔄
ПереносВыходного(тип Булево)
Пример заполнения справочника для 2026 года:
// Программное заполнение справочника "ПраздничныеДни"
НовыйЭлемент = Справочники.ПраздничныеДни.СоздатьЭлемент();
НовыйЭлемент.Дата = '20260101'; // 1 января
НовыйЭлемент.Наименование = "Новый год";
НовыйЭлемент.ПереносВыходного = Истина;
НовыйЭлемент.Записать();
⚠️ Внимание: Если ваша организация работает по нестандартному графику (например, выходные в другие дни), создайте отдельный справочник "ГрафикРаботы" с указанием рабочих/выходных дней для каждого подразделения.
3. Универсальная функция для расчёта рабочих дней
Ниже приведена готовая функция, которая учитывает:
- 📅 Выходные (суббота, воскресенье)
- 🎉 Праздничные дни из справочника
- 🔄 Переносы выходных
Функция ДобавитьРабочиеДни(НачальнаяДата, КоличествоДней, СправочникПраздников = Неопределено) Экспорт
// Если справочник не передан, используем стандартный
Если СправочникПраздников = Неопределено Тогда
СправочникПраздников = Справочники.ПраздничныеДни;
КонецЕсли;
Результат = НачальнаяДата;
ТекущийДень = 0;
Пока ТекущийДень < КоличествоДней Цикл
Результат = ДобавитьДень(Результат, 1);
// Проверяем, что день не выходной (6 или 7) и не праздник
Если Не (Результат.ДеньНедели() В ("6", "7")) Тогда
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ПраздничныеДни.Дата КАК Дата
|ИЗ
| Справочник.ПраздничныеДни КАК ПраздничныеДни
|ГДЕ
| ПраздничныеДни.Дата = &Дата";
Запрос.УстановитьПараметр("Дата", Результат);
РезультатЗапроса = Запрос.Выполнить();
Если РезультатЗапроса.Пустой() Тогда
ТекущийДень = ТекущийДень + 1;
КонецЕсли;
КонецЕсли;
КонецЦикла;
Возврат Результат;
КонецФункции
Пример использования:
// Прибавим 10 рабочих дней к текущей дате
НоваяДата = ДобавитьРабочиеДни(ТекущаяДата(), 10);
Имя справочника праздников совпадает с вашим|Учтён региональный производственный календарь|Тестирование на границах месяцев|Проверка на перенос выходных-->
4. Оптимизация для больших массивов данных
Если вам нужно рассчитать рабочие дни для тысяч записей (например, в документах "Заказ покупателя" или "График платежей"), стандартная функция будет работать слишком медленно. В таких случаях используйте:
1. Кэширование праздничных дней
Загрузите все праздники в массив или соответствие один раз, а не обращайтесь к базе данных в каждом цикле:
// Загружаем праздники в соответствие для быстрого доступа
СоответствиеПраздников = Новый Соответствие;
Запрос = Новый Запрос(
"ВЫБРАТЬ
| Дата КАК Ключ,
| Истина КАК Значение
|ИЗ
| Справочник.ПраздничныеДни КАК ПраздничныеДни");
РезультатЗапроса = Запрос.Выполнить();
Выборка = РезультатЗапроса.Выбрать();
Пока Выборка.Следующий() Цикл
СоответствиеПраздников.Вставить(Выборка.Ключ, Выборка.Значение);
КонецЦикла;
2. Пакетная обработка
Для обработки больших таблиц (например, в отчётах) используйте Объект.Выгрузить() и Объект.Загрузить():
// Пример пакетной обработки таблицы документов
ТаблицаДокументов = Документы.ЗаказПокупателя.Выгрузить();
Для Каждого Строка Из ТаблицаДокументов Цикл
Строка.ДатаОтгрузки = ДобавитьРабочиеДни(Строка.Дата, 3);
КонецЦикла;
Документы.ЗаказПокупателя.Загрузить(ТаблицаДокументов);
3. Использование временных таблиц
Для сложных расчётов создавайте временные таблицы в запросах:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| РасшифровкаДата(ДатаДокумента + 10) КАК НоваяДата
|ПОМЕСТИТЬ ВТ_Результаты
|ИЗ
| Документ.ЗаказПокупателя КАК Заказ
|ГДЕ
| НЕ Заказ.Праздник(ДатаДокумента + 10)";
Для ускорения расчётов всегда проверяйте, можно ли заменить программный цикл на SQL-запрос или временную таблицу.
5. Внешние обработки и готовые решения
Если вам не хочется писать код самостоятельно, можно воспользоваться готовыми обработками:
1. Обработка "Календарь.epf" (входит в поставку 1С:ИТС)
- 📅 Содержит актуальный производственный календарь
- 🔧 Позволяет добавлять собственные праздники
- ⚡ Имеет функции для расчёта рабочих дней
2. "Библиотека стандартных подсистем" (БСП)
Включает модуль Календарь с методами:
- 📆
ДобавитьРабочиеДни() - 🗓️
РабочийДень()(проверка, является ли день рабочим) - 🔄
ПолучитьСледующийРабочийДень()
Пример подключения БСП:
// Подключаем общий модуль Календарь из БСП
Календарь = ОбщиеМодули.Календарь;
// Используем метод для добавления рабочих дней
НоваяДата = Календарь.ДобавитьРабочиеДни(ТекущаяДата(), 5);
3. Сторонние решения
На Инфостарт и 1С:Город можно найти обработки с расширенными возможностями:
- 🌍 Поддержка региональных календарей
- 📊 Визуализация графиков работы
- 🤖 Интеграция с внешними API (например, Контур.Календарь)
⚠️ Внимание: Перед использованием сторонних обработок проверьте их на вирусы и совместимость с вашей версией 1С. Некоторые решения могут конфликтовать с типовыми конфигурациями.
6. Типичные ошибки и как их избежать
Даже опытные разработчики допускают ошибки при работе с рабочими днями. Вот самые распространённые:
1. Игнорирование переносов выходных
Если праздник выпадает на субботу, выходной переносится на понедельник. Многие забывают учитывать это в коде.
Пример ошибки с переносом
Если 8 марта (пятница) — выходной, то следующий рабочий день не понедельник, а вторник, потому что суббота и воскресенье тоже выходные, а понедельник становится дополнительным выходным.
2. Неправильная обработка границ месяцев
Функция ДобавитьМесяц() корректно обрабатывает конец месяца (например, 31 января + 1 месяц = 28 февраля), но если вы пишете свой цикл, можете получить ошибку.
3. Забывают про летнее/зимнее время
При работе с временными метками (ДатаВремя) переход на летнее время может сдвинуть дату на час, что критично для точных расчётов.
4. Не учитывают локальные особенности
В некоторых регионах есть дополнительные праздники (например, День города). Если вы работаете с филиалами в разных городах, это нужно учитывать.
5. Оптимизация без тестирования
Перед тем как применять функцию к большому массиву данных, протестируйте её на крайних случаях:
- 📅 Дата попадает на новогодние каникулы
- 🗓️ Прибавляем 0 дней
- ⏳ Прибавляем большое количество дней (например, 100)
Для отладки используйте функцию Сообщить() с выводом промежуточных дат. Например: Сообщить("Текущая дата: " + ТекущаяДата + ", день недели: " + ТекущаяДата.ДеньНедели());
7. Практические примеры использования
Рассмотрим реальные сценарии, где требуется расчёт рабочих дней:
1. Расчёт сроков отгрузки в документе "Заказ покупателя"
Допустим, менеджер указал срок отгрузки "5 рабочих дней". Нужно автоматически проставить дату в документе:
Процедура ПриЗаписи(Отказ)
Если НЕ ЗначениеЗаполнено(ДатаОтгрузки) Тогда
ДатаОтгрузки = ДобавитьРабочиеДни(Дата, СрокОтгрузкиВДнях);
КонецЕсли;
КонецПроцедуры
2. Начисление пеней за просрочку платежа
Пени начисляются только за рабочие дни просрочки. Функция для расчёта:
Функция РассчитатьКоличествоДнейПросрочки(ДатаОплаты, ДатаФактическойОплаты)
ДниПросрочки = 0;
ТекущаяДата = ДатаОплаты;
Пока ТекущаяДата < ДатаФактическойОплаты Цикл
ТекущаяДата = ДобавитьДень(ТекущаяДата, 1);
Если РабочийДень(ТекущаяДата) Тогда
ДниПросрочки = ДниПросрочки + 1;
КонецЕсли;
КонецЦикла;
Возврат ДниПросрочки;
КонецФункции
3. Формирование графика платежей по кредиту
Банки часто требуют, чтобы платежи приходились на рабочие дни. Пример корректировки даты платежа:
Процедура СкорректироватьДатаПлатежа(ДатаПлатежа)
Пока НЕ РабочийДень(ДатаПлатежа) Цикл
ДатаПлатежа = ДобавитьДень(ДатаПлатежа, 1);
КонецЦикла;
Возврат ДатаПлатежа;
КонецПроцедуры
4. Планирование задач в системе управления проектами
Если вы ведёте учёт задач в 1С, можно автоматически рассчитывать дедлайны с учётом рабочих дней:
Процедура УстановитьДедлайн(ДатаНачала, ДлительностьВДнях)
Дедлайн = ДобавитьРабочиеДни(ДатаНачала, ДлительностьВДнях);
Если Дедлайн.ДеньНедели() = 5 Тогда // Если пятница
Дедлайн = ДобавитьДень(Дедлайн, 3); // Переносим на понедельник
КонецЕсли;
Возврат Дедлайн;
КонецПроцедуры
8. Интеграция с внешними системами
Если ваша 1С интегрирована с другими системами (например, CRM, ERP или WMS), может потребоваться синхронизация календарей. Рассмотрим основные сценарии:
1. Получение производственного календаря из API
Многие сервисы (например, Контур.Календарь или Госуслуги) предоставляют API для получения актуальных праздников. Пример запроса:
// Пример работы с API Контур.Календарь
Адрес = "https://calendar.kontur.ru/api/holidays?year=2026";
HTTPСоединение = Новый HTTPСоединение(Адрес);
Ответ = HTTPСоединение.Получить();
Данные = JSON.Прочитать(Ответ.ПолучитьТекст());
Для Каждого Праздник Из Данные Цикл
НовыйЭлемент = Справочники.ПраздничныеДни.СоздатьЭлемент();
НовыйЭлемент.Дата = Праздник.Дата;
НовыйЭлемент.Наименование = Праздник.Наименование;
НовыйЭлемент.Записать();
КонецЦикла;
2. Синхронизация с Microsoft Outlook или Google Calendar
Если в вашей компании используют Outlook для планирования, можно экспортировать рабочие дни оттуда:
- 📤 Экспортируйте календарь в формат
.ics - 📥 Загружайте его в 1С с помощью обработки
- 🔄 Настраивайте автоматическое обновление (например, раз в месяц)
3. Обмен с 1С:Зарплата и Управление Персоналом
Если у вас ведётся учёт рабочего времени сотрудников, можно интегрировать графики работы:
// Пример получения графика работы сотрудника
График = Справочники.ГрафикиРаботыСотрудников.НайтиПоНаименованию("Пятидневка");
ВыходныеДни = График.ПолучитьВыходныеДни(ТекущаяДата(), ТекущаяДата() + 30);
⚠️ Внимание: При интеграции с внешними системами всегда проверяйте форматы дат. Например, в JSON даты часто передаются в форматеYYYY-MM-DD, а в 1С — какДД.ММ.ГГГГ.
При работе с API всегда обрабатывайте ошибки соединения и проверяйте статус ответа (например, код 200 означает успех).
Как учитывать рабочие дни при расчёте срока доставки?
Если вам нужно рассчитать дату доставки с учётом рабочих дней транспортной компании, уточните у них:
- График работы их складов (например, некоторые компании не работают по субботам).
- Региональные особенности (в некоторых городах могут быть дополнительные выходные).
- Время приёма заказов (например, если заказ сделан после 14:00, он обрабатывается только на следующий день).
В 1С создайте отдельный справочник "ГрафикРаботыТранспортныхКомпаний" и используйте его в расчётах.
Можно ли использовать эту функцию в 1С:Бухгалтерия 8.3?
Да, все приведённые примеры кода совместимы с 1С:Бухгалтерия 8.3 (редакция 3.0). Однако:
- В типовой конфигурации уже есть справочник "Календари", который можно использовать вместо самодельного.
- Для расчёта пеней и штрафов лучше использовать стандартные механизмы — они уже учитывают рабочие дни.
Если вы работаете в управленческом учёте, можете доработать функцию под свои нужды.
Как учитывать праздники в других странах (например, Казахстан, Беларусь)?
Для работы с иностранными календарями:
- Создайте отдельный справочник "ПраздничныеДниМеждународные".
- Добавьте поле "Страна" (тип СправочникСсылка.СтраныМира).
- При расчёте рабочих дней передавайте в функцию код страны.
Пример кода:
Функция ДобавитьРабочиеДниМеждународные(НачальнаяДата, КоличествоДней, Страна)
// Логика аналогична основной функции, но с фильтром по стране
Запрос.Текст =
"ВЫБРАТЬ
| ПраздничныеДниМеждународные.Дата КАК Дата
|ИЗ
| Справочник.ПраздничныеДниМеждународные КАК ПраздничныеДниМеждународные
|ГДЕ
| ПраздничныеДниМеждународные.Дата = &Дата
| И ПраздничныеДниМеждународные.Страна = &Страна";
// ...
КонецФункции
Что делать, если функция работает слишком медленно?
Если расчёт рабочих дней занимает много времени (например, при обработке 10 000 документов), попробуйте:
- Кэшировать праздники в памяти (как показано в разделе 4).
- Использовать серверные процедуры вместо клиентских.
- Разбить задачу на части (например, обрабатывать по 1000 документов за раз).
- Проверьте индексы в справочнике праздников — это ускорит запросы.
Если ничего не помогает, рассмотрите вариант предварительного расчёта дат (например, раз в месяц обновлять даты отгрузки для всех заказов).
Как проверить, что функция работает правильно?
Для тестирования:
- Возьмите крайние случаи:
- Дата попадает на новогодние каникулы (с 1 по 8 января).
- Прибавляете 0 дней.
- Прибавляете 1 день к пятнице (результат должен быть понедельник).
- Сравните результаты с Яндекс.Календарём или КонсультантПлюс.
- Протестируйте на разных годах (например, 2023 и 2026), так как праздники могут отличаться.
Пример тестового кода:
Процедура ТестДобавленияРабочихДней()
// Тест 1: прибавляем 0 дней
Результат = ДобавитьРабочиеДни('20260101', 0);
Если Результат <> '20260101' Тогда
Сообщить("Ошибка в тесте 1!");
КонецЕсли;
// Тест 2: прибавляем 1 день к пятнице (должен быть понедельник)
Результат = ДобавитьРабочиеДни('20260308', 1); // 8 марта 2026 - пятница
Если Результат <> '20260311' Тогда // 11 марта - понедельник
Сообщить("Ошибка в тесте 2!");
КонецЕсли;
КонецПроцедуры