При разработке отчетов, алгоритмов расчета или сложных выборок в 1С мы часто сталкиваемся с необходимостью работать с датами. Одна из классических задач — определить, сколько дней было в предыдущем месяце, если у нас есть только параметр с текущей датой. Разберем эту задачу по шагам и проанализируем несколько удобных способов ее решения прямо в языке запросов.
Сразу отметим важное правило: в отличие от встроенного языка 1С, в языке запросов недоступно прямое вычитание одной даты из другой с помощью математических операторов. Мы обязаны использовать специализированные функции. Посмотрим на три отличных варианта решения.
Это самое популярное решение, которое часто используется разработчиками. Логика очень проста: чтобы получить последний день предыдущего месяца, нам нужно от начала текущего месяца отнять ровно одну секунду. После этого мы просто извлекаем номер дня из полученной даты.
Рассмотрим подробнее код данного решения:
ДЕНЬ(
ДОБАВИТЬКДАТЕ(
НАЧАЛОПЕРИОДА(&ТекущаяДата, МЕСЯЦ),
СЕКУНДА,
-1
)
)
Разберем по шагам, как это работает:
НАЧАЛОПЕРИОДА с параметром МЕСЯЦ сбрасывает текущую дату на первое число текущего месяца (00:00:00).ДОБАВИТЬКДАТЕ вычитает из этой даты ровно одну секунду (указываем параметр -1). Мы оказываемся в 23:59:59 последнего дня предыдущего месяца.ДЕНЬ возвращает номер дня из этой даты, что и является количеством дней в искомом месяце.Этот вариант считается более элегантным, так как не требует манипуляций с секундами. Мы просто сдвигаем нашу дату на один месяц назад, получаем конец этого периода и берем из него день.
Посмотрим на пример в запросе:
ДЕНЬ(
КОНЕЦПЕРИОДА(
ДОБАВИТЬКДАТЕ(&ТекущаяДата, МЕСЯЦ, -1),
МЕСЯЦ
)
)
Проанализируем этот алгоритм:
ДОБАВИТЬКДАТЕ мы сдвигаем дату из параметра на один месяц назад.КОНЕЦПЕРИОДА превращает эту дату в последнюю секунду месяца.ДЕНЬ вытаскивает число. Это решение очень легко читается в коде.Количество дней в любом месяце — это ровно разница в днях между первым числом этого месяца и первым числом следующего. Мы можем использовать функцию РАЗНОСТЬДАТ для вычисления этого значения.
Напишем код для этого способа:
РАЗНОСТЬДАТ(
НАЧАЛОПЕРИОДА(ДОБАВИТЬКДАТЕ(&ТекущаяДата, МЕСЯЦ, -1), МЕСЯЦ),
НАЧАЛОПЕРИОДА(&ТекущаяДата, МЕСЯЦ),
ДЕНЬ
)
Важный нюанс: Обратите внимание, что обе даты обязательно приводятся к началу месяца через НАЧАЛОПЕРИОДА. Функция РАЗНОСТЬДАТ вычисляет разницу в целых единицах (в данном случае — в днях) и отбрасывает часы и минуты. Если в параметре &ТекущаяДата будет содержаться случайное время (например, середина дня), прямое вычисление без округления до начала суток может привести к потере одного дня в результате. Поэтому приведение к началу периода здесь критически важно.
Многих разработчиков волнует вопрос, как описанные методы ведут себя с февралем. Выясним эту деталь. Абсолютно все встроенные функции языка запросов 1С, такие как ДОБАВИТЬКДАТЕ или КОНЕЦПЕРИОДА, автоматически учитывают високосные годы.
Если значение параметра &ТекущаяДата выпадает на любую дату марта в високосном году, вычитание одного месяца и последующий расчет дней корректно и без ошибок вернет значение 29. В обычный год результат будет 28. Никаких дополнительных условий и проверок на кратность года писать не нужно, платформа 1С берет эти вычисления на себя.
Все три рассмотренных способа являются правильными и оптимальными с точки зрения производительности. Вы можете выбрать любой из них в зависимости от стандартов написания кода, принятых в вашей команде.