Как передать дату с часовым поясом в веб-сервис (XDTO) в 1С?

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

При интеграции с внешними системами через веб-сервисы (SOAP) часто возникает задача передать дату и время с указанием часового пояса. Стандарт XML Schema (XSD) предусматривает для этого тип dateTime, который может включать смещение относительно UTC (например, 2024-05-20T15:45:00+03:00). Однако в 1С при попытке сериализовать такое значение программисты сталкиваются с тем, что информация о часовом поясе теряется.

Давайте подробно разберем, почему так происходит и какие существуют надежные способы решения этой проблемы.

Анализ проблемы: почему 1С "отрезает" часовой пояс?

Рассмотрим типичную ситуацию. Мы пытаемся создать объект XDTO типа dateTime из строки, содержащей часовой пояс, надеясь, что эта информация сохранится при формировании XML.


// Казалось бы, логичный код
ТипDateTime = ФабрикаXDTO.Тип("http://www.w3.org/2001/XMLSchema", "dateTime");
// Пытаемся создать значение из строки с часовым поясом (AET или +10:00)
ЗначениеXDTO = ФабрикаXDTO.Создать(ТипDateTime, "2007-11-12T12:58:00+10:00");

// Проверяем, что получилось
Сообщить(ЗначениеXDTO.ЛексическоеЗначение); // Результат: "2007-11-12T12:58:00"

Как мы видим, часовой пояс +10:00 исчез. Причина кроется в фундаментальном ограничении платформы 1С: ее внутренний примитивный тип Дата не содержит информации о часовом поясе. Он хранит лишь конкретный момент времени.

Когда вы вызываете метод ФабрикаXDTO.Создать() со строковым представлением, 1С выполняет следующий алгоритм:

  1. Берет строку ("2007-11-12T12:58:00+10:00").
  2. Парсит ее и конвертирует во внутренний тип Дата. На этом этапе часовой пояс учитывается для вычисления правильного момента времени (часто относительно времени сервера), но сама информация о смещении (+10:00) отбрасывается.
  3. Создает объект ЗначениеXDTO на основе уже преобразованного значения типа Дата.
  4. При последующей сериализации в XML берется это "чистое" значение даты без часового пояса.

Интересно, что при обратном процессе — десериализации (чтении XML и преобразовании его в объекты 1С) — часовой пояс как раз учитывается корректно. Платформа правильно преобразует входящую дату со смещением в локальное время. Но проблема возникает именно при отправке данных из 1С.

Поскольку прямое управление часовым поясом через стандартные объекты XDTO для типа dateTime невозможно, нам нужны обходные пути. Рассмотрим три основных решения.

Решение 1: Модификация WSDL-схемы (замена dateTime на string)

Это самый популярный и зачастую самый простой способ обойти ограничение платформы. Идея заключается в том, чтобы "обмануть" 1С, заставив ее считать поле с датой обычной строкой. Тогда мы получим полный контроль над ее содержимым.

Разберем по шагам:

  1. Сохраните WSDL-файл. Скачайте WSDL-описание веб-сервиса, к которому вы обращаетесь, и сохраните его как локальный файл (например, service.wsdl.xml).
  2. Отредактируйте файл. Откройте сохраненный WSDL-файл в любом текстовом редакторе. Найдите в нем описание типов (обычно в секции <xsd:schema>) и найдите все поля, которые вы хотите отправлять с часовым поясом. Их тип будет указан как xsd:dateTime или类似.
  3. Замените тип. Измените тип этих полей с xsd:dateTime на xsd:string.

    Было: <xsd:element name="my_date_field" type="xsd:dateTime"/>

    Стало: <xsd:element name="my_date_field" type="xsd:string"/>

  4. Подключите локальный WSDL. При создании объекта WSПрокси в коде 1С укажите путь к вашему измененному локальному файлу, а не URL веб-сервиса.
    
    ПутьКWSDL = "C:\WSDL\service.wsdl.xml";
    Прокси = Новый WSПрокси(Новый ОписаниеWS(ПутьКWSDL), ИмяСервиса, ИмяПорта);
    
  5. Формируйте строку программно. Теперь 1С будет рассматривать поле my_date_field как строку. Вы можете сформировать в коде нужное строковое представление даты в формате ISO 8601 и присвоить его этому полю.
    
    // Получаем нужную дату
    МояДата = ТекущаяДатаСеанса();
        
    // Формируем строку с часовым поясом +03:00
    ЧасовойПояс = "+03:00";
    СтрокаДатыСПоясом = Формат(МояДата, "ДФ=yyyy-MM-dd'T'HH:mm:ss") + ЧасовойПояс;
        
    // Создаем объект XDTO и присваиваем ему строковое значение
    ОбъектXDTO = Прокси.ФабрикаXDTO.Создать(Прокси.ФабрикаXDTO.Тип(ИмяПространстваИмен, "ИмяТипаОбъекта"));
    ОбъектXDTO.my_date_field = СтрокаДатыСПоясом;
        
    // Теперь при вызове метода веб-сервиса в XML будет корректная строка
    // <my_date_field>2024-05-20T15:45:00+03:00</my_date_field>
    

Преимущества: Относительная простота реализации, не требует ручной работы с XML.

Недостатки: Необходимо поддерживать локальную копию WSDL. Если веб-сервис обновится, придется скачивать и редактировать файл заново.

