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

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

В практике разработки на платформе 1С:Предприятие часто возникает классическая, но от этого не менее сложная задача: извлечь дату из текстового поля (например, из комментария к документу, назначения платежа или строкового свойства) непосредственно внутри запроса. Трудность заключается в том, что в языке запросов 1С отсутствует стандартная функция явного приведения типов вроде CAST(Строка КАК ДАТА) или ПОЛУЧИТЬДАТУ().

Такое ограничение обусловлено архитектурными особенностями платформы. Транслятор запросов SDBL должен обеспечивать кроссплатформенность кода на различных СУБД (MS SQL Server, PostgreSQL, IBM DB2, Oracle), у каждой из которых свои стандарты и локали преобразования строк. Тем не менее, разработчики нашли несколько обходных путей. В этой статье мы подробно разберем все способы решения этой задачи — от классических «костылей» до элегантных современных подходов.

Причина сложности: почему нельзя сделать это одной командой?

Прежде чем перейти к решениям, выясним причину ограничения. Строка — это нетипизированный набор символов. Дата может быть записана множеством способов: "31.12.2023", "2023-12-31", "31/12/23" или даже "31 декабря 2023 года". Если бы СУБД пыталась неявно преобразовать строку в дату на уровне базы данных, это приводило бы к постоянным аварийным завершениям запроса при малейшем несовпадении формата или региональных настроек сервера приложений и СУБД.

Поэтому нам приходится использовать альтернативные методы. Рассмотрим их по шагам.

---

Способ 1. Использование Системы компоновки данных (СКД) и функций общих модулей

Если ваша задача — вывести эту дату в отчете, то самым простым, производительным и методически верным решением будет перенос логики преобразования из SQL-запроса на уровень встроенного языка 1С с помощью механизмов СКД.

Проанализируем этот алгоритм:

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

Посмотрим на пример реализации такой функции общего модуля:


Функция ПолучитьДатуИзСтроки(Знач ИсходнаяСтрока) Экспорт
    
    // Очищаем строку от лишних символов и ищем паттерн даты ДД.ММ.ГГГГ
    // Например, если строка содержит: "Оплата по счету от 15.10.2023"
    
    РегулярноеВыражение = Новый RegExp;
    РегулярноеВыражение.Pattern = "\d{2}\.\d{2}\.\d{4}";
    
    Если РегулярноеВыражение.Test(ИсходнаяСтрока) Тогда
        РезультатМассив = РегулярноеВыражение.Execute(ИсходнаяСтрока);
        СтрокаДаты = РезультатМассив[0].Value; // Получили "15.10.2023"
        
        День = Число(Лев(СтрокаДаты, 2));
        Месяц = Число(Сред(СтрокаДаты, 4, 2));
        Год = Число(Прав(СтрокаДаты, 4));
        
        Попытка
            Возврат Дата(Год, Месяц, День);
        Исключение
            Возврат '00010101';
        КонецПопытки;
    КонецЕсли;
    
    Возврат '00010101';
    
КонецФункции

В СКД во вкладке Вычисляемые поля выражение для нового поля будет выглядеть следующим образом:

ИмяОбщегоМодуля.ПолучитьДатуИзСтроки(КомментарийСчета)

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

---

Способ 2. Метод декартова произведения (генерация таблицы соответствий дат внутри запроса)

Что делать, если использовать СКД и программную постобработку категорически нельзя (например, дата нужна внутри сложного запроса для связи с другими таблицами или для последующей группировки)? В этом случае на помощь приходит чистый SQL-метод генерации дат через временную таблицу.

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

Давайте разберем, как выглядит этот запрос:


ВЫБРАТЬ
    ДОБАВИТЬКДАТЕ(ДОБАВИТЬКДАТЕ(ДОБАВИТЬКДАТЕ(ДАТАВРЕМЯ(1, 1, 1), ГОД, ТГоды.Год - 1), МЕСЯЦ, ТМесяцы.Месяц - 1), ДЕНЬ, ТДни.День - 1) КАК Дата,
    ТДни.ДеньС + "." + ТМесяцы.МесяцС + "." + ТГоды.ГодС КАК ДатаСтрока
