К статье
Практический материал

Разработка и Конфигурация · 10 мин

Сколько дней в месяце: определение количества дней в 1С

Как определить количество дней в месяце в 1С: КонецМесяца, День, ДобавитьМесяц, разность дат. Готовые примеры кода, типичные ошибки и проверка результата.

Задача узнать, сколько дней в конкретном месяце, возникает в 1С постоянно: при расчёте оклада пропорционально отработанным дням, начислении амортизации, формировании графиков платежей, проверке корректности дат документов. Встроенного оператора «ДнейВМесяце» во встроенном языке нет, поэтому количество дней вычисляется комбинацией стандартных функций работы с датами.

Самый надёжный способ — получить дату конца месяца функцией КонецМесяца() и извлечь из неё номер дня функцией День(). Поскольку последний день месяца всегда равен количеству дней в нём, формула даёт точный результат для любого месяца, включая февраль високосного года. Ручные таблицы «31–28–31» при этом не нужны вообще.

Ниже разобраны рабочие варианты кода, различия между тонким и серверным контекстом, особенности запросов и типичные ошибки, из-за которых расчёт «съезжает» на день или падает с ошибкой.

Базовый способ: День(КонецМесяца(Дата))

Функция КонецМесяца(Дата) принимает значение типа Дата и возвращает дату-время последнего дня того же месяца с точностью до секунды — например, для 15.02 получится 28.02 (или 29.02 в високосном году) 23:59:59. Функция День(Дата) извлекает из даты номер дня в месяце как число.

Соединяя обе функции, получаем универсальную формулу:

Функция ДнейВМесяце(ПроверяемаяДата) Экспорт

Возврат День(КонецМесяца(ПроверяемаяДата));

КонецФункции

// Примеры вызова:

ДнейВМесяце('20260115'); // 31

ДнейВМесяце('20260210'); // 29 — високосный год

ДнейВМесяце('20230210'); // 28

ДнейВМесяце('20230405'); // 30

Такую функцию удобно разместить в общем модуле (например, «РаботаСДатами» или «ОбщегоНазначенияКлиентСервер»), чтобы вызывать её из любых процедур конфигурации. Убедитесь, что у модуля установлены нужные флаги контекста: «Сервер», «Клиент (обычное приложение)» или «Клиент (управляемое приложение)» — в зависимости от того, откуда планируется вызов.

bsl
Функция ДнейВМесяце(Дата) Экспорт
    Возврат День(КонецМесяца(Дата));
КонецФункции

Альтернативные способы расчёта

Второй рабочий вариант — через функцию ДобавитьМесяц(). Логика такая: берём первое число текущего месяца, прибавляем один месяц (получаем первое число следующего) и вычитаем один день. Разность между первым числом следующего и первым числом текущего месяца, делённая на число секунд в сутках, даёт количество дней:

Функция ДнейВМесяцеЧерезДобавитьМесяц(Дата) Экспорт

НачалоТекущего = НачалоМесяца(Дата);

НачалоСледующего = ДобавитьМесяц(НачалоТекущего, 1);

Возврат (НачалоСледующего - НачалоТекущего) / 86400;

КонецФункции

Здесь 86400 — количество секунд в сутках (60 × 60 × 24). Результат вычитания двух дат в 1С — это число секунд между ними, поэтому деление обязательно. Способ чуть длиннее, но полезен, когда в той же процедуре уже вычисляются границы периодов через ДобавитьМесяц.

Третий вариант — через РазностьДат() с типом разности «День»: РазностьДат(НачалоМесяца(Дата), ДобавитьМесяц(НачалоМесяца(Дата), 1), ТипРазностиДат.День). Он читается явнее, но работает медленнее простого вычитания и на практике используется реже.

Сравнение способов

День(КонецМесяца(Дата))

Самый короткий и читаемый. Подходит практически всегда, рекомендуется по умолчанию.

Через ДобавитьМесяц и разность секунд

