Разработка и Конфигурация · 9 мин
Как определить количество дней в месяце в 1С
Способы узнать количество дней в месяце в 1С: функция День() с КонецМесяца(), встроенный язык, запросы, СКД. Примеры кода, типичные ошибки и проверка результата.
Во встроенном языке 1С нет отдельной функции «КоличествоДнейВМесяце», поэтому задача решается комбинацией стандартных функций работы с датами. Самый распространённый приём — взять конец месяца от нужной даты и извлечь из него номер дня: он и будет равен количеству дней в этом месяце. Приём работает одинаково в тонком и толстом клиенте, на сервере и в веб-клиенте, поскольку использует базовые функции платформы.
Задача встречается постоянно: расчёт оклада по фактически отработанным дням, нормы времени, проверка корректности введённой даты, формирование отчётов по дням, построение календарных графиков. Важно понимать, что количество дней зависит не только от месяца, но и от года — февраль високосного года содержит 29 дней. Платформа учитывает високосность автоматически, поэтому ручные таблицы «31-28-31» в коде писать не нужно и не следует.
Ниже разобраны способы получения количества дней во встроенном языке, в запросах и в СКД, а также вариант через разность дат, типичные ошибки и способы проверить результат.
Основной способ: День(КонецМесяца(Дата))
Функция КонецМесяца(Дата) возвращает дату-время, соответствующее последнему моменту месяца, — например, для 15.03 возвращается 31.03 23:59:59. Функция День(Дата) извлекает из даты число месяца. Вместе они дают искомое значение одной строкой:
ДнейВМесяце = День(КонецМесяца(ТекущаяДата));
Если нужно получить количество дней для произвольного месяца и года, сначала сформируйте любую дату внутри этого месяца через функцию Дата(Год, Месяц, День), а затем примените тот же приём:
НужнаяДата = Дата(Год, Месяц, 1);
ДнейВМесяце = День(КонецМесяца(НужнаяДата));
Приём корректно обрабатывает все частные случаи: февраль обычного года вернёт 28, февраль високосного — 29, апрель — 30, январь — 31. Високосность определяется платформой по григорианскому календарю, вмешательство разработчика не требуется.
Функция КоличествоДнейВМесяце(Год, Месяц)
Возврат День(КонецМесяца(Дата(Год, Месяц, 1)));
КонецФункцииАльтернатива через разность дат
Второй рабочий вариант — вычислить разность между началом следующего месяца и началом текущего. Разность дат в 1С выражается в секундах, поэтому результат делится на количество секунд в сутках — 86400:
ДнейВМесяце = (НачалоМесяца(ДобавитьМесяц(НужнаяДата, 1)) - НачалоМесяца(НужнаяДата)) / 86400;
Этот способ полезен, когда в алгоритме уже вычисляются границы периода и дополнительный вызов КонецМесяца избыточен. Математически оба варианта дают одинаковый результат, но первый короче и читается легче, поэтому в новом коде предпочтителен именно он.
Обратите внимание: вычитание дат даёт число секунд, а не дней. Частая ошибка новичков — забыть деление на 86400 и получить значение вроде 2678400 вместо 31. Если результат выглядит аномально большим, проверьте единицы измерения.
Количество дней в запросе
В языке запросов 1С доступны те же функции с другим синтаксисом. Конец месяца получается через КОНЕЦПЕРИОДА(&Дата, МЕСЯЦ), номер дня — через ДЕНЬ(). Типовой фрагмент запроса:
ВЫБРАТЬ
ДЕНЬ(КОНЕЦПЕРИОДА(&Дата, МЕСЯЦ)) КАК ДнейВМесяце
Если количество дней нужно для каждой строки таблицы — например, для списка документов с разными датами, — функции применяются к полю выборки, а не к параметру:
ВЫБРАТЬ
Документы.Дата КАК Дата,
ДЕНЬ(КОНЕЦПЕРИОДА(Документы.Дата, МЕСЯЦ)) КАК ДнейВМесяце
ИЗ
Документ.НачислениеЗарплаты КАК Документы
Такой подход позволяет рассчитать, например, дневную ставку начисления прямо в запросе: Сумма / ДЕНЬ(КОНЕЦПЕРИОДА(Документы.Дата, МЕСЯЦ)). Это удобнее, чем выгружать данные и считать в цикле, особенно на больших объёмах.
Использование в СКД и выражениях компоновки
В системе компоновки данных количество дней обычно вычисляется на уровне запроса набора данных — тем же выражением ДЕНЬ(КОНЕЦПЕРИОДА(...)). Это предпочтительный путь: расчёт выполняется на сервере СУБД, результат доступен как обычное поле для группировок, ресурсов и отборов.
Если поле нужно вычислить уже на уровне компоновки (например, источник данных — внешний набор), используйте вычисляемое поле с функциями языка выражений СКД. Там доступен вызов функций общих модулей с признаком доступности из компоновки, поэтому можно вынести расчёт в общий модуль и ссылаться на него из выражения. Учтите, что общий модуль должен быть серверным и с включённым флагом «Вызов сервера», иначе выражение не выполнится.
Для отчётов по норме времени и производственным календарям учтите: количество календарных дней месяца — не то же самое, что количество рабочих дней. Рабочие дни определяются по регистру сведений с данными календаря (в типовых конфигурациях — «Данные производственного календаря» или аналог), а не арифметикой дат.
Типичные ошибки и как их избежать
Первая ошибка — жёстко зашитая таблица дней по месяцам без учёта високосного года. Такой код ломается раз в четыре года, причём сбой проявляется только в феврале и легко ускользает от тестирования. Всегда полагайтесь на функции платформы.
Вторая ошибка — путаница между КонецМесяца и КонецДня или ручным конструированием даты вида Дата(Год, Месяц, 31). Последний вариант для месяцев с 30 днями либо даст неверную дату, либо приведёт к неочевидному поведению при переносе даты. Доверяйте вычисление границы месяца платформе.
Третья группа ошибок связана с типами: если на вход функции подаётся строка или дата с пустым значением (01.01.0001), результат будет некорректным или вызовет ошибку. Перед расчётом проверяйте заполненность даты через ЗначениеЗаполнено().
Как проверить результат
Проверка проста и выполняется на контрольных значениях. Для января любого года функция должна вернуть 31, для апреля — 30, для февраля невисокосного года — 28, для февраля високосного — 29. Високосными считаются годы, делящиеся на 4, кроме делящихся на 100, но с сохранением високосности для делящихся на 400.
Быстрый способ проверить код — выполнить выражение в консоли кода или через «Вычислить выражение» в отладчике, подставив даты из разных месяцев. Если все четыре контрольных значения совпали, реализация корректна.
Как проверить результат
Возвращается 31
30 или другое число — ошибка в логике
Возвращается 28
29 — не учтена високосность
Возвращается 29
28 — жёсткая таблица без високосности
Обработана проверкой ЗначениеЗаполнено
Ошибка выполнения или 31 от 01.01.0001
Где это применяется на практике
Наиболее частый сценарий — расчёт дневной ставки: оклад, делённый на количество дней в месяце начисления. Аналогично считаются среднедневные показатели выручки, нормы выработки, пени за дни просрочки в пределах месяца.
Второй сценарий — валидация пользовательского ввода: если форма принимает год, месяц и день раздельными полями, проверка День <= День(КонецМесяца(Дата(Год, Месяц, 1))) отсекает несуществующие даты вроде 30 февраля ещё до записи данных.
Третий сценарий — построение таблиц периодов: при обходе дней месяца в цикле верхняя граница цикла задаётся именно этим значением, что исключает выход за пределы месяца.
Что важно запомнить
Количество дней в месяце получают выражением День(КонецМесяца(Дата)) во встроенном языке и ДЕНЬ(КОНЕЦПЕРИОДА(Дата, МЕСЯЦ)) в запросах. Платформа сама учитывает високосные годы; ручные таблицы дней не нужны и опасны. Для рабочих дней используйте производственный календарь, а не календарные дни.
Частые вопросы
Есть ли в 1С готовая функция «КоличествоДнейВМесяце»?
В стандартном встроенном языке такой функции нет. В некоторых типовых конфигурациях и библиотеках (например, в составе БСП) встречаются обёртки с похожими названиями, но внутри они используют ту же пару День(КонецМесяца(Дата)).
Как учесть високосный год при расчёте?
Никаких дополнительных действий не требуется: функция КонецМесяца сама возвращает 28 или 29 февраля в зависимости от года переданной даты. Главное — не заменять расчёт жёстко зашитой таблицей дней по месяцам.
Чем количество календарных дней отличается от рабочих?
Календарные дни — это просто длина месяца (28–31). Рабочие дни определяются производственным календарём с учётом выходных и праздников и хранятся в регистрах сведений конфигурации. Для расчёта зарплаты и норм времени используется именно календарь, а не арифметика дат.
Как получить количество дней в произвольном периоде, а не в месяце?
Вычислите разность дат в секундах и разделите на 86400: (КонецПериода - НачалоПериода) / 86400. Если нужно включить обе граничные даты, добавьте к результату единицу.
Почему КонецМесяца возвращает время 23:59:59 и не мешает ли это?
Это штатное поведение: функция возвращает последнюю секунду последнего дня месяца. Для функции День это не имеет значения — номер дня извлекается корректно. Время важно учитывать только при сравнении дат в условиях и запросах.
Работает ли этот приём в мобильной платформе и веб-клиенте?
Да, функции День, КонецМесяца, НачалоМесяца и ДобавитьМесяц доступны во всех режимах исполнения: тонкий, толстый, веб-клиент, мобильная платформа и сервер. Код переносим без изменений.