Как записать значение типа "Дата" из 1С во внешнюю таблицу Oracle через внешний источник данных, минуя ошибку форматирования?

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

При работе с внешними источниками данных в 1С, особенно при взаимодействии с СУБД Oracle, мы часто сталкиваемся с проблемой несоответствия форматов данных, в частности, типа "Дата". Такая ситуация может привести к ошибкам вроде "literal does not match format string" или "a non-numeric character was found where a numeric was expected". Давайте подробно разберем, как решить эту распространенную задачу и обеспечить корректную запись дат из 1С в Oracle.

Описание проблемы и первые попытки решения

Представим, что у нас есть задача по модификации данных в таблице внешнего источника данных (СУБД Oracle). Мы создали в 1С (на платформе 8.3.25.1394, что характерно для управляемых форм) внешний источник данных и добавили в него соответствующую таблицу. Одно из полей таблицы, например, с именем period, в 1С автоматически определилось как тип "Дата" (Дата + время). На стороне Oracle это поле также имеет тип DATE и содержит значения в формате, например, 2022-01-01 00:00:00.000.

Когда мы пытаемся записать новую строку в эту таблицу, присваивая полю period значение типа "Дата" из переменной 1С, например, ДанныеКОбработке.НачалоПериода, мы получаем ошибку: "literal does not match format string".

Вот пример нашего первоначального кода:


ТекущееСогласование = МенеджерВИ.СоздатьОбъект;
ТекущееСогласование.CODE = 1111111111111100;
ТекущееСогласование.period = ДанныеКОбработке.НачалоПериода; // Здесь возникает ошибка
ТекущееСогласование.cd_coll_boss = ДанныеКОбработке.КодРуководителя;
ТекущееСогласование.cd_dept = ДанныеКОбработке.КодПодразделения;
ТекущееСогласование.status = 0;
ТекущееСогласование.condition = 0;
ТекущееСогласование.doc_tab_src = Т13ДвоичныеДанные;
ТекущееСогласование.doc_tab_type = "XLSX";
ТекущееСогласование.ERR_MSG = null;
ТекущееСогласование.MODIFIED = null;
Попытка
    ТекущееСогласование.Записать;
Исключение
    Сообщить(ОписаниеОшибки);
    Продолжить;
КонецПопытки;

Мы выяснили, что если заменить строку ТекущееСогласование.period = ДанныеКОбработке.НачалоПериода; на ТекущееСогласование.period = null;, то новая запись успешно создается. Это явно указывает на то, что проблема заключается в формате передаваемой даты.

В поисках решения мы пробовали изменить тип поля period на стороне 1С на "Строка" и попытаться передать дату в виде строки, используя функцию Oracle TO_DATE(), например, так:


ТекущееСогласование.period = "to_date(" + "'" + ДанныеКОбработке.НачалоПериода + "','"+ "YYYY-MM-DD HH24:MI:SS" + "')";

или даже как простой строковый литерал с вызовом TO_DATE:


ТекущееСогласование.period = "to_date('2025.08.21','yyyy.mm.dd')";

В обоих случаях мы получили ошибку Oracle: "a non-numeric character was found where a numeric was expected".

Важно понимать: менеджер внешнего источника данных в 1С (МенеджерВИ) не интерпретирует строку "to_date('...','...')" как SQL-функцию, которую нужно выполнить на стороне СУБД. Вместо этого он передает ее как строковое значение для поля period. Oracle затем пытается неявно преобразовать эту строку "to_date('...','...')" в дату, что, очевидно, не удается, поскольку это не является корректным строковым представлением даты.

Даже когда разработчик Oracle сообщил, что ожидаемый формат даты — 'dd.mm.yyyy hh24:mi', прямые попытки присвоить строку в этом формате также приводили к ошибке "a non-numeric character was found where a numeric was expected". Это подтверждает, что механизм неявного преобразования на стороне Oracle очень чувствителен к точным настройкам локали и формату входящей строки, а прямое внедрение SQL-функций через поля внешнего источника данных не работает.

Рабочее решение: Форматирование даты в 1С для неявного преобразования Oracle

