Разработка и Конфигурация · 13 мин
Как высчитать возраст в 1С по дате рождения
Как высчитать возраст в 1С по дате рождения: функции на встроенном языке, вычисление в запросе, типовые механизмы ЗУП, проверка результата и частые ошибки.
Задача вычисления возраста встречается в 1С постоянно: в кадровых отчётах, при проверке права на льготы, в печатных формах, при расчёте стажа и северных надбавок. На первый взгляд всё просто — вычесть дату рождения из текущей даты. На практике такой подход даёт ошибку в 1–2 дня вокруг дня рождения, потому что возраст зависит не только от разности лет, но и от того, наступил ли день рождения в текущем году.
Правильный алгоритм учитывает месяц и день: если в текущем году день рождения ещё не наступил, из разности лет нужно вычесть единицу. Ниже разобраны готовые функции на встроенном языке, вычисление возраста прямо в запросе, типовые механизмы конфигураций и способы проверить результат на граничных датах.
Материал применим к платформе 1С:Предприятие 8.2 и 8.3, к типовым конфигурациям (ЗУП, Бухгалтерия, Управление торговлей) и к самописным решениям. Код приведён в виде, готовом для вставки в общий модуль.
Где в 1С хранится дата рождения и почему это важно
Прежде чем считать возраст, нужно понять, откуда брать дату рождения. В типовых конфигурациях она хранится в разных местах: в ЗУП — в реквизите «ДатаРождения» справочника «ФизическиеЛица», в Бухгалтерии — аналогично в справочнике физических лиц, в самописных решениях — в реквизитах справочников сотрудников, клиентов или пациентов.
Обратите внимание на тип реквизита. Если дата рождения хранится как строка (так бывает после некачественных обменов), функции работы с датами не сработают — сначала потребуется преобразование через Дата() или обработку строки. Проверьте также заполненность: пустая дата рождения (Дата(1,1,1)) при вычислении даст огромное число, которое попадёт в отчёт как возраст в тысячи лет.
Базовая функция вычисления возраста на встроенном языке
Универсальная функция принимает дату рождения и дату, на которую считается возраст (по умолчанию — текущая). Логика: берём разность лет, затем проверяем, наступил ли день рождения в году расчётной даты.
Функция ВозрастНаДату(ДатаРождения, НаДату = Неопределено) Экспорт
Если Не ЗначениеЗаполнено(ДатаРождения) Тогда
Возврат Неопределено;
КонецЕсли;
Если НаДату = Неопределено Тогда
НаДату = ТекущаяДатаСеанса();
КонецЕсли;
Если ДатаРождения > НаДату Тогда
Возврат Неопределено;
КонецЕсли;
Лет = Год(НаДату) - Год(ДатаРождения);
Если Месяц(НаДату) < Месяц(ДатаРождения)
Или (Месяц(НаДату) = Месяц(ДатаРождения)
И День(НаДату) < День(ДатаРождения)) Тогда
Лет = Лет - 1;
КонецЕсли;
Возврат Лет;
КонецФункции
Функция корректно обрабатывает три ситуации: день рождения уже прошёл в текущем году, день рождения сегодня (возраст увеличивается именно в этот день) и день рождения ещё впереди. Отдельная проверка защищает от даты рождения в будущем — такое бывает при ошибках ввода, и лучше вернуть Неопределено, чем отрицательный возраст.
Обратите внимание на ТекущаяДатаСеанса() вместо устаревшей ТекущаяДата(): функция сеанса учитывает часовой пояс сервера и корректно работает в клиент-серверном режиме, где локальное время пользователя может отличаться от серверного.
- 1Создайте общий модуль, например «РаботаСДатами», с признаками Сервер и ВызовСервера
- 2Вставьте функцию ВозрастНаДату и включите её в интерфейс (Экспорт)
- 3Вызовите функцию из формы, отчёта или процедуры проведения, передав дату рождения
- 4Протестируйте на граничных датах: день рождения сегодня, завтра, вчера, 29 февраля
Вычисление возраста в запросе
Когда возраст нужен в отчёте по списку сотрудников, вызывать функцию в цикле по строкам результата — плохая идея: это множит обращения и усложняет код. Язык запросов 1С позволяет вычислить возраст на стороне СУБД через функции ГОД(), МЕСЯЦ(), ДЕНЬ() и конструкцию ВЫБОР.
ВЫБРАТЬ
ФизическиеЛица.Ссылка КАК ФизЛицо,
ФизическиеЛица.ДатаРождения КАК ДатаРождения,
ГОД(&НаДату) - ГОД(ФизическиеЛица.ДатаРождения)
- ВЫБОР
КОГДА МЕСЯЦ(&НаДату) < МЕСЯЦ(ФизическиеЛица.ДатаРождения)
ТОГДА 1
КОГДА МЕСЯЦ(&НаДату) = МЕСЯЦ(ФизическиеЛица.ДатаРождения)
И ДЕНЬ(&НаДату) < ДЕНЬ(ФизическиеЛица.ДатаРождения)
ТОГДА 1
ИНАЧЕ 0
КОНЕЦ КАК Возраст
ИЗ
Справочник.ФизическиеЛица КАК ФизическиеЛица
ГДЕ
ФизическиеЛица.ДатаРождения <> ДАТАВРЕМЯ(1, 1, 1)
Параметр &НаДату передаётся при выполнении запроса. Такой подход даёт готовую колонку возраста для всех записей за одно обращение к базе и легко встраивается в СКД-отчёты: вычисляемое поле добавляется прямо в текст запроса набора данных.
В СКД есть альтернатива — вычисляемое поле с выражением на встроенном языке и вызовом функции общего модуля. Это проще в написании, но медленнее на больших выборках, потому что выражение выполняется для каждой строки уже после получения данных. Для списков в сотни записей разница незаметна, для отчётов по десяткам тысяч сотрудников предпочтителен расчёт в запросе.
Где считать возраст
Подходит для разовых вычислений: форма элемента, печатная форма, проверка при записи. Не подходит для массовых отчётов из-за построчных вызовов.
Оптимально для отчётов и динамических списков: один запрос, готовая колонка. Минус — более громоздкий текст запроса.
Компромисс для отчётов на СКД без изменения запроса. На больших выборках медленнее, чем расчёт в запросе.
Готовые функции и роли уже учитывают кадровые особенности (стаж, возраст для отчётности). Подходят, если конфигурация типовая и дорабатывать ничего не нужно.
Типовые механизмы в конфигурациях
В типовой 1С:ЗУП вычисление возраста уже реализовано. В общих модулях зарплатной библиотеки есть функции вида ЗарплатаКадрыКлиентСервер.ВозрастВГодах (точное имя зависит от редакции конфигурации), а в отчётах по персоналу возраст выводится штатно. Прежде чем писать свой код, поищите в конфигураторе по слову «Возраст» в общих модулях — велика вероятность, что готовая функция уже есть и учтена в обновлениях.
В ЗУП возраст также используется в кадровых отчётах (например, списки сотрудников с возрастом на дату), в механизмах стажа и при формировании сведений для отчётности. Если ваша задача — вывести возраст в типовом отчёте, часто достаточно настроить вариант отчёта, добавив поле в структуру, без программирования.
В Бухгалтерии и Управлении торговлей специализированных функций возраста обычно нет — там применяется собственная функция общего модуля по образцу из предыдущего раздела.
Особые случаи: 29 февраля, возраст в месяцах и днях
Люди, родившиеся 29 февраля, — классический тест на корректность алгоритма. В невисокосном году дня 29.02 не существует, и разные системы трактуют день рождения по-разному: одни считают его 28 февраля, другие — 1 марта. Приведённая выше функция по сути считает, что в невисокосном году день рождения наступает 1 марта, поскольку 28 февраля условие «месяц равен и день меньше» ещё выполняется. Для кадрового учёта такое поведение обычно приемлемо, но если в вашем учёте принято другое правило — зафиксируйте его в коде явно.
Иногда нужен возраст не в годах, а в месяцах (дети до года) или в днях (медицинские и кадровые расчёты). Для дней достаточно разности дат: (НаДату - ДатаРождения) / 86400, поскольку разность дат в 1С даёт результат в секундах. Для полных месяцев логика аналогична годам: разность месяцев с поправкой на день.
// Полных месяцев на дату
Месяцев = (Год(НаДату) - Год(ДатаРождения)) * 12
+ Месяц(НаДату) - Месяц(ДатаРождения);
Если День(НаДату) < День(ДатаРождения) Тогда
Месяцев = Месяцев - 1;
КонецЕсли;
Для строкового представления вида «35 лет 4 месяца» в типовых конфигурациях есть функции склонения и представления интервалов, либо используйте СтрокаСЧисломДляЛюбогоЯзыка() из БСП для корректного склонения «год/года/лет».
Проверка результата и типичные ошибки
Проверять функцию нужно на граничных датах, а не на случайных значениях. Минимальный набор тестов: дата рождения сегодня (возраст должен увеличиться именно в этот день), завтра (возраст ещё не увеличился), вчера (уже увеличился), 29 февраля високосного года с расчётом на 28 февраля и 1 марта невисокосного, пустая дата рождения, дата рождения в будущем.
Как проверить результат
Возраст увеличился на 1 в день рождения
Возраст увеличивается на день раньше или позже
Показано старое значение возраста
Возраст уже увеличен
Функция возвращает Неопределено или 0 по правилу, принятому в решении
В отчёт попадает возраст в сотни или тысячи лет
Оба способа дают одинаковое значение на одних данных
Результаты расходятся на граничных датах
Чаще всего ошибки сводятся к нескольким повторяющимся сценариям. Их удобно держать перед глазами при ревью кода.
Практические сценарии применения
Возраст в печатной форме
В макете печатной формы возраст обычно выводится как параметр. Вычислите его в процедуре формирования печатной формы функцией общего модуля и передайте в параметры макета. Для представления вида «42 года» используйте функции склонения числительных из БСП, если она входит в конфигурацию.
Отбор сотрудников по возрасту в отчёте
Если нужен отчёт «сотрудники старше 50 лет» или «моложе 30», не вычисляйте возраст и не сравнивайте его с порогом — это мешает использовать индексы. Разверните условие: возраст ≥ 50 означает дату рождения не позже ДобавитьМесяц(НаДату, -50*12) с поправкой на день. Такой отбор по дате рождения работает быстрее и проще проверяется.
Проверка при записи данных
Для контроля ввода (например, сотрудник не младше 14 и не старше разумного предела) вызовите функцию в обработчике проверки заполнения и выдайте пользователю сообщение с указанием введённой даты. Это отсекает опечатки вида «1902» вместо «1920» ещё на этапе ввода.
Удобно хранить функцию возраста в одном общем модуле вместе с функциями стажа и интервалов дат. Тогда правило расчёта (например, трактовка 29 февраля) зафиксировано в одном месте, и все отчёты дают согласованные значения.
Ограничения и границы самостоятельной доработки
Собственная функция возраста — безопасная доработка: общий модуль не затрагивает типовые объекты, и обновление конфигурации его не сломает. Риски появляются, когда вы меняете типовые отчёты или модули напрямую: при обновлении такие изменения могут быть затёрты или вступить в конфликт с новыми версиями объектов.
Если возраст участвует в регламентированной отчётности или расчётах, влияющих на выплаты, перед внедрением согласуйте правило расчёта с кадровой службой и сверьте результат с типовым механизмом на контрольных примерах. Расхождение в один день вокруг дня рождения — формально мелочь, но в спорной ситуации именно она станет предметом вопросов.
Что важно запомнить
Возраст считают как разность лет с поправкой на наступление дня рождения в текущем году, а не делением разности дат. Для отчётов вычисление выполняют в запросе, для разовых случаев — функцией общего модуля. Пустые даты и 29 февраля обрабатывают явно, результат проверяют на граничных датах.
Частые вопросы
Чем отличается ТекущаяДата() от ТекущаяДатаСеанса() при расчёте возраста?
ТекущаяДата() возвращает время компьютера, на котором выполняется код — на клиенте это время рабочей станции пользователя. ТекущаяДатаСеанса() учитывает часовой пояс сеанса и приводится к единому серверному времени. В файловом режиме разницы обычно нет, в клиент-серверном с филиалами в разных поясах использование ТекущаяДата() может дать «вчерашнюю» дату и неверный возраст в день рождения.
Как вывести возраст со словом «год», «года» или «лет»?
Используйте функцию СтрокаСЧисломДляЛюбогоЯзыка() из Библиотеки стандартных подсистем: она принимает шаблон вида «;год;;года;годов» и сама выбирает форму по числу. Если БСП в конфигурации нет, напишите простую функцию: смотрите на две последние цифры числа — для 11–14 всегда «лет», иначе по последней цифре 1 — «год», 2–4 — «года», остальное — «лет».
Можно ли посчитать возраст в динамическом списке без доработки запроса?
Нет, динамический список строится только на запросе, и вычисляемые поля в нём не поддерживаются. Добавьте вычисление возраста в текст запроса списка через конструкцию ВЫБОР, как показано выше, либо замените динамический список на таблицу значений, заполняемую программно.
Почему в отчёте у части сотрудников возраст — огромное число?
Почти всегда это незаполненная дата рождения: пустая дата равна 01.01.0001, и разность лет даёт тысячи лет. Добавьте в запрос отбор ДатаРождения <> ДАТАВРЕМЯ(1,1,1), а в функцию — проверку ЗначениеЗаполнено(). Заодно стоит проверить, откуда взялись пустые значения: часто это следствие неполной миграции данных.
Как посчитать возраст на конкретную дату в прошлом или будущем?
Передайте нужную дату вторым параметром функции или в параметр запроса вместо текущей даты. Алгоритм не меняется: разность лет с поправкой на наступление дня рождения. Так считают, например, возраст на дату приёма на работу или на первое января отчётного года.
Нужно ли учитывать время в дате рождения?
Нет. Дата рождения в учётных системах хранится без времени (начало дня), и сравнение идёт по датам. Если в данных вдруг присутствует время, приведите значения к началу дня функцией НачалоДня() перед сравнением, иначе запись, созданная позже времени рождения, может дать лишний день.