При разработке отчетов, обработок и логики проведения документов часто возникает необходимость скорректировать дату или ограничить период: например, если пользователь указал дату окончания позже марта, установить границу на последний день марта того же года. Разберем, как правильно решить эту задачу на платформе 1С:Предприятие, какие ошибки чаще всего возникают при работе со строками и датами, а также рассмотрим оптимальные варианты кода.
Начинающие разработчики нередко пытаются собрать дату путем склеивания строк. Посмотрим на типичный пример, который приводит к ошибке времени выполнения:
МояДата = '2016-08-20';
Год = Год(МояДата);
ДатаСтрока = Строка(Год) + "0331";
Дата = КонецДня(Дата(ДатаСтрока));
При выполнении этого кода платформа выдает ошибку преобразования к типу Дата. Выясним причину такого поведения: функция Строка() для числовых значений по умолчанию применяет системные настройки форматирования с разделением групп разрядов. Число 2016 превращается в строку "2 016" с неразрывным пробелом. В результате строка "2 0160331" не может быть распознана конструктором дат 1С.
Если вам действительно необходимо сформировать строковое представление даты, отключите группировку разрядов с помощью явного форматирования:
ДатаСтрока = Формат(Год, "ЧГ=0") + "0331";
Дата = Дата(ДатаСтрока);
Однако использование строковых операций для работы с датами считается неоптимальной практикой. В 1С есть более надежные и лаконичные механизмы.
Самый простой и читаемый способ получить произвольный день нужного года — использовать встроенный конструктор Дата() с передачей года, месяца и дня в виде чисел.
Разберем пример:
Если Месяц(ДатаОкончания) > 3 Тогда
ДатаОкончания = КонецДня(Дата(Год(ДатаОкончания), 3, 31));
КонецЕсли;
Обратите внимание на время: вызов Дата(Год, 3, 31) вернет дату со временем 00:00:00. Если эта дата используется как граница периода для отчетов, виртуальных таблиц или запросов к регистрам, документы за 31 марта не попадут в выборку. Поэтому обязательно используйте функцию КонецДня() либо передавайте время напрямую в конструктор:
ДатаОкончания = Дата(Год(ДатаОкончания), 3, 31, 23, 59, 59);
Чтобы не помнить точное количество дней в месяце (что особенно актуально для февраля в високосные годы), можно инициализировать первое число месяца и применить к нему функцию КонецМесяца():
Если Месяц(МояДата) > 3 Тогда
МояДата = КонецМесяца(Дата(Год(МояДата), 3, 1));
КонецЕсли;
Функция КонецМесяца() автоматически установит дату на последний день месяца и выставит время на 23:59:59.
Если задача заключается в ограничении границы периода концом первого квартала текущего года, можно обойтись без условных операторов Если ... КонецЕсли. Для этого объединим функции Мин(), КонецКвартала() и НачалоГода():
ДатаОкончания = Мин(ДатаОкончания, КонецКвартала(НачалоГода(ДатаОкончания)));
Рассмотрим, как работает эта конструкция по шагам:
НачалоГода(ДатаОкончания) возвращает 01.01.ГГГГ 00:00:00.КонецКвартала() берет начало года (первый квартал) и возвращает 31.03.ГГГГ 23:59:59.Мин() выбирает наименьшую из двух дат: если исходная дата была раньше конца марта, она останется без изменений; если позже — будет установлено 31 марта 23:59:59.Если аналогичную логику требуется реализовать внутри текста запроса к базе данных, используем условную конструкцию ВЫБОР и встроенные функции работы с датами языка запросов:
ВЫБОР
КОГДА МЕСЯЦ(Таблица.ДатаОкончания) > 3
ТОГДА КОНЕЦПЕРИОДА(НАЧАЛОПЕРИОДА(Таблица.ДатаОкончания, ГОД), КВАРТАЛ)
ИНАЧЕ Таблица.ДатаОкончания
КОНЕЦ
Совет по обработке пустых значений: перед выполнением любых преобразований проверяйте дату на заполненность с помощью ЗначениеЗаполнено(ДатаОкончания). Для пустой даты '0001-01-01' вызов функции Год() вернет 1, что может привести к неожиданным результатам в дальнейших расчетах.