Как записать дату без времени в переменную или реквизит 1С и избежать ошибки несоответствия типов?

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

При разработке в среде «1С:Предприятие 8» программисты, особенно начинающие, часто сталкиваются с задачей хранения или сопоставления дат без учета времени. Типичная ситуация: в коде пишется конструкция, которая должна «отсечь» время, но при попытке записать полученный результат в реквизит или переменную система либо выдает ошибку, либо просто не изменяет значение. Проанализируем, почему так происходит, и разберем правильные методы решения этой задачи.

Выясняем причину: почему возникает ошибка?

Рассмотрим код, который изначально пытался использовать автор темы:


Процедура ПередЗаписью(Отказ, РежимЗаписи, РежимПроведения)
    ДатаБезВремени = Формат(Дата, "ДФ=dd.MM.yyyy");
    ДатаТренДок = ДатаБезВремени;
КонецПроцедуры

На первый взгляд логика понятна: разработчик берет дату, применяет к ней форматную строку "ДФ=dd.MM.yyyy", чтобы получить привычный вид без часов и секунд (например, "15.07.2026"), а затем пытается записать этот результат в переменную ДатаТренДок. Однако эта схема не работает.

Главная причина ошибки кроется в несовпадении типов данных. В платформе 1С реквизиты и переменные имеют строгую типизацию (или ожидают определенный тип данных при заполнении реквизитов БД). Давайте разберем, что происходит «под капотом»:

  1. Функция Формат() всегда возвращает значение типа Строка. Она предназначена исключительно для визуального представления данных пользователю в интерфейсе или вывода в печатные формы. Она физически не способна изменить или обрезать саму дату.
  2. Переменная или реквизит ДатаТренДок на форме или в метаданных имеет тип Дата.
  3. При попытке присвоить строковое значение (например, "15.07.2026") переменной с типом Дата происходит конфликт типов. Платформа 1С не может неявно преобразовать строку такого формата в полноценную дату внутри этой операции, поэтому значение либо не записывается, либо вызывает аварийное завершение работы процедуры.

Для наглядности вспомним аналогичный пример автора, который у него успешно работал:


&НаСервере
Процедура ПриОткрытииНаСервере()
    ДеньПрописью = Формат(Объект.Дата, "ДФ=дддд");
    Объект.ДеньНедели = ДеньПрописью;
КонецПроцедуры

Почему этот код работал? Всё просто: реквизит Объект.ДеньНедели изначально имел тип Строка. Ему без проблем присваивалось строковое значение ("понедельник", "вторник" и т. д.), возвращенное функцией Формат(). В случае же с датой мы имеем совершенно другую картину.

Ниже мы подробно разберем, как правильно решить эту проблему на уровне программного кода и метаданных конфигурации.

Решение по шагам: Способ 1. Использование функции НачалоДня()

В платформе «1С:Предприятие» физически не существует обособленного типа данных «Дата без времени». Любая переменная типа Дата всегда хранит в себе и дату, и время с точностью до секунды. Когда нам требуется сохранить «чистую дату», в системе это реализуется путем обнуления времени — то есть установки его в значение 00:00:00.

Для того чтобы быстро и корректно привести любую дату к началу дня (обнулить время), используется встроенная функция НачалоДня(). Она принимает на вход исходную дату, обнуляет время и возвращает значение, которое по-прежнему имеет тип Дата. Это позволяет без ошибок записывать результат в любые типизированные реквизиты.

Перепишем исходный код правильно:


Процедура ПередЗаписью(Отказ, РежимЗаписи, РежимПроведения)
    // Функция НачалоДня обнуляет время, сохраняя тип "Дата"
    ДатаТренДок = НачалоДня(Дата); 
КонецПроцедуры

После выполнения этой строки в реквизите ДатаТренДок будет лежать полноценная дата (например, 15.07.2026 00:00:00), которая прекрасно подходит для дальнейших расчетов, проведения документов и построения отчетов.

Решение по шагам: Способ 2. Настройка свойств реквизита в Конфигураторе

Если ДатаТренДок является полноценным реквизитом объекта (документа, справочника или части формы), мы можем переложить задачу очистки времени на саму платформу 1С. Для этого выполним следующие действия:

  1. Откроем конфигурацию в режиме «Конфигуратор».
  2. Найдем нужный документ (или справочник) и перейдем к свойствам реквизита ДатаТренДок.
  3. В палитре свойств найдем строку «Части даты» (по умолчанию там установлено значение «Дата и время»).
  4. Изменим это значение на «Дата».

