Как корректно проверить, находится ли текущее время в заданном диапазоне в 1С:Предприятие?

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

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

В данной статье мы с вами подробно рассмотрим ситуацию, возникшую при тестировании 1С:Напарника – интеллектуального помощника для разработчиков 1С, встроенного в 1С:Enterprise Development Tools (EDT). Эта ситуация ярко демонстрирует как потенциальные возможности, так и существенные недостатки ИИ-помощников в генерации корректного, оптимального и соответствующего стандартам 1С кода. Мы проанализируем различные варианты решения одной и той же задачи, выявленные в ходе обсуждения на форуме, от заведомо некорректных до наиболее эффективных.

Первое знакомство: неочевидные ошибки 1С:Напарника

Наш коллега задал 1С:Напарнику, чату с ИИ, работающему с платформой 1С, достаточно простую и распространенную задачу: написать код, который проверяет, находится ли текущее время в диапазоне между 8 и 12 часами. Казалось бы, такая элементарная проверка должна быть легко реализована любым ИИ-ассистентом. Однако полученный результат вызвал удивление и породил вопросы о реальном уровне "знаний" 1С у этого помощника. Рассмотрим код, предложенный 1С:Напарником в первом варианте:


// Функция проверяет, находится ли текущее время в диапазоне с 8:00 до 12:00.
//
// Возвращаемое значение:
// Булево - Истина, если время в диапазоне, иначе Ложь.
//
Функция ТекущееВремяВДиапазонеРабочихЧасов Экспорт
    ТекущаяДата = ТекущаяДата; // Получаем полную текущую дату и время
    ТекущееВремя = ТекущаяДата - Дата(ТекущаяДата)); // Выделяем только время
    НачалоДиапазона = "08:00";
    КонецДиапазона = "12:00:00";
    Если ТекущееВремя >= НачалоДиапазона И ТекущееВремя = 8 И Час < 12 Тогда
        Сообщить("Текущее время в диапазоне 8-12 часов");
    Иначе
        Сообщить("Текущее время НЕ в диапазоне 8-12 часов");
    КонецЕсли;
