Серверное время в 1С 8.3: ТекущаяДатаСеанса, ТекущаяДата и часовые пояса

· 9 мин

Содержание

Дату сервера 1С получают серверной функцией: в модуле формы пишут процедуру с директивой &НаСервереБезКонтекста, которая возвращает ТекущаяДатаСеанса(), и вызывают её с клиента. Если нужно «голое» время компьютера-сервера без поправки на пояс пользователя — на сервере вызывают ТекущаяДата(), а для времени по UTC — ТекущаяУниверсальнаяДата().

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

Чем дата сервера отличается от даты на компьютере пользователя

В 1С 8.3 у каждой строки кода есть место исполнения: клиент или сервер. Одна и та же функция ТекущаяДата() даёт разный результат в зависимости от того, где она выполнилась.

  • 🖥️ На клиенте (тонкий, толстый, веб-клиент) — часы компьютера, на котором открыта программа.
  • 🗄️ На сервере — часы компьютера, где работает рабочий процесс сервера 1С (rphost). В файловом варианте «сервер» живёт в том же процессе, что и клиент, поэтому часы совпадают.
  • 🌍 В сеансе — время сервера, пересчитанное в часовой пояс сеанса. Это ТекущаяДатаСеанса().

Зачем различать:

  1. Дата документа. Если пользователь перевёл часы, документ может уйти в закрытый период или в будущее.
  2. Сроки и таймауты. Расчёт «прошло 15 минут с момента блокировки» ломается, если клиент и сервер живут в разном времени.
  3. Распределённая работа. Филиалы в Новосибирске и Калининграде видят один сервер, но должны получать своё местное время.
  4. Журналы и интеграции. Отметки времени в обменах удобнее хранить в UTC, иначе при сверке появляются «дыры».

Типовые конфигурации на БСП почти не используют ТекущаяДата() в прикладном коде. Вместо неё — ТекущаяДатаСеанса() или функции общего модуля ОбщегоНазначения. Проверка конфигурации в EDT и АПК тоже ругается на ТекущаяДата().

Какие функции возвращают серверное время

ФункцияЧто возвращаетГде вызывать
ТекущаяДата()Часы компьютера, где выполняется кодКлиент даёт время клиента, сервер — время сервера
ТекущаяДатаСеанса()Время сервера в часовом поясе сеансаОсновной способ для прикладного кода
ТекущаяУниверсальнаяДата()Время по UTCДля журналов, обменов, сравнения отметок
ЧасовойПоясСеанса()Идентификатор пояса сеансаДиагностика
ЧасовойПояс()Пояс компьютера, где выполняется кодДиагностика

Для 9 задач из 10 достаточно ТекущаяДатаСеанса(). Она одинаковая для всех пользователей с одним поясом, не зависит от часов на клиенте и учитывает настройку пояса информационной базы.

Как получить дату сервера в коде: базовый пример

Вызов из модуля управляемой формы

Самый частый случай: нужна дата сервера в обработчике команды формы. Пишем серверную функцию без контекста, чтобы не гонять данные формы на сервер:

&НаКлиенте
Процедура ПоказатьДатуСервера(Команда)

	ДатаСервера = ПолучитьДатуСервера();
	ДатаКлиента = ТекущаяДата();
	Разница = ДатаСервера - ДатаКлиента; // результат в секундах

	Сообщить("Дата сервера: " + Формат(ДатаСервера, "ДФ='dd.MM.yyyy HH:mm:ss'"));
	Сообщить("Дата клиента: " + Формат(ДатаКлиента, "ДФ='dd.MM.yyyy HH:mm:ss'"));
	Сообщить("Расхождение, сек: " + Формат(Разница, "ЧГ=0; ЧН=0"));

КонецПроцедуры

&НаСервереБезКонтекста
Функция ПолучитьДатуСервера()

	Возврат ТекущаяДатаСеанса();

КонецФункции

Разность двух дат в 1С — число секунд. Поэтому расхождение в 3 часа покажется как 10800.

Вызов из общего модуля

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

// Общий модуль СерверноеВремя (Сервер, Вызов сервера)
Функция ДатаСервера() Экспорт
	Возврат ТекущаяДатаСеанса();
КонецФункции

Функция ДатаСервераUTC() Экспорт
	Возврат ТекущаяУниверсальнаяДата();
КонецФункции

С клиента: Дата = СерверноеВремя.ДатаСервера();. Каждый такой вызов — серверный вызов, поэтому в цикле на клиенте его не делают. Получите дату один раз и передайте дальше.

