В практике разработки на платформе 1С часто возникает задача определить время создания документа или иного объекта, особенно если в самой конфигурации нет соответствующего реквизита. Платформа 1С:Предприятие генерирует ссылки (UUID) на основе стандарта RFC 4122 (версия 1), который включает в себя метку времени. В данной статье мы подробно разберем, как извлечь эту дату и эффективно записать ее в таблицу значений, избегая типичных ошибок производительности.
Прежде чем переходить к коду, проанализируем ситуацию. Уникальный идентификатор версии 1 (UUID v1) состоит из временной метки (timestamp), которая представляет собой количество 100-наносекундных интервалов с 15 октября 1582 года (начало григорианского календаря). Платформа 1С переставляет байты в этом идентификаторе, поэтому для корректного извлечения даты нам необходимо выполнить определенные манипуляции со строковым представлением GUID.
Рассмотрим функцию, которая принимает ссылку на объект и возвращает дату его создания. Мы будем использовать строковые операции для перестановки частей уникального идентификатора, чтобы получить корректное шестнадцатеричное число, соответствующее времени.
Функция ДатаСозданияСсылки(Ссылка) Экспорт
Если НЕ ЗначениеЗаполнено(Ссылка) Тогда
Возврат Неопределено;
КонецЕсли;
Момент = Дата(1, 1, 1);
Попытка
// Получаем строковое представление UUID
УИ = Строка(Ссылка.УникальныйИдентификатор());
УИ = ВРег(СтрЗаменить(УИ, "-", ""));
// Проверяем 13-й символ: он должен быть равен "1" для UUID v1
Если Сред(УИ, 13, 1) = "1" Тогда
// Переставляем части строки согласно алгоритму формирования метки времени
Значение16 = ВРег(Сред(УИ, 14, 3) + Сред(УИ, 9, 4) + Сред(УИ, 1, 8));
// Преобразуем из 16-ричной системы в 10-тичную
Цифры16 = "0123456789ABCDEF";
Значение10 = 0;
Длина16 = СтрДлина(Значение16);
Для Ном = 1 По Длина16 Цикл
Значение10 = Значение10 + Pow(16, Длина16 - Ном) * (Найти(Цифры16, Сред(Значение16, Ном, 1)) - 1);
КонецЦикла;
// Конвертируем интервалы в секунды и добавляем к базовой дате
Значение10 = Значение10 / 10000000;
Момент = Дата(1582, 10, 15, 3, 0, 0) + Значение10;
КонецЕсли;
Исключение
// В случае ошибки возвращаем пустую дату
КонецПопытки;
Возврат Момент;
КонецФункции
Обратите внимание на важный нюанс: в коде выше используется смещение 3 часа (в строке Дата(1582, 10, 15, 3, 0, 0)). Это ручная корректировка под часовой пояс (например, MSK). В идеале следует получать время в UTC и использовать функцию МестноеВремя() для корректного отображения пользователю.
Теперь выясним, как правильно организовать цикл по документам и заполнить таблицу значений (ТЗ). Распространенная ошибка начинающих разработчиков — попытка добавлять новые строки в ту же таблицу, по которой идет обход, что приводит к бесконечным циклам или некорректным данным.
Рассмотрим правильный подход к заполнению ТЗ, где мы сначала получаем список заказов, а затем дополняем таблицу колонкой с датой:
// Предположим, Результат2 — это таблица значений с колонкой "ЗаказПокупателя"
Результат2.Колонки.Добавить("Момент", Новый ОписаниеТипов("Дата"));
Для Каждого СтрокаТЗ Из Результат2 Цикл
// Вызываем нашу функцию для каждой строки
СтрокаТЗ.Момент = ДатаСозданияСсылки(СтрокаТЗ.ЗаказПокупателя);
КонецЦикла;
Если в вашем отчете тысячи строк, код из первого примера может работать очень медленно. Проанализируем причины низкой скорости и исправим их:
ЧислоИзШестнадцатеричнойСтроки().Ссылка.УникальныйИдентификатор (как свойство) лучше использовать Ссылка.УникальныйИдентификатор() (как метод), чтобы избежать лишних обращений к базе данных.ЗаполнитьЗначенияСвойств со структурами внутри цикла создает лишнюю нагрузку на память. Прямое присваивание Строка.Момент = ... гораздо эффективнее.Посмотрим на оптимизированный вариант функции конвертации:
Функция ДатаСозданияСсылкиОптимизированная(Ссылка) Экспорт
УИ = СтрЗаменить(Строка(Ссылка.УникальныйИдентификатор()), "-", "");
Если Сред(УИ, 13, 1) <> "1" Тогда
Возврат Дата(1, 1, 1);
КонецЕсли;
// Формируем правильную последовательность байтов
Стр16 = Сред(УИ, 14, 3) + Сред(УИ, 9, 4) + Сред(УИ, 1, 8);
// Используем встроенную функцию платформы для скорости
Тики = ЧислоИзШестнадцатеричнойСтроки(Стр16);
Секунды = Тики / 10000000;
// 12219292800 — это разница в секундах между 15.10.1582 и 01.01.0001
Возврат Дата(1, 1, 1) + (Секунды - 12219292800);
КонецФункции
Проанализируем ситуацию с точки зрения Системы Компоновки Данных (СКД). Вместо того чтобы выполнять постобработку таблицы значений в коде формы, мы можем вынести расчет даты на уровень схемы компоновки. Это позволит отчету работать быстрее и сделает код чище.
Для этого выполним следующие шаги:
ДатаСозданияСсылки в общий модуль с включенным признаком "Сервер" и "Вызов сервера".ДатаСоздания.ИмяОбщегоМодуля.ДатаСозданияСсылки(ЗаказПокупателя).Этот метод предпочтительнее, так как 1С сама оптимизирует выборку данных и вывод результата.
Важно помнить, что дата, зашитая в GUID, — это момент генерации самой ссылки, а не момент записи объекта в базу данных. Рассмотрим ситуации, когда эти даты могут различаться:
УстановитьСсылкуНового() с произвольным идентификатором, извлеченная дата может быть бессмысленной.Таким образом, мы разобрали несколько способов решения задачи. Для простых разовых задач подойдет прямой обход таблицы значений, а для высоконагруженных отчетов лучше использовать возможности СКД и встроенные функции платформы для работы с шестнадцатеричными числами.