Работа с типом данных Дата в платформе 1С:Предприятие часто становится источником скрытых ошибок и логических несоответствий для разработчиков любого уровня. Проблема кроется в самой структуре хранения: любое значение даты всегда включает в себя не только календарные сутки, но и точное время до миллисекунды. Когда система хранит момент "12 мая 2026 года", внутри это может выглядеть как "12.05.2026 14:35:12.000", что кардинально меняет результаты сравнений и группировок.
Необходимость получить "чистую" дату, отбросив временную составляющую, возникает повсеместно: от формирования ежедневных отчетов до сложной логики взаиморасчетов между контрагентами. Если вы просто попытаетесь сравнить две даты, одна из которых была введена пользователем через календарь (где время часто обнуляется), а другая получена из системного лога (где время актуально), условие Если Дата1 = Дата2 Тогда вернет ложь, даже если визуально даты совпадают. Понимание механизмов усечения времени критически важно для стабильной работы конфигурации.
В этой статье мы детально разберем все доступные способы получения даты без времени, оценим их производительность и применимость в различных сценариях. Вы узнаете, почему стандартные функции предпочтительнее ручной арифметики, и какие подводные камни скрываются при работе с часовыми поясами и летним временем.
Природа типа данных Дата и скрытая проблема сравнения
Внутреннее представление типа Дата в 1С представляет собой числовое значение, отсчитывающее количество секунд (или более мелких единиц) от некоего условного начала эпохи. Визуализация этого значения зависит от формата отображения, установленного в пользовательском интерфейсе или метаданных объекта. Однако, независимо от того, видите вы время на экране или нет, оно физически присутствует в переменной.
Когда пользователь выбирает дату в диалоге ввода, система по умолчанию часто устанавливает время в 00:00:00. В то же время, функции типа ТекущаяДата() возвращают момент вызова с точностью до текущей секунды. Попытка сравнить эти два значения напрямую приведет к неожиданному результату. Например, документ, созданный сегодня утром, не будет найден запросом, который ищет документы за "сегодняшнее число", если в запросе используется жесткое сравнение с датой начала дня.
Особую сложность представляет ситуация, когда данные поступают из внешних источников через обмен данными. Внешние системы могут передавать временную метку в своем часовом поясе, и при конвертации в формат 1С может происходить сдвиг времени, который перебрасывает дату на соседние сутки. Игнорирование этого фактора приводит к тому, что отчеты за месяц не сходятся на копейку или на одну позицию номенклатуры.
При отладке кода всегда выводите значение даты в полном формате (включая время) в окно сообщений, чтобы убедиться, не содержит ли переменная скрытых миллисекунд, которые мешают сравнению.
Использование встроенной функции НачалоДня
Самым надежным, читаемым и рекомендуемым способом получения даты без времени является использование встроенной функции НачалоДня(). Эта функция принимает на вход значение типа Дата и возвращает новую дату, у которой время принудительно установлено в 00:00:00. Алгоритм работы функции оптимизирован на уровне платформы, что делает её выполнение крайне быстрым даже в больших циклах обработки документов.
Преимущество данного метода заключается в его явности. Читая код, любой разработчик сразу понимает намерение автора: "нам нужна только дата, время не важно". Это снижает вероятность ошибок при поддержке конфигурации новыми сотрудниками. Кроме того, функция корректно обрабатывает граничные случаи перехода через сутки и учитывает настройки регионального стандарта системы.
Пример использования в коде выглядит лаконично и не требует дополнительных вычислений:
ИсходнаяДата = ТекущаяДата();
ЧистаяДата = НачалоДня(ИсходнаяДата);
// Теперь ЧистаяДата равна, например, 25.10.2026 00:00:00
Важно отметить, что функция НачалоДня не изменяет исходную переменную, а возвращает новое значение. Это соответствует принципам функционального программирования, принятым в 1С, и предотвращает случайную порчу данных в других частях алгоритма, где могла использоваться оригинальная дата с временем.
Функция НачалоДня() является стандартом де-факто в экосистеме 1С. Её использование гарантирует кросс-платформенную совместимость и правильную работу с любыми типами дат.
Метод форматирования строки и обратного преобразования
Альтернативным, хотя и менее производительным подходом, является конвертация даты в строку с заданным форматом и последующее преобразование строки обратно в дату. Этот метод часто используется в тех случаях, когда требуется не просто отбросить время, но и привести дату к специфическому виду, зависящему от настроек пользователя или языка интерфейса.
Суть метода заключается в вызове функции Формат(), где в качестве строки формата указывается только дата, например, "ДФ=dd.MM.yyyy". Полученная строка лишена какой-либо информации о времени. Затем эта строка передается в функцию Дата() или СтрокаВДата(), которая интерпретирует её как начало суток.
- 📝 Этот способ полезен при формировании печатных форм, где время не должно отображаться ни при каких обстоятельствах.
- ⚙️ Метод требует больше вычислительных ресурсов из-за двойного преобразования типов (Дата → Строка → Дата).
- 🌍 Форматирование зависит от локальных настроек операционной системы клиента, что может привести к ошибкам на серверах с другой языковой средой.
Использование этого подхода внутри циклов по большим выборкам данных (например, обработка тысяч движений регистра) категорически не рекомендуется. Накладные расходы на работу со строками могут замедлить выполнение запроса или обработки в разы по сравнению с арифметическими методами или функцией НачалоДня.
Почему строковые операции медленнее?
Операции со строками требуют выделения памяти под новый объект, копирования символов и парсинга. Числовые операции с датами выполняются процессором напрямую с регистрами, что на порядки быстрее.
Арифметический подход: вычитание времени
Для разработчиков, предпочитающих понимание внутреннего устройства системы, существует арифметический метод очистки даты. Поскольку время в сутках — это дробная часть, а дата — целая часть (в определенных единицах измерения), можно математически отсечь лишнее. В 1С сутки равны 1, поэтому время представляет собой дробь от единицы.
Чтобы получить дату без времени, можно вычесть из текущей даты её же время, выраженное в долях суток. Для этого используется функция Время(), которая возвращает разницу между полной датой и началом этого дня. Формула выглядит следующим образом: НоваяДата = ИсходнаяДата - Время(ИсходнаяДата).
Данный метод полностью эквивалентен функции НачалоДня() с точки зрения результата, но требует от разработчика более глубокого понимания типов данных. Ошибка в порядке операндов или неверное понимание возвращаемого типа функции Время может привести к логическим сбоям. Тем не менее, в некоторых специфических алгоритмах, где запрещено использование встроенных функций по соображениям совместимости со старыми версиями платформы (что сейчас редкость), этот прием может быть полезен.
Очистка времени непосредственно в запросах 1С
При работе с языком запросов 1С ситуация усложняется тем, что синтаксис не всегда позволяет напрямую применять функции обработки дат в условиях отбора так же гибко, как в коде. Однако, для задач группировки и отбора по дням существуют специальные возможности. Чаще всего разработчики сталкиваются с необходимостью сгруппировать документы по дням, игнорируя время проведения.
В языке запросов можно использовать функцию НАЧАЛОДНЯ() прямо в списке полей или в условиях. Это позволяет базе данных (или движку запросов 1С) выполнить оптимизацию и использовать индексы, если структура запроса это позволяет.
Рассмотрим таблицу с примерами различных подходов к фильтрации в запросе:
| Подход | Синтаксис в запросе | Влияние на индекс | Рекомендация |
|---|---|---|---|
| Прямое сравнение | ГДЕ Дата = &Дата |
Полное использование | Только если время обнулено |
| Диапазон дат | ГДЕ Дата >= &Начало И Дата < &Конец |
Полное использование | 🏆 Лучший вариант для производительности |
| Функция в условии | ГДЕ НАЧАЛОДНЯ(Дата) = &Дата |
Частичное или отсутствует | Использовать с осторожностью на больших данных |
Наиболее эффективным способом фильтрации по дате без времени в запросах является использование диапазона. Вместо того чтобы пытаться "отрезать" время у каждой записи в базе, мы задаем границы: от начала нужного дня (00:00:00) до начала следующего дня (00:00:00). Это условие Дата >= НачалоДня(&Период) И Дата < НачалоДня(&Период + 1) позволяет движку максимально эффективно использовать индексы по полю даты.
☑️ Оптимизация запроса по дате
Специфика работы с периодическими регистрами и СКД
В системах компоновки данных (СКД), которые лежат в основе всех современных отчетов 1С, вопрос даты без времени решается на уровне настроек группировок. Пользователь часто видит в отчете группировку "По дням", которая автоматически скрывает время. Однако программисту необходимо понимать, как это реализовано, чтобы писать корректные расшифровки и дополнительные обработки.
При настройке группировки в СКД можно указать периодичность "День". Система автоматически применит логику усечения времени для группировки записей. Но если вы пишете обработчик события ПриКомпоновкеРезультата или формируете выборку данных вручную через ПостроительЗапроса, вам придется самостоятельно внедрять логику НачалоДня. Ошибка здесь приведет к тому, что в расшифровке отчета пользователь увидит дублирование строк для одного и того же дня, но с разным временем.
⚠️ Внимание: При работе с регистрами накопления, имеющими периодичность "Внутри дня", игнорирование времени может привести к потере детализации. Убедитесь, что бизнес-логика отчета действительно требует агрегации по суткам, прежде чем отбрасывать время.
Также стоит учитывать поведение функции ПериодРегистра в запросах. Она возвращает период, к которому относится запись, и для регистров с периодичностью день она уже возвращает дату без времени. Попытка дополнительно очистить такую дату функцией НачалоДня избыточна, но не ошибочна. Знание метаданных регистра позволяет писать более чистый и быстрый код.
Часовые пояса и проблемы летнего времени
Одной из самых коварных проблем при работе с датами является несоответствие часовых поясов. Сервер 1С может находиться в одном часовом поясе, а клиентское рабочее место — в другом. При передаче даты между ними платформа автоматически выполняет конвертацию. Однако, если вы вручную манипулируете датой, очищая время на клиенте, а затем сохраняете её на сервере, может возникнуть сдвиг на сутки.
Например, если на клиенте (Москва) время 23:30, и мы очищаем время до 00:00, а затем отправляем эту дату на сервер (Владивосток), где уже наступил новый день, сервер может интерпретировать это время по-своему. Функции платформы, такие как НачалоДня, работают в контексте текущего сеанса, но при сохранении в базу данных значение хранится в универсальном времени (UTC).
Чтобы избежать ошибок, связанных с переходом на летнее время (хотя в РФ оно отменено, исторические данные и международные компании все еще сталкиваются с этим), рекомендуется всегда оперировать началами дней в локальном времени пользователя, но хранить и сравнивать данные с учетом смещения. Платформа 1С берет на себя большую часть этой работы, но только если вы используете стандартные средства, а не "костыли" в виде вычитания констант.
Если ваша конфигурация работает в режиме веб-клиента или тонкого клиента с сервером в другом регионе, никогда не жестко не задавайте смещение времени в часах. Используйте только встроенные функции конвертации.
Частые ошибки и антипаттерны разработки
В процессе анализа чужого кода часто встречаются решения, которые работают "как будто бы правильно", но являются бомбой замедленного действия. Один из таких антипаттернов — приведение даты к строке вида "ДД.ММ.ГГГГ" и сравнение строк. Это не только медленно, но и ломается при смене настроек регионального стандарта пользователя (например, если пользователь переключит язык на английский, формат станет "MM/DD/YYYY").
Еще одна распространенная ошибка — использование функции Цел() для отбрасывания дробной части даты. В старых версиях 1С или в некоторых специфических контекстах это могло работать, но в современной платформе тип Дата не всегда корректно приводится к числу таким образом без явного преобразования. Это делает код хрупким и зависимым от версии платформы.
⚠️ Внимание: Интерфейсы и поведение функций могут незначительно отличаться в различных релизах платформы 1С:Предприятие 8.3. Всегда сверяйте поведение критических функций с официальной документацией или тестируйте на актуальной версии платформы перед внедрением в промышленную эксплуатацию.
Наконец, стоит избегать создания собственных общих модулей с функциями типа МоеНачалоДня, которые просто вызывают стандартную функцию. Это загромождает код и создает лишние уровни вложенности. Используйте встроенный инструментарий платформы, который гарантированно поддерживается разработчиками 1С и оптимизирован для любых обновлений.
FAQ: Часто задаваемые вопросы
Как сравнить две даты в 1С, если в одной есть время, а в другой нет?
Для корректного сравнения необходимо привести обе даты к единому формату. Самый надежный способ — применить функцию НачалоДня() к обеим переменным перед сравнением. Это гарантирует, что временная составляющая не повлияет на результат логического условия.
Почему в отчете по остаткам товары показываются на вчерашний день?
Скорее всего, в условии отбора используется дата с временем (например, текущее время 15:00), а документы проведены сегодня утром. При выборке остатков на "текущий момент" система видит, что документы еще не провелись относительно времени запроса. Используйте НачалоДня(ТекущаяДата()) + 1 или конец дня, чтобы захватить все документы сегодняшнего числа.
Можно ли хранить в базе дату без времени?
Физически в базе данных (SQL) тип данных DateTime всегда хранит время. Невозможно сохранить дату "без времени" на уровне таблицы. Очистка времени происходит только на уровне логики приложения (в коде 1С) или в момент выборки данных (в запросе), но в ячейке базы всегда будет значение вида 00:00:00.
Как получить вчераший день без времени в одной строке кода?
Используйте вложенную функцию: НачалоДня(ТекущаяДата() - 1). Сначала вычитается один день из текущей даты (с учетом времени), а затем результат округляется до начала суток. Это безопасный и стандартный способ.