При интеграции платформы 1С с внешними веб-сервисами (SOAP / WSDL) разработчики часто сталкиваются с ситуацией, когда отправленная дата интерпретируется приемником некорректно. Например, при передаче даты 02.01.2017 (2 января) сервис возвращает данные за 1 февраля (2017-01-02), а при попытке передать 28.01.2017 возникает ошибка о том, что 28-го месяца не существует.
Разберем подробнее, почему происходит искажение даты при использовании объектов WSПрокси и XDTO, и рассмотрим практические способы решения этой проблемы.
Проанализируем механику работы платформы 1С при сериализации данных в XML-формат xs:dateTime стандарта W3C:
Дата: В 1С примитивный тип Дата хранит значение года, месяца, дня и времени, но не содержит смещения часового пояса относительно UTC.WSПрокси, платформа автоматической конвертацией преобразует значение в строку вида YYYY-MM-DDTHH:mm:ss без суффикса Z (UTC) или указания смещения (например, +03:00).en-US). В результате сервер интерпретирует строку по американскому формату YYYY-DD-MM или MM/dd/yyyy, меняя день и месяц местами.Если веб-сервис жестко требователен к наличию временной зоны в XML-теге, штатными средствами WSПрокси изменить формат сериализации примитивного типа xs:dateTime не удастся — платформа всегда формирует конверт автоматически по правилам WSDL-схемы.
Посмотрим на пример решения этой проблемы путем перехода на `HTTPСоединение` и самостоятельного формирования SOAP-пакета с явным указанием суффикса Z (UTC) или смещения часового пояса:
Функция СформироватьТекстSOAPЗапроса(Знач ИсходнаяДата)
// Форматируем дату в ISO-строку с добавлением временной зоны UTC (Z)
СтрокаДатыXML = Формат(ИсходнаяДата, "ДФ=yyyy-MM-ddTHH:mm:ss") + "Z";
ЗаписьXML = Новый ЗаписьXML;
ЗаписьXML.УстановитьСтроку();
ЗаписьXML.ЗаписатьНачалоЭлемента("soap:Envelope");
ЗаписьXML.ЗаписатьСоответствиеГраницПространствИмен("soap", "http://schemas.xmlsoap.org/soap/envelope/");
ЗаписьXML.ЗаписатьСоответствиеГраницПространствИмен("web", "http://api-efactura.fisc.md/");
ЗаписьXML.ЗаписатьНачалоЭлемента("soap:Body");
ЗаписьXML.ЗаписатьНачалоЭлемента("web:SearchInvoices");
ЗаписьXML.ЗаписатьНачалоЭлемента("web:StartDate");
ЗаписьXML.ЗаписатьТекст(СтрокаДатыXML);
ЗаписьXML.ЗаписатьКонецЭлемента(); // StartDate
ЗаписьXML.ЗаписатьКонецЭлемента(); // SearchInvoices
ЗаписьXML.ЗаписатьКонецЭлемента(); // Body
ЗаписьXML.ЗаписатьКонецЭлемента(); // Envelope
Возвратить ЗаписьXML.Закрыть();
КонецФункции
Далее отправляем сформированный XML через объект HTTPСоединение:
Соединение = Новый HTTPСоединение("api-efactura.fisc.md", 443, , , , Истина);
Запрос = Новый HTTPЗапрос("/Service.svc");
Запрос.Заголовки.Вставить("Content-Type", "text/xml; charset=utf-8");
Запрос.Заголовки.Вставить("SOAPAction", "http://api-efactura.fisc.md/IService/SearchInvoices");
Запрос.УстановитьТелоИзСтроки(СформироватьТекстSOAPЗапроса(ТекущаяДата()));
Ответ = Соединение.ОтправитьДляОбработки(Запрос);
СтрокаОтвета = Ответ.ПолучитьТелоКакСтроку();
Рассмотрим случай, когда SOAP-структура требует использования фабрики XDTO. Вместо прямого присвоения значения типа Дата объекту XDTO, можно принудительно сформировать объект значения из пространства имен XMLSchema (http://www.w3.org/2001/XMLSchema).
Проанализируем пример создания корректного значения даты XDTO:
// Получаем фабрику XDTO веб-сервиса
Фабрика = Прокси.ФабрикаXDTO;
// Формируем строковое представление с буквой Z или нужным форматом
СтрокаДаты = Формат(ТекущаяДатаСеанса(), "ДФ=yyyy-MM-ddTHH:mm:ss") + "Z";
// Создаем явный объект XDTO типа dateTime из пространства имен XMLSchema
ТипDateTime = Фабрика.Тип("http://www.w3.org/2001/XMLSchema", "dateTime");
ОбъектДатаXDTO = Фабрика.Создать(ТипDateTime, СтрокаДаты);
// Присваиваем объект полю параметров
ПараметрыПоиска.StartDate = ОбъектДатаXDTO;
При работе со стандартными механизмами сериализации платформы полезно помнить о функции XMLСтрока(). Данная функция преобразует значение типа Дата в канонический формат XML:
ТекущаяДата1С = ТекущаяДата();
СтрокаXML = XMLСтрока(ТекущаяДата1С);
// Возвращает строку вида: "2017-01-10T00:00:00"
Если для работы внешнего API требуется явное приведение строки к формату ISO с пользовательской подстановкой дней и месяцев, можно воспользоваться функцией Формат() с локализованной строкой маски:
// Преобразование даты в формат MM.dd.yyyy при нетипизированной передаче строк
СтрокаАмериканскогоФормата = Формат(ТекущаяДата1С, "ДФ='MM.dd.yyyy HH:mm:ss'");
Выяснив причину путаницы между днем и месяцем (отсутствие таймзоны при десериализации на стороне стороннего WCF/Oracle сервиса), выделим рекомендуемую последовательность действий:
Z на конце.WSПрокси, формировать значение через ФабрикаXDTO.Создать() для типа xs:dateTime.HTTPСоединение и HTTPЗапрос.