Почему одинаковые даты в 1С не равны и как избавиться от скрытых миллисекунд

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

Каждый разработчик 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. Однако для оператора сравнения это два разных момента времени.

Выясним, как проверить наличие миллисекунд. Самый простой способ — вычислить разность двух подозрительных дат в табло отладчика. Если результат не является целым числом, значит, одна из дат содержит дробную часть секунды.

Как «грязные» даты попадают в систему

Проанализируем основные источники появления дробной части в значениях времени. Даже если вы не создаете такие даты намеренно, они могут появиться следующими путями:

  1. Математические операции с датами. Прибавление к дате нецелого числа секунд автоматически создает миллисекунды.
    
    // Пример создания даты с миллисекундами
    ДатаСМиллисекундами = '20230101000000' + 0.20; 
    
  2. Внешние источники данных. При получении данных через ADO, COM-соединение или из внешних SQL-баз (где типы datetime или datetime2 имеют высокую точность), значения попадают в переменные 1С вместе с дробной частью.
  3. Веб-сервисы и JSON. При десериализации данных из форматов XML или JSON (стандарт ISO 8601), если во входящей строке были указаны миллисекунды, платформа сохранит их в переменной.
  4. Системные функции. Использование функции ТекущаяУниверсальнаяДатаВМиллисекундах() напрямую работает с повышенной точностью.

Проблемы в запросах и работе с СУБД

Наиболее коварно эта особенность проявляется при работе с запросами. Важно понимать различие между хранением в базе данных и параметрами запроса. В большинстве стандартных реквизитов документов и справочников платформа при записи принудительно отсекает дробную часть, записывая в SQL только целые секунды.

Однако, если мы передаем в параметр запроса переменную, содержащую миллисекунды, условие сравнения в секции ГДЕ или в ПО при соединении таблиц не сработает. СУБД будет сравнивать значение с .000 миллисекунд из таблицы и значение с .123 миллисекундами из параметра. Результат — пустая выборка.

Методы очистки даты от миллисекунд

Разберем по шагам, как гарантированно избавиться от дробной части секунды, чтобы обеспечить корректное сравнение. Рассмотрим три основных способа.

Способ 1: Использование функций приведения периода

Самый простой и очевидный метод — использование функции НачалоСекунды() или НачалоДня(). Если нам нужно сохранить время, но убрать миллисекунды, мы используем приведение к началу секунды. Однако помните, что встроенной функции НачалоСекунды() во встроенном языке нет (она есть только в запросах), поэтому для кода чаще используют обнуление через начало дня, если время не критично.


// Если время не важно
ОчищеннаяДата = НачалоДня(ИсходнаяДата); 

Способ 2: Преобразование через строку

Это универсальный и надежный метод. При преобразовании даты в строковое представление (которое по умолчанию не поддерживает миллисекунды) и последующем обратном преобразовании в дату, дробная часть безвозвратно теряется.


// Очистка через строковое представление
ОчищеннаяДата = Дата(Строка(ИсходнаяДата));

Этот метод гарантирует, что в переменной ОчищеннаяДата останутся только целые секунды, и она будет корректно сравниваться с любыми другими датами.

Способ 3: Математический метод (остаток от деления)

Для тех, кто предпочитает более элегантные математические решения, можно использовать расчет остатка. Поскольку дата в 1С — это количество секунд с начала времен, остаток от деления на 1 даст нам дробную часть секунды.


// Вычитаем дробную часть из даты
ОчищеннаяДата = ИсходнаяДата - (ИсходнаяДата % 1);

Этот метод работает быстрее, чем строковое преобразование, и отлично подходит для использования в циклах с большим количеством итераций.

Особенности работы в запросах

Посмотрим на пример того, как правильно обрабатывать параметры в запросах, если есть риск попадания «грязных» дат. Рекомендуется всегда использовать функцию НАЧАЛОПЕРИОДА внутри текста запроса для входящих параметров.


Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |   Продажи.Ссылка
    |ИЗ
    |   Документ.РеализацияТоваровУслуг КАК Продажи
    |ГДЕ
    |   Продажи.Дата = НАЧАЛОПЕРИОДА(&ПараметрДата, СЕКУНДА)";

Запрос.УстановитьПараметр("ПараметрДата", ТекущаяДатаСМиллисекундами);

Такой подход защитит ваш код от «невидимых» ошибок и обеспечит стабильную работу системы даже при интеграции с внешними источниками данных. Проанализировав данную особенность, мы видим, что «неверие в 1С» часто вызвано просто глубоким техническим устройством платформы, которое необходимо учитывать при разработке сложных систем.

← На главную