При работе с данными в 1С разработчикам часто приходится решать задачи, связанные с обработкой дат. Одной из таких типовых задач является необходимость получить список всех дат конкретного дня недели (например, всех понедельников) в заданном временном интервале. Эта задача актуальна для формирования отчетов, планирования или выполнения регламентных операций.
Мы рассмотрим несколько подходов к решению этой проблемы, от простого ежедневного перебора до более оптимизированных методов, учитывающих особенности работы с датами в 1С. Проанализируем каждый из них, обратим внимание на их преимущества и недостатки, а также разберем важные нюансы, которые стоит учитывать при работе с датами в системе 1С:Предприятие.
Самый очевидный и простой для понимания способ — это пройтись по каждой дате в заданном диапазоне, начиная с даты начала и заканчивая датой окончания, и для каждой даты проверить, является ли она искомым днем недели. Если условие выполняется, дату добавляем в результирующий массив.
ДеньНедели() (для 1С 8.х) или НомерДняНедели() (для 1С 7.7) для определения дня недели ТекущейДаты. В 1С ДеньНедели() возвращает число от 1 (понедельник) до 7 (воскресенье).ДеньНедели(ТекущаяДата) совпадает с искомым значением (например, 1 для понедельника), мы добавляем ТекущуюДату в наш результирующий массив.ТекущаяДата = ТекущаяДата + 60 * 60 * 24;. Система 1С корректно обрабатывает такие операции, учитывая високосные годы и переходы на летнее/зимнее время, автоматически нормализуя дату к следующему календарному дню.Рассмотрим функцию, которая реализует этот подход. Она универсальна и может быть использована для поиска любого дня недели в заданном диапазоне.
// Функция ПолучитьДатыДняНедели
// Параметры:
// ИскомыйДеньНедели - Число (1=Понедельник, 7=Воскресенье)
// ДатаНачала - Дата (начало периода)
// ДатаОкончания - Дата (конец периода)
// Возвращает: Массив дат, соответствующих ИскомомуДнюНедели
Функция ПолучитьДатыДняНедели(ИскомыйДеньНедели, ДатаНачала, ДатаОкончания) Экспорт
Результат = Новый Массив;
// Нормализуем начальную дату до начала дня для корректных сравнений и расчетов.
ТекущаяДата = НачалоДня(ДатаНачала);
// Цикл по всем дням в заданном диапазоне
Пока ТекущаяДата <= ДатаОкончания Цикл
// Проверяем, является ли текущая дата искомым днем недели
Если ДеньНедели(ТекущаяДата) = ИскомыйДеньНедели Тогда
Результат.Добавить(ТекущаяДата);
КонецЕсли;
// Переходим к следующему дню, прибавляя 24 часа в секундах.
// 1С автоматически обрабатывает переходы на летнее/зимнее время и високосные годы.
ТекущаяДата = ТекущаяДата + 60 * 60 * 24;
КонецЦикла;
Возврат Результат;
КонецФункции
// Пример использования функции для получения понедельников с 18.05.2022 по 31.05.2022
ДатаНачалаПериода = Дата("20220518");
ДатаОкончанияПериода = Дата("20220531");
ИскомыйДень = 1; // 1 соответствует понедельнику в 1С
МассивПонедельников = ПолучитьДатыДняНедели(ИскомыйДень, ДатаНачалаПериода, ДатаОкончанияПериода);
// Для вывода результата в консоль:
// Для Каждого ПонедельникИзМассива Из МассивПонедельников Цикл
// Сообщить("Найден понедельник: " + ПонедельникИзМассива);
// КонецЦикла;
// Результат для данного примера будет: 23.05.2022, 30.05.2022
В 1С 7.7 используется похожий синтаксис для работы с датами и циклами, но для определения дня недели применяется функция НомерДняНедели().
// Пример для 1С 7.7
ДатаНачала = Дата("18.05.2022");
ДатаОкончания = Дата("31.05.2022");
Для Счетчик = ДатаНачала По ДатаОкончания Цикл
// НомерДняНедели(Счетчик) = 1 также соответствует понедельнику
Если НомерДняНедели(Счетчик) = 1 Тогда;
Сообщить("Понедельник - "+Счетчик);
КонецЕсли;
КонецЦикла;
Этот метод, хотя и прост, имеет потенциальный недостаток в производительности: если диапазон дат очень большой (например, несколько лет), функция ДеньНедели() будет вызываться для каждого дня, что может замедлить выполнение кода.
Для повышения производительности, особенно при работе с большими диапазонами дат, мы можем использовать более эффективный подход. Он основан на том, что один и тот же день недели повторяется ровно через 7 дней. Таким образом, вместо ежедневного перебора, мы можем найти первую нужную дату в диапазоне, а затем просто прибавлять к ней 7 дней, пока не выйдем за пределы конечной даты.
Для того чтобы найти первый понедельник, который находится в диапазоне [ДатаНачала, ДатаОкончания], или сразу после ДатаНачала, мы можем использовать следующую логику:
ДеньНедели().ДеньНедели() возвращает 1), то это и есть наш первый понедельник.(8 - ДеньНедели(НачалоДня(ДатаНачала))) * 24 * 3600 секунд.
ДеньНедели() = 3), то 8 - 3 = 5. Прибавив 5 дней к среде, мы получим следующий понедельник.ДеньНедели() = 7), то 8 - 7 = 1. Прибавив 1 день к воскресенью, мы получим понедельник.МассивПонедельников)Эта функция демонстрирует оптимизированный подход, находя первый понедельник и затем используя шаг в 7 дней.
// Функция МассивПонедельников
// Параметры:
// ДатаНачала - Дата (начало периода)
// ДатаОкончания - Дата (конец периода)
// Возвращает: Массив дат, являющихся понедельниками в заданном диапазоне
Функция МассивПонедельников(ДатаНачала, ДатаОкончания) Экспорт
Понедельники = Новый Массив;
// Нормализуем начальную дату до начала дня
НачальнаяДатаНормализована = НачалоДня(ДатаНачала);
// Определяем день недели нормализованной начальной даты
// В 1С: 1 = Понедельник, 7 = Воскресенье
НачальныйДеньНедели = ДеньНедели(НачальнаяДатаНормализована);
// Вычисляем дату первого понедельника, который >= НачальнаяДатаНормализована.
// Если НачальнаяДатаНормализована уже понедельник, берем ее.
// Иначе, вычисляем, сколько дней нужно прибавить, чтобы дойти до следующего понедельника.
ТекущийПонедельник = НачальнаяДатаНормализована;
Если НачальныйДеньНедели <> 1 Тогда
// Количество дней до следующего понедельника: (8 - текущий_день_недели).
// Например, если вторник (2), то (8-2)=6 дней до понедельника.
// Если воскресенье (7), то (8-7)=1 день до понедельника.
ТекущийПонедельник = ТекущийПонедельник + (8 - НачальныйДеньНедели) * 24 * 3600;
КонецЕсли;
// После расчета убедимся, что ТекущийПонедельник не раньше ДатаНачала.
// Этот шаг важен, если исходная ДатаНачала имела время, а мы работали с НачалоДня(ДатаНачала).
// Хотя в данном случае, с учетом НачалоДня(ДатаНачала), ТекущийПонедельник всегда будет >= НачалоДня(ДатаНачала).
// Если бы мы не нормализовали, этот шаг мог бы быть критичен.
// Например, если ДатаНачала была "18.05.2022 10:00:00" (среда) и мы искали понедельник 23.05.2022,
// а потом ДатаОкончания "20.05.2022".
// Важно, чтобы найденный ТекущийПонедельник был в пределах [ДатаНачала, ДатаОкончания].
// Так как мы уже нормализовали ДатаНачала, достаточно просто убедиться, что
// ТекущийПонедельник находится в заданном диапазоне.
// Цикл по понедельникам с шагом в 7 дней
Пока ТекущийПонедельник <= ДатаОкончания Цикл
// Добавляем понедельник в массив только если он находится в исходном диапазоне.
// Это условие `ТекущийПонедельник >= НачальнаяДатаНормализована` избыточно
// после правильного определения первого понедельника, но не повредит.
Если ТекущийПонедельник >= НачальнаяДатаНормализована Тогда
Понедельники.Добавить(ТекущийПонедельник);
КонецЕсли;
// Переходим к следующему понедельнику, прибавляя ровно 7 дней (в секундах).
// 1С автоматически обрабатывает особенности календаря.
ТекущийПонедельник = ТекущийПонедельник + 7 * 24 * 3600;
КонецЦикла;
Возврат Понедельники;
КонецФункции
// Пример использования функции для получения понедельников с 18.05.2022 по 31.05.2022
ДатаНачалаПериода = Дата("20220518");
ДатаОкончанияПериода = Дата("20220531");
МассивПонедельников = МассивПонедельников(ДатаНачалаПериода, ДатаОкончанияПериода);
// Для вывода результата в консоль:
// Для Каждого ПонедельникИзМассива Из МассивПонедельников Цикл
// Сообщить("Найден понедельник (оптимизированно): " + ПонедельникИзМассива);
// КонецЦикла;
// Результат для данного примера будет: 23.05.2022, 30.05.2022
Этот метод является более эффективным с точки зрения производительности, поскольку количество итераций сокращается в 7 раз по сравнению с ежедневным перебором. Он особенно предпочтителен при работе с длительными временными периодами.
При выполнении любых операций с датами в 1С всегда следует учитывать ряд особенностей платформы, которые могут повлиять на корректность и предсказуемость результатов.
НачалоДня(): Мы настоятельно рекомендуем всегда использовать функцию НачалоДня() для нормализации дат перед их использованием в логике сравнений или арифметических операций. Если в дате присутствует временная компонента (часы, минуты, секунды), это может привести к неожиданным результатам. Например, дата "01.01.2023 10:00:00" при сравнении с "01.01.2023 09:00:00" будет больше, а прибавив к ней 24 часа, мы можем получить "02.01.2023 10:00:00", что не всегда является "началом следующего дня" с точки зрения календарной логики. Нормализация до начала дня (НачалоДня(Дата)) гарантирует, что мы работаем исключительно с календарными датами без учета времени.60 * 60 * 24 секунд к дате приведет к переходу ровно на следующий календарный день, а не к сдвигу времени суток. То есть, если мы прибавили 24 часа к "01.01.2023 00:00:00", мы всегда получим "02.01.2023 00:00:00", независимо от того, был ли в этот интервал переход на DST. Это делает операции с датами через секунды в 1С надежными для определения календарных дней.Таким образом, опасения относительно високосных годов или DST при использовании фиксированных секунд для перехода на следующий день или неделю в 1С обычно беспочвенны, так как платформа берет эти аспекты на себя.
Мы подробно рассмотрели два основных подхода к решению задачи получения списка дат конкретного дня недели в заданном диапазоне в 1С. Метод прямого перебора (Метод 1) прост в реализации и понимании, подходит для небольших временных интервалов и ситуаций, где производительность не является критически важной. Однако для повышения эффективности, особенно при работе с длительными периодами, мы настоятельно рекомендуем использовать оптимизированный метод (Метод 2), который сначала находит первый искомый день недели, а затем циклически прибавляет 7 дней.
Независимо от выбранного метода, всегда помните о нормализации дат с помощью функции НачалоДня() и знайте, что платформа 1С корректно обрабатывает такие календарные особенности, как високосные годы и переходы на летнее/зимнее время, при арифметических операциях с датами.
Выбор оптимального решения всегда зависит от конкретных требований вашей задачи и ожидаемой нагрузки. Надеемся, что это подробное руководство поможет вам уверенно работать с датами в 1С.