КонецФункции;

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

  1. Некорректное выделение времени: Строка ТекущаяДата = ТекущаяДата; не имеет смысла, так как переменная присваивает сама себе значение. Более того, выделение времени путем вычитания даты ТекущееВремя = ТекущаяДата - Дата(ТекущаяДата)); является хоть и рабочим, но крайне нелогичным, неочевидным и избыточным способом получения значения типа Дата (но содержащего только временную компоненту) из полной даты. Для получения текущей даты и времени достаточно использовать функцию ТекущаяДата().
  2. Ошибки в сравнении типов данных: Самая критичная проблема заключается в попытке сравнения переменной ТекущееВремя, которая имеет тип Дата (хотя и содержит только временную компоненту), со строковыми значениями НачалоДиапазона = "08:00"; и КонецДиапазона = "12:00:00";. Платформа 1С не выполняет автоматическое преобразование строк в даты при сравнении таким образом, что неизбежно приведет к ошибке выполнения или неверному результату.
  3. Нелогичная и абсурдная конструкция условия: В условии Если ТекущееВремя >= НачалоДиапазона И ТекущееВремя = 8 И Час < 12 Тогда содержится несколько серьезных проблем:
    • Попытка сравнения ТекущееВремя = 8 является абсурдом, так как мы пытаемся сравнить значение типа Дата с числом 8.
    • Использование переменной Час, которая не была объявлена и инициализирована в данном контексте, приведет к ошибке "Переменная не определена".
    • Весь фрагмент условия выглядит как смешение различных некорректных подходов или является проявлением "галлюцинаций" ИИ, когда он генерирует синтаксически похожие, но логически неверные конструкции.
  4. Избыточность и сложность: Даже если бы сравнения работали, предложенный подход является избыточным. Для решения такой простой задачи в 1С существуют гораздо более прямые и элегантные методы, которые мы рассмотрим далее.
  5. Отсутствие возвращаемого значения: Функция объявлена как возвращающая булево (см. комментарии // Возвращаемое значение: // Булево), но фактически только выводит сообщения с помощью Сообщить(), не возвращая никакого значения. Это нарушает объявленный интерфейс функции и сделает ее бесполезной для использования в других частях кода.

Такой результат вызывает серьезные опасения относительно компетентности ИИ-помощника и подчеркивает необходимость тщательного ревью и тестирования всего генерируемого им кода.

Первый шаг к улучшению: код из другого источника

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


// Функция проверяет, находится ли текущее время в диапазоне с 8:00 до 12:00.
//
// Возвращаемое значение:
// Булево - Истина, если время в диапазоне, иначе Ложь.
//
Функция ПроверитьВременнойДиапазон() Экспорт
    Перем Час;
    ТекущееВремя = ТекущаяДата();
    Час = Час(ТекущееВремя);
    Если Час >= 8 И Час < 12 Тогда
        Возврат Истина;
    Иначе
        Возврат Ложь;
    КонецЕсли;
КонецФункции

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

  1. Лишняя переменная ТекущееВремя: Участники форума справедливо отметили, что переменная ТекущееВремя является избыточной. Мы можем напрямую использовать результат функции ТекущаяДата() в вызове Час(), что делает код короче и более читаемым, не снижая при этом его понятности.
  2. Лишняя переменная Час: Аналогично, переменную Час можно избежать, если подставить вызов Час(ТекущаяДата()) непосредственно в условие. Это позволяет уменьшить количество промежуточных переменных, делая код еще более лаконичным, что особенно ценится в простых функциях.
  3. Использование ТекущаяДатаСеанса(): Для кода, исполняемого на сервере, рекомендуется использовать функцию ТекущаяДатаСеанса() вместо ТекущаяДата(). Это связано с тем, что ТекущаяДата() возвращает локальное время сервера, а ТекущаяДатаСеанса() – время сеанса пользователя, скорректированное с учетом его часового пояса и системной даты сеанса. Это важное соображение для многопользовательских систем и распределенных решений, где время на сервере и у клиента может отличаться.
  4. "Нерусское" именование процедуры: Было отмечено именование ПроверитьВременнойДиапазон как не совсем типичное для русского языка, более характерное для машинного перевода или обучения на данных от нерусскоязычных источников. Для 1С-разработки предпочтительнее использовать более естественные для русского языка формулировки, например, ВремяВДиапазоне или ТекущееВремяВДиапазонеРабочихЧасов.

Эти замечания направлены не на критику, а на улучшение качества кода, приведение его к стандартам и повышение эффективности.

Оптимальное и лаконичное решение для проверки времени

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


// Функция проверяет, находится ли текущее время в диапазоне с 8:00 до 12:00.
//
// Возвращаемое значение:
// Булево - Истина, если время в диапазоне, иначе Ложь.
//
// Вариант с промежуточной переменной для часа (чуть менее лаконичный, но понятный)
Функция ТекущееВремяВДиапазонеРабочихЧасов() Экспорт
    Перем ЧасТекущейДаты;
    // Для клиентского кода
    ЧасТекущейДаты = Час(ТекущаяДата()); 
    // Для серверного кода лучше использовать:
    // ЧасТекущейДаты = Час(ТекущаяДатаСеанса()); 
    Возврат ЧасТекущейДаты >= 8 И ЧасТекущейДаты < 12;
КонецФункции

// Еще более лаконичный вариант (наиболее оптимальный):
// Функция ТекущееВремяВДиапазонеРабочихЧасов() Экспорт
//     // Для клиентского кода
//     Возврат Час(ТекущаяДата()) >= 8 И Час(ТекущаяДата()) < 12;
//     // Для серверного кода
//     // Возврат Час(ТекущаяДатаСеанса()) >= 8 И Час(ТекущаяДатаСеанса()) < 12;
// КонецФункции

Этот код является эталонным для данной задачи. Он предельно прост, понятен и эффективен. Мы используем функцию Час() для непосредственного извлечения компоненты часа из текущей даты (или даты сеанса) и сразу же сравниваем ее с заданными границами. Таким образом, мы избегаем создания лишних переменных и сложных манипуляций с датами, что делает код более производительным и легким для поддержки.

Важно отметить логику сравнения Час < 12: это означает, что время от 8 часов 00 минут 00 секунд включительно до 11 часов 59 минут 59 секунд включительно попадает в диапазон, а ровно 12:00:00 уже нет. Как верно заметили в обсуждении, "12:00:00 относится 'направо' по оси времени, к следующему интервалу". Это классический способ определения временного интервала, который является лево-включительным и право-исключительным [Начало, Конец). Если бы мы использовали Час <= 12, то время 12:00:00 также попало бы в диапазон, что обычно не соответствует семантике "до 12 часов" в контексте рабочего дня, когда начало следующего часа не включается в предыдущий интервал.

Дальнейшие "сюрпризы" от 1С:Напарника: примеры неоптимального и галлюцинаторного кода

Проблемы с 1С:Напарником не ограничились только первоначальной задачей. Дальнейшие эксперименты и запросы показали, что ИИ склонен не только к созданию избыточного кода, но и к так называемым "галлюцинациям" – выдумыванию несуществующих методов или неправильному толкованию стандартных механизмов платформы. Рассмотрим еще несколько ярких примеров из обсуждения на форуме, которые демонстрируют эти недостатки.

Проверка диапазона с минутами: 8:15 - 12:15

Когда задачу усложнили, попросив проверить диапазон с учетом минут, например, с 8:15 до 12:15, 1С:Напарник вновь продемонстрировал склонность к чрезмерному усложнению и неоптимальным решениям. Посмотрим на его подход к этой задаче:


Функция ЭтоВремяДиапазонаРаботы Экспорт
    ТекущееВремя = ТекущаяДата; // Получаем границы рабочего интервала
    НачалоРаботы = '20240101' + "081500"; // Произвольная дата + время начала
    КонецРаботы = '20240101' + "121500"; // Произвольная дата + время конца
    // Преобразуем время в количество минут от начала дня для удобства сравнения
    ТекущиеМинуты = Час(ТекущееВремя) * 60 + Минута(ТекущееВремя);
    НачальныеМинуты = Час(НачалоРаботы) * 60 + Минута(НачалоРаботы);
    КонечныеМинуты = Час(КонецРаботы) * 60 + Минута(КонецРаботы);
    // Проверяем, попадает ли текущее время в интервал
    // Включая левую и правую границу
    Если ТекущиеМинуты >= НачальныеМинуты И ТекущиеМинуты <= КонечныеМинуты Тогда
        Возврат Истина;
    Иначе
        Возврат Ложь;
    КонецЕсли;
КонецФункции

Здесь мы видим избыточное количество действий для сравнительно простой задачи. Давайте разберем основные недостатки:

  1. Использование фиксированной даты: Использование фиксированной даты '20240101' для создания временных границ НачалоРаботы и КонецРаботы является избыточным. Нам нужны только компоненты времени, а добавление произвольной даты лишь усложняет чтение и понимание кода.
  2. Преобразование в минуты: Преобразование всех времён в количество минут от начала дня для удобства сравнения (ТекущиеМинуты = Час(ТекущееВремя) * 60 + Минута(ТекущееВремя); и аналогично для границ) является функционально рабочим, но делает код менее читаемым и добавляет ненужную логику. Для сравнения временных интервалов с минутами можно использовать более прямолинейные методы, например, создавая объекты Дата с "нулевой" датой и нужными часами/минутами, или даже сравнивая строковые представления времени, если они имеют единообразный формат (хотя это менее надежно и не рекомендуется).
  3. Переменная ТекущееВремя: Снова используется избыточная переменная ТекущееВремя = ТекущаяДата;, когда можно было бы сразу работать с результатом функции ТекущаяДата() или ТекущаяДатаСеанса().
  4. Избыточность в целом: Хотя код и работает, он слишком многословен для задачи, которая может быть решена гораздо элегантнее с использованием встроенных функций 1С по работе со временем.

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


Функция ЭтоВремяВДиапазонеСМинутами() Экспорт
    // Получаем текущее время
    ТекущееВремя = ТекущаяДата(); // Или ТекущаяДатаСеанса() для серверного кода
    
    // Создаем объекты Дата с нужными временными границами, но без привязки к конкретной дате
    НачалоДиапазона = Дата(1, 1, 1, 8, 15, 0); // Условная дата 01.01.0001 08:15:00
    КонецДиапазона = Дата(1, 1, 1, 12, 15, 0); // Условная дата 01.01.0001 12:15:00
    
    // "Обнуляем" дату у текущего времени, оставляя только временную компоненту
    ТекущееВремяБезДаты = Дата(1, 1, 1, Час(ТекущееВремя), Минута(ТекущееВремя), Секунда(ТекущееВремя));
    
    // Проверяем, попадает ли текущее время в интервал
    // Предполагаем, что правая граница не включается в диапазон, как было в первом примере.
    // Если нужно включить правую границу, используйте "<="
    Возврат ТекущееВремяБезДаты >= НачалоДиапазона И ТекущееВремяБезДаты < КонецДиапазона;
КонецФункции

Этот подход более прямолинеен и использует возможности платформы 1С по работе с типом Дата, что делает код более интуитивно понятным.

Выдуманные предопределенные методы (Галлюцинации ИИ)

Один из наиболее тревожных аспектов работы ИИ-ассистентов – это их способность "галлюцинировать", то есть генерировать информацию, которая кажется правдоподобной, но на самом деле является ложной или неточной. 1С:Напарник продемонстрировал это, "придумав" несуществующий предопределенный метод объекта ПриСохраненииНомераДокумента. Вот что он предложил:


// 1С:Напарник предложил:
// "Чтобы добавить дополнительные данные к автоматически формируемому номеру документа,
// необходимо использовать метод ПриСохраненииНомераДокумента или ПередЗаписью модуля объекта документа.
// Однако, для изменения номера документа, предпочтительнее использовать ПриСохраненииНомераДокумента,
// так как этот метод предназначен специально для этих целей.
// Метод ПриСохраненииНомераДокумента вызывается после автоматического присвоения номера документу,
// но перед его записью. Это позволяет модифицировать номер, добавляя к нему дополнительные данные."

Метода ПриСохраненииНомераДокумента не существует в платформе 1С:Предприятие. Это классический пример "галлюцинации" большой языковой модели. ИИ синтезировал описание метода, которое звучит логично и правдоподобно в контексте работы с нумерацией документов, но на самом деле не соответствует действительности. Для изменения номера документа до его записи обычно используется предопределенный обработчик события ПриУстановкеНовогоНомера() или ПередЗаписью() в модуле объекта. Подобные "советы" от ИИ могут привести начинающих разработчиков в тупик и заставить их тратить часы на поиск несуществующего функционала или попытки его реализации, что, безусловно, снижает доверие к инструменту и увеличивает стоимость разработки. Это является ярким примером того, почему весь генерируемый ИИ код и советы должны проходить строгую проверку и верификацию опытными специалистами.

Некорректный код для обработки копирования табличных частей

Еще один пример, демонстрирующий недостаточную "глубину" понимания ИИ платформы 1С, касается обработки события ПриКопировании для запрета копирования табличных частей документа или другого объекта. ИИ предложил следующий код:


Процедура ПриКопировании(ОбъектКопирования, ПараметрыКопирования)
    // Получаем коллекцию всех табличных частей объекта
    КоллекцияТЧ = ОбъектКопирование.ТабличныеЧасти;
    // Перебираем каждую табличную часть в цикле
    Для Каждого ИмяТЧ Из КоллекцияТЧ Цикл
        // Очищаем текущую табличную часть
        ИмяТЧ.Очистить;
    КонецЦикла;
КонецПроцедуры

В этом коде содержится две критические ошибки, которые делают его неработоспособным:

  1. Некорректное получение коллекции табличных частей: Во-первых, строка КоллекцияТЧ = ОбъектКопирование.ТабличныеЧасти; является некорректной. Свойство ТабличныеЧасти у объекта конфигурации (в данном контексте ОбъектКопирования) возвращает не коллекцию *объектов* табличных частей, а их метаданные. Если мы хотим перебрать все табличные части объекта для работы с их данными, необходимо обращаться к метаданным объекта, например, через ОбъектКопирования.Метаданные().ТабличныеЧасти, а затем уже использовать имена табличных частей для доступа к соответствующим коллекциям в самом объекте.
  2. Ошибка вызова метода Очистить: Во-вторых, в цикле Для Каждого ИмяТЧ Из КоллекцияТЧ Цикл ИмяТЧ.Очистить; переменная ИмяТЧ будет содержать *объект метаданных* табличной части (который содержит ее имя, синоним и другие свойства), а не сам объект табличной части, содержащий данные. Соответственно, попытка вызвать метод Очистить у объекта метаданных приведет к ошибке, так как такого метода у него нет. Правильно было бы обратиться к коллекции табличной части по её имени, которое хранится в свойстве Имя объекта метаданных: ОбъектКопирования[ИмяТЧ.Имя].Очистить();.

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


Процедура ПриКопировании(ОбъектКопирования, ПараметрыКопирования)
    // Перебираем метаданные всех табличных частей объекта
    Для Каждого МетаданныеТЧ Из ОбъектКопирования.Метаданные().ТабличныеЧасти Цикл
        // Обращаемся к коллекции табличной части по ее имени и очищаем ее
        ОбъектКопирования[МетаданныеТЧ.Имя].Очистить();
    КонецЦикла;
КонецПроцедуры

Такой подход обеспечивает правильный обход и очистку всех табличных частей, предотвращая их копирование при создании нового объекта.

Выводы: ИИ как помощник, но не замена разработчика

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

  1. Ускорение разработки, но с оговорками: ИИ-ассистенты могут значительно ускорить рутинные операции, помочь в прототипировании и написании шаблонного кода. Однако их код часто требует серьезной доработки, оптимизации и исправления ошибок. Исследования показывают, что ИИ-генерируемый код может содержать больше ошибок, быть более технически задолженным и менее поддерживаемым по сравнению с кодом, написанным опытными разработчиками.
  2. Проблема "галлюцинаций": Модели ИИ могут генерировать синтаксически верный, но логически неверный или даже выдуманный код и советы, которые не существуют в реальной платформе. Это особенно актуально для локальных и специфических языков, где объем качественных обучающих данных может быть ограничен. Это требует от разработчика не только понимания синтаксиса, но и глубоких знаний платформы.
  3. Необходимость глубокого понимания платформы: ИИ не может заменить опытного разработчика, который обладает глубокими знаниями платформы 1С, понимает бизнес-логику, стандарты кодирования и может критически оценивать предлагаемые решения на предмет их корректности, производительности и поддерживаемости.
  4. Контроль и ревью – ключ к успеху: Весь генерируемый ИИ код должен подвергаться тщательному контролю, ревью со стороны опытных специалистов и всестороннему тестированию. Слепое копирование и использование кода, предложенного ИИ, без понимания его логики и возможных последствий может привести к увеличению технического долга, снижению качества программного обеспечения и появлению труднодиагностируемых ошибок.

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

← На главную