Как эффективно и чисто работать с временными интервалами в 1С:Предприятие, избегая повторяющихся выражений типа '24 * 60 * 60'?

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

Мы, как программисты 1С, постоянно сталкиваемся с необходимостью выполнения различных операций над датами и временем. Часто перед нами стоит задача рассчитать временные интервалы, добавить определенное количество дней или часов к дате, или вычислить разницу между двумя датами. При выполнении этих операций, многие из нас сталкиваются с повторяющимся в коде выражением 24 * 60 * 60, которое используется для обозначения количества секунд в сутках. Рассмотрим, почему такой подход становится привычным, какие потенциальные проблемы он может создавать и какие более эффективные, читаемые и поддерживаемые альтернативы предлагает платформа 1С.

Проблема повторяющегося кода "24 * 60 * 60"

Выражение 24 * 60 * 60 для многих разработчиков является интуитивно понятным способом представления количества секунд в сутках. Оно явно показывает, что мы умножаем 24 часа на 60 минут, а затем на 60 секунд, чтобы получить общее число секунд. Этот подход имеет свои преимущества, но также и существенные недостатки.

Преимущества выражения "24 * 60 * 60"

Давайте рассмотрим, почему разработчики часто выбирают именно такой способ записи:

  1. Самодокументированность и читаемость: Выражение 24 * 60 * 60 сразу же раскрывает свою суть, не требуя дополнительных комментариев или знания "магического числа" 86400. Даже новый разработчик, впервые столкнувшийся с кодом, с легкостью поймет, что здесь рассчитывается длительность суток в секундах.
  2. Гибкость для специфических сценариев: В редких случаях, когда логика программы должна учитывать нестандартное определение суток (например, при моделировании процессов на других планетах, где сутки могут иметь иную длительность), изменение множителей (например, 23 * 50 * 50 для гипотетических марсианских суток) будет более наглядным и интуитивно понятным.
  3. Снижение вероятности опечаток: Набирая последовательность из нескольких коротких чисел, разделенных операторами умножения, мы совершаем меньше сложных последовательностей цифр, чем при вводе длинного числа 86400. Это потенциально снижает количество опечаток.

Рассмотрим типовые примеры из кода 1С, где встречается такое выражение:


// Добавление одного дня к дате путем прибавления секунд
ТекДата = ТекущаяДата();
ДатаЧерезОдинДень = ТекДата + 24 * 60 * 60; 
Сообщить("Дата через один день: " + ДатаЧерезОдинДень);

Или при определении интервалов в списках выбора:


СписокИнтервалов = Новый СписокЗначений();
СписокИнтервалов.Добавить(1 * 24 * 60 * 60, "1 день");
СписокИнтервалов.Добавить(1 * 7 * 24 * 60 * 60, "1 неделя");

Недостатки выражения "24 * 60 * 60"

