Работа с датой и временем в 1С 8.3: от поля документа до запроса

· 9 мин

Содержание

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

Частое заблуждение: думают, что раз в документе видна только дата, время там нулевое. Это не так. Платформа хранит момент с точностью до секунды, даже когда в форме показан один календарь без часов. Ниже — как увидеть это время, задать его и работать с ним в запросах и во встроенном языке.

Поле со временем в документе и справочнике: откуда оно берётся

Отображение часов и минут задаёт свойство реквизита «Состав даты». У любого поля типа Дата оно принимает три значения:

  • 📅 Дата — показывается только календарь, время скрыто, но физически равно 00:00:00.
  • ⏱ Время — только часы, минуты, секунды.
  • 📆 Дата и время — полный момент, с выбором и даты, и времени.

Чтобы поле в форме позволяло ввести время руками, у реквизита должно стоять «Дата и время» (или «Время»). Меняется это в конфигураторе или расширении — в палитре свойств реквизита. Для своих обработок и дополнительных реквизитов состав даты выбирается там же, при создании поля.

Если поле уже умеет показывать время, ввод простой: встаёте в часть с часами и печатаете, либо открываете выпадающий календарь — внизу у него есть строка времени.

Литерал даты во встроенном языке пишется без разделителей, в порядке ГГГГММДДЧЧММСС:

Срок = '20260115093000'; // 15 января 2026, 09:30:00

Пустая дата — это '00010101000000', а не Неопределено. Сравнивать поле с пустотой нужно именно с пустой датой.

У документов время играет особую роль: оно участвует в порядке проведения. Два документа с одной календарной датой выстраиваются в учёте по времени, а при совпадении и его — по внутренней ссылке. Это и есть момент времени документа, к которому обращаются через метод МоментВремени(). Его передают в запросы остатков как границу, когда нужны данные строго «на момент этого документа», а не на календарную дату.

Как вписать текущее время автоматически

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

  • ТекущаяДатаСеанса() — серверная, возвращает дату и время с учётом часового пояса сеанса. Это рабочий выбор для записи в документы и регистры.
  • ТекущаяДата() — клиентская, берёт время с компьютера пользователя. Её значению доверять нельзя: часы на клиенте могут быть сбиты.
// на сервере или в общем модуле с серверным контекстом
ВремяОперации = ТекущаяДатаСеанса();

Для заполнения реквизитов, которые идут в учёт, используйте только ТекущаяДатаСеанса(). Клиентское время допустимо лишь для интерфейсных мелочей вроде подсветки «свежих» строк.

Время нового документа. Когда вы создаёте документ на сегодняшнюю дату, платформа сама подставляет текущее время. А вот при вводе документа задним числом поведение зависит от свойства конфигурации «Автоматическое время документов». Оно и решает, какое время получит документ на прошедшую дату — начало дня, конец дня или другой вариант. Чаще всего ставят конец дня, чтобы задним числом введённый документ встал последним среди своих и не «перепрыгнул» уже проведённые операции того же дня.

Если документ упорно встаёт не в том порядке, смотрите не на календарь, а именно на время: два документа за одно число сортируются по нему.

Время в запросах и отчётах

В языке запросов для времени есть свой набор функций — строковые литералы тут не работают. Момент задают функцией ДАТАВРЕМЯ:

ВЫБРАТЬ
    Продажи.Регистратор,
    Продажи.Период,
    Продажи.Количество
ИЗ
    РегистрНакопления.Продажи КАК Продажи
ГДЕ
    Продажи.Период МЕЖДУ ДАТАВРЕМЯ(2026, 1, 1, 0, 0, 0)
                       И ДАТАВРЕМЯ(2026, 1, 31, 23, 59, 59)

Чаще границы периода передают параметром, а не литералом — так отчёт остаётся настраиваемым.

Три функции закрывают почти все задачи с периодами:

Функция запросаЧто делаетПример
НАЧАЛОПЕРИОДА(Дата, ДЕНЬ)округляет вниз до начала дня, месяца, годаполучить 00:00:00 суток
КОНЕЦПЕРИОДА(Дата, МЕСЯЦ)округляет вверх до конца периодапоследняя секунда месяца
РАЗНОСТЬДАТ(Д1, Д2, СЕКУНДА)разница между моментами в выбранных единицахдлительность операции