Мы выяснили, что Oracle способен неявно преобразовывать строки в даты, если формат строки соответствует его ожиданиям, особенно с учетом настроек локали сессии. Решение, которое успешно заработало, заключалось в форматировании даты в 1С таким образом, чтобы она максимально точно соответствовала одному из стандартных форматов Oracle для неявного преобразования, особенно с учетом английской локали.

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


ТекущееСогласование = МенеджерВИ.СоздатьОбъект;
ТекущееСогласование.CODE = 1111111111111100;
ТекущееСогласование.period = Формат(ДанныеКОбработке.НачалоПериода, "Л=en; ДФ='дд МММ гггг'"); // Это рабочее решение!
ТекущееСогласование.cd_coll_boss = ДанныеКОбработке.КодРуководителя;
ТекущееСогласование.cd_dept = ДанныеКОбработке.КодПодразделения;
ТекущееСогласование.status = 0;
ТекущееСогласование.condition = 0;
ТекущееСогласование.doc_tab_src = Т13ДвоичныеДанные;
ТекущееСогласование.doc_tab_type = "XLSX";
ТекущееСогласование.ERR_MSG = null;
ТекущееСогласование.MODIFIED = null;
Попытка
    ТекущееСогласование.Записать;
Исключение
    Сообщить(ОписаниеОшибки);
    Продолжить;
КонецПопытки;

Давайте разберем параметры функции Формат() в 1С, которые сделали это решение эффективным:

  1. Л=en (Локаль = English): Этот параметр имеет критическое значение. Он гарантирует, что названия месяцев в строковом представлении даты будут на английском языке (например, "Jan" вместо "Янв", "Feb" вместо "Фев" и так далее). Многие инсталляции Oracle по умолчанию настроены на английский язык для обработки дат, и неявное преобразование работает гораздо более надежно с англоязычными названиями месяцев.
  2. ДФ='дд МММ гггг' (Дата Формат = день Месяц (сокращенное) год): Этот параметр определяет желаемый шаблон форматирования даты. В данном случае мы получаем:
    • дд – день месяца (две цифры, например, "21").
    • МММ – сокращенное название месяца (три буквы, например, "Jan").
    • гггг – полный год (четыре цифры, например, "2025").

    Пример вывода такой строки: "21 Jan 2025". Именно этот формат Oracle смог успешно преобразовать в свой внутренний тип DATE.

Таким образом, мы видим, что ключ к решению заключался не в использовании сложных SQL-функций, а в точном подборе формата строкового представления даты, которое 1С отправляет в Oracle, чтобы оно соответствовало ожиданиям СУБД для неявного преобразования.

Почему другие форматы и подходы могли не сработать?

В процессе поиска решения мы испробовали несколько других подходов, которые не привели к успеху. Давайте проанализируем, почему они не сработали, чтобы лучше понять механику взаимодействия 1С и Oracle.

  1. Прямые строковые литералы ('YYYYMMDD', 'YYYY-MM-DDThh:mm:ss', 'MM/DD/YYYY')

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

    
    ТекущееСогласование.period = '20250801'; // Получена ошибка "not a valid month"
    ТекущееСогласование.period = '2025-08-01T12:43:05'; // Получена ошибка "not a valid month"
    ТекущееСогласование.period = "'08/01/2025'"; // Получена ошибка Oracle "a non-numeric character was found where a numeric was expected"
    

    Эти ошибки показывают, что хотя Oracle может работать с подобными форматами при явном указании в TO_DATE(), для неявного преобразования необходима строка, которая **точно** соответствует текущему значению параметра сессии NLS_DATE_FORMAT (например, 'DD-MON-YY' или 'DD.MM.YYYY HH24:MI'). Малейшее отклонение, например, в разделителях, порядке компонентов даты или наличии компонента времени, которого Oracle не ожидает в этом контексте, приводит к ошибке. Если 1С передает такую строку как значение поля, Oracle просто не может ее разобрать.

  2. Использование синтаксиса JDBC/ODBC {ts '...'}

    Один из предложенных вариантов был использовать синтаксис {ts '"+Формат(Знч,"ДФ=гггг-ММ-дд")+" "+Формат(Знч,"ДФ=ЧЧ:мм:сс")+"'}". Этот синтаксис является стандартным способом передачи отметок времени (timestamps) в SQL-запросах через JDBC/ODBC драйверы и предназначен для обеспечения совместимости между различными СУБД.

    Однако, когда мы работаем с объектами ОбъектВнешнегоИсточникаДанных в 1С (например, ТекущееСогласование), менеджер внешнего источника данных 1С, скорее всего, не обертывает значение поля в этот специальный синтаксис {ts '...'}, а передает его в базу данных как обычную строку. Таким образом, база данных Oracle не распознает эту конструкцию как инструкцию для обработки даты, а пытается неявно преобразовать всю строку, что снова приводит к ошибке.

