Два знака неравенства в одном операторе в 1С: разбираем скрытую логику и неявное приведение типов

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

В процессе поддержки или рефакторинга чужого кода на платформе 1С:Предприятие разработчики иногда натыкаются на крайне загадочные конструкции. Одним из таких примеров является использование нескольких операторов сравнения в одной строке без явного указания скобок. Рассмотрим реальный пример из практики, который вызывает массу вопросов у программистов:


Если Дата <> НачалоДня(Дата) <> 25200 Тогда
    // Выполнение определенных действий
КонецЕсли;

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

1. Порядок вычислений: приоритет операторов и ассоциативность

Для того чтобы понять, как компилятор встроенного языка 1С видит данную строку, обратимся к правилам приоритета операций. Во встроенном языке 1С все операторы сравнения (такие как =, <>, <, >, <=, >=) имеют абсолютно одинаковый приоритет.

Когда в одном выражении встречаются операторы с одинаковым приоритетом и отсутствуют группирующие скобки, вычисление происходит строго слева направо (левая ассоциативность). Это означает, что платформа автоматически расставляет виртуальные скобки следующим образом:

Если (Дата <> НачалоДня(Дата)) <> 25200 Тогда

Таким образом, выражение разбивается на два последовательных этапа вычислений. Сначала система вычисляет результат левого сравнения, а затем сравнивает полученное значение с числом 25200.

2. Пошаговый разбор логики работы выражения

Рассмотрим детально каждый этап вычисления этой конструкции на конкретных примерах.

Шаг 1: Вычисление левой части выражения

Сначала выполняется сравнение Дата <> НачалоДня(Дата). Функция НачалоДня() сбрасывает время переданной даты в значение 00:00:00.

Здесь возможны два исхода:

  1. Если в переменной Дата есть время (например, 15.10.2023 14:30:00), то сравнение вернет значение Истина (так как дата со временем не равна дате с началом дня).
  2. Если в переменной Дата время ровно 00:00:00, то сравнение вернет значение Ложь (так как значения идентичны).

В результате первого шага наше выражение гарантированно сводится к сравнению значения типа Булево с числом:

(Истина ИЛИ Ложь) <> 25200

Шаг 2: Сравнение Булево с Числом (особенности приведения типов в 1С)

В классических языках программирования сравнение разных типов данных (например, логического значения и числа) часто вызывает ошибку времени выполнения (Run-time error). Однако встроенный язык 1С спроектирован иначе: сравнения на равенство (=) и неравенство (<>) между разнородными типами данных разрешены.

При сравнении значений разных типов в 1С действует важное и неочевидное правило: левый операнд неявно приводится к типу правого операнда.

Давайте посмотрим, как это отражается на нашем примере:

Система пытается привести левый логический операнд к типу Число по стандартным правилам платформы:

Теперь подставим полученные значения в итоговое сравнение:

  1. Если на первом шаге была получена Истина: получаем выражение 1 <> 25200. Результат: Истина.
  2. Если на первом шаге была получена Ложь: получаем выражение 0 <> 25200. Результат: Истина.

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

3. Другие примеры неявного приведения типов в 1С

Чтобы окончательно закрепить понимание того, как работает приведение левого операнда к типу правого, проанализируем еще несколько интересных примеров:

4. В чем заключался изначальный замысел автора кода?

Вряд ли разработчик сознательно писал условие, которое всегда будет истинным. Зачем в коде вообще фигурирует странное число 25200? Разгадка кроется в особенностях работы с типом данных Дата в 1С.

Вспомним важные правила работы с датами:

  1. При вычитании одной даты из другой платформа возвращает разность между ними в секундах (тип данных Число).
  2. Если из переменной Дата вычесть значение НачалоДня(Дата), мы получим точное количество секунд, прошедших с начала текущего дня (то есть время суток в секундах).
  3. Число 25200 — это ровно 7 часов времени (7 часов * 3600 секунд в одном часе = 25 200 секунд).

Очевидно, что первоначальный разработчик планировал написать следующую проверку: "Если время в переменной Дата НЕ равно 7 часам утра, то...".

Правильный вариант написания этой логики должен был выглядеть так:


Если (Дата - НачалоДня(Дата)) <> 25200 Тогда
    // Код выполняется, если время не равно 07:00:00
КонецЕсли;

Однако при написании кода или в процессе поспешного рефакторинга автозаменой была допущена банальная опечатка: вместо знака минус (-) разработчик случайно вставил оператор неравенства (<>). Из-за того, что платформа 1С лояльно относится к сравнению разных типов данных, эта критическая ошибка не была обнаружена на этапе компиляции и превратилась в скрытый логический баг.

5. Как исправить ошибку и сделать код более понятным?

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

Рассмотрим более элегантные и понятные варианты решения этой задачи.

Вариант 1: Использование функций работы со временем

Мы можем извлечь часы, минуты и секунды напрямую из даты с помощью встроенных функций платформы:


Если Час(Дата) <> 7 Или Минута(Дата) <> 0 Или Секунда(Дата) <> 0 Тогда
    // Время не равно 07:00:00
КонецЕсли;

Этот вариант максимально нагляден и легко читается любым разработчиком без калькулятора.

Вариант 2: Сравнение с пустой датой и добавлением времени

Также можно использовать приведение к началу дня и добавление нужного количества секунд в явном виде, что делает намерение кода очевидным:


Если Дата <> (НачалоДня(Дата) + 25200) Тогда
    // Время не равно 07:00:00 (7 утра)
КонецЕсли;

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

← На главную