Почему веб-сервис путает день и месяц при передаче даты из 1С и как это исправить

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

При интеграции платформы 1С с внешними веб-сервисами (SOAP / WSDL) разработчики часто сталкиваются с ситуацией, когда отправленная дата интерпретируется приемником некорректно. Например, при передаче даты 02.01.2017 (2 января) сервис возвращает данные за 1 февраля (2017-01-02), а при попытке передать 28.01.2017 возникает ошибка о том, что 28-го месяца не существует.

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

Причины некорректного парсинга даты

Проанализируем механику работы платформы 1С при сериализации данных в XML-формат xs:dateTime стандарта W3C:

  1. Отсутствие информации о часовом поясе в типе Дата: В 1С примитивный тип Дата хранит значение года, месяца, дня и времени, но не содержит смещения часового пояса относительно UTC.
  2. Форматирование при автоматической сериализации: Когда дата 1С передается в объект XDTO через WSПрокси, платформа автоматической конвертацией преобразует значение в строку вида YYYY-MM-DDTHH:mm:ss без суффикса Z (UTC) или указания смещения (например, +03:00).
  3. Особенности работы приемника (.NET / WCF / Oracle): Если веб-сервис написан на платформах вроде .NET WCF или работает с базой данных Oracle без явного указания часового пояса, он пытается распарсить полученную ISO-строку с использованием системных региональных настроек (Culture Settings, например en-US). В результате сервер интерпретирует строку по американскому формату YYYY-DD-MM или MM/dd/yyyy, меняя день и месяц местами.

Способ 1. Использование прямых HTTP-запросов вместо WSПрокси

Если веб-сервис жестко требователен к наличию временной зоны в 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Запроса(ТекущаяДата()));

Ответ = Соединение.ОтправитьДляОбработки(Запрос);
СтрокаОтвета = Ответ.ПолучитьТелоКакСтроку();

Способ 2. Явное создание значения XDTO с типом xs:dateTime

Рассмотрим случай, когда 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;

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

При работе со стандартными механизмами сериализации платформы полезно помнить о функции 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 сервиса), выделим рекомендуемую последовательность действий:

  1. Проверить, позволяет ли WSDL-схема передавать строковое значение в тег даты. Если да — передавать отформатированную ISO-строку с Z на конце.
  2. Если используется WSПрокси, формировать значение через ФабрикаXDTO.Создать() для типа xs:dateTime.
  3. В случаях, когда автосериализация 1С вырезает временную зону и приводит к ошибке приемника, использовать отправку SOAP-конверта через HTTPСоединение и HTTPЗапрос.
← На главную