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

Дата: 19.09.2026 09:22:56
Даты
Встроенный язык
Високосный год
КонецМесяца
Язык запросов
Комментарий:
0
Платформа:
8.3
Конфигурация:
Любая
Формы:
Любые

Високосный год 1С-разработчику приходится учитывать везде, где сумма делится на число дней: аренда за неполный месяц, проценты по займу, пени по договору. Готовой функции проверки во встроенном языке платформы нет, поэтому каждый пишет свою — и нередко с ошибкой. Ниже три надёжных способа проверки: по формуле григорианского календаря, через число дней в феврале (День(КонецМесяца(...))) и через длину года (ДеньГода(КонецГода(...))). Там же — количество дней в месяце и в году, те же вычисления в запросе и два расчёта, где 29 февраля меняет итоговую сумму: оплата за неполный месяц и проценты за период, который переходит через Новый год.

Код рассчитан на платформу 8.3 и любую конфигурацию. Функции работы с датами (Год, День, КонецМесяца, ДеньГода, ДобавитьМесяц) доступны и на клиенте, и на сервере, поэтому модуль ниже можно вызывать прямо из формы, без серверного вызова. Запрос, как обычно, выполняется только на сервере.

Где встречается задача

  • Сумма за неполный месяц: аренда, абонентская плата, подписка, начисление по дням. Делитель — число дней конкретного месяца, а в феврале это 28 или 29.
  • Проценты по займам и депозитам, пени по договору: годовую ставку переводят в дневную делением на число дней в году — 365 или 366.
  • Сроки и графики: «через год от даты», «последний день месяца», продление договора, подписанного 29 февраля.
  • Отчёты с нормированием по дням: среднее в день, месячный план, разложенный по календарным дням.
  • Контроль при загрузке данных: дата 29.02 в невисокосном году — признак ошибки в исходном файле.

Високосный год 1С: три способа проверки

Правило григорианского календаря: год високосный, если делится на 4, кроме годов, кратных 100; годы, кратные 400, снова високосные. Поэтому 2024 и 2028 — високосные, 1900 и 2100 — нет, 2000 — да. Во встроенном языке правило записывается через оператор остатка от деления %.

Второй способ не требует помнить правило: платформа сама знает календарь, достаточно спросить, сколько дней в феврале нужного года. Третий — сравнить номер последнего дня года с 366. Результат у всех трёх одинаковый, выбор зависит от того, что у вас на входе — номер года или дата.

ЗадачаВыражениеЧто на входе
Проверка високосного года по формулеГ % 4 = 0 И (Г % 100 <> 0 ИЛИ Г % 400 = 0)Число (номер года)
Проверка через февральДень(КонецМесяца(Дата(Г, 2, 1))) = 29Число или Год(Дата)
Проверка через длину годаДеньГода(КонецГода(Д)) = 366Дата
Количество дней в месяцеДень(КонецМесяца(Д))Дата
Количество дней в годуДеньГода(КонецГода(Д))Дата
Дата через годДобавитьМесяц(Д, 12)Дата

Решение: общий модуль с календарными функциями

Чтобы не копировать выражения по конфигурации, соберите их в общий модуль, например КалендарныеРасчетыКлиентСервер. Флаги: «Клиент (управляемое приложение)», «Сервер», «Внешнее соединение»; для обычных форм добавьте «Клиент (обычное приложение)». Суффикс КлиентСервер в имени — по стандартам разработки 1С для модулей, доступных в обоих контекстах. Перед созданием убедитесь, что модуля с таким именем в конфигурации ещё нет.

// Общий модуль КалендарныеРасчетыКлиентСервер
// Флаги: «Клиент (управляемое приложение)», «Сервер», «Внешнее соединение».
// Для обычных форм дополнительно отметьте «Клиент (обычное приложение)».

#Область ПрограммныйИнтерфейс

// Проверяет, високосный ли год (правила григорианского календаря).
//
// Параметры:
//  ГодИлиДата - Число, Дата - номер года (2024) или любая дата внутри года.
//
// Возвращаемое значение:
//  Булево - Истина, если в году 366 дней.
//
Функция ЭтоВисокосныйГод(ГодИлиДата) Экспорт

	Если ТипЗнч(ГодИлиДата) = Тип("Дата") Тогда
		НомерГода = Год(ГодИлиДата);
	Иначе
		НомерГода = ГодИлиДата;
	КонецЕсли;

	Возврат НомерГода % 4 = 0 И (НомерГода % 100 <> 0 ИЛИ НомерГода % 400 = 0);

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

