При работе с системой 1С:Предприятие разработчики и пользователи часто сталкиваются с типом данных Дата, который по умолчанию хранит и календарную дату, и точное время. В стандартном поведении системы при создании новой записи время часто устанавливается в момент создания, либо выбирается из интерактивного календаря. Однако для построения корректных отчетов, группировок и фильтрации выборки часто требуется игнорировать временную составляющую.
Проблема актуальна при сравнении периодов, когда пользователю нужно найти все документы, созданные «сегодня», независимо от того, в 9:00 или в 23:59 они были проведены. Если просто сравнить дату документа с текущей датой сервера, результат может быть отрицательным из-за разницы в секундах или минутах. Поэтому знание того, как отсечь время от даты, является фундаментальным навыком для любого специалиста по платформе.
В этой статье мы разберем встроенные механизмы платформы, функции языка запросов и специфические приемы работы с временными зонами. Мы рассмотрим, почему простое присваивание не всегда работает так, как ожидается, и как избежать ошибок в логике сравнения интервалов.
Встроенные функции платформы для работы с датой
Платформа 1С:Предприятие 8 предоставляет ряд встроенных функций, специально предназначенных для манипуляций с датами. Самым очевидным и часто используемым способом является функция НачалоДня(). Она принимает значение типа Дата и возвращает новую дату, у которой время установлено в 00:00:00. Это самый надежный способ нормализовать дату перед сравнением или группировкой.
Использование этой функции в коде на языке 1С выглядит следующим образом:
ТекущаяДата = ТекущаяДата();
ДатаБезВремени = НачалоДня(ТекущаяДата);
Важно понимать, что функция НачалоДня не просто «обрезает» лишние символы, а математически вычисляет начало суток в соответствии с календарем. Существует также парная функция КонецДня(), которая устанавливает время на 23:59:59.999. Использование этих функций гарантирует, что вы работаете с полными сутками, а не с усеченными значениями, что критично для корректности расчетов.
Помимо начала и конца дня, платформа поддерживает функции для работы с другими периодами, такими как НачалоМесяца() или НачалоГода(). Все они работают по единому принципу: возвращают дату, очищенную от младших составляющих времени. Это позволяет строить иерархические отчеты без написания сложной логики вычислений.
При фильтрации в формах списка используйте функцию НачалоДня() в отборе, чтобы пользователь мог выбрать дату в календаре, не задумываясь о времени проведения документа.
⚠️ Внимание: Функции начала периода возвращают дату в контексте текущего сеанса. Если ваш код выполняется в фоновом задании или на сервере с другим часовым поясом, результат может отличаться от ожиданий пользователя.
Обработка даты в языке запросов 1С
Когда речь заходит о выборке данных из базы, использование функций в коде 1С становится неэффективным, так как это требует выгрузки всех записей в память клиента или сервера перед обработкой. Правильный подход — выполнять усечение времени непосредственно на уровне СУБД с помощью языка запросов. В запросах 1С также доступна функция НАЧАЛОДНЯ().
Рассмотрим пример запроса, который выбирает все документы за конкретный день, игнорируя время:
ВЫБРАТЬ
РеализацияТоваровУслуг.Ссылка КАК Ссылка,
РеализацияТоваровУслуг.Дата КАК ДатаДокумента
ИЗ
Документ.РеализацияТоваровУслуг КАК РеализацияТоваровУслуг
ГДЕ
НАЧАЛОДНЯ(РеализацияТоваровУслуг.Дата) = &ВыбраннаяДата
В данном примере параметр &ВыбраннаяДата должен передаваться уже очищенным от времени, либо запрос должен быть адаптирован под диапазон. Часто более производительным решением является использование диапазона дат вместо вызова функции в условии ГДЕ. Это позволяет базе данных использовать индексы по полю даты.
Для реализации подхода с диапазоном необходимо сформировать интервал от начала дня до начала следующего дня. Это классический паттерн оптимизации запросов:
- 📅 Нижняя граница:
НАЧАЛОДНЯ(&Дата) - 📅 Верхняя граница:
НАЧАЛОДНЯ(&Дата) + 1 - ⚡ Условие:
Дата >= &Начало И Дата < &Конец
Использование такого подхода гарантирует, что СУБД не будет выполнять полное сканирование таблицы (Table Scan), а воспользуется индексом для быстрого поиска нужного диапазона записей. Это особенно важно в высоконагруженных системах с миллионами документов.
☑️ Оптимизация запроса даты
Форматирование и вывод даты без времени
Часто задача «отсечь время» возникает не для вычислений, а для отображения информации пользователю. В формах 1С и печатных формах нет необходимости менять само значение в базе данных, достаточно изменить способ его представления. Для этого используется механизм Формат.
Строка формата позволяет гибко настраивать вывод. Чтобы скрыть время, используется квалификатор ДФ (Date Format) или предопределенные строки. Например, формат "ДФ=dd.MM.yyyy" выведет только день, месяц и год. При этом внутреннее значение объекта останется неизменным, содержащим время.
В коде это реализуется так:
СтрокаДаты = Формат(ТекущаяДата(), "ДФ=dd.MM.yyyy");
Однако, если вам нужно передать это значение дальше по цепочке вычислений как дату, а не как строку, форматирование не подойдет. В этом случае лучше использовать конструктор даты или явное приведение типов. Важно различать задачу «показать без времени» и «считать без времени».
| Метод | Тип результата | Назначение |
|---|---|---|
НачалоДня() |
Дата | Вычисления, сравнения, запросы |
Формат() |
Строка | Вывод на экран, печать, экспорт |
КонструкторДаты() |
Дата | Создание даты с нуля |
Строка() |
Строка | Быстрое преобразование (зависит от настроек) |
При экспорте данных в Excel или другие внешние системы часто возникает проблема: время экспортируется вместе с датой, создавая «мусор» в ячейках. Использование правильного формата перед выгрузкой решает эту проблему на уровне представления, не затрагивая логику хранения.
Нюанс экспорта в Excel
При выгрузке табличного документа в файл XLSX значения дат могут автоматически форматироваться средствами Excel. Если в ячейке отображается время, проверьте стиль ячейки в макете табличного документа.
Особенности работы с временными зонами
В распределенных информационных базах или при работе через веб-клиент критически важным становится учет временных зон. Сервер 1С может находиться в Москве, сервер базы данных — во Владивостоке, а пользователь — в Калининграде. При простом усечении времени можно получить логическую ошибку: «начало дня» для сервера и «начало дня» для пользователя — это разные моменты времени.
Платформа предоставляет функцию НачалоДняВЧасовомПоясе() (или аналогичные механизмы в зависимости от версии платформы и контекста вызова), которая позволяет привести дату к началу суток в конкретном часовом поясе. Это необходимо при расчете регламентных заданий, которые должны запускаться строго в 00:00 по местному времени филиала.
Если игнорировать этот аспект, возможны ситуации дублирования документов или, наоборот, пропуска проведения важных операций. Например, документ, проведенный в 23:50 по времени сервера, может оказаться «вчерашним» для пользователя в другом регионе, если не выполнена корректная конвертация.
⚠️ Внимание: При миграции баз данных между серверами в разных регионах обязательно проверьте настройки часовых поясов в профиле пользователя и параметрах запуска сервера 1С.
Для корректной работы рекомендуется всегда хранить даты в базе в универсальном координированном времени (UTC) или времени сервера, а конвертацию в локальное время выполнять только на этапе отображения интерфейса. Это упрощает синхронизацию между узлами РИБ (Распределенной Информационной Базы).
Сравнение дат и борьба с погрешностями
Одна из самых частых ошибок начинающих разработчиков — попытка сравнить две даты на равенство, когда у одной из них время усечено, а у другой — нет. Даже если визуально даты одинаковы (например, 10.10.2023), выражение Дата1 = Дата2 вернет Ложь, если в одной дате время 00:00:00, а в другой 14:30:00.
Чтобы избежать этого, необходимо привести обе сравниваемые величины к единому стандарту. Лучшей практикой считается явное приведение обеих дат к началу дня перед сравнением:
Если НачалоДня(ДатаДокумента) = НачалоДня(ТекущаяДата()) Тогда
// Логика обработки
КонецЕсли;
Также стоит учитывать точность хранения времени. В 1С время хранится с точностью до секунды (и миллисекунд во внутренних представлениях). При импорте данных из внешних систем, где время может иметь другую точность или формат, могут возникать микроскопические расхождения, которые ломают логику строгого равенства.
В таких случаях иногда оправдано использование сравнения с допуском или сравнение диапазонов, о котором говорилось в разделе про запросы. Никогда не полагайтесь на то, что пользователь ввел дату без времени в поле ввода — система все равно подставит текущее время, если оно не было явно указано.
Всегда приводите обе части выражения сравнения к одному формату (например, с помощью НачалоДня), прежде чем проверять их на равенство.
Типичные ошибки и способы их устранения
Несмотря на простоту функций, разработчики часто допускают ошибки, связанные с пониманием типа данных. Попытка присвоить дате строковое значение «10.10.2023» без явного преобразования может привести к ошибке или неожиданному результату, зависящему от региональных настроек ОС.
Еще одна распространенная проблема — работа с null-значениями. Функции работы с датой могут некорректно обрабатывать пустые значения, если не предусмотрена проверка на заполненность. Всегда проверяйте дату на пустоту перед вызовом НачалоДня().
- 🚫 Ошибка: Прямое сравнение
Дата1.Дата = Дата2.Датав запросе без учета времени. - 🚫 Ошибка: Использование
Строка(Дата)для получения даты без времени (зависит от локали). - 🚫 Ошибка: Игнорирование перехода на летнее/зимнее время в старых версиях платформы.
Для отладки таких проблем удобно использовать панель отладки и выводить полные значения дат в журнал регистрации. Часто бывает полезно вывести разницу между датами в секундах, чтобы понять, есть ли скрытое смещение.
⚠️ Внимание: Интерфейсы и поведение функций могут незначительно отличаться в разных версиях платформы 1С (например, 8.2, 8.3, 8.4). Всегда сверяйте синтаксис со справочником разработчика для вашей конкретной версии.
Можно ли отсечь время, просто присвоив дату другой переменной?
Нет, простое присваивание копирует значение целиком, включая время. Необходимо использовать функцию преобразования, такую как НачалоДня().
Влияет ли усечение времени на производительность запроса?
Да, использование функции в условии WHERE может отключить использование индекса. Лучше использовать диапазон дат (от и до).
Как получить вчерашнюю дату без времени?
Используйте выражение: НачалоДня(ТекущаяДата() - 1). Это вычтет один день и обнулит время.
Почему в отчете дата сдвигается на один день?
Скорее всего, проблема в часовых поясах. Сервер и клиент находятся в разных зонах, и при конвертации UTC в локальное время происходит переход через полночь.
Есть ли разница между НачалоДня и КонструкторДаты?
Да. НачалоДня берет существующую дату и обнуляет время. КонструкторДаты создает новую дату из переданных компонентов (год, месяц, день), игнорируя исходные значения времени.