Как изменить формат даты в запросе 1С: отсечение времени и преобразование в строку

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

Работа с датами в языке запросов 1С — одна из самых частых задач, с которой мы сталкиваемся при разработке отчетов или механизмов выгрузки данных — для этого подойдёт универсальная выгрузка документов в XML, JSON и CSV. Часто возникает ситуация, когда в базе данных хранится полная дата со временем (тип Дата), а в результат выборки нам необходимо получить либо "чистую" дату (начало дня), либо специфическое строковое представление (например, "ДД-ММ-ГГГГ").

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

Задача 1: Получение даты без времени

Самая распространенная потребность — это отбросить время от значения даты, чтобы сгруппировать данные по дням или просто вывести красивое значение в отчет. Протестировать такие запросы поможет консоль запросов для управляемых форм (поможет универсальная консоль запросов и СКД для разработки). В обсуждении на форуме автор изначально пытался использовать сложные конструкции, но решение оказалось гораздо проще.

Рассмотрим, почему интуитивная попытка собрать дату заново через функцию ДАТАВРЕМЯ не работает с полями выборки.

Распространенная ошибка

Многие начинающие разработчики пытаются использовать конструкцию вида:

ДАТАВРЕМЯ(ГОД(Документ.Дата), МЕСЯЦ(Документ.Дата), ДЕНЬ(Документ.Дата))

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

Правильное решение: Функция НАЧАЛОПЕРИОДА

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

Пример правильного запроса:


ВЫБРАТЬ
    ЧекККМ.Ссылка КАК Ссылка,
    НАЧАЛОПЕРИОДА(ЧекККМ.Дата, ДЕНЬ) КАК ДатаДокументаБезВремени,
    ЧекККМ.СуммаДокумента
ИЗ
    Документ.ЧекККМ КАК ЧекККМ

Используя параметр ДЕНЬ, мы указываем системе, что нас интересует начало текущих суток для выбранной даты.

Задача 2: Преобразование даты в строку определенного формата

Иногда требования задачи жестче: нам нужно получить не тип Дата, а именно строку вида "DD.MM.YYYY" или "YYYY-MM-DD" непосредственно в выборке. Это часто требуется для выгрузки данных во внешние системы (например, в XML или CSV), где формат строго регламентирован (удобно через обработка выгрузки любых данных через произвольные запросы), а постобработка результата запроса нежелательна из-за больших объемов данных.

В языке запросов 1С нет функции Формат(). Поэтому нам придется "собирать" строку вручную. Рассмотрим несколько способов, предложенных опытными программистами. Также важно учитывать особенности, которые накладывает сравнение строки с представлением ссылки в запросе при дальнейшей работе с данными.

Способ А: Использование ПОДСТРОКА для маппинга цифр

Этот метод выглядит громоздким, но он эффективен, так как позволяет избежать неявных преобразований типов, которые могут вести себя по-разному в разных СУБД.

Идея заключается в том, чтобы брать день, месяц и год, и "вырезать" соответствующие символы из строки-шаблона "0123456789".

Рассмотрим пример формирования строки формата "ДД.ММ.ГГГГ":


ВЫБРАТЬ
    // Формируем День (ДД)
    ПОДСТРОКА("0123456789", ДЕНЬ(&Дата) / 10 + 1, 1) + 
    ПОДСТРОКА("0123456789", СЕКУНДА(ДОБАВИТЬКДАТЕ(ДАТАВРЕМЯ(1, 1, 1), СЕКУНДА, 6 * ДЕНЬ(&Дата))) / 6 + 1, 1) 
    + "." +
    // Формируем Месяц (ММ)
    ПОДСТРОКА("0123456789", МЕСЯЦ(&Дата) / 10 + 1, 1) + 
    ПОДСТРОКА("0123456789", СЕКУНДА(ДОБАВИТЬКДАТЕ(ДАТАВРЕМЯ(1, 1, 1), СЕКУНДА, 6 * МЕСЯЦ(&Дата))) / 6 + 1, 1) 
    + "." +
    // Формируем Год (ГГГГ) - упрощенно
    СТРОКА(ГОД(&Дата)) 
    
    КАК СтроковаяДата

