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