К статье
Практический материал

Разработка и Конфигурация · 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. 1
    Определите, нужны все дни или только рабочие, и есть ли в конфигурации производственный календарь
  2. 2
    Выберите способ: запрос к календарю, генерация в запросе или цикл в коде
  3. 3
    Приведите границы периода к началу дня, чтобы исключить время
  4. 4
    Получите таблицу и проверьте количество строк и первую/последнюю дату
  5. 5
    При необходимости передайте таблицу в запрос как временную таблицу

Даты из периодических регистров и документов

Иногда «таблица дат» нужна не как календарь, а как список дат, когда что-то происходило: дни с продажами, дни с проведёнными документами, дни работы сотрудника. Тогда источником служит сам регистр или журнал документов с группировкой по периоду.

ВЫБРАТЬ РАЗЛИЧНЫЕ

| НАЧАЛОПЕРИОДА(Продажи.Период, ДЕНЬ) КАК Дата

|ИЗ

| РегистрНакопления.Продажи КАК Продажи

|ГДЕ

| Продажи.Период МЕЖДУ &НачалоПериода И &КонецПериода

Функция НАЧАЛОПЕРИОДА отсекает время, а РАЗЛИЧНЫЕ убирает дубли. Аналогично можно группировать по неделе, месяцу или кварталу — это стандартный приём для отчётов с динамикой.

Если таблица дат нужна регулярно в разных отчётах, имеет смысл один раз создать собственный регистр сведений «КалендарныеДаты» и заполнить его на десятилетие вперёд служебной обработкой. Запросы к готовой таблице работают заметно быстрее, чем генерация через декартово произведение чисел.
ПЗ
Практическое замечание

Проверка результата и типичные ошибки

После получения таблицы дат обязательно проверьте три вещи: количество строк соответствует длине периода, первая и последняя даты совпадают с границами, во всех строках время равно 00:00:00. Наличие времени — самая коварная ошибка: соединение такой таблицы с периодами регистров даст частичные совпадения или пустой результат.

Типичные ошибки
01
Передача в параметр даты со временем — в таблице появляется время, соединения с периодами регистров перестают работать
02
Отсутствие условия по конечной дате при генерации через числа — лишние сотни строк за пределами периода
03
Использование производственного календаря без проверки его заполнения — пустой результат без сообщения об ошибке
04
Прибавление дней через ДОБАВИТЬКДАТЕ в цикле кода вместо 86400 секунд — лишние вызовы и путаница с типами
05
Забытый РАЗЛИЧНЫЕ при выборке дат из регистра — дубли строк раздувают итоговую выборку
Контрольные точки

Как проверить результат

Количество строк
Норма

Равно числу дней в периоде включительно

Тревожный признак

Меньше или больше — сбиты границы или фильтр

Время в поле даты
Норма

Все значения 00:00:00

Тревожный признак

Есть время — соединения будут работать неправильно

Первая и последняя даты
Норма

Совпадают с границами периода

Тревожный признак

Сдвиг на день — ошибка в условиях сравнения

Данные календаря
Норма

Регистр заполнен на весь нужный год

Тревожный признак

Пустые участки периода без строк

Частые вопросы

Как получить таблицу дат без производственного календаря?

Используйте генерацию в запросе через таблицу чисел с функцией ДОБАВИТЬКДАТЕ или цикл в коде с прибавлением 86400 секунд. Оба способа не зависят от состава конфигурации и работают на любой базе.

Как получить только рабочие дни недели без календаря?

В коде проверяйте ДеньНедели(Дата) и пропускайте субботу и воскресенье. Учтите, что праздничные и перенесённые дни таким способом не отловить — для них нужен заполненный производственный календарь.

Почему соединение таблицы дат с регистром не находит совпадений?

Чаще всего в таблице дат присутствует время, а периоды регистра хранятся с точностью до секунды. Приведите даты к началу дня функцией НАЧАЛОПЕРИОДА с параметром ДЕНЬ или НачалоДня() в коде.

Как получить таблицу месяцев вместо дней?

Теми же способами, но с шагом МЕСЯЦ в ДОБАВИТЬКДАТЕ или группировкой НАЧАЛОПЕРИОДА(Период, МЕСЯЦ) при выборке из регистра. Количество строк в таблице чисел при этом можно сократить.

Можно ли сгенерировать даты в СКД без кода?

Да, в запросе набора данных СКД работает тот же приём с таблицей чисел и ДОБАВИТЬКДАТЕ. Параметры периода передаются стандартно через &НачалоПериода и &КонецПериода, а левое соединение с данными обеспечит строки по дням без движений.

Что быстрее: генерация в запросе или готовый регистр с датами?

Запрос к заранее заполненной таблице быстрее и проще для оптимизатора СУБД. Генерация через декартово произведение приемлема для разовых отчётов, но при интенсивном использовании лучше создать служебный регистр с календарём.

Итог

Что важно запомнить

Встроенной функции генерации дат в запросах 1С нет. Используйте производственный календарь для рабочих дней, таблицу чисел с ДОБАВИТЬКДАТЕ для генерации в запросе и цикл с 86400 секундами в коде. Всегда приводите даты к началу дня и проверяйте границы периода и количество строк.