Готовые функции БСП

В конфигурациях на Библиотеке стандартных подсистем (БП 3.0, ЗУП 3.1, УТ 11 и др.) свой код писать не обязательно:

  • ОбщегоНазначенияКлиент.ДатаСеанса() — дата сеанса прямо на клиенте, без серверного вызова. БСП при входе запоминает поправку между часами клиента и сервера и пересчитывает.
  • ОбщегоНазначенияКлиент.УниверсальнаяДата() — то же для UTC.
  • ОбщегоНазначения.ТекущаяДатаПользователя() — на сервере: учитывает рабочую дату пользователя, если она задана, иначе отдаёт дату сеанса.

Клиентские функции БСП удобны в обработчиках, которые срабатывают часто: например, при изменении поля, где лишний серверный вызов заметно тормозит форму.

Файловый и клиент-серверный вариант: где «живёт» сервер

Ответ на вопрос «чья это дата» зависит от варианта работы базы.

ВариантГде выполняется серверный кодЧьи часы даёт ТекущаяДата() на сервере
Файловая база, локальноВ процессе клиентаКомпьютер пользователя
Файловая база в сетевой папкеВ процессе каждого клиентаКомпьютер каждого пользователя
Файловая база, опубликованная на веб-сервереНа веб-сервереВеб-сервер
Клиент-серверная базаРабочий процесс кластераСервер 1С (rphost)

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

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

Время СУБД (MS SQL, PostgreSQL) — это ещё одни часы. Если сервер 1С и сервер СУБД на разных машинах, GETDATE() или now() в прямом SQL-запросе может расходиться с ТекущаяДатаСеанса(). В языке запросов 1С функции текущей даты нет, дату передают параметром.

Запрос = Новый Запрос;
Запрос.Текст = "ВЫБРАТЬ Ссылка ИЗ Документ.ЗаказКлиента ГДЕ Дата >= &НачалоДня";
Запрос.УстановитьПараметр("НачалоДня", НачалоДня(ТекущаяДатаСеанса()));

Рабочая дата: когда она важнее даты сервера

Рабочая дата — это дата, которую пользователь (или администратор за него) задаёт вручную, чтобы программа подставляла её в новые документы. Классический сценарий — бухгалтер в начале месяца доводит предыдущий и хочет, чтобы все документы создавались 31-м числом.

Как это устроено в разных вариантах:

  • 🧱 Обычное приложение (толстый клиент, конфигурации на обычных формах). Рабочая дата — параметр платформы, задаётся в Сервис → Параметры, по умолчанию совпадает с датой компьютера. Читается функцией РабочаяДата().
  • 🚕 Управляемое приложение (интерфейс «Такси»). Платформенной рабочей даты нет. Механизм реализован в БСП: администратор задаёт пользователям рабочие даты, а код получает её через ОбщегоНазначения.ТекущаяДатаПользователя().

Отсюда правило выбора:

  1. Для даты нового документа — ОбщегоНазначения.ТекущаяДатаПользователя(): так уважается выбор бухгалтера.
  2. Для отметок времени, сроков, журналов — ТекущаяДатаСеанса() или UTC: рабочая дата здесь только мешает.
  3. ТекущаяДата() на клиенте — ни для того, ни для другого.

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

Дата сервера через COM-соединение и внешнюю обработку

Внешняя обработка

Во внешней обработке на управляемой форме код тот же, что в примере выше: функция с &НаСервереБезКонтекста и ТекущаяДатаСеанса(). В клиент-серверной базе серверная часть обработки выполняется в рабочем процессе кластера, значит, вы получите время сервера 1С.

Подключённая через Дополнительные отчёты и обработки обработка работает в безопасном режиме. Функции даты в нём доступны, ограничения касаются файлов, COM-объектов и внешних ресурсов.

COM-соединение из другой программы

Внешнее соединение (V83.COMConnector) запускает контекст 1С внутри процесса вызывающей программы. Код модулей, вызванный через соединение, выполняется на той машине, где работает скрипт. Поэтому ТекущаяДата(), полученная так, — это часы компьютера со скриптом, а не сервера 1С:

Set Connector = CreateObject("V83.COMConnector")
Set Base = Connector.Connect("Srvr=""srv1c"";Ref=""buh"";Usr=""Robot"";Pwd=""***"";")
WScript.Echo Base.ТекущаяДата()

