При написании запросов в системе «1С:Предприятие» разработчики часто сталкиваются с необходимостью динамического формирования даты. Одной из самых распространенных ошибок является попытка передать функции или параметры внутрь конструкции ДАТАВРЕМЯ. Проанализируем, почему это происходит, и рассмотрим корректные способы решения данной задачи.
Рассмотрим ситуацию, с которой столкнулся автор темы. В коде запроса была сделана попытка вычислить 31 декабря текущего года, используя следующий синтаксис:
ДАТАВРЕМЯ(ГОД(&НачалоПериода), 12, 31)
Система выдает ошибку «Неверные параметры», указывая именно на использование функции внутри скобок. Разберем по шагам, в чем здесь подвох. В языке запросов 1С конструкция ДАТАВРЕМЯ не является функцией в классическом понимании (как, например, функция Дата() во встроенном языке). Это так называемый литерал — жестко заданная константа, которая вычисляется еще на этапе подготовки запроса к выполнению.
Важные особенности литерала ДАТАВРЕМЯ:
&НачалоПериода).ГОД(), МЕСЯЦ() и т.д.).Проанализируем ситуацию: когда платформа видит ДАТАВРЕМЯ, она ожидает увидеть структуру вида ДАТАВРЕМЯ(2023, 12, 31, 23, 59, 59). Любые отклонения от этого формата приводят к синтаксической ошибке. Это фундаментальное различие между языком запросов и встроенным языком 1С, где динамическое создание даты из переменных — обычное дело.
Если наша задача — получить начало или конец какого-то периода относительно имеющейся даты (параметра), нам не нужно пытаться «собрать» дату по частям. В языке запросов предусмотрены мощные функции НАЧАЛОПЕРИОДА и КОНЕЦПЕРИОДА.
Посмотрим на пример. Если нам нужно условие, проверяющее попадание даты в границы текущего года, вместо ошибочного конструирования 31 декабря, мы можем написать:
ГДЕ
&ДатаНачала МЕЖДУ &НачалоПериода И КОНЕЦПЕРИОДА(&НачалоПериода, ГОД)
Функция КОНЕЦПЕРИОДА принимает два параметра: саму дату и тип периода (ДЕНЬ, НЕДЕЛЯ, МЕСЯЦ, КВАРТАЛ, ГОД). Она возвращает полноценное значение типа Дата, соответствующее последней секунде указанного периода. Это наиболее производительный и правильный с точки зрения синтаксиса способ.
Профессиональная разработка в 1С подразумевает правило: «Все, что можно вычислить заранее до запроса, должно быть вычислено заранее». Если вам нужно сравнить поле таблицы с концом года, гораздо эффективнее передать это значение в запрос как готовый параметр.
Рассмотрим, как это выглядит в коде модуля:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Данные.Ссылка
|ИЗ
| Справочник.Номенклатура КАК Данные
|ГДЕ
| Данные.ДатаСоздания < &КонецГода";
// Вычисляем дату во встроенном языке
КонецГодаДляЗапроса = КонецГода(Объект.НачалоПериода);
// Передаем готовую дату в параметр
Запрос.УстановитьПараметр("КонецГода", КонецГодаДляЗапроса);
Преимущества этого метода:
Иногда возникают задачи сложнее, чем просто получение конца года. Например, в Сообщении 3 автор приводит пример проверки «Дня рождения» контрагента, который должен попасть в произвольный период. Здесь стандартного КОНЕЦПЕРИОДА недостаточно, так как нужно «перенести» дату рождения в текущий год.
Разберем рабочий пример реализации такого переноса даты внутри запроса:
ВЫБРАТЬ
ЗначенияСвойствОбъектов.Значение КАК ДеньРождения,
ЗначенияСвойствОбъектов.Объект КАК Контрагент
ИЗ
РегистрСведений.ЗначенияСвойствОбъектов КАК ЗначенияСвойствОбъектов
ГДЕ
(ДОБАВИТЬКДАТЕ(ВЫРАЗИТЬ(ЗначенияСвойствОбъектов.Значение КАК ДАТА),
ГОД,
ГОД(&НачалоПериода) - ГОД(ВЫРАЗИТЬ(ЗначенияСвойствОбъектов.Значение КАК ДАТА)))
МЕЖДУ &НачалоПериода И &КонецПериода
ИЛИ
ДОБАВИТЬКДАТЕ(ВЫРАЗИТЬ(ЗначенияСвойствОбъектов.Значение КАК ДАТА),
ГОД,
ГОД(&КонецПериода) - ГОД(ВЫРАЗИТЬ(ЗначенияСвойствОбъектов.Значение КАК ДАТА)))
МЕЖДУ &НачалоПериода И &КонецПериода)
И ЗначенияСвойствОбъектов.Свойство.Код = "000000003"
Проанализируем эту конструкцию:
ВЫРАЗИТЬ(... КАК ДАТА). Это необходимо, если поле Значение имеет составной тип или тип ЛюбаяСсылка, чтобы система понимала, что мы работаем именно с датой.ГОД(&НачалоПериода) - ГОД(ДатаРождения) вычисляет разницу в годах между текущим периодом и годом рождения.ДОБАВИТЬКДАТЕ прибавляет эту разницу к дате рождения, тем самым «виртуально» перемещая день рождения в текущий год для сравнения.Важное замечание: Будьте осторожны с использованием сложных вычислений в секции ГДЕ. Если таблица содержит миллионы записей, такие вычисления для каждой строки могут существенно замедлить отчет, так как индексы по полю даты перестанут использоваться.
Подводя итог, можно выделить следующие правила работы с датами в 1С:
1. ДАТАВРЕМЯ — это константа. Пишем туда только цифры: ДАТАВРЕМЯ(2024, 1, 1).
2. Если нужно начало или конец периода от даты-параметра — используем НАЧАЛОПЕРИОДА(&Дата, ГОД).
3. Если нужно прибавить день, месяц или год — используем ДОБАВИТЬКДАТЕ(ПолеДата, МЕСЯЦ, 1).
4. Если нужно проверить разницу между двумя датами — используем РАЗНОСТЬДАТ(Дата1, Дата2, ДЕНЬ).
5. Во всех остальных случаях старайтесь подготовить все необходимые даты во встроенном языке и передать их в запрос готовыми параметрами. Это застрахует вас от синтаксических ошибок и проблем с производительностью базы данных.