Удобен, если границы периодов уже вычислены. Требует деления на 86400, легко забыть.

РазностьДат с типом День

Наглядно выражает намерение, но громоздко и медленнее. Оправдано в сложных расчётах интервалов.

Количество дней в месяце в запросе

Когда расчёт нужен прямо в запросе — например, для отчёта по отработанным дням — используются функции языка запросов: КОНЕЦПЕРИОДА(Дата, МЕСЯЦ) и ДЕНЬ(Дата). Запрос вернёт число дней без обращения к коду модулей:

ВЫБРАТЬ

&Период КАК Дата,

ДЕНЬ(КОНЕЦПЕРИОДА(&Период, МЕСЯЦ)) КАК ДнейВМесяце

В отчётах на СКД то же выражение можно записать в вычисляемом поле на языке выражений системы компоновки, где доступны те же функции периодов. Если нужно количество дней не в месяце конкретной даты, а в произвольном периоде «с даты по дату», используйте РАЗНОСТЬДАТ(НачалоПериода, КонецПериода, ДЕНЬ) + 1 — единица добавляется, потому что РАЗНОСТЬДАТ возвращает число полных интервалов между датами, не включая первый день.

Учёт високосных лет и граничных случаев

Главное преимущество встроенных функций — автоматическая обработка високосных лет. КонецМесяца для февраля вернёт 28 или 29 число в зависимости от года, и никаких дополнительных проверок писать не нужно. Это исключает класс ошибок, типичных для самодельных таблиц «количество дней по номеру месяца», где февраль зашит константой 28.

Обратите внимание на пустую дату. Если в функцию передать значение '00010101' (пустая дата), КонецМесяца вернёт 31.01.0001, и результатом будет 31 — формально корректно, но почти наверняка не то, что ожидалось в бизнес-логике. Перед расчётом стоит проверять входное значение:

Если Не ЗначениеЗаполнено(ПроверяемаяДата) Тогда

ВызватьИсключение "Не указана дата для расчёта количества дней";

КонецЕсли;

Ещё один нюанс — тип входного значения. Если дата приходит из формы как строка, сначала преобразуйте её через Дата(Строка) или обеспечьте явный тип реквизита. Передача строки в КонецМесяца вызовет ошибку выполнения.

Практические сценарии применения

Расчёт пропорции отработанного времени

Типовая задача зарплатных конфигураций — рассчитать долю месяца, которую сотрудник отработал: принят 15 числа, уволен 20-го, больничный с середины месяца. Формула выглядит так: Доля = ОтработанныеДни / День(КонецМесяца(Период)). Полученный коэффициент умножается на оклад или тариф. В типовых решениях (ЗУП) для этого есть свои механизмы учёта времени, но в самописных расчётах формула применяется напрямую.

Формирование графика платежей

При генерации помесячного графика оплаты часто требуется ставить платёж на последний день месяца или проверять, не выходит ли дата платежа за его пределы. Комбинация НачалоМесяца, ДобавитьМесяц и КонецМесяца позволяет строить такие графики циклом без ручной корректировки дат под длину каждого месяца.

Проверка введённых дат в документах

В обработчиках проверки заполнения бывает нужно убедиться, что дата окончания договора попадает в тот же месяц, что и начало, или что срок не превышает количество дней в текущем месяце. Здесь достаточно сравнить День(ДатаОкончания) с День(КонецМесяца(ДатаНачала)) и выдать пользователю понятное сообщение об ошибке до записи документа.

Порядок внедрения функции в конфигурацию
  1. 1
    Создайте или откройте общий модуль с флагом Сервер и ВызовСервера при необходимости.
  2. 2
    Добавьте функцию ДнейВМесяце с проверкой на пустую дату.
  3. 3
    Замените в прикладном коде константы 30/31 на вызов функции.
  4. 4
    Протестируйте на датах: 31.01, 28.02 невисокосного, 29.02 високосного, 30.04.
  5. 5
    Проверьте отчёты и расчёты, где раньше использовались фиксированные значения.

