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

Разработка и Конфигурация · 10 мин

1С запрос: как получить день из даты

Как в запросе 1С получить день из даты: функция ДЕНЬ(), НАЧАЛОПЕРИОДА, группировка по дням, фильтрация и типичные ошибки. Примеры кода на языке запросов.

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

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

Ниже разобраны все три сценария с примерами кода, особенностями составных типов, влиянием на производительность и типичными ошибками, которые приводят к неверным итогам или падению запроса.

Функция ДЕНЬ(): извлечение номера дня из даты

Функция ДЕНЬ() — самый прямой ответ на запрос «дата как день». Она принимает выражение типа «Дата» и возвращает целое число — день месяца. Синтаксис предельно прост:

ВЫБРАТЬ

ДЕНЬ(РеализацияТоваровУслуг.Дата) КАК ДеньМесяца,

РеализацияТоваровУслуг.Ссылка

ИЗ

Документ.РеализацияТоваровУслуг КАК РеализацияТоваровУслуг

Для документа, проведённого 15 марта в 14:32:07, результат будет число 15. Время полностью игнорируется, как и месяц с годом. Это важно: если нужно различать 15 марта и 15 апреля, одной функции ДЕНЬ() недостаточно — потребуется комбинация с МЕСЯЦ() и ГОД() или приведение к началу суток.

Результат функции имеет числовой тип, поэтому его можно использовать в вычислениях, сравнениях и условиях ВЫБОР. Например, так выделяют документы последней декады месяца:

ВЫБОР

КОГДА ДЕНЬ(Документ.Дата) > 20

ТОГДА "Последняя декада"

ИНАЧЕ "Начало месяца"

КОНЕЦ КАК ПериодМесяца

НАЧАЛОПЕРИОДА: приведение даты к началу суток

Когда требуется «дата без времени», функция ДЕНЬ() не подходит — она теряет месяц и год. Правильный инструмент — НАЧАЛОПЕРИОДА(<Выражение>, ДЕНЬ). Она возвращает дату того же дня, но со временем 00:00:00:

ВЫБРАТЬ

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

СУММА(Продажи.СуммаОборот) КАК Сумма

ИЗ

РегистрНакопления.Продажи.Обороты(, , , ) КАК Продажи

СГРУППИРОВАТЬ ПО

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

Такой приём стандартен для отчётов «продажи по дням», «поступления по дням» и подобных. Две записи, сделанные в 09:15 и 18:40 одного календарного дня, попадут в одну группу, потому что после приведения обе даты равны полуночи этого дня.

Обратная функция — КОНЕЦПЕРИОДА(<Выражение>, ДЕНЬ) — возвращает 23:59:59 того же дня. Она полезна для фильтрации диапазонов: условие Период <= КОНЕЦПЕРИОДА(&Дата, ДЕНЬ) гарантированно захватит все записи выбранного дня независимо от времени.

bsl
Запрос.Текст = "ВЫБРАТЬ НАЧАЛОПЕРИОДА(Т.Дата, ДЕНЬ) КАК День ИЗ Документ.Заказ КАК Т";

Группировка данных регистров по дням

Для регистров накопления и сведений с периодичностью есть более эффективный путь, чем ручное приведение дат. Виртуальные таблицы оборотов умеют группировать по периодичности сами — достаточно указать параметр периодичности при обращении к таблице:

ВЫБРАТЬ

ПродажиОбороты.Период КАК День,

СУММА(ПродажиОбороты.КоличествоОборот) КАК Количество

ИЗ

РегистрНакопления.Продажи.Обороты(&Начало, &Конец, День, ) КАК ПродажиОбороты

СГРУППИРОВАТЬ ПО

ПродажиОбороты.Период

УПОРЯДОЧИТЬ ПО

День

Параметр День в позиции периодичности указывает платформе агрегировать обороты на уровне суток. Поле Период в результате уже содержит начало каждого дня — дополнительно применять НАЧАЛОПЕРИОДА не нужно. Такой запрос работает быстрее, чем выборка всех записей с последующей группировкой, потому что агрегация выполняется на стороне СУБД по индексированным данным.

Допустимые значения периодичности: Период (как есть), Запись, Секунда, День, Неделя, Месяц, Квартал, Год, Регистратор, Авто. Для задачи «по дням» используется именно День.

Три способа работы с днём

ДЕНЬ(Дата)

Нужен только номер дня месяца (1–31) для условий и вычислений. Не подходит для группировки по календарным дням.

НАЧАЛОПЕРИОДА(Дата, ДЕНЬ)

Нужно свести произвольные даты-время к началу суток и сгруппировать. Универсально, но требует вычислений над каждой строкой.

Периодичность День в виртуальной таблице

Нужна агрегация оборотов регистра по дням. Самый быстрый вариант, но работает только с виртуальными таблицами регистров.

Фильтрация записей за конкретный день

Частая подзадача — отобрать документы или записи за один календарный день, когда пользователь указал только дату без времени. Наивное сравнение Документ.Дата = &Дата почти никогда не сработает, потому что у документов время отлично от 00:00:00.

Правильные варианты фильтра:

ГДЕ

Документ.Дата МЕЖДУ НАЧАЛОПЕРИОДА(&Дата, ДЕНЬ) И КОНЕЦПЕРИОДА(&Дата, ДЕНЬ)

либо эквивалентное условие через приведение:

ГДЕ

