В процессе поддержки или рефакторинга чужого кода на платформе 1С:Предприятие разработчики иногда натыкаются на крайне загадочные конструкции. Одним из таких примеров является использование нескольких операторов сравнения в одной строке без явного указания скобок. Рассмотрим реальный пример из практики, который вызывает массу вопросов у программистов:
Если Дата <> НачалоДня(Дата) <> 25200 Тогда
// Выполнение определенных действий
КонецЕсли;
На первый взгляд данный код кажется синтаксической ошибкой, которая должна приводить к остановке компиляции. Однако система успешно выполняет этот код, не выдавая никаких предупреждений. Давайте подробно разберем по шагам, как именно встроенный язык 1С интерпретирует это выражение, почему оно работает именно так, какие неявные механизмы приведения типов при этом задействуются и какая логическая задумка на самом деле скрывалась за этим странным кодом.
Для того чтобы понять, как компилятор встроенного языка 1С видит данную строку, обратимся к правилам приоритета операций. Во встроенном языке 1С все операторы сравнения (такие как =, <>, <, >, <=, >=) имеют абсолютно одинаковый приоритет.
Когда в одном выражении встречаются операторы с одинаковым приоритетом и отсутствуют группирующие скобки, вычисление происходит строго слева направо (левая ассоциативность). Это означает, что платформа автоматически расставляет виртуальные скобки следующим образом:
Если (Дата <> НачалоДня(Дата)) <> 25200 Тогда
Таким образом, выражение разбивается на два последовательных этапа вычислений. Сначала система вычисляет результат левого сравнения, а затем сравнивает полученное значение с числом 25200.
Рассмотрим детально каждый этап вычисления этой конструкции на конкретных примерах.
Сначала выполняется сравнение Дата <> НачалоДня(Дата). Функция НачалоДня() сбрасывает время переданной даты в значение 00:00:00.
Здесь возможны два исхода:
15.10.2023 14:30:00), то сравнение вернет значение Истина (так как дата со временем не равна дате с началом дня).В результате первого шага наше выражение гарантированно сводится к сравнению значения типа Булево с числом:
(Истина ИЛИ Ложь) <> 25200
В классических языках программирования сравнение разных типов данных (например, логического значения и числа) часто вызывает ошибку времени выполнения (Run-time error). Однако встроенный язык 1С спроектирован иначе: сравнения на равенство (=) и неравенство (<>) между разнородными типами данных разрешены.
При сравнении значений разных типов в 1С действует важное и неочевидное правило: левый операнд неявно приводится к типу правого операнда.
Давайте посмотрим, как это отражается на нашем примере:
25200 (тип Число).Булево, значения Истина или Ложь).Система пытается привести левый логический операнд к типу Число по стандартным правилам платформы:
Истина приводится к числу 1.Ложь приводится к числу 0.Теперь подставим полученные значения в итоговое сравнение:
1 <> 25200. Результат: Истина.0 <> 25200. Результат: Истина.Проанализировав оба сценария, мы приходим к удивительному выводу: при любых значениях переменной Дата итоговое условие в операторе Если всегда возвращает Истина. Код внутри этой конструкции будет выполняться абсолютно всегда, превращая проверку в бессмысленную фикцию.
Чтобы окончательно закрепить понимание того, как работает приведение левого операнда к типу правого, проанализируем еще несколько интересных примеров:
Истина + 2 = 3 — логическое Истина приводится к числу 1. Выражение превращается в 1 + 2 = 3, что дает результат Истина.Истина - 1 = 0 — логическое Истина приводится к 1. Получаем 1 - 1 = 0, результат Истина.1 <> 2 <> 1 — вычисляется слева направо. Сначала 1 <> 2 (результат Истина). Затем выражение принимает вид Истина <> 1. Левый операнд (Истина) приводится к типу правого (число 1). Получаем 1 <> 1. Результат: Ложь.1 <> 2 <> 3 — сначала 1 <> 2 (результат Истина). Затем Истина <> 3. Приводим Истина к числу (получаем 1). Вычисляем 1 <> 3. Результат: Истина.Вряд ли разработчик сознательно писал условие, которое всегда будет истинным. Зачем в коде вообще фигурирует странное число 25200? Разгадка кроется в особенностях работы с типом данных Дата в 1С.
Вспомним важные правила работы с датами:
Число).Дата вычесть значение НачалоДня(Дата), мы получим точное количество секунд, прошедших с начала текущего дня (то есть время суток в секундах).7 часов * 3600 секунд в одном часе = 25 200 секунд).Очевидно, что первоначальный разработчик планировал написать следующую проверку: "Если время в переменной Дата НЕ равно 7 часам утра, то...".
Правильный вариант написания этой логики должен был выглядеть так:
Если (Дата - НачалоДня(Дата)) <> 25200 Тогда
// Код выполняется, если время не равно 07:00:00
КонецЕсли;
Однако при написании кода или в процессе поспешного рефакторинга автозаменой была допущена банальная опечатка: вместо знака минус (-) разработчик случайно вставил оператор неравенства (<>). Из-за того, что платформа 1С лояльно относится к сравнению разных типов данных, эта критическая ошибка не была обнаружена на этапе компиляции и превратилась в скрытый логический баг.
Если перед вами стоит задача проверить время в переменной типа Дата, лучше избегать сложных математических расчетов в секундах, так как они снижают читаемость кода. Другие разработчики будут тратить лишнее время на перевод секунд в часы в уме.
Рассмотрим более элегантные и понятные варианты решения этой задачи.
Мы можем извлечь часы, минуты и секунды напрямую из даты с помощью встроенных функций платформы:
Если Час(Дата) <> 7 Или Минута(Дата) <> 0 Или Секунда(Дата) <> 0 Тогда
// Время не равно 07:00:00
КонецЕсли;
Этот вариант максимально нагляден и легко читается любым разработчиком без калькулятора.
Также можно использовать приведение к началу дня и добавление нужного количества секунд в явном виде, что делает намерение кода очевидным:
Если Дата <> (НачалоДня(Дата) + 25200) Тогда
// Время не равно 07:00:00 (7 утра)
КонецЕсли;
Данный подход гораздо безопаснее. Он не содержит скрытых ловушек с неявным приведением типов и сразу дает понять, что мы сравниваем конкретное время суток.