Проверка результата и типичные ошибки

После внедрения функции прогоните её на контрольном наборе дат: январь (31), апрель (30), февраль високосного и невисокосного года. Достаточно четырёх-пяти проверок, чтобы убедиться в корректности. В отладчике удобно смотреть промежуточное значение КонецМесяца — оно должно показывать 23:59:59 последнего дня.

Частая ошибка — забыть, что КонецМесяца возвращает не начало дня, а конец (23:59:59). Если результат сравнивается с датой документа, у которой время 00:00:00, сравнение «равно» не сработает. Для сравнений по дню используйте НачалоДня(КонецМесяца(Дата)) или сравнивайте только части даты.

Вторая распространённая проблема — смешение контекстов: функция объявлена в серверном модуле без флага «Вызов сервера», а вызывается из клиентской процедуры. Платформа выдаст ошибку «Процедура или функция с указанным именем не определена». Проверьте флаги модуля или перенесите функцию в клиент-серверный модуль.

Типичные ошибки
01
Зашитая константа 30 или 31 вместо вычисления — расчёт ломается на феврале и коротких месяцах.
02
Сравнение даты документа с КонецМесяца без НачалоДня — условие никогда не выполняется из-за времени 23:59:59.
03
Забытое деление на 86400 при расчёте через разность дат — результат в секундах вместо дней.
04
Отсутствие проверки пустой даты — функция возвращает 31 для 01.01.0001 и искажает расчёты.
05
РАЗНОСТЬДАТ без +1 при подсчёте дней периода — результат меньше фактического на один день.
Контрольные точки

Как проверить результат

Февраль невисокосного года
Норма

Функция возвращает 28

Тревожный признак

Возвращает 30 или 31 — используется константа

Февраль високосного года
Норма

Функция возвращает 29

Тревожный признак

Возвращает 28 — год не учитывается

Сравнение с датой документа
Норма

Используется НачалоДня или сравнение частей даты

Тревожный признак

Сравнение с 23:59:59 напрямую

Пустая входная дата
Норма

Есть проверка и сообщение об ошибке

Тревожный признак

Молча возвращается 31

Часто задаваемые вопросы

Есть ли в 1С готовая функция «ДнейВМесяце»?

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

Как получить количество рабочих дней в месяце, а не календарных?

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

Чем КонецМесяца отличается от КОНЕЦПЕРИОДА в запросе?

По сути ничем: обе возвращают 23:59:59 последнего дня месяца. КонецМесяца — функция встроенного языка для кода модулей, КОНЕЦПЕРИОДА с параметром МЕСЯЦ — аналог в языке запросов. Выбор определяется только местом вычисления.

Как посчитать дни между двумя произвольными датами?

Вычтите одну дату из другой и разделите на 86400 — получите число дней между ними. Если нужно включить обе граничные даты в счёт, добавьте единицу. В запросах для этого служит РАЗНОСТЬДАТ с типом ДЕНЬ.

Что вернёт функция для пустой даты?

Для значения 01.01.0001 КонецМесяца вернёт 31.01.0001, и итоговый результат будет 31. Это формально корректный, но бессмысленный для прикладной логики ответ, поэтому пустую дату следует отсекать проверкой ЗначениеЗаполнено до вызова.

Работает ли этот код на мобильной платформе и в веб-клиенте?

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

Итог

Что важно запомнить

Количество дней в месяце в 1С определяется выражением День(КонецМесяца(Дата)), в запросах — ДЕНЬ(КОНЕЦПЕРИОДА(&Дата, МЕСЯЦ)). Встроенные функции сами учитывают високосные годы, поэтому константы и самодельные таблицы использовать нельзя. Не забывайте про время 23:59:59 в результате КонецМесяца и проверяйте входную дату на заполненность.