Несмотря на кажущуюся очевидность и удобство, многократное повторение выражения 24 * 60 * 60 по всему коду может создать ряд проблем для поддерживаемости и чистоты кода:

  1. Нарушение принципа DRY (Don't Repeat Yourself): Когда одно и то же значение дублируется во множестве мест программы, мы нарушаем важный принцип разработки "Не повторяйся". Если в будущем потребуется изменить логику (например, из-за изменения требований к расчету временных интервалов или появлению специфического учета), придется искать и править все вхождения этого выражения по всему коду, что является трудоемким и чревато ошибками.
  2. Визуальный шум: Длинные математические выражения могут загромождать код, особенно если они встречаются внутри более сложных формул, запросов или условий. Это ухудшает общую читаемость и усложняет быстрый анализ логики.
  3. Не всегда очевидно в плохом коде: Как было справедливо отмечено в обсуждении, если окружающий код уже сам по себе является "непонятным говнокодом", то даже такое "самодокументируемое" выражение как 24 * 60 * 60 не сделает его существенно более читаемым или понятным.
  4. Вопрос производительности (в большинстве случаев несущественный): Хотя современные компиляторы 1С (и других языков) обычно оптимизируют такие выражения на этапе компиляции, преобразуя их в константное значение (86400), и разницы в производительности практически нет, сама концепция использования сложных выражений там, где можно использовать простое число или константу, может быть спорной.

Альтернативные и более предпочтительные подходы

Мы выясним, какие существуют более "чистые", эффективные и поддерживаемые способы работы с датами и временными интервалами в 1С:Предприятие.

1. Использование стандартной функции "ДобавитьКДате()"

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

Преимущества использования ДобавитьКДате():

  1. Читаемость и интуитивность: Код становится значительно более понятным, поскольку мы явно указываем, какой тип интервала (ВидДаты.День, ВидДаты.Час, ВидДаты.Минута, ВидДаты.Секунда) и в каком количестве хотим добавить.
  2. Надежность и точность: Функция корректно обрабатывает все календарные особенности, избавляя разработчика от необходимости вручную учитывать такие детали, как переменная длина месяца, високосные годы или даже "високосные секунды" (если они влияют на интервал). Платформа 1С:Предприятие взаимодействует с системным временем, которое учитывает эти корректировки.
  3. Снижение ошибок: Вероятность ошибок, связанных с ручными расчетами количества секунд, значительно уменьшается, поскольку эта логика инкапсулирована в функции платформы.

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


// Добавляем один день к текущей дате
ТекДата = ТекущаяДата();
ДатаЧерезОдинДень = ДобавитьКДате(ТекДата, ВидДаты.День, 1);
Сообщить("Дата через один день: " + ДатаЧерезОдинДень); // Пример: 2023.10.26 12:00:00 -> 2023.10.27 12:00:00

// Добавляем 5 часов к дате
ДатаЧерезПятьЧасов = ДобавитьКДате(ТекДата, ВидДаты.Час, 5);
Сообщить("Дата через пять часов: " + ДатаЧерезПятьЧасов);

// Добавляем 30 секунд к дате
ДатаЧерезТридцатьСекунд = ДобавитьКДате(ТекДата, ВидДаты.Секунда, 30);
Сообщить("Дата через тридцать секунд: " + ДатаЧерезТридцатьСекунд);

// Добавляем 7 дней (одна неделя)
ДатаЧерезНеделю = ДобавитьКДате(ТекДата, ВидДаты.День, 7);
Сообщить("Дата через неделю: " + ДатаЧерезНеделю);

// Пример использования для вычитания (отрицательное значение)
ДатаВчера = ДобавитьКДате(ТекДата, ВидДаты.День, -1);
Сообщить("Дата вчера: " + ДатаВчера);

Использование ДобавитьКДате() делает код более лаконичным, выразительным и устойчивым к возможным ошибкам.

2. Использование именованных констант или переменных

Если все же по какой-то причине нам необходимо оперировать точным числовым значением количества секунд (например, для специфических расчетов длительности, взаимодействия с внешними системами, или в параметрах функций, требующих числового значения в секундах), более предпочтительно использовать именованные константы или переменные вместо "магического числа" 86400 или его развернутого выражения 24 * 60 * 60.

Преимущества именованных констант:

  1. Централизация: Значение определяется один раз в легкодоступном месте (например, в общем модуле, модуле констант или как константа конфигурации). Это реализует принцип DRY.
  2. Улучшенная читаемость: Имя константы (например, КоличествоСекундВСутках) сразу передает смысл числа, делая код более самодокументируемым.
  3. Легкость модификации: При необходимости изменить значение (например, если логика потребует учета более коротких или длинных суток, или исправления некорректного значения), достаточно будет внести правку в одном месте, и все ссылки на эту константу автоматически обновятся.

Рассмотрим примеры использования именованных констант:


// В общем модуле (например, "ОбщегоНазначенияСервер")
// Можно объявить как экспортируемую переменную или функцию
// Для примера:
Перем мКоличествоСекундВСутках Экспорт = 86400; 

// Или как функцию, если нужно инкапсулировать вычисление
Функция ПолучитьДлинуСутокВСекундах() Экспорт
    Возврат 24 * 3600; // Или 86400
КонецФункции

// Использование в другом модуле:
ДлительностьДняВСекундах = ОбщегоНазначенияСервер.мКоличествоСекундВСутках;
ДатаПлюсОдинДень = ТекущаяДата() + ДлительностьДняВСекундах;
Сообщить("Дата плюс один день (через константу): " + ДатаПлюсОдинДень);

// Или через функцию:
ДлительностьДняВСекундахФункция = ОбщегоНазначенияСервер.ПолучитьДлинуСутокВСекундах();
ДатаПлюсОдинДеньФункция = ТекущаяДата() + ДлительностьДняВСекундахФункция;
Сообщить("Дата плюс один день (через функцию): " + ДатаПлюсОдинДеньФункция);

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


// Использование "магического числа" с явным комментарием
ДатаПлюсОдинДень = ТекущаяДата() + 86400; // 86400 секунд в сутках

Как показывают примеры из Библиотеки стандартных подсистем (БСП) 1С, встречаются и более сложные конструкции, использующие 86400 для производных расчетов:


// Пример из типового кода 1С, демонстрирующий использование 86400
Если Шаг = "Час" Тогда
    // Расчет количества секунд в часе на основе общего количества секунд в сутках
    ШагЧисло = 86400 / 24; 
КонецЕсли;

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

3. Создание общих функций или модулей для работы с датами

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

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


// Пример функции в общем модуле "РаботаСДатами" (ОбщегоНазначенияКлиентСервер)
Функция ДобавитьДнейКДатеСУчетомРабочих(ДатаНачала, КоличествоРабочихДней) Экспорт
    НоваяДата = ДатаНачала;
    Для Индекс = 1 По КоличествоРабочихДней Цикл
        НоваяДата = ДобавитьКДате(НоваяДата, ВидДаты.День, 1);
        // Дополнительная логика, например, пропуск выходных
        Пока ДеньНедели(НоваяДата) = 6 Или ДеньНедели(НоваяДата) = 7 Цикл // Суббота или воскресенье
            НоваяДата = ДобавитьКДате(НоваяДата, ВидДаты.День, 1);
        КонецЦикла;
    КонецЦикла;
    Возврат НоваяДата;
КонецФункции

// Другой пример - функция для получения длины суток в секундах,
// если это значение может быть динамическим или зависит от контекста
Функция ПолучитьДлинуСутокВСекундахДляРасчета() Экспорт
    // Может быть сложная логика, учитывающая часовые пояса или другие факторы
    Возврат 24 * 3600; // Или использовать именованную константу
КонецФункции

// Использование в коде:
ИсходнаяДата = ТекущаяДата();
// Добавляем 5 рабочих дней к текущей дате
НоваяДатаРабочая = РаботаСДатами.ДобавитьДнейКДатеСУчетомРабочих(ИсходнаяДата, 5);
Сообщить("Дата через 5 рабочих дней: " + НоваяДатаРабочая);

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

4. Программный расчет длительности суток

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


// Расчет количества секунд между началом текущего и началом следующего дня
НачалоТекущегоДня = НачалоДня(ТекущаяДата());
НачалоСледующегоДня = НачалоДня(ДобавитьКДате(ТекущаяДата(), ВидДаты.День, 1));
ДлинаСутокВСекундах = НачалоСледующегоДня - НачалоТекущегоДня;

Сообщить("Длина суток в секундах (программно): " + ДлинаСутокВСекундах);

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

Влияние "високосных секунд" на точность расчетов

В ходе обсуждения был затронут важный и довольно глубокий вопрос о "високосных секундах" — дополнительных секундах, которые периодически добавляются к универсальному координированному времени (UTC) для синхронизации с фактическим вращением Земли. Этот вопрос напрямую связан с точностью значения 86400.

  1. Редкость и нерегулярность: Високосные секунды добавляются нерегулярно, в среднем раз в несколько лет, обычно в конце июня или декабря. Это означает, что продолжительность некоторых суток может быть не строго 86400 секунд, а иногда 86401 (или очень редко 86399, если бы происходило ускорение вращения).
  2. Влияние на системы: Системы, которые напрямую работают с Unix-временем (которое обычно отсчитывается с 1 января 1970 года и не учитывает високосные секунды), могут столкнуться с проблемами при расчетах продолжительности, если не учитывать эти корректировки.
  3. Как 1С это обрабатывает: Платформа 1С:Предприятие обычно оперирует с датами и временем на более высоком уровне абстракции, где эти детали обрабатываются базовой операционной системой и внутренними механизмами платформы. При вычитании двух дат в 1С результат будет представлять фактическое количество секунд между этими датами, и если между ними была високосная секунда, платформа 1С, как правило, это учтет. Таким образом, использование ДобавитьКДате() или программное вычитание дат (как в предыдущем разделе) является наиболее надежным способом, поскольку опирается на внутренние механизмы платформы, учитывающие фактическое системное время.

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

Заключение

Выбор между 24 * 60 * 60, 86400, именованной константой или использованием функции ДобавитьКДате() часто зависит от внутренних стандартов кодирования команды и личных предпочтений разработчика. Однако, мы рекомендуем придерживаться следующих принципов для написания чистого, надежного и поддерживаемого кода:

  1. Для добавления или вычитания дней, часов, минут или секунд к дате всегда используйте стандартную функцию ДобавитьКДате(). Это самый чистый, надежный и читаемый способ, который корректно обрабатывает все календарные особенности.
  2. Если вам необходимо получить числовое значение количества секунд в сутках или другом интервале, рассмотрите возможность использования именованной константы (например, Константа.КоличествоСекундВСутках или переменной в общем модуле), чтобы централизовать это значение и повысить читаемость кода.
  3. Для очень специфичных или сложных операций с датами, которые не покрываются стандартными функциями или требуют уникальной бизнес-логики, создавайте собственные функции в общих модулях, следуя принципам модульности и переиспользуемости.
  4. Если требуется максимальная точность длительности суток с учетом всех системных корректировок (включая високосные секунды), используйте программный расчет разницы между началом текущего и началом следующего дня.

Главное — обеспечить единообразие и понятность кода для всех, кто будет с ним работать, и использовать инструменты платформы 1С максимально эффективно, минимизируя повторения и потенциальные источники ошибок.

← На главную