Работа с датой и временем в 1С 8.3: от поля документа до запроса
Содержание
В 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-сервису) даты почти всегда передаются строкой. Два момента, на которых спотыкаются:
- Формат строки. Для сериализации пользуйтесь штатными
XMLСтрока(Момент)и обратнымXMLЗначение(Тип("Дата"), Строка)— они дают корректный ISO-вид. Самодельное форматирование легко расходится с ожиданиями приёмника. - Часовой пояс. Если во внешней системе время «уехало» на несколько часов — почти наверняка стороны по-разному трактуют пояс. Договоритесь передавать UTC и помечать это в формате, либо явно указывать смещение.
СтрокаДляОбмена = XMLСтрока(ТекущаяУниверсальнаяДата());
Прежде чем править правила обмена из-за «неправильного времени», сверьте часы и пояс на сервере 1С и на сервере внешней системы. Часто проблема не в коде, а в том, что на одном из серверов сбито системное время.
Для регулярных обменов держите преобразование времени в одном месте — в общем модуле, а не копируйте форматирование по разным обработкам. Так при смене формата вы правите одну функцию, а не ищете её по десятку обработок.
Ещё одна типичная причина сдвига — летнее и зимнее время во внешних данных. 1С хранит момент однозначно, а вот текстовые выгрузки из старых систем иногда приходят без указания пояса и с «плавающим» переводом часов. Если расхождение ровно в один час и появляется сезонно — ищите его на стороне источника, а в 1С фиксируйте приём через универсальное время.
Частые ошибки при работе со временем
Ошибки здесь редко про синтаксис — чаще про логику пояса и состава даты.
- ❌ Взяли
ТекущаяДата()вместоТекущаяДатаСеанса(). В учёт попадает время клиентского компьютера; если оно сбито, документы встают не по порядку. Лечится заменой функции и серверным контекстом. - ❌ Сравнивают поле с
Неопределено. Незаполненная дата — это'00010101000000', а неНеопределено. Проверка всегда ложна, условие не срабатывает. - ❌ Фильтр по
<= КонецПериода(Дата, ДЕНЬ)теряет записи. Последняя секунда суток попадает в границу неоднозначно; надёжнее фильтровать «меньше начала следующего дня». - ❌ Складывают дату со строкой при выводе. Нужен
Формат(Момент, "ДФ=..."), иначе получите либо ошибку типов, либо нечитаемую строку вида00010101.... - ❌ Документы задним числом перескакивают другие операции дня. Причина не в дате, а во времени: смотрите свойство «Автоматическое время документов» и момент времени.
Частые вопросы
Как убрать время из поля, чтобы пользователи его не меняли?
00:00:00.Почему в отчёте за день не видно документов, созданных поздно вечером?
00:00:00. Для конца дня используйте КОНЕЦПЕРИОДА(Дата, ДЕНЬ) или условие «меньше начала следующего дня».Чем `ТекущаяДатаСеанса()` отличается от `ТекущаяДата()`?
Как задать время в литерале прямо в коде?
'20260115143000' — это 15 января 2026 года, 14:30:00.Как получить только время без даты для сравнения?
Час(), Минута(), Секунда() либо приводите обе даты к одному дню функцией НачалоДня, оставляя значимой только временную часть.