При разработке кадровых отчетов и обработок часто возникает задача определить точный стаж сотрудника или интервал между двумя датами с выводом результата в формате «N лет, N месяцев, N дней». Попытка решить эту задачу исключительно средствами языка запросов 1С сталкивается с серьезными ограничениями. Разберем, почему чистый расчет в запросе не дает точного результата, и рассмотрим правильные варианты реализации на встроенном языке и в схеме компоновки данных (СКД).
В языке запросов 1С для работы с временными интервалами предусмотрена функция РАЗНОСТЬДАТ. Однако важно понимать принцип ее работы: функция считает не фактически прошедшие полные интервалы, а количество пересечений календарных границ (начал дней, месяцев или лет).
Проанализируем пример: если вычислить разность дат между 31 января и 1 февраля с типом МЕСЯЦ, функция вернет значение 1, хотя фактически между датами прошел всего один календарный день. Аналогично возникают расхождения при переходе через високосные года и месяцы с разным количеством дней (28, 29, 30 и 31 день).
Попытка выразить стаж в днях через запрос с последующим делением:
ВЫБОР
КОГДА Сотрудники.ДатаУвольнения = ДАТАВРЕМЯ(1, 1, 1)
ТОГДА РАЗНОСТЬДАТ(Сотрудники.ДатаПриема, &ТекущаяДата, ДЕНЬ)
ИНАЧЕ РАЗНОСТЬДАТ(Сотрудники.ДатаПриема, Сотрудники.ДатаУвольнения, ДЕНЬ)
КОНЕЦ КАК ДнейСтажа
Такой подход дает лишь приблизительное число дней. При обратном переводе в годы и месяцы (делением на 365 или 30) неизбежно накапливаются ошибки, которые не согласуются с официальными правилами кадрового учета.
Для корректного получения составляющих интервала (лет, месяцев и дней) применяется алгоритм последовательного помесячного сдвига с использованием функции ДобавитьМесяц. При расчете стажа работы также важно помнить кадровое правило: день увольнения является рабочим днем, поэтому конечную дату периода увеличивают на один день.
Рассмотрим по шагам реализацию универсальной функции:
Посмотрим на пример кода функции:
Функция ПолучитьСтажПрописью(Знач ДатаНачала, Знач ДатаОкончания) Экспорт
Если Не ЗначениеЗаполнено(ДатаНачала) Тогда
Возврат "";
КонецЕсли;
Если Не ЗначениеЗаполнено(ДатаОкончания) Тогда
ДатаОкончания = ТекущаяДатаСеанса();
КонецЕсли;
ДатаСч = НачалоДня(ДатаНачала);
// Включаем день окончания в стаж (+1 день)
ДатаКон = НачалоДня(ДатаОкончания) + 86400;
Если ДатаСч >= ДатаКон Тогда
Возврат "0 лет 0 мес 0 дн";
КонецЕсли;
Лет = 0;
Месяцев = 0;
// Считаем полные годы
Пока ДобавитьМесяц(ДатаСч, 12) <= ДатаКон Цикл
Лет = Лет + 1;
ДатаСч = ДобавитьМесяц(ДатаСч, 12);
КонецЦикла;
// Считаем полные месяцы
Пока ДобавитьМесяц(ДатаСч, 1) <= ДатаКон Цикл
Месяцев = Месяцев + 1;
ДатаСч = ДобавитьМесяц(ДатаСч, 1);
КонецЦикла;
// Оставшиеся дни
Дней = (ДатаКон - ДатаСч) / 86400;
Возврат СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
"%1 г. %2 мес. %3 дн.",
Формат(Лет, "ЧГ=0"),
Формат(Месяцев, "ЧГ=0"),
Формат(Дней, "ЧГ=0")
);
КонецФункции
В конфигурациях, использующих Библиотеку Стандартных Подсистем (БСП), а также в 1С:ЗУП, 1С:ERP и 1С:КА, нет необходимости писать алгоритм с нуля. В типовом общем модуле ОбщегоНазначенияКлиентСервер или специализированных модулях кадрового учета доступны готовые функции (например, ПериодВГодахМесяцахДнях), которые возвращают структуру с полями Лет, Месяцев, Дней либо сразу формируют строковое представление.
Если отчет строится на СКД, оптимальным решением является получение «сырых» дат в запросе и их форматирование через вычисляемые поля с вызовом функции общего модуля.
Разберем порядок настройки:
ДатаПриема и ДатаУвольнения.СтажСтрокой и в колонке Выражение указываем вызов функции:
ИмяВашегоОбщегоМодуля.ПолучитьСтажПрописью(ДатаПриема, Выбор Когда ДатаУвольнения = ДАТАВРЕМЯ(1,1,1) Тогда &Период Иначе ДатаУвольнения Конец)
Таким образом, запрос остается простым и производительным, а вся логика работы с календарем и високосными годами безошибочно выполняется на встроенном языке 1С.