КОНЕЦПЕРИОДА(..., ДЕНЬ) возвращает 23:59:59. Если фильтруете строго «меньше начала следующего дня» — используйте < НАЧАЛОПЕРИОДА(СледующаяДата, ДЕНЬ), иначе рискуете потерять записи, попавшие на последнюю секунду суток.

Когда в отчёте нужно сгруппировать данные по часам внутри дня, добавьте в выборку вычисляемое поле с НАЧАЛОПЕРИОДА(Период, ЧАС) и группируйте уже по нему:

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

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

Время в коде: функции встроенного языка

Во встроенном языке работа с датой и временем строится на нескольких группах функций. Собрать дату из чисел проще всего конструктором Дата:

Момент = Дата(2026, 1, 15, 9, 30, 0); // те же год, месяц, день, час, минута, секунда

Разобрать момент на части:

Ч = Час(Момент);     // 9
М = Минута(Момент);  // 30
С = Секунда(Момент); // 0

Границы интервалов задают парные функции:

НачалоСуток = НачалоДня(ТекущаяДатаСеанса());   // сегодня 00:00:00
КонецСуток  = КонецДня(ТекущаяДатаСеанса());    // сегодня 23:59:59
НачалоЧ     = НачалоЧаса(ТекущаяДатаСеанса());   // текущий час, 00 минут

Разность и смещение. Вычитание двух дат даёт число секунд — отдельной функции для этого не нужно:

Длительность = Окончание - Начало; // секунды
МинутРаботы  = Длительность / 60;

А чтобы сдвинуться на календарные единицы (месяцы, годы с учётом длины месяца), есть ДобавитьМесяц:

ЧерезКвартал = ДобавитьМесяц(ТекущаяДатаСеанса(), 3);

Для сдвига на фиксированное число секунд, минут или часов достаточно арифметики: прибавьте нужное количество секунд (3600 на час).

Пара приёмов, которые экономят строки кода:

ПоследнийДеньМесяца = КонецМесяца(ТекущаяДатаСеанса());  // конец текущего месяца
ПерваяСекундаНедели = НачалоНедели(ТекущаяДатаСеанса());  // понедельник, 00:00:00

Когда вы запрашиваете остатки на момент (по товарам, деньгам, расчётам), важна не только дата, но и время среза. Остатки «на конец дня» и «на 12:00» того же числа дадут разные цифры, если за день были движения. Поэтому границу среза задают полным моментом — обычно КонецДня(Дата), чтобы учесть все операции суток.

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

Строка = Формат(Момент, "ДФ=dd.MM.yyyy HH:mm");

Шаблон ДФ даёт нужный вид без возни с ведущими нулями.

Часовой пояс сеанса и универсальное время

Это тот слой, где чаще всего всплывают расхождения «показывает не то время». В клиент-серверной базе сервер может стоять в одном поясе, а пользователь работать в другом.

Платформа решает это часовым поясом сеанса. Функция ЧасовойПояс() возвращает текущий пояс, а ТекущаяДатаСеанса() уже учитывает его — поэтому она и предпочтительнее клиентской функции.

Когда нужно сравнивать моменты независимо от пояса — например, фиксировать точное время события для нескольких регионов — берите универсальное (UTC) время:

UTC = ТекущаяУниверсальнаяДата();

Правило простое: 🕓 внутри одной базы для учёта работайте с датой сеанса, а 🌍 для межсистемного обмена — приводите к универсальному времени, чтобы приёмник сам пересчитал его в свой пояс.

Отдельно о «особенностях конфигураций»: в типовых решениях (Бухгалтерия предприятия, ЗУП, Управление торговлей) пути к полю даты в документах стандартны — дата всегда в шапке, рядом с номером. А вот умеет ли поле показывать время, зависит от состава даты конкретного реквизита, а не от названия конфигурации. Если времени не видно там, где оно нужно, причина одна — состав даты, и правится она в конфигураторе или расширении.

Поменять состав даты у типового реквизита напрямую нельзя без снятия с поддержки или расширения. Если нужно именно отобразить время существующего документа, не трогая конфигурацию, добавьте в список колонку с этим реквизитом и задайте ей формат с временем через настройку формы (Ещё → Изменить форму) — значение покажется с часами и минутами, хотя в карточке состав даты останется прежним.