Решение 2: Ручное формирование XML и отправка через HTTP-запрос

Этот метод дает максимальный контроль, но он и самый трудоемкий. Мы полностью отказываемся от удобных объектов WSПрокси и формируем SOAP-сообщение как обычный XML-текст, после чего отправляем его стандартным HTTP-запросом.

Разберем по шагам:

  1. Сформируйте XML-тело запроса. Вам нужно создать строку, которая будет содержать полный текст SOAP-конверта, включая все пространства имен, заголовки и тело сообщения. Для этого удобно сначала посмотреть, какой XML генерирует WSПрокси (например, с помощью Fiddler), и взять его за основу.
  2. Вставьте дату как текст. В нужное место в XML-строке вставьте ваше строковое представление даты с часовым поясом.
    
    МояДата = ТекущаяДатаСеанса();
    ЧасовойПояс = "-05:00";
    СтрокаДатыСПоясом = Формат(МояДата, "ДФ=yyyy-MM-dd'T'HH:mm:ss") + ЧасовойПояс;
        
    ТелоЗапросаXML = 
    "<soapenv:Envelope xmlns:soapenv=""http://schemas.xmlsoap.org/soap/envelope/"" xmlns:ns=""http://example.com/service"">
    |   <soapenv:Header/>
    |   <soapenv:Body>
    |      <ns:MyWebServiceMethod>
    |         <ns:my_date_field>" + СтрокаДатыСПоясом + "</ns:my_date_field>
    |      </ns:MyWebServiceMethod>
    |   </soapenv:Body>
    |</soapenv:Envelope>";
    
  3. Отправьте HTTP-запрос. Используйте объекты HTTPСоединение и HTTPЗапрос для отправки вашего XML на эндпоинт веб-сервиса.
    
    АдресСервиса = "service.example.com";
    Ресурс = "/path/to/service";
        
    Соединение = Новый HTTPСоединение(АдресСервиса);
    Запрос = Новый HTTPЗапрос(Ресурс);
        
    // Установка обязательных для SOAP-запросов заголовков
    Запрос.Заголовки.Вставить("Content-Type", "text/xml; charset=utf-8");
    Запрос.Заголовки.Вставить("SOAPAction", "ИмяSOAPДействия"); // Уточнить в документации к сервису
        
    Запрос.УстановитьТелоИзСтроки(ТелоЗапросаXML, КодировкаТекста.UTF8);
        
    Ответ = Соединение.ОтправитьДляОбработки(Запрос); // или .Post(Запрос)
        
    // Далее необходимо вручную разбирать XML ответа (Ответ.ПолучитьТелоКакСтроку())
    

Преимущества: Полный и абсолютный контроль над исходящим XML-сообщением.

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

Решение 3: Согласование формата и передача дат в UTC

Это не столько технический трюк, сколько архитектурно правильный подход. Проблема неоднозначности часовых поясов — общая для многих систем. Поэтому в мире API принято стандартное решение: всегда обмениваться датами во Всемирном координированном времени (UTC).

Формат даты в UTC — это стандартный формат ISO 8601, но с буквой Z (от Zulu Time) в конце, которая означает нулевое смещение от Гринвича.

Пример: 2024-05-20T12:45:00Z

Разберем по шагам:

  1. Договоритесь с другой стороной. Обсудите с разработчиками принимающего веб-сервиса и договоритесь, что все даты будут передаваться исключительно в UTC (с суффиксом 'Z'). Уточните, как они обрабатывают даты без указания часового пояса (многие по умолчанию считают их UTC, но не все).
  2. Преобразуйте дату в UTC в 1С. В 1С можно получить текущее время в UTC и затем отформатировать его в нужную строку.
    
    // Получаем текущую дату в UTC.
    // ТекущаяУниверсальнаяДатаВМиллисекундах() возвращает число миллисекунд с 01.01.0001
    // Чтобы получить количество секунд с 01.01.1970 (Unix time), можно использовать ТекущаяУниверсальнаяДата()
    ДатаUTC = ТекущаяУниверсальнаяДата(); // Это число секунд с 01.01.1970
        
    // Преобразуем в тип Дата. Начало эпохи Unix - это '19700101'
    ДатаОбъектUTC = Дата('19700101') + ДатаUTC;
        
    // Форматируем в строку ISO 8601 с 'Z' на конце
    СтрокаДатыUTC = Формат(ДатаОбъектUTC, "ДФ=yyyy-MM-dd'T'HH:mm:ss'Z'");
        
    // Пример результата: "2024-05-20T12:45:00Z"
    
  3. Используйте совместно с Решением 1. Этот подход идеально сочетается с методом модификации WSDL. Вы меняете тип поля на string и присваиваете ему программно сформированную строку с датой в UTC.

Преимущества: Устраняет любую двусмысленность. Является лучшей практикой при проектировании API. Снижает вероятность ошибок, связанных с часовыми поясами.

Недостатки: Требует согласования с разработчиками внешней системы.

Заключение

Итак, мы выяснили, что прямая передача часового пояса через тип dateTime в XDTO ограничена платформой 1С. Чтобы решить эту задачу, у нас есть несколько надежных стратегий:

Выбор конкретного метода зависит от ваших полномочий (можете ли вы влиять на формат обмена), сложности веб-сервиса и ваших предпочтений в разработке.

← На главную