Приведение значений к типу Дата в языке запросов 1С: что умеет ВЫРАЗИТЬ и где нужны другие приёмы

· 9 мин

Содержание

ВЫРАЗИТЬ(Поле КАК ДАТА) в запросе 1С оставляет от поля только значения типа Дата: если в поле лежит дата — она и вернётся, если что-то другое (строка, число, ссылка) — вернётся NULL. Это приведение типа, а не разбор текста. Превратить строку «15.01.2024» в дату оператор Выразить не умеет: для этого дату собирают из чисел функцией ДОБАВИТЬКДАТЕ или преобразуют строку уже в коде, после выполнения запроса.

На практике ВЫРАЗИТЬ КАК ДАТА нужен в трёх ситуациях: поле составного типа (дополнительные реквизиты, значения свойств, субконто), временная таблица, куда должна попасть колонка строго типа Дата, и объединение запросов, где тип колонки «расплывается». Ниже — синтаксис, рабочие примеры для каждого случая и ошибки, на которых чаще всего спотыкаются.

Как устроен оператор ВЫРАЗИТЬ для типа Дата

Общий вид оператора:

ВЫРАЗИТЬ(<Выражение> КАК <Тип>)

В качестве типа указывают БУЛЕВО, ЧИСЛО, СТРОКА, ДАТА или имя ссылочной таблицы (Справочник.Контрагенты, Документ.РеализацияТоваровУслуг). У строки и числа есть уточнения — СТРОКА(100), ЧИСЛО(15, 2). У ДАТА уточнений нет: состав даты (только дата, только время, дата и время) в ВЫРАЗИТЬ не задаётся.

Что происходит при выполнении:

  • 📅 значение уже имеет тип Дата — возвращается без изменений, вместе со временем;
  • 🚫 значение другого типа — результат NULL;
  • ⚪ значение NULL — остаётся NULL.

Отсюда главное правило: ВЫРАЗИТЬ ничего не вычисляет и не распознаёт. Он сужает тип. Если поле и так описано в метаданных как Дата (например, Документ.Дата или реквизит ДатаОплаты с типом Дата), оборачивать его в ВЫРАЗИТЬ бессмысленно — запрос станет только длиннее.

Ключевые слова языка запросов регистронезависимы: ВЫРАЗИТЬ, Выразить и CAST в английском синтаксисе равнозначны. В примерах ниже — заглавные буквы, так их проще отличить от имён полей.

Строка с датой в запросе: почему ВЫРАЗИТЬ не подходит и как быть

Самый частый вопрос по теме — как в запросе выразить строку как дату. Например, во внешней таблице или в реквизите типа Строка хранится «20240115» или «15.01.2024». Запрос вида ВЫРАЗИТЬ(Т.ДатаСтрокой КАК ДАТА) не даст нужной даты: строка — не дата, и приведения текста к дате язык запросов не предусматривает.

Рабочих путей три.

Способ 1. Преобразовать строку в коде после запроса

Самый надёжный вариант. Запрос выбирает строку как есть, а встроенный язык превращает её в дату. Конструктор Дата() принимает строку из цифр в порядке «ГГГГММДДччммсс»:

// Строка вида «20240115»
ДатаЗначение = Дата(Выборка.ДатаСтрокой + "000000");

// Строка вида «15.01.2024»
С = Выборка.ДатаСтрокой;
ДатаЗначение = Дата(Сред(С, 7, 4) + Сред(С, 4, 2) + Сред(С, 1, 2) + "000000");

Если в данных бывает мусор (пустые строки, «31.02.2024», лишние пробелы), оберните преобразование в Попытка … Исключение. Иначе первая же кривая строка остановит обработку с ошибкой преобразования к типу Дата.

Попытка
    ДатаЗначение = Дата(Сред(С, 7, 4) + Сред(С, 4, 2) + Сред(С, 1, 2) + "000000");
Исключение
    ДатаЗначение = Дата(1, 1, 1); // пустая дата
КонецПопытки;

Для строк в формате XML («2024-01-15T00:00:00») подходит XMLЗначение(Тип("Дата"), С).

Способ 2. Подготовить таблицу значений и передать её в запрос

Если дата нужна внутри запроса — для отбора или соединения — разберите строки заранее. Сформируйте ТаблицаЗначений с колонкой типа Дата, заполните её в коде и передайте параметром во временную таблицу:

Запрос.УстановитьПараметр("ВнешниеДанные", ТаблицаСДатами);
Запрос.Текст =
"ВЫБРАТЬ
|    Т.Код КАК Код,
|    Т.ДатаДокумента КАК ДатаДокумента
|ПОМЕСТИТЬ ВТ_Внешние
|ИЗ
|    &ВнешниеДанные КАК Т";

Колонке таблицы значений задайте тип явно: Новый ОписаниеТипов("Дата"). Без описания типа запрос не сможет поместить её во временную таблицу.

Способ 3. Собрать дату из чисел прямо в запросе

Если год, месяц и день хранятся отдельными числовыми полями, дату можно построить без кода. Функция ДАТАВРЕМЯ() тут не поможет: в запросе она принимает только числовые константы, а не поля. Поэтому берут пустую дату и прибавляют к ней периоды:

ВЫБРАТЬ
    ДОБАВИТЬКДАТЕ(
        ДОБАВИТЬКДАТЕ(
            ДОБАВИТЬКДАТЕ(ДАТАВРЕМЯ(1, 1, 1), ГОД, Т.Год - 1),
        МЕСЯЦ, Т.Месяц - 1),
    ДЕНЬ, Т.День - 1) КАК СобраннаяДата
ИЗ
    ВТ_Периоды КАК Т

Порядок важен: сначала год, потом месяц, потом день. Проверка на примере: 01.01.0001 + 2023 года = 01.01.2024, + 0 месяцев = 01.01.2024, + 14 дней = 15.01.2024 при День = 15.

Поля составного типа: ВЫРАЗИТЬ КАК ДАТА в деле

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

ВЫБРАТЬ
    ДопРек.Ссылка КАК Контрагент,
    ВЫРАЗИТЬ(ДопРек.Значение КАК ДАТА) КАК ДатаДоговора
ИЗ
    Справочник.Контрагенты.ДополнительныеРеквизиты КАК ДопРек
ГДЕ
    ДопРек.Свойство = &СвойствоДатаДоговора
    И ВЫРАЗИТЬ(ДопРек.Значение КАК ДАТА) >= &НачалоПериода

Что даёт ВЫРАЗИТЬ в этом примере:

  1. Колонка ДатаДоговора в результате имеет тип Дата, а не составной. С ней корректно работают сортировка, группировка и вывод в отчёт.
  2. Сравнение >= &НачалоПериода идёт по датам, а строки и ссылки из колонки Значение отсекаются — для них ВЫРАЗИТЬ вернул NULL, а сравнение с NULL ложно.
  3. При помещении во временную таблицу колонка занимает меньше места и её можно проиндексировать.

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

Как проверить тип, не теряя строки

Иногда нужна не сама дата, а понимание, сколько записей содержат дату. Для этого служит пара ТИПЗНАЧЕНИЯ и ТИП:

ВЫБОР
    КОГДА ТИПЗНАЧЕНИЯ(ДопРек.Значение) = ТИП(ДАТА)
        ТОГДА ВЫРАЗИТЬ(ДопРек.Значение КАК ДАТА)
    ИНАЧЕ ДАТАВРЕМЯ(1, 1, 1)
КОНЕЦ КАК ДатаИлиПусто

Так в колонке не будет NULL: вместо него — пустая дата 01.01.0001. Короче то же самое записывается через ЕСТЬNULL(ВЫРАЗИТЬ(ДопРек.Значение КАК ДАТА), ДАТАВРЕМЯ(1, 1, 1)).

Объединение запросов

При ОБЪЕДИНИТЬ ВСЕ тип колонки берётся из всех частей. Если в первой части поле — дата, а во второй вы подставили NULL или строку-заглушку, тип колонки становится составным. Подставляйте во вторую часть ДАТАВРЕМЯ(1, 1, 1) и, если поле приходит из составного реквизита, оборачивайте его в ВЫРАЗИТЬ(… КАК ДАТА) в обеих частях.

Формат даты, параметры и литералы

В языке запросов нет функции форматирования даты: Формат() — функция встроенного языка, в тексте запроса её не вызвать. Внешний вид даты задают уже после выборки.

ЗадачаГде решатьКак
Вывести «15.01.2024»КодФормат(Д, "ДФ=dd.MM.yyyy")
Вывести «15 января 2024 г.»КодФормат(Д, "ДЛФ=DD")
Строка «20240115» для файла обменаКодФормат(Д, "ДФ=yyyyMMdd")
Формат колонки в отчёте СКДСхема компоновкиПараметр оформления поля «Формат»
Дата-константа в запросеЗапросДАТАВРЕМЯ(2024, 1, 15)
Дата со временем в запросеЗапросДАТАВРЕМЯ(2024, 1, 15, 23, 59, 59)

Параметры с датой

Дату в запрос лучше передавать параметром, а не литералом в тексте:

Запрос.УстановитьПараметр("НачалоПериода", НачалоДня(ДатаНачала));
Запрос.УстановитьПараметр("КонецПериода", КонецДня(ДатаОкончания));

Не передавайте дату строкой: УстановитьПараметр("НачалоПериода", "01.01.2024") положит в параметр строку, и сравнение с полем типа Дата не отберёт ничего. Тип параметра должен быть Дата — Дата(2024, 1, 1) или значение из реквизита формы.

Функции, которые работают с датой внутри запроса

Когда дата уже приведена к нужному типу, с ней можно работать прямо в запросе:

  • 📆 НАЧАЛОПЕРИОДА(Дата, ДЕНЬ) и КОНЕЦПЕРИОДА(Дата, МЕСЯЦ) — границы периода;
  • ➕ ДОБАВИТЬКДАТЕ(Дата, ДЕНЬ, 10) — сдвиг на 10 дней;
  • ↔️ РАЗНОСТЬДАТ(Дата1, Дата2, ДЕНЬ) — разница в днях;
  • 🔢 ГОД(), КВАРТАЛ(), МЕСЯЦ(), ДЕНЬ(), ДЕНЬНЕДЕЛИ() — части даты числом.

