Как в 1С проверить, високосный ли год: рабочие способы и подводные камни

· 9 мин

Содержание

Встроенной функции «високосный год» в платформе 1С нет. Определяют его одной строкой: год високосный, если делится на 4, но не на 100, либо делится на 400. А в запросе проще не считать остатки, а спросить платформу, сколько дней в феврале: 29 — значит год високосный, 28 — обычный.

Вопрос всплывает не на пустом месте. Нужно посчитать число календарных дней периода для пеней или процентов, построить график платежей с фактическим количеством дней в году, корректно перенести дату 29 февраля на следующий год. Везде, где в знаменателе стоит 365 или 366, ошибка в один день тянет за собой неверную сумму.

Ниже — четыре проверенных способа (три во встроенном языке, один в запросе), разбор, какой когда уместен, и список мест, где из-за високосного года расчёт тихо врёт.

Правило: делимость на 4, на 100 и на 400

Григорианский календарь определяет високосный год тремя условиями, которые проверяются по порядку:

  • 🔹 год делится на 4 — кандидат в високосные;
  • 🔹 но если он делится ещё и на 100 — обычный (исключение из первого правила);
  • 🔹 а если делится на 400 — снова високосный (исключение из исключения).

Короткая проверка на знакомых годах:

ГодДелится на 4На 100На 400Високосный
2023нет——нет
2024данет—да
1900даданетнет
2000дададада
2100даданетнет

Правило на 400 нужно из-за астрономии: год длится примерно 365,2422 суток, одни «лишние» сутки раз в 4 года переливают чуть больше, чем надо, и раз в 100 лет поправку убирают, а раз в 400 — возвращают. На практике это значит ровно одно: нельзя проверять только делимость на 4 — на рубеже столетий такой код ошибётся.

Проверка через остаток от деления

Самый прямой способ — повторить правило в коде встроенного языка. Остаток от деления в 1С даёт оператор %.

Функция ВисокосныйГод(Год)
    Возврат (Год % 4 = 0 И Год % 100 <> 0) ИЛИ Год % 400 = 0;
КонецФункции

Вызов предельно простой — на вход число года:

Если ВисокосныйГод(2024) Тогда
    Сообщить("2024 — високосный");
КонецЕсли;

Если на входе дата, а не число, достаньте год функцией Год():

ТекущийГод = Год(ТекущаяДата());
Результат = ВисокосныйГод(ТекущийГод);

Арифметические операции в 1С выполняются раньше сравнения, поэтому Год % 4 = 0 читается как (Год % 4) = 0. Скобки в примере стоят только вокруг логического И — для наглядности, менять приоритет они не обязаны.

Это компактный и быстрый вариант, не зависящий от дат и часовых поясов. Главный риск здесь — соблазн урезать условие до Год % 4 = 0. На годах вроде 1900 или 2100 такая «оптимизация» вернёт неверный ответ.

Если функция нужна в нескольких местах, вынесите её в общий модуль с признаками «Сервер» и «Клиент» — тогда одно и то же определение будет доступно и из обработок, и из форм, и из фоновых заданий. Дублировать код в каждом объекте не нужно.

Способ без формул: спросить платформу про февраль

Если держать в голове правило 4-100-400 не хочется, пусть делимость считает сама платформа. Високосность однозначно определяется длиной февраля, а длину месяца даёт глобальная функция КонецМесяца().

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

КонецМесяца(Дата(Год, 2, 1)) возвращает последнее мгновение февраля, а День() вынимает из него номер дня — 28 или 29. Логика календаря уже зашита в платформу, своё правило писать не нужно.

Тот же результат даёт вычитание суток у 1 марта. В 1С вычитание числа из даты — это вычитание секунд, сутки равны 86400:

ПоследнийФевраля = Дата(Год, 3, 1) - 86400;
Високосный = (День(ПоследнийФевраля) = 29);

А если нужна не сама високосность, а число дней в году — например, для знаменателя в расчёте процентов, — считайте разницу дат напрямую:

Функция ДнейВГоду(Год)
    Возврат (Дата(Год + 1, 1, 1) - Дата(Год, 1, 1)) / 86400;
КонецФункции