// Та же проверка через календарь платформы: в високосном феврале 29 дней.
//
// Параметры:
//  Период - Дата - любая дата внутри проверяемого года.
//
// Возвращаемое значение:
//  Булево
//
Функция ЭтоВисокосныйГодПоКалендарю(Период) Экспорт

	Возврат День(КонецМесяца(Дата(Год(Период), 2, 1))) = 29;

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

// Количество дней в месяце, которому принадлежит дата.
Функция КоличествоДнейВМесяце(Период) Экспорт

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

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

// Количество дней в году, которому принадлежит дата: 365 или 366.
Функция КоличествоДнейВГоду(Период) Экспорт

	Возврат ДеньГода(КонецГода(Период));

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

// Количество дней в каждом месяце года.
//
// Возвращаемое значение:
//  Массив - 12 чисел, индекс 0 соответствует январю.
//
Функция ДниПоМесяцам(НомерГода) Экспорт

	Результат = Новый Массив;
	Для НомерМесяца = 1 По 12 Цикл
		Результат.Добавить(День(КонецМесяца(Дата(НомерГода, НомерМесяца, 1))));
	КонецЦикла;

	Возврат Результат;

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

// Календарные дни между датами включительно. Время в датах не учитывается.
Функция КоличествоДнейВПериоде(ДатаНачала, ДатаОкончания) Экспорт

	Начало = НачалоДня(ДатаНачала);
	Окончание = НачалоДня(ДатаОкончания);
	Если Окончание < Начало Тогда
		Возврат 0;
	КонецЕсли;

	Возврат (Окончание - Начало) / 86400 + 1;

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

// Сумма за часть месяца пропорционально календарным дням.
// Обе даты должны относиться к одному месяцу, иначе вызывается исключение.
Функция СуммаЗаЧастьМесяца(СуммаЗаМесяц, ДатаНачала, ДатаОкончания) Экспорт

	Если НачалоМесяца(ДатаНачала) <> НачалоМесяца(ДатаОкончания) Тогда
		ВызватьИсключение НСтр("ru = 'Даты начала и окончания должны относиться к одному месяцу.'");
	КонецЕсли;

	Дней = КоличествоДнейВПериоде(ДатаНачала, ДатаОкончания);
	Возврат Окр(СуммаЗаМесяц * Дней / КоличествоДнейВМесяце(ДатаНачала), 2);

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

// Проценты за период, если база - фактическое число дней в году (365 или 366).
// Период, который переходит через 31 декабря, делится на части по годам.
Функция ПроцентыЗаПериод(Сумма, СтавкаГодовых, ДатаНачала, ДатаОкончания) Экспорт

	Результат = 0;
	НачалоОтрезка = НачалоДня(ДатаНачала);
	Окончание = НачалоДня(ДатаОкончания);

	Пока НачалоОтрезка <= Окончание Цикл

		КонецОтрезка = НачалоДня(КонецГода(НачалоОтрезка));
		Если КонецОтрезка > Окончание Тогда
			КонецОтрезка = Окончание;
		КонецЕсли;

		Дней = КоличествоДнейВПериоде(НачалоОтрезка, КонецОтрезка);
		Результат = Результат
			+ Сумма * СтавкаГодовых / 100 * Дней / КоличествоДнейВГоду(НачалоОтрезка);

		НачалоОтрезка = КонецОтрезка + 86400;

	КонецЦикла;

	Возврат Окр(Результат, 2);

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

#КонецОбласти

Примеры вызова — из формы или с сервера, без разницы:

Високосный2024 = КалендарныеРасчетыКлиентСервер.ЭтоВисокосныйГод(2024);        // Истина
Високосный1900 = КалендарныеРасчетыКлиентСервер.ЭтоВисокосныйГод(1900);        // Ложь
ВисокосныйПоДате = КалендарныеРасчетыКлиентСервер.ЭтоВисокосныйГод('20250610'); // Ложь

ДнейВФеврале = КалендарныеРасчетыКлиентСервер.КоличествоДнейВМесяце('20240215'); // 29
ДнейВГоду = КалендарныеРасчетыКлиентСервер.КоличествоДнейВГоду('20260301');      // 365

ДниМесяцев = КалендарныеРасчетыКлиентСервер.ДниПоМесяцам(2028);
ФевральВ2028 = ДниМесяцев[1]; // 29, индекс 0 - январь

Разбор ключевых строк

Формула и тип параметра