Синхронизация времени при обменах с другими системами

При выгрузке в сторонние системы (сайт, EDI, банк, обмен по web-сервису) даты почти всегда передаются строкой. Два момента, на которых спотыкаются:

  1. Формат строки. Для сериализации пользуйтесь штатными XMLСтрока(Момент) и обратным XMLЗначение(Тип("Дата"), Строка) — они дают корректный ISO-вид. Самодельное форматирование легко расходится с ожиданиями приёмника.
  2. Часовой пояс. Если во внешней системе время «уехало» на несколько часов — почти наверняка стороны по-разному трактуют пояс. Договоритесь передавать UTC и помечать это в формате, либо явно указывать смещение.
СтрокаДляОбмена = XMLСтрока(ТекущаяУниверсальнаяДата());

Прежде чем править правила обмена из-за «неправильного времени», сверьте часы и пояс на сервере 1С и на сервере внешней системы. Часто проблема не в коде, а в том, что на одном из серверов сбито системное время.

Для регулярных обменов держите преобразование времени в одном месте — в общем модуле, а не копируйте форматирование по разным обработкам. Так при смене формата вы правите одну функцию, а не ищете её по десятку обработок.

Ещё одна типичная причина сдвига — летнее и зимнее время во внешних данных. 1С хранит момент однозначно, а вот текстовые выгрузки из старых систем иногда приходят без указания пояса и с «плавающим» переводом часов. Если расхождение ровно в один час и появляется сезонно — ищите его на стороне источника, а в 1С фиксируйте приём через универсальное время.

Частые ошибки при работе со временем

Ошибки здесь редко про синтаксис — чаще про логику пояса и состава даты.

  • ❌ Взяли ТекущаяДата() вместо ТекущаяДатаСеанса(). В учёт попадает время клиентского компьютера; если оно сбито, документы встают не по порядку. Лечится заменой функции и серверным контекстом.
  • ❌ Сравнивают поле с Неопределено. Незаполненная дата — это '00010101000000', а не Неопределено. Проверка всегда ложна, условие не срабатывает.
  • ❌ Фильтр по <= КонецПериода(Дата, ДЕНЬ) теряет записи. Последняя секунда суток попадает в границу неоднозначно; надёжнее фильтровать «меньше начала следующего дня».
  • ❌ Складывают дату со строкой при выводе. Нужен Формат(Момент, "ДФ=..."), иначе получите либо ошибку типов, либо нечитаемую строку вида 00010101....
  • ❌ Документы задним числом перескакивают другие операции дня. Причина не в дате, а во времени: смотрите свойство «Автоматическое время документов» и момент времени.
Проверьте себя
Какая функция вернёт время с учётом часового пояса сеанса и годна для записи в учёт?
Чему равна незаполненная дата в 1С?
Что получится при вычитании одной даты из другой?
Какое свойство реквизита включает показ часов и минут в поле?

Частые вопросы

Как убрать время из поля, чтобы пользователи его не меняли?
Поставьте реквизиту состав даты «Дата» — часы и минуты перестанут отображаться и редактироваться. Само значение при этом хранит 00:00:00.
Почему в отчёте за день не видно документов, созданных поздно вечером?
Скорее всего, верхняя граница периода задана как календарная дата без времени, то есть 00:00:00. Для конца дня используйте КОНЕЦПЕРИОДА(Дата, ДЕНЬ) или условие «меньше начала следующего дня».
Чем `ТекущаяДатаСеанса()` отличается от `ТекущаяДата()`?
Первая работает на сервере и учитывает часовой пояс сеанса, вторая берёт время с компьютера клиента. Для учёта подходит только первая.
Как задать время в литерале прямо в коде?
Строкой в кавычках в формате ГГГГММДДЧЧММСС, например '20260115143000' — это 15 января 2026 года, 14:30:00.
Как получить только время без даты для сравнения?
Отдельного типа «только время» нет. Сравнивайте части через Час(), Минута(), Секунда() либо приводите обе даты к одному дню функцией НачалоДня, оставляя значимой только временную часть.