Каждый разработчик 1С хотя бы раз сталкивался с ситуацией, когда программная логика ведет себя необъяснимо: два значения даты в табло отладчика выглядят абсолютно идентичными, но условие Если Дата1 = Дата2 Тогда возвращает Ложь. Еще хуже, когда эта проблема проявляется в запросах — соединение по полям дат просто не срабатывает, хотя визуально данные в таблицах совпадают до секунды. Проанализируем, почему это происходит, и разберем способы решения данной проблемы.
Разберем природу типа Дата в платформе 1С:Предприятие. Вопреки распространенному мнению о том, что минимальной единицей времени в 1С является секунда, это не совсем так. На уровне платформы (в оперативной памяти) переменная типа Дата (DateTime) представляет собой значение григорианского календаря с точностью до 0,1 миллисекунды.
Рассмотрим ситуацию: отладчик 1С при выводе значения даты в табло использует стандартную маску форматирования, которая отсекает всё, что меньше секунды. Таким образом, значения 01.01.2023 12:00:00.123 и 01.01.2023 12:00:00.000 в окне отладчика будут выглядеть одинаково: 01.01.2023 12:00:00. Однако для оператора сравнения это два разных момента времени.
Выясним, как проверить наличие миллисекунд. Самый простой способ — вычислить разность двух подозрительных дат в табло отладчика. Если результат не является целым числом, значит, одна из дат содержит дробную часть секунды.
Проанализируем основные источники появления дробной части в значениях времени. Даже если вы не создаете такие даты намеренно, они могут появиться следующими путями:
// Пример создания даты с миллисекундами
ДатаСМиллисекундами = '20230101000000' + 0.20;
ADO, COM-соединение или из внешних SQL-баз (где типы datetime или datetime2 имеют высокую точность), значения попадают в переменные 1С вместе с дробной частью.ТекущаяУниверсальнаяДатаВМиллисекундах() напрямую работает с повышенной точностью.Наиболее коварно эта особенность проявляется при работе с запросами. Важно понимать различие между хранением в базе данных и параметрами запроса. В большинстве стандартных реквизитов документов и справочников платформа при записи принудительно отсекает дробную часть, записывая в SQL только целые секунды.
Однако, если мы передаем в параметр запроса переменную, содержащую миллисекунды, условие сравнения в секции ГДЕ или в ПО при соединении таблиц не сработает. СУБД будет сравнивать значение с .000 миллисекунд из таблицы и значение с .123 миллисекундами из параметра. Результат — пустая выборка.
Разберем по шагам, как гарантированно избавиться от дробной части секунды, чтобы обеспечить корректное сравнение. Рассмотрим три основных способа.
Самый простой и очевидный метод — использование функции НачалоСекунды() или НачалоДня(). Если нам нужно сохранить время, но убрать миллисекунды, мы используем приведение к началу секунды. Однако помните, что встроенной функции НачалоСекунды() во встроенном языке нет (она есть только в запросах), поэтому для кода чаще используют обнуление через начало дня, если время не критично.
// Если время не важно
ОчищеннаяДата = НачалоДня(ИсходнаяДата);
Это универсальный и надежный метод. При преобразовании даты в строковое представление (которое по умолчанию не поддерживает миллисекунды) и последующем обратном преобразовании в дату, дробная часть безвозвратно теряется.
// Очистка через строковое представление
ОчищеннаяДата = Дата(Строка(ИсходнаяДата));
Этот метод гарантирует, что в переменной ОчищеннаяДата останутся только целые секунды, и она будет корректно сравниваться с любыми другими датами.
Для тех, кто предпочитает более элегантные математические решения, можно использовать расчет остатка. Поскольку дата в 1С — это количество секунд с начала времен, остаток от деления на 1 даст нам дробную часть секунды.
// Вычитаем дробную часть из даты
ОчищеннаяДата = ИсходнаяДата - (ИсходнаяДата % 1);
Этот метод работает быстрее, чем строковое преобразование, и отлично подходит для использования в циклах с большим количеством итераций.
Посмотрим на пример того, как правильно обрабатывать параметры в запросах, если есть риск попадания «грязных» дат. Рекомендуется всегда использовать функцию НАЧАЛОПЕРИОДА внутри текста запроса для входящих параметров.
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Продажи.Ссылка
|ИЗ
| Документ.РеализацияТоваровУслуг КАК Продажи
|ГДЕ
| Продажи.Дата = НАЧАЛОПЕРИОДА(&ПараметрДата, СЕКУНДА)";
Запрос.УстановитьПараметр("ПараметрДата", ТекущаяДатаСМиллисекундами);
Такой подход защитит ваш код от «невидимых» ошибок и обеспечит стабильную работу системы даже при интеграции с внешними источниками данных. Проанализировав данную особенность, мы видим, что «неверие в 1С» часто вызвано просто глубоким техническим устройством платформы, которое необходимо учитывать при разработке сложных систем.