Выражение НомерГода % 4 = 0 И (НомерГода % 100 <> 0 ИЛИ НомерГода % 400 = 0) возвращает Булево, его можно сразу отдавать через Возврат. Скобки здесь для читаемости: условие «кратно 400» и без них попадает в правильную ветку, но со скобками правило читается так же, как формулируется словами.

Проверка ТипЗнч(ГодИлиДата) = Тип("Дата") позволяет передать и число, и дату. Без неё передача даты в формулу приводит к ошибке — подробнее о приёме в статье про тип значения в 1С.

Количество дней в месяце: День(КонецМесяца(Дата))

КонецМесяца возвращает последнюю секунду последнего дня месяца — для февраля 2024 года это 29.02.2024 23:59:59. День берёт из этой даты номер дня, он и равен количеству дней в месяце. Ни таблицы «31, 28, 31…», ни отдельной обработки февраля не нужно — календарь платформы уже всё учёл.

Количество дней в году: ДеньГода(КонецГода(Дата))

ДеньГода возвращает порядковый номер дня в году. Для 31 декабря это 365 или 366 — готовое количество дней в году и заодно третий способ проверки високосного года.

Дни между датами

Разность двух дат в 1С — число секунд. Значение типа Дата не хранит часовой пояс, поэтому разность двух начал дня всегда кратна 86400, и високосный день учитывается автоматически. НачалоДня отрезает время: без него документы от 10:15 и 18:30 дают дробное число дней. + 1 делает период включительным — 15 и 29 февраля дают 15 дней, а не 14.

Массив дней по месяцам

ДниПоМесяцам возвращает массив из 12 чисел. Индексы начинаются с нуля, поэтому февраль — элемент [1]. Удобно, когда нужно разложить годовую сумму по месяцам или вывести календарь в отчёт.

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

В языке запросов те же вычисления делаются через КОНЕЦПЕРИОДА, ДЕНЬ и ДЕНЬГОДА. Собрать из поля документа дату «1 февраля того же года» через ДАТАВРЕМЯ не выйдет: в запросе эта функция принимает только числовые константы. Можно дойти до февраля через ДОБАВИТЬКДАТЕ от начала года, но проще получить признак високосного года через длину года.

// Серверный код: модуль обработки или общий модуль с флагом «Сервер».
// Документ.РеализацияТоваровУслуг замените на документ своей конфигурации.
Функция ДокументыСЧисломДней(НачалоПериода, КонецПериода) Экспорт

	Запрос = Новый Запрос;
	Запрос.Текст =
	"ВЫБРАТЬ
	|	Док.Ссылка КАК Ссылка,
	|	Док.Дата КАК Дата,
	|	ДЕНЬ(КОНЕЦПЕРИОДА(Док.Дата, МЕСЯЦ)) КАК ДнейВМесяце,
	|	ДЕНЬГОДА(КОНЕЦПЕРИОДА(Док.Дата, ГОД)) КАК ДнейВГоду,
	|	ВЫБОР
	|		КОГДА ДЕНЬГОДА(КОНЕЦПЕРИОДА(Док.Дата, ГОД)) = 366
	|			ТОГДА ИСТИНА
	|		ИНАЧЕ ЛОЖЬ
	|	КОНЕЦ КАК ВисокосныйГод
	|ИЗ
	|	Документ.РеализацияТоваровУслуг КАК Док
	|ГДЕ
	|	Док.Дата МЕЖДУ &НачалоПериода И &КонецПериода";

	Запрос.УстановитьПараметр("НачалоПериода", НачалоДня(НачалоПериода));
	Запрос.УстановитьПараметр("КонецПериода", КонецДня(КонецПериода));

	Возврат Запрос.Выполнить().Выгрузить();

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

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

Расчёт пропорций с учётом високосного года

Сумма за неполный месяц

Аренда — 60 000 в месяц, арендатор въехал 15 февраля. В 2024 году это 15 дней из 29, в 2023 — 14 из 28. Если делитель зашит константой, февраль посчитается неверно: при 30 — каждый год, при 28 — в каждый високосный. Функция СуммаЗаЧастьМесяца берёт делитель из календаря. Период через границу месяца (например, 20.01–10.02) она не принимает и вызывает исключение: у такого периода нет одного делителя, и без проверки функция молча разделила бы 22 дня на 31 день января. Такой период делят по месяцам и считают каждую часть отдельно.

Проценты за период через 31 декабря

