Работа с датами в 1С:Предприятие — одна из самых востребованных задач при разработке отчётов, обработок и бизнес-логики. Казалось бы, что может быть проще, чем сравнить две даты? Но на практике даже опытные программисты сталкиваются с нюансами: учёт рабочих дней, временные зоны, формат хранения, особенности сравнения с пустыми значениями. Эта статья не просто перечислит способы сравнения — она раскроет подводные камни, которые приводят к ошибкам в коде, и покажет оптимальные решения для типовых задач.
Мы разберём всё: от элементарных операторов = и > до сложных алгоритмов с использованием Календарей и РабочихДней. Особое внимание уделим разнице между сравнением дат в запросах и встроенном языке, а также типичным ошибкам, из-за которых отчёты показывают неверные данные. Если вы когда-нибудь получали сообщение «Тип не совпадает (Дата, Неопределённое)» — здесь вы найдёте решение.
1. Базовые операторы сравнения дат в 1С
Начнём с азов. В 1С 8.3/8.2 даты сравниваются так же, как и числа, но с учётом их внутреннего представления. Платформа хранит даты в формате ДД.ММ.ГГГГ ЧЧ:ММ:СС, даже если время не указано явно. Это означает, что:
- 📅
Дата1 = Дата2— полное совпадение даты и времени (если время не указано, оно равно00:00:00). - 🕒
Дата1 > Дата2— сравниваются даты вместе с временем, поэтому'01.01.2023 23:59' > '02.01.2023 00:00'вернётЛожь. - 🗓️
Дата1 >= НачалоДня(Дата2)— проверка, чтоДата1попадает в деньДата2(включая его начало).
Пример кода для проверки, что текущая дата позже 1 января 2023 года:
Если ТекущаяДата() > '01.01.2023' Тогда
Сообщить("2023 год уже наступил!");
КонецЕсли;
Важно! Если в дате не указано время, платформа подставляет 00:00:00. Это часто приводит к ошибкам при сравнении дат с временем. Например, '01.01.2023' = '01.01.2023 00:00:00' вернёт Истина, а '01.01.2023' = '01.01.2023 12:00:00' — Ложь.
Всегда используйте функции НачалоДня(), КонецДня() или ДатаВремя(), если нужно игнорировать время при сравнении.
2. Сравнение дат без учёта времени
Частая задача — проверить, относятся ли две даты к одному дню, игнорируя часы и минуты. Для этого используют функции НачалоДня() и КонецДня():
- 🌅
НачалоДня(Дата)— возвращает дату с временем00:00:00. - 🌇
КонецДня(Дата)— возвращает дату с временем23:59:59. - 📊
Дата1 >= НачалоДня(Дата2) И Дата1 <= КонецДня(Дата2)— проверка попадания в один день.
Пример: проверка, что документ создан сегодня:
Если ДатаДокумента >= НачалоДня(ТекущаяДата()) И ДатаДокумента <= КонецДня(ТекущаяДата()) Тогда
Сообщить("Документ создан сегодня!");
КонецЕсли;
Альтернативный способ — приведение к типу Дата (без времени):
Если Дата(Дата1) = Дата(Дата2) Тогда
// Даты совпадают без учёта времени
КонецЕсли;
3. Сравнение дат в запросах 1С
В языке запросов 1С сравнение дат имеет свои особенности. Здесь нельзя использовать функции вроде НачалоДня() напрямую — нужно применять конструкции языка запросов или вычисляемые поля.
Примеры:
| Задача | Код в запросе | Пояснение |
|---|---|---|
| Выбрать документы за сегодня | ГДЕ ДатаДокумента >= &НачалоДня И ДатаДокумента <= &КонецДня |
Параметры &НачалоДня и &КонецДня передаются из встроенного языка. |
| Выбрать документы за текущий месяц | ГДЕ ДатаДокумента >= НачалоМесяца(&ТекущаяДата) И ДатаДокумента <= КонецМесяца(&ТекущаяДата) |
Используются функции языка запросов. |
| Сравнить даты без времени | ГДЕ ДЕНЬ(ДатаДокумента) = ДЕНЬ(&СравниваемаяДата) И МЕСЯЦ(ДатаДокумента) = МЕСЯЦ(&СравниваемаяДата) |
Неэффективный способ, но работает без дополнительных параметров. |
Критическая ошибка: если в запросе сравнивать даты напрямую (ГДЕ ДатаДокумента = &ПараметрДата), то запись с временем 12:00:00 не попадёт в выборку, если в параметре время не указано (00:00:00).
Почему не работает сравнение дат в отчёте?
Если в отчёте данные группируются по дате, но некоторые записи "пропадают", проверьте:
1. Формат хранения даты в регистре (возможно, там есть миллисекунды).
2. Наличие временной зоны в базе (актуально для распределённых информационных систем).
3. Использование функции ТолькоДата() в вычисляемых полях отчёта.
4. Работа с пустыми датами и неопределёнными значениями
В 1С дата может быть не только заполненной, но и пустой (Неопределённое). Attempt to compare a date with an undefined value leads to a runtime error. Чтобы избежать ошибок, используйте функции проверки:
- 🚫
ЗначениеЗаполнено(Дата)— проверяет, что дата не пустая. - ❓
ТипЗнч(Дата) = Тип("Дата")— проверяет, что переменная содержит дату (а не строку или число). - ⚠️
Дата = '00010101'— проверка на "нулевую" дату (1 января 1 года), которая иногда используется как замена пустому значению.
Пример безопасного сравнения:
Если ЗначениеЗаполнено(ДатаДокумента) И ДатаДокумента >= НачалоДня(ТекущаяДата()) Тогда
// Обработка даты
Иначе
Сообщить("Дата не указана или некорректна!");
КонецЕсли;
⚠️ Внимание: В некоторых конфигурациях (например, 1С:ЗУП) "пустые" даты могут храниться как '00010101'. Перед сравнением проверяйте это значение явно, если база мигрировалась из старых версий.
5. Сравнение дат с учётом рабочих дней и праздников
Для бизнес-логики часто нужно учитывать только рабочие дни (исключая выходные и праздники). В 1С это реализуется через:
- Объект
Календарь: позволяет проверять, является ли дата рабочей. - Функция
РабочиеДниМежду(): возвращает количество рабочих дней между двумя датами. - Производственные календари: в типовых конфигурациях (например, 1С:ЗУП) уже есть готовые календари с учётом российских праздников.
Пример: проверка, что дата является рабочим днём:
Календарь = Календари.ПроизводственныйКалендарь();
Если НЕ Календарь.ЭтоРабочийДень(ДатаПроверки) Тогда
Сообщить("Этот день выходной или праздничный!");
КонецЕсли;
Для сравнения двух дат с учётом только рабочих дней:
КоличествоДней = РабочиеДниМежду(Дата1, Дата2, Календари.ПроизводственныйКалендарь());
Если КоличествоДней > 5 Тогда
Сообщить("Между датами больше 5 рабочих дней!");
КонецЕсли;
⚠️ Внимание: В распределённых базах (с территориально удалёнными подразделениями) календари могут отличаться. Всегда уточняйте, какой календарь используется в конкретном расчёте.
Использовать объект Календарь|Проверить региональные праздники|Учесть сменный график работы (если есть)|Тестировать на граничных датах (например, 31 декабря)
-->
6. Сравнение дат с временными зонами
В распределённых информационных системах или при интеграции с внешними сервисами даты могут храниться в разных временных зонах. 1С:Предприятие поддерживает работу с временными зонами через:
- 🌍
ЧасовойПояс— объект для хранения информации о временной зоне. - ⏰
ВремяВЧасовомПоясе()— преобразует дату в указанную временную зону. - 🔄
ТекущаяВременнаяЗона()— возвращает временную зону текущего сеанса.
Пример: сравнение дат в разных временных зонах:
// Дата в московском времени
ДатаМСК = '01.01.2023 12:00:00';
ЧасовойПоясМСК = ЧасовойПояс.МСК;
// Дата в екатеринбургском времени (+2 часа)
ДатаЕКБ = ВремяВЧасовомПоясе(ДатаМСК, ЧасовойПояс.ЕКБ);
// Сравнение
Если ДатаЕКБ = '01.01.2023 14:00:00' Тогда
Сообщить("Время корректно преобразовано!");
КонецЕсли;
Важно! В облачных решениях (например, 1С:Fresh) временная зона сервера может отличаться от локальной. Всегда уточняйте настройки временной зоны в параметрах информационной базы.
7. Оптимизация сравнения дат в больших выборках
При работе с большими объёмами данных (например, в отчётах по миллиону документов) сравнение дат может тормозить систему. Оптимизируйте код следующими способами:
- ⚡ Индексируйте поля с датами в регистрах и справочниках.
- 📈 Используйте виртуальные таблицы (например,
РегистрСведений.ОстаткиИОбороты) с предварительной агрегацией по датам. - 🔍 В запросах применяйте
ИНДЕКСИРОВАТЬ ПОдля полей с датами. - 🗑️ Избегайте функций вроде
ДЕНЬ(),МЕСЯЦ()в условияхГДЕ— они блокируют использование индексов.
Пример оптимизированного запроса:
ВЫБРАТЬ
ДатаДокумента КАК Дата,
СУММА(Сумма) КАК Итого
ИЗ
Документ.РеализацияТоваровУслуг
ГДЕ
ДатаДокумента >= &НачалоПериода
И ДатаДокумента <= &КонецПериода
СГРУППИРОВАТЬ ПО
ДатаДокумента
ИНДЕКСИРОВАТЬ ПО
ДатаДокумента
Для ускорения отчётов по датам всегда используйте индексы и избегайте функций над полями в условиях ГДЕ.
8. Типичные ошибки и как их избежать
Даже опытные разработчики допускают ошибки при работе с датами. Вот самые распространённые:
| Ошибка | Причина | Решение |
|---|---|---|
| Сравнение даты со строкой | Оператор = между Дата и Строка вызывает ошибку. |
Используйте Дата(СтрокаДата) для преобразования. |
| Игнорирование времени | Сравнение '01.01.2023' = '01.01.2023 12:00:00' вернёт Ложь. |
Применяйте НачалоДня() или Дата(). |
| Пустые даты в агрегатных функциях | МАКСИМУМ(Дата) вернёт Неопределённое, если есть пустые значения. |
Используйте ВЫБОР КОГДА ЗначениеЗаполнено(Дата) ТОГДА Дата КОНЕЦ. |
| Неверный учёт временных зон | Дата в UTC и локальная дата могут отличаться на несколько часов. | Явно указывайте временную зону при сравнении. |
Пример обработки пустых дат в запросе:
ВЫБРАТЬ
ВЫБОР
КОГДА ЗначениеЗаполнено(ДатаДокумента)
ТОГДА ДатаДокумента
ИНАЧЕ '00010101'
КОНЕЦ КАК Дата,
Сумма
ИЗ
Документ.ПоступлениеТоваров
⚠️ Внимание: В некоторых версиях 1С:Предприятие 8.2 функция ЗначениеЗаполнено() в запросах работает иначе, чем во встроенном языке. Тестируйте запросы на целевой платформе.
FAQ: Частые вопросы по сравнению дат в 1С
Как сравнить дату с сегодняшним днём без учёта времени?
Используйте конструкцию:
Если ДатаДокумента >= НачалоДня(ТекущаяДата()) И ДатаДокумента <= КонецДня(ТекущаяДата()) Тогда
// Дата сегодня
КонецЕсли;
Или сокращённо:
Если Дата(ДатаДокумента) = ТекущаяДата() Тогда
Почему запрос не находит документы за текущий день?
Скорее всего, в запросе сравниваются даты с временем. Например, если в базе дата хранится как '05.05.2023 14:30:00', а в параметре передаётся '05.05.2023' (т.е. '05.05.2023 00:00:00'), то запись не попадёт в выборку.
Решение: передавайте в запрос НачалоДня() и КонецДня().
Как узнать количество дней между двумя датами?
Используйте функцию ДнейМежду():
КоличествоДней = ДнейМежду(Дата1, Дата2);
Если нужно учитывать только рабочие дни:
КоличествоДней = РабочиеДниМежду(Дата1, Дата2, Календари.ПроизводственныйКалендарь());
Можно ли сравнивать даты из разных информационных баз?
Да, но учитывайте:
- Временные зоны баз могут отличаться.
- Формат хранения дат (с миллисекундами или без) может влиять на сравнение.
- В распределённых базах используйте
УниверсальнаяДатаВремя()для корректного обмена.
Как сравнить дату с датой в формате ISO (YYYY-MM-DD)?
Преобразуйте строку в дату с помощью Дата() или ПарситьДата() (в новых версиях):
ДатаИзISO = Дата(Год, Месяц, День, 0, 0, 0); // Год, Месяц, День — числа, извлечённые из строки
Или для строки формата "2023-05-15":
ДатаИзISO = Дата(Число(Лев(СтрокаДата, 4)), Число(Сред(СтрокаДата, 6, 2)), Число(Сред(СтрокаДата, 9, 2)));