Пример: сколько дней прошло с даты договора из дополнительного реквизита до текущей даты, переданной параметром:

РАЗНОСТЬДАТ(ВЫРАЗИТЬ(ДопРек.Значение КАК ДАТА), &ТекущаяДата, ДЕНЬ) КАК ДнейСДоговора

Скорость запросов с датами

Даты чаще всего попадают в условия отбора, и именно там теряется скорость.

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

ГДЕ
    Док.Дата МЕЖДУ &НачалоДня И &КонецДня

Границы посчитайте в коде через НачалоДня() и КонецДня().

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

Виртуальные таблицы фильтруйте параметрами. Для регистров отбор по периоду передают в параметры виртуальной таблицы (Остатки(&Дата, …), Обороты(&Начало, &Конец, …)), а не в ГДЕ после неё. Иначе регистр сначала посчитает обороты за всё время.

Индексируйте временные таблицы по дате, если по ней соединяете. Колонка, приведённая через ВЫРАЗИТЬ к типу Дата, индексируется так же, как обычная: ИНДЕКСИРОВАТЬ ПО ДатаДоговора. Для маленьких таблиц (сотни строк) индекс не нужен — выигрыша не будет.

Проверяйте скорость на копии базы с реальным объёмом данных. Запрос, который на демобазе выполняется мгновенно, на рабочей базе с миллионами записей может работать минутами.

Проверьте себя
Что вернёт ВЫРАЗИТЬ(Поле КАК ДАТА), если в поле лежит строка «15.01.2024»?
Можно ли в запросе написать ДАТАВРЕМЯ(Т.Год, Т.Месяц, 1)?
Где задают формат вывода «ДФ=dd.MM.yyyy»?

Ошибки при приведении к дате и как их распознать

Сводка того, что ломается чаще всего, и как это выглядит со стороны.

СимптомПричинаЧто сделать
Запрос возвращает пустой результат, хотя данные естьПараметр передан строкой, а не датойПередать Дата(2024, 1, 1), а не “01.01.2024”
Пропали документы последнего дня периодаКонец периода передан как 00:00:00Передавать КонецДня(ДатаОкончания)
В колонке даты вместо значений — пустые ячейкиВЫРАЗИТЬ вернул NULL: в составном поле не датаПроверить данные через ТИПЗНАЧЕНИЯ, заменить NULL через ЕСТЬNULL
Ошибка синтаксиса при ДАТАВРЕМЯ с полямиФункция в запросе принимает только константыСобрать дату через ДОБАВИТЬКДАТЕ
Из строк получаются NULLВЫРАЗИТЬ не разбирает текстПреобразовать строку в коде через Дата()
Запрос с Формат() не проходит проверку синтаксисаФормат() есть только во встроенном языкеФорматировать после выборки или в СКД

Отдельная ловушка — отбор по NULL. Условие ВЫРАЗИТЬ(Поле КАК ДАТА) <> ДАТАВРЕМЯ(1, 1, 1) не вернёт строки, где в поле не дата: сравнение NULL с чем угодно ложно. Если нужны все записи без заполненной даты, пишите явно: ВЫРАЗИТЬ(Поле КАК ДАТА) ЕСТЬ NULL ИЛИ ВЫРАЗИТЬ(Поле КАК ДАТА) = ДАТАВРЕМЯ(1, 1, 1).

Ещё одна мелочь, которая съедает время: часовые пояса. Даты в запросе — это даты сервера базы без пояса. Если код на клиенте подставляет ТекущаяДата(), а сервер в другом поясе, граница «сегодня» сдвигается. Для бизнес-логики используйте ТекущаяДатаСеанса() на сервере.

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

Можно ли через ВЫРАЗИТЬ превратить число в дату?
Нет. ВЫРАЗИТЬ не переводит число в дату. Если число — это год, месяц или количество дней, прибавьте его к пустой дате через ДОБАВИТЬКДАТЕ.
Нужен ли ВЫРАЗИТЬ для реквизита, у которого в метаданных тип Дата?
Нет. Поле уже имеет тип Дата, приведение ничего не изменит. ВЫРАЗИТЬ нужен только для полей составного типа и выражений с неоднозначным типом.
Как в запросе отрезать время и оставить только дату?
Используйте НАЧАЛОПЕРИОДА(Поле, ДЕНЬ) в списке полей выборки. В условии ГДЕ лучше отбирать диапазоном от начала до конца дня.
Как записать пустую дату в тексте запроса?
ДАТАВРЕМЯ(1, 1, 1) — это 01.01.0001 00:00:00, пустое значение типа Дата в 1С. Его же подставляют вместо NULL через ЕСТЬNULL.
Работает ли ВЫРАЗИТЬ КАК ДАТА в запросе схемы компоновки данных?
Да, в наборе данных «Запрос» используется тот же язык запросов. Формат вывода колонки при этом задают в оформлении поля схемы, а не в запросе.