Заём 1 000 000 под 16% годовых с 15.12.2023 по 15.01.2024. При базе «фактическое число дней в году» период делится на два отрезка: 17 дней декабря делятся на 365, 15 дней января — на 366. Получается 14 009,43. Если считать все 32 дня с делителем 365, выйдет 14 027,40 — расхождение небольшое, но на акте сверки оно всплывёт.

// Аренда 60 000 в месяц, въезд 15 февраля, расчёт до конца месяца.
Сумма2024 = КалендарныеРасчетыКлиентСервер.СуммаЗаЧастьМесяца(60000, '20240215', '20240229'); // 31034.48
Сумма2023 = КалендарныеРасчетыКлиентСервер.СуммаЗаЧастьМесяца(60000, '20230215', '20230228'); // 30000

// Заём 1 000 000 под 16% годовых с 15.12.2023 по 15.01.2024 включительно.
Проценты = КалендарныеРасчетыКлиентСервер.ПроцентыЗаПериод(1000000, 16, '20231215', '20240115'); // 14009.43

Какая база верна — 365, 366 или фактическая — и включаются ли в расчёт первый и последний день, определяет договор или учётная политика, а не программист. Функция реализует один вариант; остальные получаются заменой делителя. Там же решается, округлять каждый отрезок или только итог: в коде округляется итог.

Типичные ошибки

  • Проверка только «делится на 4». Для 2024 и 2028 работает, но 2100 окажется високосным, а он таковым не будет. Тест на граничных годах (1900, 2000, 2100) такую проверку сразу ловит.
  • Дата вместо номера года. Выражение ДатаДокумента % 4 падает с ошибкой «Преобразование значения к типу Число не может быть выполнено». Нужен Год(ДатаДокумента) % 4 или проверка типа, как в модуле выше.
  • Год как 365 суток. Прибавление 365 * 86400 секунд даёт сдвиг на день, как только в периоде есть 29 февраля. Календарный год прибавляется через ДобавитьМесяц(Дата, 12). Учтите обратный случай: от 29.02.2024 функция вернёт 28.02.2025 — в следующем году 29 февраля нет.
  • Разность дат со временем. (ДатаОкончания - ДатаНачала) / 86400 по документам с временем даёт дробь, а период из одного дня — ноль. Если такой ноль попадает в делитель, получите «Деление на 0». Приводите даты к НачалоДня и добавляйте единицу для включительного периода.
  • Пустая дата. Для даты 01.01.0001 КоличествоДнейВМесяце вернёт 31, а Год — 1: ошибки нет, но результат бессмысленный. Проверяйте ЗначениеЗаполнено до расчёта.
  • Проверка через исключение. Вызов Дата(Год, 2, 29) внутри Попытка формально тоже отвечает на вопрос, но строит логику на исключениях: в цикле это медленно, а в Исключение заодно попадают ошибки, которые вы ловить не собирались.
// Ошибка: к дате прибавили 365 суток вместо года.
СрокОкончания = '20230301' + 365 * 86400; // 29.02.2024, а не 01.03.2024

// Правильно: календарный год.
СрокОкончания = ДобавитьМесяц('20230301', 12); // 01.03.2024

Частые вопросы

Есть ли в 1С готовая функция проверки високосного года?

Во встроенном языке платформы — нет. Служебные функции на эту тему встречаются в модулях конфигураций, но опираться на неописанные функции чужих модулей рискованно: при обновлении их могут переименовать. Три строки в своём общем модуле надёжнее.

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

Выражением ДЕНЬ(КОНЕЦПЕРИОДА(Поле, МЕСЯЦ)). Для года — ДЕНЬГОДА(КОНЕЦПЕРИОДА(Поле, ГОД)), результат 365 или 366.

Работают ли эти функции на клиенте?

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

Как посчитать количество дней между датами с учётом високосного года?

Вычтите одну дату из другой и разделите на 86400 — платформа сама учитывает 29 февраля. Предварительно приведите обе даты к началу дня и решите, включать ли границы периода.

Какой способ проверки выбрать?

Если на входе номер года из настройки или реквизита — формулу. Если дата документа — ДеньГода(КонецГода(Дата)) = 366, эта же логика без изменений переносится в запрос. Разницы в результате нет.

Когда проще заказать

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

По теме

Не хотите разбираться сами?

Адаптируем это решение под вашу базу. Оценка бесплатно.

Оставить задачу

Станьте частью сообщества!
Войдите или зарегистрируйтесь, и вы сможете участвовать в обсуждениях.


1С, 1С:Предприятие — товарные знаки ООО «1С». Сайт не является официальным представительством фирмы «1С» и не аффилирован с ней.