Работа с датами в 1С:Предприятие — одна из самых востребованных задач при разработке конфигураций, написании отчетов и обработок. Даты используются везде: от расчета сроков оплаты и поставок до определения рабочих дней, возраста сотрудников или периодов действия договоров. Однако даже опытные программисты 1С иногда сталкиваются с нюансами, когда стандартные функции не покрывают специфические бизнес-требования.
В этой статье мы разберем все аспекты работы с датами в 1С: от базовых операций сложения/вычитания до расчета рабочих дней с учетом производственного календаря, праздников и индивидуальных графиков. Вы узнаете, как избежать типовых ошибок при переходе через месяц/год, как корректно сравнивать даты с учетом времени, и какие есть «подводные камни» при работе с неполными датами (например, Дата(2026, 2, 29)).
Особое внимание уделим практическим примерам кода на встроенном языке 1С, которые можно сразу использовать в своих конфигурациях. Все решения протестированы на актуальных версиях платформы 8.3.20+ и совместимы с большинством типовых конфигураций (Бухгалтерия 3.0, ЗУП 3.1, УТ 11, ERP 2.5).
1. Базовые операции с датами в 1С
Начнем с азов: как создать дату, получить текущую дату/время, и выполнить простейшие арифметические операции. Эти знания потребуются даже для тривиальных задач вроде расчета срока исполнения заказа или определения просроченных документов.
В 1С дата представляется типом Дата, который включает как календарную дату, так и время (если оно указано). Основные функции для работы:
- 📅
ТекущаяДата()— возвращает текущую дату без времени (например,15.05.2026). - ⏰
ТекущаяДатаСек()— возвращает текущие дату и время с точностью до секунды (например,15.05.2026 14:30:45). - 📝
Дата(Год, Месяц, День)— создает дату из числовых компонентов. Пример:Дата(2026, 12, 31). - ➕/➖
ДобавитьМесяц(),ДобавитьДень(),ДобавитьГод()— сдвигают дату на заданный интервал.
Пример кода для расчета даты через 10 дней от текущей:
ТекущаяДата = ТекущаяДата();
ДатаЧерез10Дней = ДобавитьДень(ТекущаяДата, 10);
Сообщить("Через 10 дней будет: " + Формат(ДатаЧерез10Дней, "ДФ=dd.MM.yyyy"));
⚠️ Внимание: ФункцияДобавитьМесяц()корректно обрабатывает переход через конец месяца. Например,ДобавитьМесяц(Дата(2026, 1, 31), 1)вернет28.02.2026(а не 31.02, которого не существует).
2. Сравнение дат и проверка интервалов
Сравнение дат в 1С имеет свои особенности, особенно если одна из дат содержит время, а другая — нет. Например, Дата(2026, 5, 15) = Дата(2026, 5, 15, 0, 0, 0) вернет Ложь, потому что первая дата интерпретируется как 15.05.2026 00:00:00, а вторая — как 15.05.2026 00:00:00.000 (с миллисекундами).
Для корректного сравнения используйте функции:
- 🔍
НачалоДня(Дата)— обнуляет время до00:00:00. - 🕒
КонецДня(Дата)— устанавливает время в23:59:59. - 📊
ДатаВходитВИнтервал(Дата, ДатаНачала, ДатаКонца)— проверяет попадание даты в диапазон включительно.
Пример проверки, попадает ли дата документа в текущий квартал:
ТекущийКварталНачало = НачалоКвартала(ТекущаяДата());
ТекущийКварталКонец = КонецКвартала(ТекущаяДата());
Если ДатаВходитВИнтервал(ДатаДокумента, ТекущийКварталНачало, ТекущийКварталКонец) Тогда
Сообщить("Документ относится к текущему кварталу!");
КонецЕсли;
| Функция | Описание | Пример результата |
|---|---|---|
НачалоМесяца() | Возвращает первый день месяца с временем 00:00:00 | 01.05.2026 00:00:00 |
КонецМесяца() | Возвращает последний день месяца с временем 23:59:59 | 31.05.2026 23:59:59 |
НачалоГода() | Возвращает 1 января текущего года | 01.01.2026 00:00:00 |
ДеньНедели() | Возвращает номер дня недели (1=понедельник, 7=воскресенье) | 3 (среда) |
Чтобы избежать ошибок при сравнении дат с временем, всегда приводите их к одному формату с помощью НачалоДня() или КонецДня().
3. Расчет рабочих дней с учетом праздников
Одна из самых сложных задач — расчет рабочих дней с исключением выходных и праздников. В 1С для этого есть встроенный механизм производственного календаря, но он требует правильной настройки. Если календарь не настроен, функция ДобавитьРабочиеДни() будет игнорировать праздники!
Алгоритм расчета:
- Получите объект
Календарь(например, из справочника ПроизводственныеКалендари в ЗУП). - Используйте метод
ДобавитьРабочиеДни()с указанием календаря. - Для обратного расчета (определения количества рабочих дней между датами) используйте
РазностьДатВДнях()с флагомИстина(учитывать только рабочие дни).
Пример кода для расчета даты через 5 рабочих дней:
Календарь = Справочники.ПроизводственныеКалендари.ОсновнойКалендарь;
ТекущаяДата = ТекущаяДата();
ДатаОтгрузки = Календарь.ДобавитьРабочиеДни(ТекущаяДата, 5);
Сообщить("Отгрузка запланирована на: " + Формат(ДатаОтгрузки, "ДФ=dd.MM.yyyy"));
⚠️ Внимание: Если в вашей конфигурации не настроен производственный календарь, функция ДобавитьРабочиеДни() будет учитывать только выходные (суббота/воскресенье), но пропустит официальные праздники. Это приведет к ошибкам в расчетах!
Как проверить, настроен ли календарь в базе?
Откройте справочник ПроизводственныеКалендари (в ЗУП или ERP). Если он пуст или содержит устаревшие данные, загрузите актуальный календарь через обработку ЗагрузкаПроизводственногоКалендаря (поставляется с типовыми конфигурациями).
4. Работа с неполными датами и ошибки округления
В 1С даты без указанного времени (Дата(2026, 5, 15)) внутренне хранятся как 15.05.2026 00:00:00. Это может приводить к неожиданным результатам при операциях с интервалами. Например:
Дата1 = Дата(2026, 5, 15);
Дата2 = Дата(2026, 5, 16);
Разница = Дата2 - Дата1; // Вернет 1 (день), а не 24 (часа)!
Чтобы избежать путаницы:
- 🔄 Всегда явно указывайте, в каких единицах вы работаете (дни, часы, секунды).
- ⏱️ Для операций с временем используйте
ДобавитьСек(),ДобавитьМинуту(),ДобавитьЧас(). - 📅 Для календарных расчетов (сроки, возраст) используйте
ДобавитьДень(),РазностьДат().
Пример корректного расчета возраста в годах:
ДатаРождения = Дата(1990, 7, 20);
ТекущаяДата = ТекущаяДата();
Возраст = РазностьДат(ТекущаяДата, ДатаРождения, "Год");
Сообщить("Вам " + Возраст + " полных лет.");
Убедитесь, что все даты приведены к одному формату (с временем или без)
Проверьте наличие производственного календаря для рабочих дней
Учитывайте переходы через месяц/год (например, 31 января + 1 месяц = 28 февраля)
Тестируйте крайние случаи (29 февраля, 31 декабря)-->
5. Типовые задачи с датами в бизнес-логике
Рассмотрим реальные примеры, с которыми сталкиваются разработчики 1С в типовых конфигурациях.
5.1. Расчет срока оплаты по договору
Задача: если в договоре указан срок оплаты «5 рабочих дней», нужно рассчитать крайнюю дату оплаты с учетом праздников.
Календарь = Справочники.ПроизводственныеКалендари.ОсновнойКалендарь;
ДатаДоговора = Дата(2026, 5, 15);
СрокОплаты = Календарь.ДобавитьРабочиеДни(ДатаДоговора, 5);
Сообщить("Оплатить до: " + Формат(СрокОплаты, "ДФ=dd.MM.yyyy"));
5.2. Определение просроченных документов
Задача: найти все неоплаченные счета, у которых истек срок оплаты.
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| СчетаКлиентов.Ссылка КАК Счет,
| СчетаКлиентов.Дата КАК ДатаСчета,
| СчетаКлиентов.СрокОплаты КАК СрокОплаты
|ИЗ
| Документ.СчетКлиенту КАК СчетаКлиентов
|ГДЕ
| СчетаКлиентов.СтатусОплаты = ЗНАЧЕНИЕ(Перечисление.СтатусыОплаты.НеОплачен)
| И СчетаКлиентов.СрокОплаты < &ТекущаяДата";
Запрос.УстановитьПараметр("ТекущаяДата", НачалоДня(ТекущаяДата()));
Результат = Запрос.Выполнить();
5.3. Расчет возраста сотрудников в ЗУП
Задача: определить сотрудников, которым исполнится 50 лет в текущем году.
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Сотрудники.Ссылка КАК Сотрудник,
| Сотрудники.ДатаРождения КАК ДатаРождения
|ИЗ
| Справочник.Сотрудники КАК Сотрудники
|ГДЕ
| ГОД(Сотрудники.ДатаРождения) = &ГодРождения";
Запрос.УстановитьПараметр("ГодРождения", Год(ТекущаяДата()) - 50);
Результат = Запрос.Выполнить();
Для бизнес-задач всегда используйте календарные функции (например, ДобавитьРабочиеДни()), а не арифметические операции с днями. Это гарантирует корректную работу с праздничными и выходными днями.
6. Ошибки и «подводные камни» при работе с датами
Даже опытные разработчики 1С иногда допускают ошибки при работе с датами. Вот самые распространенные:
- 🗓️ Игнорирование времени: сравнение
Дата(2026,5,15) = Дата(2026,5,15,12,0,0)вернетЛожь. - 📅 29 февраля: попытка создать
Дата(2026, 2, 29)приведет к ошибке (2026 год не високосный). - 🔄 Переход через месяц/год:
ДобавитьМесяц(Дата(2026,1,31), 1)вернет 28.02.2026, а не 31.02. - 🏭 Отсутствие календаря:
ДобавитьРабочиеДни()без календаря проигнорирует праздники. - ⏳ Часовые пояса: при работе с распределенными базами даты могут сдвигаться из-за настроек сервера.
Пример обработки ошибки с 29 февраля:
Попытка
Дата29Февраля = Дата(2026, 2, 29); // Ошибка!
Исключение
Сообщить("Ошибка: " + ОписаниеОшибки());
// Альтернативный код для обработки
Если Не ВисокосныйГод(2026) Тогда
Дата29Февраля = Дата(2026, 3, 1); // Переносим на 1 марта
КонецЕсли;
КонецПопытки;
⚠️ Внимание: В распределенных информационных базах (с несколькими серверами 1С) настройки часовых поясов могут влиять на отображение дат в отчетах. Всегда проверяйте настройки сервера в Администрирование → Настройки программы → Часовой пояс.
7. Оптимизация запросов с датами
Запросы с фильтрацией по датам могут тормозить, если не оптимизированы. Вот ключевые правила:
- 🔍 Используйте
МЕЖДУвместо двух отдельных условий (Дата >= Начало И Дата <= Конец). - ⏱️ Для больших периодов (год и более) добавляйте индексы по полю даты в метаданных.
- 📊 Избегайте функций над полями даты в условии (например,
ГОД(Документ.Дата) = 2026). Вместо этого используйтеДокумент.Дата МЕЖДУ Дата(2026,1,1) И Дата(2026,12,31).
Пример оптимизированного запроса для выборки документов за текущий квартал:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Документы.Ссылка КАК Документ,
| Документы.Дата КАК Дата
|ИЗ
| Документ.ЛюбойДокумент КАК Документы
|ГДЕ
| Документы.Дата МЕЖДУ &НачалоКвартала И &КонецКвартала";
Запрос.УстановитьПараметр("НачалоКвартала", НачалоКвартала(ТекущаяДата()));
Запрос.УстановитьПараметр("КонецКвартала", КонецКвартала(ТекущаяДата()));
Результат = Запрос.Выполнить();
| Плохой вариант | Оптимизированный вариант | Причина |
|---|---|---|
ГОД(Дата) = 2026 | Дата МЕЖДУ Дата(2026,1,1) И Дата(2026,12,31) | Функция ГОД() не использует индексы |
ДеньНедели(Дата) = 1 | Дата В (&Понедельники) (где &Понедельники — массив дат) | Вычисление ДеньНедели() для каждой строки |
МЕСЯЦ(Дата) = 5 | Дата МЕЖДУ Дата(2026,5,1) И Дата(2026,5,31) | Фильтрация по месяцу без индексов |
8. Продвинутые техники: кастомные календари и сложные расчеты
Если стандартного производственного календаря недостаточно (например, у компании индивидуальный график работы), можно создать кастомный календарь:
Алгоритм:
- Создайте справочник Календари с реквизитом
Дата(тип Дата) иЯвляетсяРабочим(тип Булево). - Заполните справочник данными (можно автоматизировать загрузку из Excel).
- Напишите функцию для расчета рабочих дней с учетом кастомного календаря.
Пример функции для добавления рабочих дней с кастомным календарем:
Функция ДобавитьРабочиеДниКастом(НачальнаяДата, КоличествоДней, Календарь)
ТекущаяДата = НачальнаяДата;
ОсталосьДней = КоличествоДней;
Пока ОсталосьДней > 0 Цикл
ТекущаяДата = ДобавитьДень(ТекущаяДата, 1);
Если Календарь.НайтиПоРеквизиту("Дата", ТекущаяДата).ЯвляетсяРабочим Тогда
ОсталосьДней = ОсталосьДней - 1;
КонецЕсли;
КонецЦикла;
Возврат ТекущаяДата;
КонецФункции
Для сложных расчетов (например, «3 рабочих дня, но не раньше 5 календарных») комбинируйте стандартные функции 1С с кастомной логикой:
ДатаОплаты = ДобавитьДень(ТекущаяДата, 5); // Минимальный срок - 5 календарных дней
ДатаОплаты = Максимум(ДатаОплаты, Календарь.ДобавитьРабочиеДни(ТекущаяДата, 3));
Как автоматизировать загрузку производственного календаря?
В типовых конфигурациях (ЗУП, ERP) есть обработка ЗагрузкаПроизводственногоКалендаря.epf. Она позволяет импортировать календарь из файла .xml или .xlsx, скачанного с сайта КонсультантПлюс или других официальных источников.
FAQ: Частые вопросы по работе с датами в 1С
Как в 1С получить последний день месяца?
Используйте функцию КонецМесяца():
ПоследнийДень = КонецМесяца(ТекущаяДата()); // Вернет 31.05.2026 23:59:59 для мая 2026
Если нужно только дату без времени, оберните в НачалоДня():
ПоследнийДень = НачалоДня(КонецМесяца(ТекущаяДата()));
Почему Дата(2026, 2, 29) не вызывает ошибку, если 2026 год не високосный?
В 1С при создании даты с несуществующим днем (например, 29 февраля в невисокосный год) автоматически происходит корректировка на последний валидный день месяца. То есть Дата(2026, 2, 29) преобразуется в 28.02.2026.
Чтобы избежать скрытых ошибок, всегда проверяйте високосность года функцией ВисокосныйГод():
Если НЕ ВисокосныйГод(2026) Тогда
Сообщить("Ошибка: 29 февраля в 2026 году не существует!");
КонецЕсли;
Как рассчитать количество полных месяцев между двумя датами?
Используйте функцию РазностьДат() с параметром "Месяц":
ДатаНачала = Дата(2026, 5, 15);
ДатаОкончания = Дата(2026, 3, 20);
ПолныхМесяцев = РазностьДат(ДатаОкончания, ДатаНачала, "Месяц"); // Вернет 10
Обратите внимание: если день окончания меньше дня начала (например, с 15.05 по 10.06), функция вернет целую часть месяцев (в данном случае 0).
Можно ли в 1С работать с миллисекундами?
Да, тип Дата в 1С хранит время с точностью до миллисекунд. Например:
ТочноеВремя = ТекущаяДатаСек(); // Включает миллисекунды
Миллисекунды = Миллисекунда(ТочноеВремя); // Вернет значение от 0 до 999
Для операций с миллисекундами используйте функции ДобавитьМиллисекунду() и РазностьДат() с параметром "Миллисекунда".
Как в запросе отфильтровать документы по дню недели?
Используйте функцию ДЕНЬНЕДЕЛИ() в языке запросов:
Запрос.Текст =
"ВЫБРАТЬ
| Документы.Ссылка КАК Документ
|ИЗ
| Документ.ЛюбойДокумент КАК Документы
|ГДЕ
| ДЕНЬНЕДЕЛИ(Документы.Дата) = 1"; // 1 = понедельник
Для ускорения работы с большими базами предварительно отфильтруйте диапазон дат:
ГДЕ
Документы.Дата МЕЖДУ &НачалоПериода И &КонецПериода
И ДЕНЬНЕДЕЛИ(Документы.Дата) = 1