В процессе разработки интеграций с внешними веб-сервисами, мобильными приложениями или при работе со сторонними базами данных перед разработчиком 1С часто возникает задача передать дату в универсальном числовом формате. Наиболее распространенным стандартом для этого является Unix Timestamp — количество секунд, прошедших с полуночи (00:00:00 UTC) 1 января 1970 года.
Если во встроенном языке 1С разность дат вычисляется простым арифметическим вычитанием, то внутри запросов действуют строгие правила СУБД. В этой статье мы подробно разберем, как правильно решить эту задачу непосредственно в запросе 1С, рассмотрим важные технические нюансы, связанные с переполнением типов данных, и научимся корректировать часовые пояса.
Для вычисления разницы между двумя датами в языке запросов 1С предусмотрена специализированная функция РАЗНОСТЬДАТ. Она принимает три параметра: начальную дату, конечную дату и единицу измерения (секунда, минута, час, день и т.д.).
Давайте посмотрим на классический пример запроса, который преобразует дату из поля Период в количество секунд с начала Unix-эпохи (1 января 1970 года):
ВЫБРАТЬ
РАЗНОСТЬДАТ(ДАТАВРЕМЯ(1970, 1, 1, 0, 0, 0), Таблица.Период, СЕКУНДА) КАК СекундыUnix
ИЗ
РегистрСведений.КурсыВалют КАК Таблица
В данном примере функция ДАТАВРЕМЯ(1970, 1, 1, 0, 0, 0) задает статическую точку отсчета. Функция РАЗНОСТЬДАТ вычитает эту точку из значения поля Период и возвращает результат в секундах. Это простое и изящное решение, однако при его использовании на реальных базах данных вы гарантированно столкнетесь с серьезными проблемами, которые мы разберем далее.
Это самая распространенная ловушка, в которую попадают разработчики. Если в вашей таблице поле Период окажется незаполненным (то есть будет содержать пустую дату 1С — 01.01.0001 00:00:00), запрос аварийно завершит работу.
Вы увидите ошибку СУБД, которая в MS SQL Server выглядит следующим образом: «Функция datediff вызвала переполнение. Слишком большое количество частей даты...».
Выясним причину этой ошибки: СУБД пытается вычислить разницу в секундах между 1-м годом нашей эры и 1970-м годом. Полученное значение составляет примерно 62 миллиарда секунд. Однако для возврата результата функции разности дат СУБД использует стандартное 32-битное знаковое целое число (тип INT), максимальное значение которого ограничено числом 2 147 483 647 (около 68 лет в секундах). Число 62 миллиарда физически не помещается в этот лимит, что и вызывает аппаратный сбой.
Решение проблемы: Чтобы предотвратить падение запроса, нам необходимо обязательно защитить вычисление с помощью условной конструкции ВЫБОР КОГДА. Мы будем производить расчет только в том случае, если целевая дата больше нашей базовой даты (1970 год):
ВЫБРАТЬ
ВЫБОР
КОГДА Таблица.Период > ДАТАВРЕМЯ(1970, 1, 1, 0, 0, 0)
ТОГДА РАЗНОСТЬДАТ(ДАТАВРЕМЯ(1970, 1, 1, 0, 0, 0), Таблица.Период, СЕКУНДА)
ИНАЧЕ 0
КОНЕЦ КАК СекундыUnixЗащищенные
ИЗ
РегистрСведений.КурсыВалют КАК Таблица
Этот подход гарантирует стабильность работы информационной системы, исключая любые аномалии с пустыми или некорректными датами в базе данных.
Классический Unix Timestamp по стандарту должен рассчитываться относительно всемирного координированного времени (UTC). В то же время в базах данных 1С даты документов, транзакций и записей регистров чаще всего хранятся по местному времени (времени сервера или пользователя).
Конструкция ДАТАВРЕМЯ(1970, 1, 1) в запросе создает «локальную» дату. Если мы просто вычислим разность, полученный результат будет отличаться от истинного Unix-времени на величину вашего часового пояса. Например, для Московского времени (UTC+3) погрешность составит ровно 3 часа (или 10 800 секунд).
Так как в языке запросов 1С отсутствуют встроенные функции для динамического определения и преобразования часовых поясов, мы должны скорректировать это смещение вручную. Сделать это лучше всего через передачу внешнего параметра в запрос.
Рассмотрим пример реализации такого запроса:
ВЫБРАТЬ
ВЫБОР
КОГДА Таблица.Период > ДАТАВРЕМЯ(1970, 1, 1, 0, 0, 0)
ТОГДА РАЗНОСТЬДАТ(ДАТАВРЕМЯ(1970, 1, 1, 0, 0, 0), Таблица.Период, СЕКУНДА) - &СмещениеВСекундах
ИНАЧЕ 0
КОНЕЦ КАК ИстинныйUnixTimestamp
ИЗ
РегистрСведений.КурсыВалют КАК Таблица
В коде встроенного языка перед выполнением этого запроса мы должны передать разницу между локальным временем и UTC:
// Определяем смещение локального времени относительно UTC в секундах
ТекущаяДатаЛокал = ТекущаяДата();
ТекущаяДатаUTC = УниверсальноеВремя(ТекущаяДатаЛокал);
СмещениеВСекундах = ТекущаяДатаЛокал - ТекущаяДатаUTC;
Запрос = Новый Запрос;
Запрос.Текст = "..."; // Текст запроса, приведенный выше
Запрос.УстановитьПараметр("СмещениеВСекундах", СмещениеВСекундах);
Результат = Запрос.Выполнить();
Такой подход обеспечивает математическую точность интеграции с любыми внешними веб-сервисами независимо от того, в каком часовом поясе физически запущен сервер 1С.
Если перед вами не стоит жесткая задача агрегации или группировки данных по секундам непосредственно внутри базы данных, производить расчеты в запросе не обязательно. Встроенный язык 1С предлагает гораздо более простой синтаксис для работы с временными интервалами.
При обычном вычитании одной даты из другой платформа автоматически возвращает результат в секундах. Давайте посмотрим на примеры:
Дата1С = ТекущаяДата();
КоличествоСекунд = Дата1С - '19700101000000';
Дата1С = ТекущаяДата();
КоличествоСекундUTC = УниверсальноеВремя(Дата1С) - '19700101000000';
Использование встроенного языка значительно упрощает чтение кода и полностью защищает от ошибок переполнения СУБД, так как платформа 1С автоматически масштабирует разрядность чисел при вычислениях на клиенте или сервере.
Многие современные веб-API (в частности, написанные на Node.js, Java, а также интеграции с криптобиржами) требуют передачи Timestamp не в секундах, а в миллисекундах. Это выражается в виде 13-значного числа.
Поскольку стандартный тип данных Дата в 1С хранит время с точностью до секунды, получить реальные физические миллисекунды из стандартного реквизита невозможно. Однако мы можем легко привести наш секундный формат к требуемому стандарту. Для этого результат разности дат достаточно умножить на 1000:
ВЫБРАТЬ
ВЫБОР
КОГДА Таблица.Период > ДАТАВРЕМЯ(1970, 1, 1, 0, 0, 0)
ТОГДА РАЗНОСТЬДАТ(ДАТАВРЕМЯ(1970, 1, 1, 0, 0, 0), Таблица.Период, СEКУНДА) * 1000
ИНАЧЕ 0
КОНЕЦ КАК TimestampВМиллисекундах
ИЗ
РегистрСведений.КурсыВалют КАК Таблица
При этом стоит учитывать, что три последних знака полученного числа всегда будут нулевыми (например, 1609459200000), но такой формат будет полностью валиден и успешно принят любой принимающей JSON-системой.
Подведем итоги: Конвертация даты в секунды в запросе 1С — это простая задача, требующая, однако, внимания к деталям. Всегда используйте конструкцию ВЫБОР КОГДА для предотвращения падения СУБД при обработке пустых дат и не забывайте корректировать часовой пояс, если ваша система обменивается данными с внешним миром.