ПОМЕСТИТЬ ВтКалендарь
ИЗ
    (ВЫБРАТЬ 1 КАК День, "01" КАК ДеньС ОБЪЕДИНИТЬ ВЫБРАТЬ 2, "02" ОБЪЕДИНИТЬ ВЫБРАТЬ 3, "03" ОБЪЕДИНИТЬ ВЫБРАТЬ 4, "04"
     ОБЪЕДИНИТЬ ВЫБРАТЬ 5, "05" ОБЪЕДИНИТЬ ВЫБРАТЬ 6, "06" ОБЪЕДИНИТЬ ВЫБРАТЬ 7, "07" ОБЪЕДИНИТЬ ВЫБРАТЬ 8, "08"
     ОБЪЕДИНИТЬ ВЫБРАТЬ 9, "09" ОБЪЕДИНИТЬ ВЫБРАТЬ 10, "10" ОБЪЕДИНИТЬ ВЫБРАТЬ 11, "11" ОБЪЕДИНИТЬ ВЫБРАТЬ 12, "12"
     ОБЪЕДИНИТЬ ВЫБРАТЬ 13, "13" ОБЪЕДИНИТЬ ВЫБРАТЬ 14, "14" ОБЪЕДИНИТЬ ВЫБРАТЬ 15, "15" ОБЪЕДИНИТЬ ВЫБРАТЬ 16, "16"
     ОБЪЕДИНИТЬ ВЫБРАТЬ 17, "17" ОБЪЕДИНИТЬ ВЫБРАТЬ 18, "18" ОБЪЕДИНИТЬ ВЫБРАТЬ 19, "19" ОБЪЕДИНИТЬ ВЫБРАТЬ 20, "20"
     ОБЪЕДИНИТЬ ВЫБРАТЬ 21, "21" ОБЪЕДИНИТЬ ВЫБРАТЬ 22, "22" ОБЪЕДИНИТЬ ВЫБРАТЬ 23, "23" ОБЪЕДИНИТЬ ВЫБРАТЬ 24, "24"
     ОБЪЕДИНИТЬ ВЫБРАТЬ 25, "25" ОБЪЕДИНИТЬ ВЫБРАТЬ 26, "26" ОБЪЕДИНИТЬ ВЫБРАТЬ 27, "27" ОБЪЕДИНИТЬ ВЫБРАТЬ 28, "28"
     ОБЪЕДИНИТЬ ВЫБРАТЬ 29, "29" ОБЪЕДИНИТЬ ВЫБРАТЬ 30, "30" ОБЪЕДИНИТЬ ВЫБРАТЬ 31, "31") КАК ТДни
        ПОЛНОЕ СОЕДИНЕНИЕ (ВЫБРАТЬ 1 КАК Месяц, "01" КАК МесяцС ОБЪЕДИНИТЬ ВЫБРАТЬ 2, "02" ОБЪЕДИНИТЬ ВЫБРАТЬ 3, "03"
                            ОБЪЕДИНИТЬ ВЫБРАТЬ 4, "04" ОБЪЕДИНИТЬ ВЫБРАТЬ 5, "05" ОБЪЕДИНИТЬ ВЫБРАТЬ 6, "06"
                            ОБЪЕДИНИТЬ ВЫБРАТЬ 7, "07" ОБЪЕДИНИТЬ ВЫБРАТЬ 8, "08" ОБЪЕДИНИТЬ ВЫБРАТЬ 9, "09"
                            ОБЪЕДИНИТЬ ВЫБРАТЬ 10, "10" ОБЪЕДИНИТЬ ВЫБРАТЬ 11, "11" ОБЪЕДИНИТЬ ВЫБРАТЬ 12, "12") КАК ТМесяцы
        ПО ИСТИНА
        ПОЛНОЕ СОЕДИНЕНИЕ (ВЫБРАТЬ 2020 КАК Год, "2020" КАК ГодС ОБЪЕДИНИТЬ ВЫБРАТЬ 2021, "2021" ОБЪЕДИНИТЬ ВЫБРАТЬ 2022, "2022"
                            ОБЪЕДИНИТЬ ВЫБРАТЬ 2023, "2023" ОБЪЕДИНИТЬ ВЫБРАТЬ 2024, "2024" ОБЪЕДИНИТЬ ВЫБРАТЬ 2025, "2025") КАК ТГоды
        ПО ИСТИНА
;

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
    Реализация.Ссылка КАК Ссылка,
    Реализация.СчетНаОплатуПокупателю.Комментарий КАК Комментарий,
    ВтКалендарь.Дата КАК ПолученнаяДата
ИЗ
    Документ.РеализацияТоваровУслуг КАК Реализация
    ЛЕВОЕ СОЕДИНЕНИЕ ВтКалендарь КАК ВтКалендарь
        ПО (Реализация.СчетНаОплатуПокупателю.Комментарий ПОДОБНО "%" + ВтКалендарь.ДатаСтрока + "%")

