Разработка и Конфигурация · 10 мин
Как в 1С получить таблицу дат: запросы, календарь и код
Как в 1С получить таблицу дат: запрос с периодами, производственный календарь, регистр сведений и генерация дат в коде. Примеры для 1С 8.3.
Задача получить в 1С таблицу дат возникает постоянно: при построении отчётов с разбивкой по дням, при расчёте графиков работы, при проверке непрерывности периодов, при формировании плановых начислений. Проблема в том, что в языке запросов 1С нет встроенной функции генерации последовательности дат, как, например, generate_series в PostgreSQL. Поэтому разработчики используют несколько обходных приёмов, каждый со своими ограничениями.
Выбор способа зависит от контекста: нужны ли все календарные дни или только рабочие, требуется ли таблица внутри запроса или достаточно таблицы значений в коде, какой диапазон дат и допустима ли зависимость от заполненности данных. Ниже разобраны четыре рабочих подхода с примерами кода, их плюсы, ограничения и типичные ошибки.
Какие способы получения таблицы дат существуют
Прежде чем писать код, определите, откуда могут взяться даты. В типовых конфигурациях (БП, ЗУП, УТ, ERP) уже есть механизмы, хранящие календарные данные. Если они доступны — это самый быстрый путь. Если конфигурация самописная или данных нет, придётся генерировать даты самостоятельно.
Сравнение вариантов
Даёт рабочие, выходные и праздничные дни. Требует заполненного календаря в базе, есть только в типовых конфигурациях.
Работает в любой конфигурации без данных, но запрос громоздкий и ограничен заранее заданным числом строк.
Максимально гибкий, легко фильтровать дни недели, но результат живёт только в памяти и не участвует в запросе напрямую.
Подходит, когда нужны только даты, где есть движения. Не даёт непрерывный календарь.
Важно понимать разницу: «таблица дат в запросе» и «таблица значений с датами» — разные вещи. Первая существует внутри текста запроса и может соединяться с другими данными, вторая — объект встроенного языка, который при необходимости передаётся в запрос как временная таблица.
Таблица дат из производственного календаря
В типовых конфигурациях на БСП есть регистр сведений «ДанныеПроизводственногоКалендаря» (в некоторых версиях — «РегламентированныйПроизводственныйКалендарь»). Он хранит по каждому дню его вид: рабочий, выходной, праздничный, предпраздничный. Запрос к нему — самый корректный способ получить календарь рабочих дней.
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ДанныеКалендаря.Дата КАК Дата,
| ДанныеКалендаря.ВидДня КАК ВидДня
|ИЗ
| РегистрСведений.ДанныеПроизводственногоКалендаря КАК ДанныеКалендаря
|ГДЕ
| ДанныеКалендаря.Дата МЕЖДУ &НачалоПериода И &КонецПериода
| И ДанныеКалендаря.ПроизводственныйКалендарь = &Календарь
|
|УПОРЯДОЧИТЬ ПО
| Дата";
Запрос.УстановитьПараметр("НачалоПериода", НачалоМесяца(ТекущаяДатаСеанса()));
Запрос.УстановитьПараметр("КонецПериода", КонецМесяца(ТекущаяДатаСеанса()));
ТаблицаДат = Запрос.Выполнить().Выгрузить();
Чтобы оставить только рабочие дни, добавьте условие на вид дня. В БСП виды дня — перечисление «ВидыДнейПроизводственногоКалендаря», поэтому условие выглядит как ДанныеКалендаря.ВидДня = ЗНАЧЕНИЕ(Перечисление.ВидыДнейПроизводственногоКалендаря.Рабочий).
Если нужны все календарные дни без привязки к графику, этот регистр тоже подходит: он содержит каждый день года, а не только рабочие. Ограничение одно — диапазон заполнения календаря.
Генерация дат прямо в запросе
Когда опорных данных нет, даты генерируют арифметикой: берут таблицу чисел от 0 до N и прибавляют каждое число как количество дней к начальной дате через функцию ДОБАВИТЬКДАТЕ. Источником чисел может служить любая таблица с достаточным количеством строк или искусственно построенная последовательность.
Запрос.Текст =
"ВЫБРАТЬ 0 КАК Число
|ПОМЕСТИТЬ Числа
|ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ 1
|ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ 2
|ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ 3
|ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ 4
|ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ 5
|ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ 6
|ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ 7
|ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ 8
|ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ 9
|;
|ВЫБРАТЬ
| ДОБАВИТЬКДАТЕ(&НачалоПериода, ДЕНЬ,
| Единицы.Число + Десятки.Число 10 + Сотни.Число 100) КАК Дата
|ПОМЕСТИТЬ ТаблицаДат
|ИЗ
| Числа КАК Единицы,
| Числа КАК Десятки,
| Числа КАК Сотни
|ГДЕ
| ДОБАВИТЬКДАТЕ(&НачалоПериода, ДЕНЬ,
| Единицы.Число + Десятки.Число 10 + Сотни.Число 100) <= &КонецПериода";
Декартово произведение трёх таблиц по 10 чисел даёт 1000 комбинаций — этого хватает почти на три года ежедневной разбивки. Для большего диапазона добавьте ещё одно соединение (тысячи). Условие по конечной дате обязательно: без него получите лишние строки за пределами периода.
Таблица дат как источник для левого соединения
Главное применение сгенерированной таблицы — дополнить периодические данные пропущенными днями. Например, отчёт по продажам должен показать все дни месяца, включая дни без продаж. Для этого таблицу дат ставят первой в левое соединение с данными регистра, а отсутствующие значения обрабатывают через ЕСТЬNULL.
Таблица значений с датами в коде
Самый простой и предсказуемый способ — обычный цикл. Он не зависит от состава конфигурации, легко читается и позволяет на ходу фильтровать дни недели или применять любую логику.
Функция ПолучитьТаблицуДат(НачалоПериода, КонецПериода)
ТаблицаДат = Новый ТаблицаЗначений;
ТаблицаДат.Колонки.Добавить("Дата", Новый ОписаниеТипов("Дата"));
ТекущаяДата = НачалоДня(НачалоПериода);
Пока ТекущаяДата <= КонецПериода Цикл
НоваяСтрока = ТаблицаДат.Добавить();
НоваяСтрока.Дата = ТекущаяДата;
ТекущаяДата = ТекущаяДата + 86400; // сутки в секундах
КонецЦикла;
Возврат ТаблицаДат;
КонецФункции
Для прибавления суток используйте константу 86400 (60 × 60 × 24) — это надёжнее, чем арифметика с функциями даты в цикле. Если нужны только будни, добавьте проверку ДеньНедели(ТекущаяДата) <= 5 внутрь цикла.
Полученную таблицу можно передать в запрос как временную таблицу через менеджер временных таблиц — так она станет полноценным источником для соединений:
Запрос.МенеджерВременныхТаблиц = Новый МенеджерВременныхТаблиц;
Запрос.УстановитьПараметр("ТаблицаДат", ТаблицаДат);
Запрос.Текст =
"ВЫБРАТЬ Т.Дата ПОМЕСТИТЬ ВТ_Даты ИЗ &ТаблицаДат КАК Т";
Запрос.Выполнить();
- 1Определите, нужны все дни или только рабочие, и есть ли в конфигурации производственный календарь
- 2Выберите способ: запрос к календарю, генерация в запросе или цикл в коде
- 3Приведите границы периода к началу дня, чтобы исключить время
- 4Получите таблицу и проверьте количество строк и первую/последнюю дату
- 5При необходимости передайте таблицу в запрос как временную таблицу
Даты из периодических регистров и документов
Иногда «таблица дат» нужна не как календарь, а как список дат, когда что-то происходило: дни с продажами, дни с проведёнными документами, дни работы сотрудника. Тогда источником служит сам регистр или журнал документов с группировкой по периоду.
ВЫБРАТЬ РАЗЛИЧНЫЕ
| НАЧАЛОПЕРИОДА(Продажи.Период, ДЕНЬ) КАК Дата
|ИЗ
| РегистрНакопления.Продажи КАК Продажи
|ГДЕ
| Продажи.Период МЕЖДУ &НачалоПериода И &КонецПериода
Функция НАЧАЛОПЕРИОДА отсекает время, а РАЗЛИЧНЫЕ убирает дубли. Аналогично можно группировать по неделе, месяцу или кварталу — это стандартный приём для отчётов с динамикой.
Если таблица дат нужна регулярно в разных отчётах, имеет смысл один раз создать собственный регистр сведений «КалендарныеДаты» и заполнить его на десятилетие вперёд служебной обработкой. Запросы к готовой таблице работают заметно быстрее, чем генерация через декартово произведение чисел.
Проверка результата и типичные ошибки
После получения таблицы дат обязательно проверьте три вещи: количество строк соответствует длине периода, первая и последняя даты совпадают с границами, во всех строках время равно 00:00:00. Наличие времени — самая коварная ошибка: соединение такой таблицы с периодами регистров даст частичные совпадения или пустой результат.
Как проверить результат
Равно числу дней в периоде включительно
Меньше или больше — сбиты границы или фильтр
Все значения 00:00:00
Есть время — соединения будут работать неправильно
Совпадают с границами периода
Сдвиг на день — ошибка в условиях сравнения
Регистр заполнен на весь нужный год
Пустые участки периода без строк
Частые вопросы
Как получить таблицу дат без производственного календаря?
Используйте генерацию в запросе через таблицу чисел с функцией ДОБАВИТЬКДАТЕ или цикл в коде с прибавлением 86400 секунд. Оба способа не зависят от состава конфигурации и работают на любой базе.
Как получить только рабочие дни недели без календаря?
В коде проверяйте ДеньНедели(Дата) и пропускайте субботу и воскресенье. Учтите, что праздничные и перенесённые дни таким способом не отловить — для них нужен заполненный производственный календарь.
Почему соединение таблицы дат с регистром не находит совпадений?
Чаще всего в таблице дат присутствует время, а периоды регистра хранятся с точностью до секунды. Приведите даты к началу дня функцией НАЧАЛОПЕРИОДА с параметром ДЕНЬ или НачалоДня() в коде.
Как получить таблицу месяцев вместо дней?
Теми же способами, но с шагом МЕСЯЦ в ДОБАВИТЬКДАТЕ или группировкой НАЧАЛОПЕРИОДА(Период, МЕСЯЦ) при выборке из регистра. Количество строк в таблице чисел при этом можно сократить.
Можно ли сгенерировать даты в СКД без кода?
Да, в запросе набора данных СКД работает тот же приём с таблицей чисел и ДОБАВИТЬКДАТЕ. Параметры периода передаются стандартно через &НачалоПериода и &КонецПериода, а левое соединение с данными обеспечит строки по дням без движений.
Что быстрее: генерация в запросе или готовый регистр с датами?
Запрос к заранее заполненной таблице быстрее и проще для оптимизатора СУБД. Генерация через декартово произведение приемлема для разовых отчётов, но при интенсивном использовании лучше создать служебный регистр с календарём.
Что важно запомнить
Встроенной функции генерации дат в запросах 1С нет. Используйте производственный календарь для рабочих дней, таблицу чисел с ДОБАВИТЬКДАТЕ для генерации в запросе и цикл с 86400 секундами в коде. Всегда приводите даты к началу дня и проверяйте границы периода и количество строк.