Функция вернёт 365 или 366. Этот результат чаще и нужен на практике, чем голый флаг «да/нет».

Коротко, какой способ когда брать:

СпособКогда удобен
Остаток %нужен только флаг, без дат под рукой
Длина февраля (КонецМесяца)не хочется держать правило 4-100-400 в коде
Разница дат (ДнейВГоду)в расчёте нужно именно число дней, 365 или 366
Проверьте себя
Какой из годов високосный?
Сколько дней в феврале 2024 года?

Проверка в языке запросов

В запросе оператора остатка % нет, поэтому правило 4-100-400 там напрямую не записать. Зато работает тот же приём с февралём: конструируем дату запросной функцией ДАТАВРЕМЯ, берём конец периода и смотрим день.

ВЫБРАТЬ
    ДЕНЬ(КОНЕЦПЕРИОДА(ДАТАВРЕМЯ(2024, 2, 1), МЕСЯЦ)) = 29 КАК Високосный

Поле Високосный вернёт ИСТИНА для 2024 года и ЛОЖЬ для 2023. Чтобы проверять не фиксированный, а произвольный год из поля или параметра, соберите февраль от его начала года:

ВЫБРАТЬ
    ДЕНЬ(КОНЕЦПЕРИОДА(
        ДОБАВИТЬКДАТЕ(НАЧАЛОПЕРИОДА(&Дата, ГОД), МЕСЯЦ, 1), МЕСЯЦ)) = 29 КАК Високосный

Здесь НАЧАЛОПЕРИОДА(&Дата, ГОД) даёт 1 января нужного года, ДОБАВИТЬКДАТЕ(…, МЕСЯЦ, 1) сдвигает на февраль, а КОНЕЦПЕРИОДА(…, МЕСЯЦ) — на его последний день.

Число дней в году в запросе считают функцией РАЗНОСТЬДАТ:

ВЫБРАТЬ
    РАЗНОСТЬДАТ(
        НАЧАЛОПЕРИОДА(&Дата, ГОД),
        ДОБАВИТЬКДАТЕ(НАЧАЛОПЕРИОДА(&Дата, ГОД), ГОД, 1),
        ДЕНЬ) КАК ДнейВГоду

Результат — 365 или 366. Все перечисленные функции (ДАТАВРЕМЯ, НАЧАЛОПЕРИОДА, КОНЕЦПЕРИОДА, ДОБАВИТЬКДАТЕ, РАЗНОСТЬДАТ, ДЕНЬ) входят в стандартный набор языка запросов 1С 8.3, расширений подключать не нужно.

Такое выражение удобно использовать и в вычисляемом поле системы компоновки данных, и в отборе динамического списка: правило 4-100-400 туда не впишешь, а проверку длины февраля — вполне. Если же расчёт сложный, число дней в году логичнее получить во встроенном языке функцией ДнейВГоду() и передать в запрос параметром, а не пересобирать даты внутри самого запроса.

Где важно, сколько дней в году

Сам флаг «високосный» прикладной задаче почти не нужен — важно число дней. Вот где оно реально участвует в расчёте:

  • 💰 Проценты и пени по фактическим дням. Если договор или методика считают от «фактического числа дней в году», в знаменателе должно стоять 365 или 366, а не константа. В високосном году одна и та же ставка даёт чуть меньшую дневную сумму.
  • 📅 Пени за просрочку. Период считается в календарных днях. Если он захватывает 29 февраля, в нём на день больше — сумма вырастет.
  • 📈 Графики займов и лизинга по дням. При ежедневном начислении число дней каждого месяца и года берут фактическое, февраль в високосном году добавляет сутки.
  • 🧮 Стаж в календарных днях. 29 февраля — такой же календарный день; при подсчёте разницы дат он учитывается автоматически, вручную его отбрасывать не нужно.

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

Отдельно про отпускные и средний заработок: там коэффициент среднемесячного числа дней (29,3) задан нормативно и фиксирован, високосный год на него не влияет. А вот календарные дни самого расчётного периода, если он включает 29 февраля, платформа посчитает с учётом этого дня — и это правильно.

Пример: как один день меняет сумму