Рассмотрим подробнее, как работает этот запрос:

Важное предупреждение: Данный метод является ресурсоемким для СУБД. Подобные неявные соединения по условию ПОДОБНО приводят к полному сканированию таблиц (Index Scan / Table Scan) и сильно снижают производительность базы данных. Используйте его только на небольших объемах данных или для разовых обработок.

---

Способ 3. Посимвольный разбор строки по таблице весов (для фиксированной длины)

Если строка гарантированно имеет фиксированный формат (например, выгрузка из сторонней системы передает дату в виде "ГГГГММДД" на строго определенных позициях символов), можно применить математический разбор строки в запросе без использования тяжелых декартовых произведений. Данный способ основан на поиске порядкового номера символов через сопоставление со строкой-шаблоном чисел.

Посмотрим на алгоритм разбора для формата строки "ГГГГММДД" (длина ровно 8 символов):

  1. Каждый символ года, месяца и дня мы извлекаем с помощью встроенной функции запроса ПОДСТРОКА.
  2. Для превращения строкового символа в цифру мы используем конструкцию ВЫБОР, сравнивая символ со строкой "0123456789".
  3. Перемножаем полученные цифры на их математический вес (для единиц — 1, для десятков — 10, для сотен — 100 и т.д.), собирая числовые значения года, месяца и дня.
  4. Формируем дату через функцию ДОБАВИТЬКДАТЕ.

Разберем пример кода для определения числового значения одного символа:


ВЫБРАТЬ
    // Вычисляем значение первой цифры года (тысячи)
    ВЫБОР ПОДСТРОКА(Док.Комментарий, 1, 1)
        КОГДА "0" ТОГДА 0 КОГДА "1" ТОГДА 1 КОГДА "2" ТОГДА 2 КОГДА "3" ТОГДА 3
        КОГДА "4" ТОГДА 4 КОГДА "5" ТОГДА 5 КОГДА "6" ТОГДА 6 КОГДА "7" ТОГДА 7
        КОГДА "8" ТОГДА 8 КОГДА "9" ТОГДА 9 ИНАЧЕ 0 
    КОНЕЦ * 1000 КАК ГодТысячи
ИЗ
    Документ.РеализацияТоваровУслуг КАК Док

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

---

Способ 4. Современные возможности платформы 1С v8.3.20+

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

Хотя прямое преобразование типов по-прежнему не поддерживается, эти строковые функции позволяют прямо в запросе отсечь весь лишний текст комментария, найти позиции разделителей (точек, дефисов) и передать «чистые» цифры для последующей сборки даты. Это исключает необходимость написания громоздких конструкций с ПОДСТРОКА.

---

Способ 5. Постобработка результата запроса средствами БСП

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

В БСП входит общий модуль СтроковыеФункцииКлиентСервер, в котором реализованы мощные механизмы разбора строк. Проанализируем пример кода:


Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |   Реализация.Ссылка КАК Ссылка,
    |   Реализация.СчетНаОплатуПокупателю.Комментарий КАК Комментарий
    |ИЗ
    |   Документ.РеализацияТоваровУслуг КАК Реализация";

РезультатЗапроса = Запрос.Выполнить().Выгрузить();
РезультатЗапроса.Колонки.Добавить("НормализованнаяДата", Новый ОписаниеТипов("Дата"));

Для Каждого СтрокаТЗ Из РезультатЗапроса Цикл
    // Используем функцию БСП для автоматического поиска и извлечения даты
    СтрокаТЗ.НормализованнаяДата = СтроковыеФункцииКлиентСервер.СтрокаВДату(СтрокаТЗ.Комментарий);
КонецЦикла;

Функция СтрокаВДату() самостоятельно распознает большинство популярных форматов записи дат (включая разделители косой чертой, дефисы и точки) и вернет корректный тип данных Дата.

---

Резюме и архитектурный совет

Мы подробно разобрали различные варианты получения даты из строки. Однако важно помнить: необходимость извлечения даты из текстовых полей — это верный признак архитектурной ошибки проектирования базы данных.

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

  1. Добавить в документ РеализацияТоваровУслуг или СчетНаОплатуПокупателю новый реквизит с типом Дата.
  2. При заполнении документов программно копировать туда дату.
  3. Провести обработку обновления старых данных для заполнения нового реквизита.

Это не только решит текущую проблему разбора строк раз и навсегда, но и ускорит выполнение ваших отчетов и запросов в сотни раз благодаря индексации СУБД.

← На главную