НАЧАЛОПЕРИОДА(Документ.Дата, ДЕНЬ) = НАЧАЛОПЕРИОДА(&Дата, ДЕНЬ)

Первый вариант предпочтительнее: диапазон МЕЖДУ позволяет СУБД использовать индекс по полю даты. Второй вариант вычисляет функцию от поля таблицы для каждой строки, что на больших таблицах отключает индексацию и замедляет запрос в разы.

Составные типы, NULL и граничные случаи

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

Для защиты используйте ЕСТЬNULL или явное приведение через ВЫРАЗИТЬ(... КАК ДАТА). Учтите, что ВЫРАЗИТЬ не преобразует строку в дату — он лишь сужает составной тип, а несовместимые значения превращаются в NULL.

Отдельный нюанс — пустая дата ДАТАВРЕМЯ(1,1,1). Функция ДЕНЬ() от неё вернёт 1, а НАЧАЛОПЕРИОДА — ту же пустую дату. Если в данных возможны незаполненные даты, отфильтруйте их условием Дата <> ДАТАВРЕМЯ(1,1,1), иначе в отчёте появится загадочная группа «1 января года 1».

Типичные ошибки
01
Сравнение Дата = &Дата без учёта времени — условие не находит документы, проведённые после полуночи. Используйте диапазон от НАЧАЛОПЕРИОДА до КОНЕЦПЕРИОДА.
02
Группировка по ДЕНЬ(Дата) вместо НАЧАЛОПЕРИОДА — в одну группу сливаются одинаковые числа разных месяцев, итоги искажаются.
03
Применение функции к полю в условии ГДЕ на большой таблице — индекс не используется, запрос выполняется полным сканированием.
04
Игнорирование NULL в составных типах — часть записей молча выпадает из результата, и это замечают только при сверке итогов.

Проверка результата и производительность

После написания запроса проверьте его на контрольном примере. Возьмите один документ с известной датой и временем, выполните запрос в консоли запросов и убедитесь: ДЕНЬ() возвращает ожидаемое число, а НАЧАЛОПЕРИОДА(..., ДЕНЬ) — ту же дату со временем 00:00:00.

Для группировок сверьте итог двумя способами: сумма по дням должна сойтись с общей суммой без группировки. Если не сходится — ищите потерянные записи: NULL, пустые даты или документы за границами диапазона отбора.

По производительности ориентируйтесь на простое правило: вычисления над параметрами (константами запроса) выполняются один раз и дёшево, вычисления над полями таблицы — для каждой строки. Поэтому НАЧАЛОПЕРИОДА(&Дата, ДЕНЬ) в условии безопасна, а НАЧАЛОПЕРИОДА(Таблица.Дата, ДЕНЬ) в ГДЕ — потенциально дорогая операция.

Контрольные точки

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

Значение ДЕНЬ() для тестового документа
Норма

Число от 1 до 31, совпадает с датой документа

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

NULL, 0 или число из другого месяца — проверьте тип поля

НАЧАЛОПЕРИОДА(Дата, ДЕНЬ)
Норма

Та же календарная дата, время 00:00:00

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

Сохранилось время или дата сместилась на сутки

Сумма по дням против общей суммы
Норма

Итоги совпадают до копейки

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

Разница — ищите NULL, пустые даты и границы отбора

Время выполнения на рабочем объёме
Норма

Запрос выполняется за приемлемое время без полного сканирования

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

Резкое замедление — функция применена к полю в условии ГДЕ

FAQ: частые вопросы о работе с днём в запросах 1С

Как получить день недели из даты в запросе 1С?

В языке запросов функции дня недели нет. Выберите саму дату запросом, а затем в коде примените функцию встроенного языка ДеньНедели(Дата) — она вернёт число от 1 (понедельник) до 7 (воскресенье). Альтернатива — соединение с предварительно подготовленной таблицей календаря.

Чем ДЕНЬ() отличается от НАЧАЛОПЕРИОДА(Дата, ДЕНЬ)?

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

Почему условие Дата = &Параметр не находит документы?

Потому что дата документа содержит время, а параметр обычно задан как начало дня. Сравнение идёт с точностью до секунды. Используйте диапазон: Дата МЕЖДУ НАЧАЛОПЕРИОДА(&Параметр, ДЕНЬ) И КОНЕЦПЕРИОДА(&Параметр, ДЕНЬ).

Можно ли сгруппировать обороты регистра по дням без НАЧАЛОПЕРИОДА?

Да. Укажите периодичность День в параметрах виртуальной таблицы оборотов регистра накопления и группируйте по полю Период. Платформа выполнит агрегацию на уровне СУБД, что быстрее ручного приведения дат.

Что вернёт ДЕНЬ() для пустой даты или NULL?

Для пустой даты ДАТАВРЕМЯ(1,1,1) функция вернёт 1. Для NULL результатом будет NULL, и такая строка выпадет из условий сравнения, но попадёт в отдельную группу при группировке. Фильтруйте пустые даты явно.

Как отобрать записи за последние N дней?

Вычислите границу через ДОБАВИТЬКДАТЕ: условие вида Период >= ДОБАВИТЬКДАТЕ(НАЧАЛОПЕРИОДА(&ТекущаяДата, ДЕНЬ), ДЕНЬ, -N) отберёт записи за N полных суток, начиная с полуночи соответствующего дня.

Итог

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

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