При интеграции 1С с внешними системами (веб-сервисами, API, XML-выгрузками) разработчики часто сталкиваются со строками вида PT10H37M17S. На первый взгляд это может показаться специфическим часовым поясом или зашифрованным временем, однако это стандартное представление длительности (интервала времени) согласно международному стандарту ISO 8601. В этой статье мы подробно разберем, как правильно интерпретировать такие строки и преобразовывать их в привычные типы данных 1С, такие как Число (секунды) или Дата.
Прежде чем приступать к написанию кода, проанализируем, из чего состоит строка PT10H37M17S. Согласно стандарту, каждый символ в этой записи имеет строгое значение:
Важно учитывать, что стандарт допускает отсутствие некоторых компонентов. Например, строка PT1H5S означает 1 час и 5 секунд (минуты пропущены). Также значения могут быть больше 24 для часов или больше 60 для минут, а иногда могут встречаться дробные значения (например, PT0.5H). В контексте 1С наша задача обычно сводится к получению общего количества секунд, которые затем можно прибавить к дате или использовать в расчетах.
Рассмотрим самый доступный способ — написание функции для ручного разбора строки. Мы не можем просто брать фиксированные символы по индексам, так как количество цифр перед буквами может меняться (например, 1 или 100 часов). Разберем алгоритм, который будет искать вхождения букв H, M и S и извлекать числовые значения перед ними.
Для реализации создадим функцию, которая последовательно обрабатывает строку. Выясним причину, почему это надежнее простого разделения: нам нужно игнорировать префиксы P и T, но четко определять, к чему относится число.
Функция ПреобразоватьISO8601ВСекунды(Знач ВходнаяСтрока) Экспорт
Результат = 0;
// Удаляем начальный префикс периода
Если Лев(ВходнаяСтрока, 1) = "P" Тогда
ВходнаяСтрока = Сред(ВходнаяСтрока, 2);
КонецЕсли;
// Ищем разделитель времени
ПозТ = Найти(ВходнаяСтрока, "T");
Если ПозТ > 0 Тогда
СтрокаВремени = Сред(ВходнаяСтрока, ПозТ + 1);
Иначе
СтрокаВремени = ВходнаяСтрока;
КонецЕсли;
// Массив для поиска компонентов времени
Части = "HMS";
Для Ном = 1 По СтрДлина(Части) Цикл
Буква = Сред(Части, Ном, 1);
ПозБуквы = Найти(СтрокаВремени, Буква);
Если ПозБуквы > 0 Тогда
// Извлекаем все цифры перед буквой
Счетчик = ПозБуквы - 1;
СтрЧисло = "";
Пока Счетчик > 0 Цикл
Символ = Сред(СтрокаВремени, Счетчик, 1);
// Проверяем, является ли символ цифрой или точкой (для дробных чисел)
Если Найти("0123456789.", Символ) > 0 Тогда
СтрЧисло = Символ + СтрЧисло;
Счетчик = Счетчик - 1;
Иначе
Прервать;
КонецЕсли;
КонецЦикла;
Значение = Число(СтрЧисло);
// Конвертируем в секунды в зависимости от компонента
Если Буква = "H" Тогда
Результат = Результат + Значение * 3600;
ИначеЕсли Буква = "M" Тогда
Результат = Результат + Значение * 60;
ИначеЕсли Буква = "S" Тогда
Результат = Результат + Значение;
КонецЕсли;
КонецЕсли;
КонецЦикла;
Возврат Результат;
КонецФункции
Проанализируем более "продвинутый" метод. Поскольку формат ISO 8601 является родным для XML, в платформе 1С реализована поддержка типа данных xsd:duration. Если ваша строка получена из XML-документа, 1С может автоматически преобразовать её при чтении через фабрику XDTO или сериализатор.
Рассмотрим пример, когда у нас есть строка и мы хотим заставить платформу распознать её как длительность. Посмотрим на этот код:
// Создаем тип данных xsd:duration через фабрику XDTO
ТипДлительность = ФабрикаXDTO.Тип("http://www.w3.org/2001/XMLSchema", "duration");
Попытка
ОбъектДлительность = ФабрикаXDTO.Создать(ТипДлительность, "PT10H37M17S");
// Платформа 1С может возвращать длительность в разных формах в зависимости от версии
// Чаще всего это специализированный объект или просто строка при сериализации
// Но данный метод полезен при чтении готовых XML файлов
Исключение
Сообщить("Неверный формат длительности");
КонецПопытки;
Важно: Прямое преобразование xsd:duration в число секунд через XDTO в 1С не всегда возвращает примитивный тип Число. Чаще этот способ используется при работе с веб-сервисами, где типизация происходит автоматически.
Для максимально надежного извлечения данных, особенно если строки могут содержать дни (D) или недели (W), целесообразно использовать объект VBScript.RegExp. Разберем по шагам, как настроить шаблон поиска.
Функция ПолучитьСекундыЧерезRegExp(СтрокаISO)
RegExp = Новый COMОбъект("VBScript.RegExp");
RegExp.IgnoreCase = Истина;
RegExp.Global = Истина;
// Шаблон ищет числа перед буквами H, M, S
RegExp.Pattern = "(?:(\d+)H)?(?:(\d+)M)?(?:(\d+)S)?";
Matches = RegExp.Execute(СтрокаISO);
ВсегоСекунд = 0;
Если Matches.Count > 0 Тогда
Группы = Matches.Item(0).SubMatches;
Часы = ?(Группы.Item(0) = Неопределено, 0, Число(Группы.Item(0)));
Минуты = ?(Группы.Item(1) = Неопределено, 0, Число(Группы.Item(1)));
Секунды = ?(Группы.Item(2) = Неопределено, 0, Число(Группы.Item(2)));
ВсегоСекунд = (Часы * 3600) + (Минуты * 60) + Секунды;
КонецЕсли;
Возврат ВсегоСекунд;
КонецФункции
В 1С тип Дата всегда содержит и календарную дату, и время. Если ваша цель — просто отобразить 10:37:17 в поле формы, вы можете прибавить полученное количество секунд к "пустой" дате (начало времен). Проанализируем ситуацию: в 1С пустая дата — это 01.01.0001 00:00:00.
Посмотрим на пример кода для вывода времени:
Секунды = ПреобразоватьISO8601ВСекунды("PT10H37M17S");
// Прибавляем к пустой дате
РезультатДата = '00010101' + Секунды;
// Теперь РезультатДата будет содержать 01.01.0001 10:37:17
// При выводе в формате "ДЛФ=T" (Только время) пользователь увидит "10:37:17"
Сообщить(Формат(РезультатДата, "ДЛФ=T"));
Подведем итоги: Мы выяснили, что префикс PT не имеет отношения к часовым поясам (Pacific Time), а является индикатором начала записи времени в периоде ISO 8601. Самым гибким способом решения задачи в 1С остается строковый парсинг или использование регулярных выражений, так как они позволяют корректно обрабатывать необязательные компоненты строки.