Если скрипт запущен прямо на сервере 1С (например, регламентный скрипт в планировщике Windows), часы совпадут. Если скрипт работает на другой машине, а нужна именно дата сервера, — синхронизируйте часы обеих машин с одним источником или берите время из HTTP-сервиса базы.

Для интеграций, где 1С опрашивают регулярно, надёжнее HTTP-сервис: его код выполняется на стороне базы. Метод отдаёт ТекущаяУниверсальнаяДата() в формате ISO, и вызывающей стороне не нужна установленная платформа 1С.

Функция ВремяGET(Запрос)
	Ответ = Новый HTTPСервисОтвет(200);
	Ответ.УстановитьТелоИзСтроки(ЗаписатьДатуJSON(ТекущаяУниверсальнаяДата(), ФорматДатыJSON.ISO));
	Возврат Ответ;
КонецФункции

Часовые пояса и переход на летнее время

С поясами в 1С связаны три настройки, и они применяются по цепочке:

  1. Пояс сеанса — УстановитьЧасовойПоясСеанса(). Если не задан, берётся пояс базы.
  2. Пояс информационной базы — УстановитьЧасовойПоясИнформационнойБазы(), в типовых его задают в общих настройках раздела Администрирование. Если не задан, берётся пояс компьютера-сервера.
  3. Пояс компьютера-сервера — настройка операционной системы.

Пример расхождения. Сервер 1С развёрнут в облаке, его ОС работает в UTC. Пользователи в Москве (UTC+3). В 12:00 по Москве:

Что вызвалиРезультат
ТекущаяДата() на сервере09:00
ТекущаяУниверсальнаяДата()09:00
ТекущаяДатаСеанса(), пояс базы не задан09:00
ТекущаяДатаСеанса(), пояс базы Europe/Moscow12:00

Вывод: если документы в облачной базе «съезжают» на 3 часа назад, а около полуночи попадают во вчерашний день — задайте пояс информационной базы.

Перевести время из UTC в местное и обратно помогают функции МестноеВремя(Дата, ЧасовойПояс) и УниверсальноеВремя(Дата, ЧасовойПояс):

ДатаUTC = ТекущаяУниверсальнаяДата();
ВремяНовосибирск = МестноеВремя(ДатаUTC, "Asia/Novosibirsk");
ВремяКалининград = МестноеВремя(ДатаUTC, "Europe/Kaliningrad");

Летнее время в России не переводят с 2011 года, а с октября 2014 года действует нынешнее поясное время. Проблемы возникают в двух случаях:

  • 🕰️ на сервере стоит ОС без обновлений часовых поясов, и она «по старой памяти» сдвигает время на час;
  • 🌐 часть пользователей работает из стран, где летнее время есть. Тогда для них пояс сеанса задают отдельно, а смещение проверяют функциями СмещениеСтандартногоВремени() и СмещениеЛетнегоВремени().

Частые ошибки и как их найти

Диагностика: выберите симптом

Ещё две ошибки на уровне кода:

  • ❌ ТекущаяДата() в клиентской процедуре для даты документа или проверки срока. Исправление — серверный вызов или ОбщегоНазначенияКлиент.ДатаСеанса().
  • ❌ Серверный вызов в цикле. 1000 строк табличной части — 1000 обращений к серверу ради одной и той же даты. Получите дату один раз до цикла.

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

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

Можно ли получить время сервера СУБД средствами 1С?
Языком запросов — нет, функции текущей даты в нём нет. Только прямым SQL-запросом к СУБД через ADO, что в рабочей базе делать не рекомендуется.
Чем ТекущаяДатаСеанса отличается от ТекущаяДата на сервере?
ТекущаяДата() возвращает время в поясе ОС сервера. ТекущаяДатаСеанса() пересчитывает то же время в пояс сеанса. Если пояса совпадают, результат одинаковый.
Как узнать, какой часовой пояс сейчас у сеанса?
Вызовите ЧасовойПоясСеанса() и ЧасовойПоясИнформационнойБазы() и выведите результат через Сообщить(). Пустой пояс базы значит, что используется пояс компьютера-сервера.
Работает ли ТекущаяДатаСеанса в веб-клиенте?
Да. Она не зависит от часов браузера и считается от времени сервера с учётом пояса сеанса.
Как хранить время в обменах между базами?
В UTC: ТекущаяУниверсальнаяДата() при записи и МестноеВремя() при показе. Тогда базы в разных поясах не путают порядок событий.