Разработка и Конфигурация · 10 мин
Как сравнивать даты в 1С
Сравнение дат в 1С: операторы больше/меньше, функции НачалоДня и КонецДня, пустая дата, запросы и отборы. Примеры кода, ошибки и ответы на вопросы.
Сравнение дат — одна из базовых операций во встроенном языке 1С: она нужна при проверке сроков действия договоров, формировании отчётов за период, контроле опозданий сотрудников и отборе документов. Встроенный язык поддерживает прямое сравнение значений типа «Дата» обычными операторами, но на практике почти всегда возникают нюансы: время внутри даты, пустые значения, сравнение в запросах и в условиях отборов.
Главная особенность типа «Дата» в 1С — он всегда содержит и дату, и время с точностью до секунды. Поэтому сравнение Дата1 = Дата2 сработает только при полном совпадении до секунды, а проверка «попадает ли документ в сегодняшний день» требует приведения значений к границам дня. Игнорирование этого правила — самая частая причина «неработающих» условий.
Ниже разобраны все основные способы сравнения: операторы языка, функции приведения, работа с пустой датой, сравнение в запросах, в СКД и на форме, а также типичные ошибки и способы их проверки.
Базовое сравнение дат операторами встроенного языка
Значения типа «Дата» поддерживают полный набор операторов сравнения: =, <>, <, >, <=, >=. Сравнение выполняется хронологически: более ранняя дата считается меньшей. Результат операции — булево значение, которое можно использовать в условиях, циклах и присваиваниях.
Если Документ.Дата > ТекущаяДатаСеанса() Тогда
Сообщить("Дата документа в будущем");
КонецЕсли;
Если СрокДействияДоговора <= ТекущаяДатаСеанса() Тогда
Сообщить("Договор истёк или истекает сегодня");
КонецЕсли;
Обратите внимание на разницу между строгим и нестрогим сравнением. Условие Срок < ТекущаяДата не сработает в момент, когда срок равен текущей дате с точностью до секунды. Если «последний день» должен считаться ещё действующим, используйте <= с приведением к концу дня: ТекущаяДатаСеанса() <= КонецДня(Срок).
Сравнивать можно только значения совместимых типов. Если один из операндов — строка или число, сравнение либо вернёт результат по правилам приведения типов, либо вызовет ошибку. Перед сравнением убедитесь, что обе переменные имеют тип «Дата», например через функцию ТипЗнч().
Учёт времени: НачалоДня, КонецДня и сравнение по дню
Тип «Дата» в 1С хранит время, и это меняет логику сравнений. Значение ТекущаяДатаСеанса() возвращает дату с текущим временем, поэтому условие НачалоДня(ТекущаяДатаСеанса()) = ДатаДокумента не сработает, если у документа время отлично от 00:00:00. Чтобы сравнить две даты «по дню», приведите обе к началу дня.
Если НачалоДня(Дата1) = НачалоДня(Дата2) Тогда
// Даты относятся к одному календарному дню
КонецЕсли;
Для проверки попадания момента в конкретный день удобнее диапазон: от начала дня до конца дня включительно. Функция КонецДня() возвращает 23:59:59 указанной даты, что закрывает интервал полностью.
Если ДатаПроверки >= НачалоДня(НужнаяДата)
И ДатаПроверки <= КонецДня(НужнаяДата) Тогда
// Момент попадает в нужный день
КонецЕсли;
Аналогично работают функции НачалоМесяца(), КонецМесяца(), НачалоКвартала(), НачалоНедели() и другие — они нужны, когда сравнение выполняется по периоду, а не по конкретному моменту. Для сдвига дат при сравнении используйте ДобавитьКДате(): например, проверка «истекает ли срок в ближайшие 7 дней» выглядит как Срок <= ДобавитьКДате(ТекущаяДатаСеанса(), "День", 7).
Сравнение с пустой датой и проверка заполненности
Пустая дата в 1С — это значение 01.01.0001 00:00:00. Её можно задать литералом '00010101' или получить как значение по умолчанию незаполненного реквизита. Прямое сравнение с пустой датой — рабочий способ проверки: Если Реквизит.ДатаОкончания = '00010101' Тогда.
Более универсальный вариант — функция ЗначениеЗаполнено(). Она возвращает Ложь для пустой даты, пустой строки, нуля, Неопределено и пустых ссылок, поэтому подходит для проверки реквизитов любого типа:
Если ЗначениеЗаполнено(Договор.ДатаОкончания) Тогда
// Срок задан, можно сравнивать с текущей датой
Иначе
// Договор бессрочный
КонецЕсли;
Частая ошибка — сравнивать пустую дату с ТекущаяДатаСеанса() без предварительной проверки. Пустая дата всегда меньше любой реальной, поэтому условие «срок истёк» ложно сработает для бессрочного договора. Сначала проверяйте заполненность, потом выполняйте хронологическое сравнение.
Сравнение дат в запросах
В языке запросов 1С даты сравниваются теми же операторами в секции ГДЕ. Дату-параметр передают через УстановитьПараметр(), а литерал пустой даты записывают как ДАТАВРЕМЯ(1,1,1):
ВЫБРАТЬ
Док.Ссылка
ИЗ
Документ.РеализацияТоваровУслуг КАК Док
ГДЕ
Док.Дата МЕЖДУ &НачалоПериода И &КонецПериода
И Док.ДатаОплаты <> ДАТАВРЕМЯ(1,1,1)
Оператор МЕЖДУ включает обе границы, поэтому параметр конца периода обычно передают как КонецДня(Период) — иначе документы последнего дня с ненулевым временем выпадут из выборки. Это одна из самых частых причин «неполных» отчётов.
В запросе доступны функции работы с датами: НАЧАЛОПЕРИОДА(Дата, ДЕНЬ), КОНЕЦПЕРИОДА(Дата, МЕСЯЦ), ДОБАВИТЬКДАТЕ(), РАЗНОСТЬДАТ(). Последняя удобна для сравнения по разнице: например, отобрать документы, где между датой отгрузки и оплатой прошло больше 30 дней.
Запрос.УстановитьПараметр("КонецПериода", КонецДня(Период));
Запрос.УстановитьПараметр("НачалоПериода", НачалоДня(Период));Сравнение дат в СКД, отборах и на форме
В схеме компоновки данных сравнение дат оформляется через параметры и отборы. Для периодических отчётов принято использовать стандартный параметр «Период» или пару «НачалоПериода»/«КонецПериода» с видом сравнения «Больше или равно» и «Меньше или равно». Если отчёт строится по дню, границу конца периода в выражении отбора оборачивают в КОНЕЦПЕРИОДА(&КонецПериода, ДЕНЬ).
При программной установке отбора динамического списка или таблицы значений используйте вид сравнения из перечисления ВидСравненияКомпоновкиДанных — «Равно», «БольшеИлиРавно», «ВИнтервалеВключаяГраницы» и другие. Установка отбора «Равно» по дате с временем снова упирается в проблему секунд: для списка документов за день ставьте интервал, а не точное равенство.
Как выбрать способ сравнения
Разница между датами и сравнение по разности
Иногда сравнивать нужно не сами даты, а расстояние между ними: возраст сотрудника, длительность просрочки, стаж. Для этого служит функция запроса РАЗНОСТЬДАТ(Дата1, Дата2, Единица) — она возвращает целое число единиц (дней, месяцев, лет) между датами. Во встроенном языке прямого аналога нет: разность дат даёт число секунд, которое затем делят на 86400 для получения дней.
РазностьВСекундах = Дата2 - Дата1;
ДнейПросрочки = Цел(РазностьВСекундах / 86400);
Если ДнейПросрочки > 30 Тогда
// Критическая просрочка
КонецЕсли;
Учтите, что деление разности на 86400 считает «физические» сутки, а не календарные дни. Если нужны именно календарные дни между датами, приведите обе к началу дня перед вычитанием. Для месяцев и лет арифметика секунд не подходит — используйте РАЗНОСТЬДАТ в запросе или поэлементное сравнение через Год(), Месяц(), День().
Проверка корректности сравнения дат
После написания условия проверьте его на граничных значениях: пустая дата, дата ровно в полночь, последняя секунда дня, первый и последний день месяца. Большинство ошибок сравнения дат проявляется именно на границах интервалов, а не в середине периода.
Для отладки выводите значения в явном виде через Формат(Дата, "ДЛФ=DT") — так видно и дату, и время. Если условие «не срабатывает», в девяти случаях из десяти причина в скрытом времени внутри значения, которое не отображается в стандартном представлении даты.
Как проверить результат
После НачалоДня() обе даты равны, условие срабатывает
Даты выглядят одинаково, но равенство ложно — внутри разное время
В выборку попадают документы первого и последнего дня включительно
Документы последнего дня отсутствуют — граница задана без КонецДня()
Незаполненный реквизит определяется через ЗначениеЗаполнено() = Ложь
Пустая дата участвует в хронологическом сравнении и искажает логику
Частые вопросы
Как сравнить только даты без учёта времени?
Приведите оба значения функцией НачалоДня() и сравнивайте результат: НачалоДня(Дата1) = НачалоДня(Дата2). Для сравнения «раньше/позже» по дню используйте те же приведённые значения с операторами < и >.
Как проверить, что дата не заполнена?
Два рабочих способа: сравнение с литералом '00010101' или функция ЗначениеЗаполнено(Дата) — она вернёт Ложь для пустой даты. В запросе используйте сравнение с ДАТАВРЕМЯ(1,1,1).
Почему условие Дата = ТекущаяДатаСеанса() никогда не срабатывает?
ТекущаяДатаСеанса() возвращает момент с точностью до секунды и постоянно меняется. Совпадение с заранее заданной датой до секунды практически невозможно. Сравнивайте по дню через НачалоДня() или по диапазону.
Как в запросе отобрать документы за один день?
Передайте параметр дня и используйте условие: Док.Дата МЕЖДУ НАЧАЛОПЕРИОДА(&День, ДЕНЬ) И КОНЕЦПЕРИОДА(&День, ДЕНЬ). Либо задайте параметры НачалоДня(День) и КонецДня(День) из кода.
Чем отличается ТекущаяДата() от ТекущаяДатаСеанса()?
ТекущаяДата() возвращает время компьютера, где выполняется код, а ТекущаяДатаСеанса() — время сеанса на сервере с учётом часового пояса информационной базы. В серверном коде корректнее использовать ТекущаяДатаСеанса(), чтобы избежать расхождений между клиентом и сервером.
Как сравнить даты, хранящиеся в строковом реквизите?
Строковое сравнение дат хронологически некорректно. Преобразуйте строку в дату через Дата() с форматом "ГГГГММДДччммсс" или собственным разбором, и только потом сравнивайте. Хранить даты в строковых реквизитах не рекомендуется.
Что важно запомнить
Даты в 1С сравниваются стандартными операторами, но тип всегда содержит время: для сравнения по дню приводите значения через НачалоДня() и КонецДня(). Пустую дату проверяйте через ЗначениеЗаполнено() или '00010101'. В запросах и СКД передавайте границы периодов с учётом конца дня, иначе данные последнего дня потеряются.