После этого платформа 1С автоматически начнет «отсекать» время (приводить его к 00:00:00) при записи объекта в базу данных. В коде вам больше не потребуется вручную прописывать вызовы функции НачалоДня() для этого реквизита.

Опасность сравнения дат в виде строк: почему нельзя использовать строки?

В ходе обсуждения у автора темы возникла идея: «Хотя может и строка подойдёт, если я потом буду просто две строки сравнивать...». Это критическая архитектурная ошибка! Настоятельно рекомендуем никогда так не делать по трем важным причинам:

  1. Логическая ошибка посимвольного сравнения: Строки сравниваются посимвольно, слева направо. Если вы преобразуете даты в текстовый формат "ДД.ММ.ГГГГ", то при сравнении строка "31.01.2023" окажется больше, чем строка "01.02.2024", потому что первый символ "3" больше, чем "0". Ваша логика проверок дат полностью сломается. Сравнивать строки дат корректно можно только в каноническом формате "ГГГГММДД" (например, "20240201" > "20230131"), но использовать строковые манипуляции в коде вместо работы со специализированным типом данных — это признак плохого тона в разработке.
  2. Критическая потеря производительности СУБД: Если вы решите хранить даты как текстовые поля в базе данных, СУБД (MS SQL, PostgreSQL и др.) не сможет использовать свои встроенные индексы и механизмы оптимизации для работы с временными типами данных. Поиск по диапазону дат, сортировка и построение отчетов на больших объемах данных будут выполняться крайне медленно.
  3. Невозможность построения качественной аналитики: Поскольку автору темы в дальнейшем нужна аналитика по этим датам, использование строкового типа заблокирует работу со стандартными механизмами системы компоновки данных (СКД). Вы не сможете легко и гибко группировать данные по неделям, месяцам, кварталам или годам, так как для системы это будут просто бессмысленные наборы текстовых символов.

Как правильно работать с датами без времени в запросах 1С?

Поскольку аналитика в 1С строится преимущественно с помощью запросов, важно знать, как обрабатывать даты без времени внутри самого текста запроса. Обратите внимание, что стандартная функция встроенного языка НачалоДня() в запросах не поддерживается.

Вместо нее в языке запросов 1С используется специальная функция НАЧАЛОПЕРИОДА(). Проанализируем пример запроса, который выбирает продажи и группирует их строго по дням, полностью игнорируя время проведения документов:


ВЫБРАТЬ
    НАЧАЛОПЕРИОДА(Продажи.Период, ДЕНЬ) КАК ДеньПродажи,
    СУММА(Продажи.Сумма) КАК Сумма
ИЗ
    РегистрНакопления.Продажи КАК Продажи
СГРУППИРОВАТЬ ПО
    НАЧАЛОПЕРИОДА(Продажи.Период, ДЕНЬ)

Такой подход гарантирует высокую скорость выполнения запроса на уровне базы данных и обеспечивает корректность группировок для любых отчетов.

Важное правило работы с границами периодов

При построении отчетов или выборок за определенный период (например, при получении документов с Даты А по Дату Б), разработчики часто совершают еще одну популярную ошибку, забывая про время:

Если вы не приведете конечную дату к концу дня, то все документы, созданные в последний день периода (например, в 14:30:00), гарантированно не попадут в ваш отчет. Система будет сравнивать их с началом этого дня (00:00:00) и посчитает, что они находятся за рамками указанного интервала.

Альтернативные способы создания чистых дат в коде

Если вам необходимо жестко задать или сконструировать конкретную дату без времени непосредственно в коде программы, используйте следующие проверенные конструкции:

Способ А. Использование литералов дат (прямой ввод в коде):


// Дата задается в формате 'ГГГГММДД'. Время автоматически будет 00:00:00
МояДата = '20260715'; 

Способ Б. Использование конструктора даты:


// Передаем параметры в формате: Дата(Год, Месяц, День)
МояДата = Дата(2026, 07, 15); 

Способ В. Преобразование строго заданной строки:


// Преобразуем строку канонического формата в полноценную дату
МояДата = Дата("20260715000000"); 

Подводя итог: всегда разделяйте понятия значения данных и их представления. Для записи данных в базу используйте тип Дата в связке с функцией НачалоДня(), а функцию Формат() оставьте исключительно для вывода красивого текста на экраны пользователей.

← На главную