Начислим проценты по займу 1 000 000 ₽ под 12% годовых за 31 день, по формуле «сумма × ставка × дни ÷ число дней в году».

  • В 2024 году (366 дней): 1 000 000 × 12% × 31 ÷ 366 ≈ 10 163,93 ₽.
  • В 2023 году (365 дней): та же сумма и ставка ÷ 365 ≈ 10 191,78 ₽.

Разница — около 27,85 ₽ на этом небольшом примере только из-за знаменателя. Если бы код жёстко делил на 365 и в високосном году, он завысил бы начисление. Поэтому знаменатель берут из ДнейВГоду(), а не из константы.

Второй эффект — длина самого периода. Интервал с 1 февраля по 31 марта включительно в 2024 году — это 60 календарных дней, а в 2023-м — 59: разницу даёт как раз 29 февраля. РАЗНОСТЬДАТ посчитает это сама, вручную поправлять не нужно.

Подводные камни и граничные случаи

Несколько мест, где легко ошибиться.

Конструирование несуществующей даты. Не собирайте 29 февраля «вслепую» для произвольного года. Выражение Дата(Год, 2, 29) для невисокосного года даёт некорректную дату, и поведение здесь лучше не закладывать в логику. Нужен последний день февраля — берите КонецМесяца(Дата(Год, 2, 1)) или Дата(Год, 3, 1) - 86400, они всегда вернут настоящую дату.

Перенос даты 29 февраля на год вперёд. Функция ДобавитьМесяц() и годовой сдвиг аккуратно обрабатывают конец месяца: прибавление года к 29 февраля даёт 28 февраля следующего (невисокосного) года, а не ошибку. Учитывайте это в договорах и графиках с «той же датой через год».

Столетия. 1900 и 2100 — не високосные, 2000 — високосный. Если данные пришли из внешнего источника (например, из выгрузки, где 1900 год помечен високосным по известной ошибке табличных редакторов), не переносите эту ошибку в 1С — платформа считает такие годы верно.

Тип аргумента. В функцию ВисокосныйГод() должно приходить целое число года, а не дата и не строка. Если источник — дата, явно извлекайте год через Год(); если строка — через Число() с проверкой на пустоту.

Текущий год на сервере. Чтобы узнать, високосный ли идёт год, нужен корректный текущий год. На сервере ТекущаяДата() возвращает дату сервера в его часовом поясе. Если сеансы работают в других поясах и на границе 31 декабря / 1 января это важно, берите год из ТекущаяДатаСеанса() — она учитывает часовой пояс сеанса. На практике для високосности расхождение возможно лишь в новогоднюю ночь, но помнить о нём стоит.

Проверка в отладчике. Быстро убедиться в ответе, не создавая обработку, можно в Табло во время отладки: введите выражение ВисокосныйГод(2024) или День(КонецМесяца(Дата(2024, 2, 1))) и посмотрите результат.

Есть ли в 1С встроенная функция для проверки високосного года?
Нет. В платформе 8.3 нет глобальной функции вроде ГодВисокосный. Свою пишут в одну строку — через остаток от деления или через длину февраля.
Что вернёт `Дата(Год, 2, 29)` для невисокосного года?
Корректной даты 29 февраля в таком году не существует, поэтому полагаться на это выражение нельзя. Последний день февраля безопасно получать через КонецМесяца() или вычитанием суток у 1 марта.
Нужно ли учитывать високосный год при расчёте пеней и процентов?
Да, если методика считает по фактическому числу дней. Тогда в знаменателе стоит 365 или 366, а период, захватывающий 29 февраля, длиннее на день. На коэффициент 29,3 для отпускных високосный год не влияет.
Как определить високосный год в запросе без модуля?
Оператора остатка в запросе нет, поэтому проверяют длину февраля: ДЕНЬ(КОНЕЦПЕРИОДА(ДАТАВРЕМЯ(Год, 2, 1), МЕСЯЦ)) = 29. Число дней в году даёт РАЗНОСТЬДАТ между 1 января и 1 января следующего года.
Связан ли високосный год с «високосной секундой»?
Нет, это разные вещи. Високосный год — лишний день (29 февраля) в григорианском календаре. Високосная секунда — отдельная поправка точного времени, к типу «Дата» в 1С и к расчётам по дням отношения не имеет.