Здесь используется хитрость с математикой для получения остатка от деления (так как функции Mod или Остаток в запросе нет для всех контекстов, приходится эмулировать получение второй цифры числа через СЕКУНДА и ДОБАВИТЬКДАТЕ).

Способ Б: Условное форматирование с добавлением нуля

Более понятный для чтения способ — использование оператора ВЫБОР для добавления ведущего нуля, если число меньше 10.


ВЫБРАТЬ
    // День
    (ВЫБОР КОГДА ДЕНЬ(Таблица.Дата) < 10 ТОГДА "0" ИНАЧЕ "" КОНЕЦ + СТРОКА(ДЕНЬ(Таблица.Дата))) 
    + "." + 
    // Месяц
    (ВЫБОР КОГДА МЕСЯЦ(Таблица.Дата) < 10 ТОГДА "0" ИНАЧЕ "" КОНЕЦ + СТРОКА(МЕСЯЦ(Таблица.Дата))) 
    + "." + 
    // Год
    СТРЗАМЕНИТЬ(СТРОКА(ГОД(Таблица.Дата)), " ", "") 
    
    КАК ДатаСтрокой
ИЗ
    Документ.РеализацияТоваровУслуг КАК Таблица

Проблема пробела в году

Обратите внимание на последнюю строку в предыдущем примере: СТРЗАМЕНИТЬ(СТРОКА(ГОД(...)), " ", ""). Это критически важный момент.

Когда вы используете функцию СТРОКА() для преобразования числа (а функция ГОД() возвращает число), платформа 1С применяет региональные настройки представления чисел. По умолчанию для чисел используется разделитель групп разрядов — неразрывный пробел (символ с кодом 160). В результате год "2023" превращается в строку "2 023".

Чтобы избавиться от этого пробела в запросе, мы используем функцию СТРЗАМЕНИТЬ (или SUBSTRING, если хотим просто вырезать символы, но замена надежнее).

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


// Пример для года, если он > 999
ПОДСТРОКА(СТРОКА(ГОД(Таблица.Дата)), 1, 1) + ПОДСТРОКА(СТРОКА(ГОД(Таблица.Дата)), 3, 3)

В этом примере мы берем 1-й символ (тысячи) и 3 символа начиная с 3-го (сотни, десятки, единицы), пропуская 2-й символ, который является разделителем.

Задача 3: Отбор данных по времени

Еще один интересный вопрос, поднятый в обсуждении: как отобрать документы, созданные в определенный часовой интервал, например, с 22:00 до 23:59.

Для этого можно использовать функции работы с частями даты непосредственно в секции ГДЕ.

  1. Простой вариант (в рамках одного часа): ГДЕ ЧАС(Документ.Дата) = 22
  2. Интервал часов: ГДЕ ЧАС(Документ.Дата) МЕЖДУ 22 И 23
  3. Сложный интервал с минутами:

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

Также можно передать в параметр массив интересующих часов:


// Код 1С
МассивЧасов = Новый Массив;
МассивЧасов.Добавить(22);
МассивЧасов.Добавить(23);
Запрос.УстановитьПараметр("НужныеЧасы", МассивЧасов);

// Текст запроса
ГДЕ ЧАС(Документ.Дата) В (&НужныеЧасы)

Рекомендации по производительности

Необходимо помнить, что любые преобразования функций в секции ГДЕ (например, ГДЕ ГОД(Дата) = 2023) могут привести к тому, что СУБД перестанет использовать индекс по полю даты. Это касается и других сложных условий, таких как подобно в списке. Это приведет к полному сканированию таблицы (Full Scan) и замедлению работы.

Поэтому:

← На главную