Рекомендации для обеспечения надежности интеграции с Oracle

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

  1. Всегда используйте явное преобразование TO_DATE() в Oracle (при возможности прямого SQL)

    Если в вашем сценарии есть возможность формировать SQL-запросы напрямую (например, через выполнение запроса к внешнему источнику 1С или используя хранимые процедуры Oracle), это самый надежный и предпочтительный способ работы с датами. В таких случаях мы явно указываем Oracle, какой формат имеет наша строка даты, используя функцию TO_DATE().

    
    -- Пример SQL-запроса, который вы могли бы отправить через Execute(...)
    INSERT INTO ТАБЛИЦА_ORACLE (period, other_field)
    VALUES (TO_DATE('2025-01-01 10:30:00', 'YYYY-MM-DD HH24:MI:SS'), 'some_value');
    

    В этом примере 'YYYY-MM-DD HH24:MI:SS' является форматом, в котором вы подготовили строку даты в 1С. Такой подход исключает зависимость от настроек NLS_DATE_FORMAT сессии и обеспечивает однозначное толкование даты.

  2. Стандартизируйте формат даты в 1С

    Независимо от того, используете ли вы прямое SQL или механизм внешних источников данных 1С, всегда выбирайте один универсальный и однозначный формат даты для формирования строкового представления в 1С. Хорошим примером является формат YYYY-MM-DD HH:MM:SS.

    
    // Пример форматирования для получения универсальной строки
    СтрокаДатыДляOracle = Формат(МояДата, "ДФ='гггг-ММ-дд ЧЧ:мм:СС'");
    

    Этот формат является международным стандартом (ISO 8601) и чаще всего легко распознается базами данных или может быть использован в функции TO_DATE(). В случае с внешними источниками данных 1С, если найденное решение с "Л=en; ДФ='дд МММ гггг'" перестанет работать (например, после обновления Oracle или изменения настроек локали), попытка использовать ISO-формат с английской локалью может быть следующим шагом.

  3. Понимание NLS_DATE_FORMAT

    Мы должны всегда помнить, что значение параметра NLS_DATE_FORMAT в сессии Oracle (или на уровне базы данных) определяет, как Oracle ожидает и отображает даты при неявных преобразованиях. Если у нас есть необходимые права, мы можем проверить его значение:

    
    SELECT VALUE FROM NLS_SESSION_PARAMETERS WHERE PARAMETER = 'NLS_DATE_FORMAT';
    

    Знание этого формата критически важно. Оно поможет нам точно настроить функцию Формат() в 1С, чтобы генерировать строки, которые Oracle будет гарантированно понимать.

  4. Консультируйтесь с администратором Oracle

    В сложных случаях, при нестандартных настройках базы данных или если ни один из стандартных подходов не работает, всегда полезно проконсультироваться с администратором базы данных Oracle. Он сможет предоставить точные сведения об ожидаемых форматах даты, настройках локали и возможных ограничениях, которые могут влиять на процесс интеграции.

В заключение, мы выяснили, что основной трудностью при записи даты из 1С в Oracle через внешний источник данных является необходимость формирования строкового представления даты в 1С таким образом, чтобы Oracle мог неявно преобразовать ее в свой внутренний тип DATE. Решение с использованием Формат(ДанныеКОбработке.НачалоПериода, "Л=en; ДФ='дд МММ гггг'") оказалось эффективным благодаря точному соответствию одному из форматов, которые Oracle ожидает для неявного преобразования, особенно с учетом английской локали. При этом важно осознавать ограничения механизма внешних источников данных 1С в отношении прямого выполнения SQL-функций.

← На главную