Почему при чтении даты из XML через XMLЗначение смещается время и как получить точную дату?

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

При интеграции с внешними системами через XML-файлы разработчики часто сталкиваются с ситуацией, когда дата в файле представлена в формате со смещением часового пояса (например, <date>2017-10-31T12:24:35.387+00:00</date>), однако после выполнения метода XMLЗначение(Тип("Дата"), ...) время в 1С автоматически увеличивается на несколько часов (например, становится 31.10.2017 15:24:35).

Разберем детально, почему платформа выполняет такое преобразование, какие ограничения накладывает примитивный тип Дата в 1С и как правильно получить исходное или всемирное координированное время (UTC).

Принцип работы десериализатора XML в платформе 1С

Поведение платформы при десериализации строковых данных в тип xs:dateTime строго соответствует стандарту XML Schema:

  1. Наличие часового пояса в XML: Если в строке присутствует информация о часовом поясе (суффикс Z, +00:00, +03:00 и т. д.), 1С воспринимает это значение как абсолютный момент времени и автоматически конвертирует его в локальное время текущего сеанса пользователя — ЧасовойПоясСеанса(). Например, если время сеанса настроено на Московский часовой пояс (+3), то время 12:24:35+00:00 логично преобразуется в 15:24:35.
  2. Отсутствие часового пояса в XML: Если строка времени не содержит суффикса смещения (например, 2017-10-31T12:24:35), платформа считает дату локальной и загружает часы и минуты ровно так, как они записаны в файле.

Ограничения типа Дата в 1С:

  1. Примитивный тип Дата в 1С не хранит в себе информацию о часовом поясе. После конвертации строки в дату знание об исходном смещении безвозвратно теряется.
  2. Платформа 1С хранит время с точностью до секунды, поэтому доли секунд (миллисекунды .387) при преобразовании в тип Дата отбрасываются.

Рассмотрим основные способы решения задачи в зависимости от требований бизнес-логики.

Способ 1. Приведение прочитанной даты обратно к формату UTC

Если во входящем XML время гарантированно указано в нулевом часовом поясе (UTC / +00:00 / Z) и требуется получить именно значение 12:24:35, применим функцию УниверсальноеВремя к результату вызова XMLЗначение:


ЗначениеУзла = ЧтениеXML.Значение; // "2017-10-31T12:24:35.387+00:00"
ДатаСеанса = XMLЗначение(Тип("Дата"), ЗначениеУзла); // Получаем 31.10.2017 15:24:35 (для пояса +3)
ДатаUTC = УниверсальноеВремя(ДатаСеанса, ЧасовойПоясСеанса()); // Получаем 31.10.2017 12:24:35

Важное замечание: Этот способ возвращает исходное строковое время только в том случае, если в XML был передан именно пояс +00:00. Если файл будет содержать другое смещение (например, +05:00), функция УниверсальноеВремя пересчитает его в истинный UTC, а не вернет локальное время системы-отправителя.

Способ 2. Временная смена часового пояса сеанса

При пакетной обработке больших файлов XML, содержащих данные в UTC, эффективным решением является программная установка часового пояса сеанса перед чтением:


ТекущийПояс = ЧасовойПоясСеанса();
УстановитьЧасовойПоясСеанса("UTC");

Попытка
    // При чтении дат через XMLЗначение смещение происходить не будет
    ДатаИзXML = XMLЗначение(Тип("Дата"), ЧтениеXML.Значение);
    // 31.10.2017 12:24:35
Исключение
    УстановитьЧасовойПоясСеанса(ТекущийПояс);
    ВызватьИсключение;
КонецПопытки;

УстановитьЧасовойПоясСеанса(ТекущийПояс);

Способ 3. Строковый разбор даты (получение «как есть»)

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


СтрокаДаты = ЧтениеXML.Значение; // "2017-10-31T12:24:35.387+00:00"

// Выделяем первые 19 символов вида "YYYY-MM-DDTHH:MM:SS"
ТолькоДатаВремя = Лев(СтрокаДаты, 19);

// Удаляем разделители для приведения к формату "YYYYMMDDHHMMSS"
ТолькоДатаВремя = СтрЗаменить(ТолькоДатаВремя, "-", "");
ТолькоДатаВремя = СтрЗаменить(ТолькоДатаВремя, "T", "");
ТолькоДатаВремя = СтрЗаменить(ТолькоДатаВремя, ":", "");

РезультатДата = Дата(ТолькоДатаВремя); // Получаем 31.10.2017 12:24:35

Способ 4. Корректная обратная выгрузка даты в XML в формате UTC

Проанализируем также обратную задачу: стандартный метод XMLСтрока(Дата) выгружает дату без информации о часовом поясе (например, 2017-10-31T12:24:35). Если принимающая внешняя система строго требует формат даты по стандарту ISO 8601 с признаком UTC (суффикс Z), формируем строку вручную через функцию Формат:


ТекущийМоментUTC = УниверсальноеВремя(ТекущаяДатаСеанса());
СтрокаДляXML = Формат(ТекущийМоментUTC, "ДФ=yyyy-MM-ddTHH:mm:ssZ");
// Результат: "2017-10-31T12:24:35Z"
← На главную