При интеграции 1С:Предприятия с внешними информационными системами (веб-сервисами, государственными порталами, базами данных SQL) разработчики часто сталкиваются с жесткими требованиями к форматам обмена. Одним из наиболее коварных объектов обмена является дата и время. В XML-документах стандарт представления даты-времени регулируется спецификацией W3C XML Schema (тип dateTime). Строка вида 2018-07-09T16:33:04.6396109+03:00 нередко вызывает вопросы у начинающих разработчиков.
В этой статье мы подробно разберем структуру этой строки, выясним назначение загадочных цифр после точки, снимем путаницу с часовыми поясами и рассмотрим готовые программные решения для корректной выгрузки даты и времени из 1С.
Рассмотрим детально структуру полной даты в формате XML:
2018-07-09T16:33:04.6396109+03:00
Часть строки после времени секунд перед знаком часового пояса (в нашем примере это .6396109) представляет собой дробную часть секунды (fractional seconds). Давайте разберем, откуда берется именно такое количество символов и зачем это нужно:
16:33:04), так и с любым количеством знаков (например, 16:33:04.63 или 16:33:04.123456789).datetime в MS SQL Server поддерживает точность лишь до 1/300 секунды (около 3.33 миллисекунды). Из-за этого при записи числа округляются до тысячных долей. Более современный тип datetime2 полноценно поддерживает точность до 7 знаков (100 нс), что идеально соответствует нашему примеру.Если ваша внешняя система не требует микросекундной точности для сортировки транзакций внутри одной секунды, эти цифры можно безболезненно занулять (например, приводить к виду .0000000) или отбрасывать дробную часть целиком.
Часто возникает путаница: как соотносится указанный в XML часовой пояс с реальным локальным временем? Давайте проанализируем пример:
2018-07-09T16:33:04+03:00
Правило стандарта W3C гласит: время, указанное до знака плюс/минус, является местным временем для того часового пояса, который указан после этого знака.
2018-07-09T16:33:04+03:00 означает: на локальных часах в этот момент было ровно 16 часов, 33 минуты и 4 секунды, а само событие происходило в часовом поясе со смещением UTC+3 (например, московское время).16:33:04 - 03:00 = 13:33:04 UTC. В формате XML это время будет записано как 2018-07-09T13:33:04Z (буква Z означает нулевое смещение, Zulu time).2018-07-09T16:33:04), такое время считается «неопределенным» (unspecified). Это крайне опасный формат для интеграции, так как принимающая система может интерпретировать его произвольно (например, посчитать его временем по Гринвичу или применить часовой пояс своего сервера).Платформа 1С имеет свою специфику при импорте и экспорте значений типа Дата через XML. Выясним эти особенности подробнее.
При чтении XML (Импорт в 1С):
Когда встроенные механизмы 1С (например, XMLЗначение или парсер XDTO) считывают строку даты с указанным часовым поясом, платформа автоматически преобразует ее к часовому поясу текущего сеанса 1С (функция ЧасовойПоясСеанса()).
Представим ситуацию: в XML-файле пришло значение 2017-10-31T12:24:35+00:00 (время по Гринвичу). Если ваш сеанс 1С запущен в Москве (UTC+3), то при чтении функция 1С вернет дату 31.10.2017 15:24:35. Платформа самостоятельно прибавила 3 часа, чтобы локальный пользователь увидел корректное время на своем экране. Это очень удобно и предотвращает ошибки временных сдвигов.
При записи XML (Экспорт из 1С):
Тип Дата в системе 1С:Предприятие является простым типом. Физически в базе данных он хранит только дату и время (год, месяц, день, час, минута, секунда), но не содержит информации о часовом поясе. По этой причине стандартный СериализаторXDTO или базовая функция XMLСтрока() выгружают дату «как есть», без указания смещения часового пояса (например, просто 2018-07-09T16:33:04).
Если внешняя система строго требует передавать дату со смещением таймзоны (или в универсальном формате UTC), стандартная сериализация XDTO «из коробки» нам не поможет. Рассмотрим три практических способа решения этой задачи.
В современной веб-разработке стандартом де-факто считается передача времени в универсальном формате UTC с суффиксом Z на конце. Это исключает любые разночтения. Перед выгрузкой мы переводим нашу локальную дату в универсальное время и вручную добавляем символ часового пояса.
Рассмотрим пример кода:
// Предположим, у нас есть локальная дата документа
ЛокальнаяДата = Объект.Дата; // Например, 09.07.2018 16:33:04
// Переводим локальное время сеанса в универсальное UTC
УниверсальнаяДата = УниверсальноеВремя(ЛокальнаяДата);
// Сериализуем в стандартную строку XML и добавляем маркер UTC
СтрокаXML = XMLСтрока(УниверсальнаяДата) + "Z";
// Результат: "2018-07-09T13:33:04Z"
Если принимающая сторона требует передавать именно локальное время со смещением (например, +03:00), нам придется вычислить разницу часовых поясов сеанса вручную, так как встроенный метод Формат() в 1С не поддерживает маску вывода смещения таймзоны (символы zzz).
Давайте разберем по шагам, как написать универсальную функцию для формирования такой строки:
Функция ПолучитьДатуВФорматеXMLСЧасовымПоясом(Знач ИсходнаяДата) Экспорт
// Получаем текущее локальное и универсальное время для расчета смещения
ТекущееВремяСеанса = ТекущаяДатаСеанса();
ТекущееВремяUTC = УниверсальноеВремя(ТекущееВремяСеанса);
// Вычисляем разницу часовых поясов в секундах
РазницаСекунд = ТекущееВремяСеанса - ТекущееВремяUTC;
СмещениеЧасов = Цел(РазницаСекунд / 3600);
СмещениеМинут = Цел((РазницаСекунд % 3600) / 60);
// Формируем строковое представление смещения часового пояса
ЗнакСмещения = ?(РазницаСекунд >= 0, "+", "-");
СтрокаСмещения = ЗнакСмещения
+ Формат(Абс(СмещениеЧасов), "ЧЦ=2; ЧДЦ=0; ЧГ=0")
+ ":"
+ Формат(Абс(СмещениеМинут), "ЧЦ=2; ЧДЦ=0; ЧГ=0");
// Собираем итоговую XML-строку без миллисекунд
СтрокаДаты = Формат(ИсходнаяДата, "ДФ=yyyy-MM-ddTHH:mm:ss");
Возврат СтрокаДаты + СтрокаСмещения;
КонецФункции
Данный метод вернет строку в точном соответствии со спецификацией W3C, например: 2018-07-09T16:33:04+03:00. Это решение является полностью автономным и не зависит от сторонних библиотек.
Если выгрузка XML-документа происходит с использованием фабрики XDTO и валидацией по схеме (XSD), то попытка записать сформированную вручную строку в свойство с типом dateTime вызовет ошибку несоответствия типов платформы 1С.
Чтобы обойти это ограничение, проанализируем следующий прием:
string (строка) вместо стандартного dateTime.-\d{2}-\d{2}T\d{2}:\d{2}:\d{2(Z|(\+|-)\d{2}:\d{2}).Используя предложенные методики, вы сможете решить любые задачи, связанные с передачей и преобразованием временных меток в интеграционных сценариях любой сложности.