Почему возникает ошибка Неверные параметры ДАТАВРЕМЯ в запросе 1С и как правильно собрать дату?

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

При написании запросов в системе «1С:Предприятие» разработчики часто сталкиваются с необходимостью динамического формирования даты. Одной из самых распространенных ошибок является попытка передать функции или параметры внутрь конструкции ДАТАВРЕМЯ. Проанализируем, почему это происходит, и рассмотрим корректные способы решения данной задачи.

Выясняем причину: почему ДАТАВРЕМЯ не принимает функции?

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

ДАТАВРЕМЯ(ГОД(&НачалоПериода), 12, 31)

Система выдает ошибку «Неверные параметры», указывая именно на использование функции внутри скобок. Разберем по шагам, в чем здесь подвох. В языке запросов 1С конструкция ДАТАВРЕМЯ не является функцией в классическом понимании (как, например, функция Дата() во встроенном языке). Это так называемый литерал — жестко заданная константа, которая вычисляется еще на этапе подготовки запроса к выполнению.

Важные особенности литерала ДАТАВРЕМЯ:

  1. В качестве аргументов он принимает только целые числа.
  2. В него нельзя передавать параметры (&НачалоПериода).
  3. В нем нельзя вызывать другие функции (ГОД(), МЕСЯЦ() и т.д.).
  4. Он используется для указания фиксированных дат, которые известны заранее на моменте написания программного кода.

Проанализируем ситуацию: когда платформа видит ДАТАВРЕМЯ, она ожидает увидеть структуру вида ДАТАВРЕМЯ(2023, 12, 31, 23, 59, 59). Любые отклонения от этого формата приводят к синтаксической ошибке. Это фундаментальное различие между языком запросов и встроенным языком 1С, где динамическое создание даты из переменных — обычное дело.

Решение №1: Использование стандартных функций работы с периодами

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

Посмотрим на пример. Если нам нужно условие, проверяющее попадание даты в границы текущего года, вместо ошибочного конструирования 31 декабря, мы можем написать:


ГДЕ
    &ДатаНачала МЕЖДУ &НачалоПериода И КОНЕЦПЕРИОДА(&НачалоПериода, ГОД)

Функция КОНЕЦПЕРИОДА принимает два параметра: саму дату и тип периода (ДЕНЬ, НЕДЕЛЯ, МЕСЯЦ, КВАРТАЛ, ГОД). Она возвращает полноценное значение типа Дата, соответствующее последней секунде указанного периода. Это наиболее производительный и правильный с точки зрения синтаксиса способ.

Решение №2: Вычисление даты на стороне встроенного языка (Best Practice)

Профессиональная разработка в 1С подразумевает правило: «Все, что можно вычислить заранее до запроса, должно быть вычислено заранее». Если вам нужно сравнить поле таблицы с концом года, гораздо эффективнее передать это значение в запрос как готовый параметр.

Рассмотрим, как это выглядит в коде модуля:


Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |   Данные.Ссылка
    |ИЗ
    |   Справочник.Номенклатура КАК Данные
    |ГДЕ
    |   Данные.ДатаСоздания < &КонецГода";

// Вычисляем дату во встроенном языке
КонецГодаДляЗапроса = КонецГода(Объект.НачалоПериода); 

// Передаем готовую дату в параметр
Запрос.УстановитьПараметр("КонецГода", КонецГодаДляЗапроса);

Преимущества этого метода:

Решение №3: Сложные манипуляции с помощью ДОБАВИТЬКДАТЕ

Иногда возникают задачи сложнее, чем просто получение конца года. Например, в Сообщении 3 автор приводит пример проверки «Дня рождения» контрагента, который должен попасть в произвольный период. Здесь стандартного КОНЕЦПЕРИОДА недостаточно, так как нужно «перенести» дату рождения в текущий год.

Разберем рабочий пример реализации такого переноса даты внутри запроса:


ВЫБРАТЬ
    ЗначенияСвойствОбъектов.Значение КАК ДеньРождения,
    ЗначенияСвойствОбъектов.Объект КАК Контрагент
ИЗ
    РегистрСведений.ЗначенияСвойствОбъектов КАК ЗначенияСвойствОбъектов
ГДЕ
    (ДОБАВИТЬКДАТЕ(ВЫРАЗИТЬ(ЗначенияСвойствОбъектов.Значение КАК ДАТА), 
        ГОД, 
        ГОД(&НачалоПериода) - ГОД(ВЫРАЗИТЬ(ЗначенияСвойствОбъектов.Значение КАК ДАТА))) 
    МЕЖДУ &НачалоПериода И &КонецПериода
    ИЛИ 
    ДОБАВИТЬКДАТЕ(ВЫРАЗИТЬ(ЗначенияСвойствОбъектов.Значение КАК ДАТА), 
        ГОД, 
        ГОД(&КонецПериода) - ГОД(ВЫРАЗИТЬ(ЗначенияСвойствОбъектов.Значение КАК ДАТА))) 
    МЕЖДУ &НачалоПериода И &КонецПериода)
    И ЗначенияСвойствОбъектов.Свойство.Код = "000000003"

Проанализируем эту конструкцию:

  1. Мы используем функцию ВЫРАЗИТЬ(... КАК ДАТА). Это необходимо, если поле Значение имеет составной тип или тип ЛюбаяСсылка, чтобы система понимала, что мы работаем именно с датой.
  2. Функция ГОД(&НачалоПериода) - ГОД(ДатаРождения) вычисляет разницу в годах между текущим периодом и годом рождения.
  3. Функция ДОБАВИТЬКДАТЕ прибавляет эту разницу к дате рождения, тем самым «виртуально» перемещая день рождения в текущий год для сравнения.

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

Резюме по работе с датами в запросах

Подводя итог, можно выделить следующие правила работы с датами в 1С:

1. ДАТАВРЕМЯ — это константа. Пишем туда только цифры: ДАТАВРЕМЯ(2024, 1, 1).

2. Если нужно начало или конец периода от даты-параметра — используем НАЧАЛОПЕРИОДА(&Дата, ГОД).

3. Если нужно прибавить день, месяц или год — используем ДОБАВИТЬКДАТЕ(ПолеДата, МЕСЯЦ, 1).

4. Если нужно проверить разницу между двумя датами — используем РАЗНОСТЬДАТ(Дата1, Дата2, ДЕНЬ).

5. Во всех остальных случаях старайтесь подготовить все необходимые даты во встроенном языке и передать их в запрос готовыми параметрами. Это застрахует вас от синтаксических ошибок и проблем с производительностью базы данных.

← На главную