Серверное время в 1С 8.3: ТекущаяДатаСеанса, ТекущаяДата и часовые пояса
Содержание
Дату сервера 1С получают серверной функцией: в модуле формы пишут процедуру с директивой &НаСервереБезКонтекста, которая возвращает ТекущаяДатаСеанса(), и вызывают её с клиента. Если нужно «голое» время компьютера-сервера без поправки на пояс пользователя — на сервере вызывают ТекущаяДата(), а для времени по UTC — ТекущаяУниверсальнаяДата().
Проблема почти всегда одна: на клиенте ТекущаяДата() берёт часы компьютера пользователя. Сбились часы на ноутбуке у менеджера — и документы получают «завтрашнюю» дату, а контроль сроков врёт. Ниже — какие функции что возвращают, как вызвать их правильно и откуда берутся расхождения на час или три.
Чем дата сервера отличается от даты на компьютере пользователя
В 1С 8.3 у каждой строки кода есть место исполнения: клиент или сервер. Одна и та же функция ТекущаяДата() даёт разный результат в зависимости от того, где она выполнилась.
- 🖥️ На клиенте (тонкий, толстый, веб-клиент) — часы компьютера, на котором открыта программа.
- 🗄️ На сервере — часы компьютера, где работает рабочий процесс сервера 1С (
rphost). В файловом варианте «сервер» живёт в том же процессе, что и клиент, поэтому часы совпадают. - 🌍 В сеансе — время сервера, пересчитанное в часовой пояс сеанса. Это
ТекущаяДатаСеанса().
Зачем различать:
- Дата документа. Если пользователь перевёл часы, документ может уйти в закрытый период или в будущее.
- Сроки и таймауты. Расчёт «прошло 15 минут с момента блокировки» ломается, если клиент и сервер живут в разном времени.
- Распределённая работа. Филиалы в Новосибирске и Калининграде видят один сервер, но должны получать своё местное время.
- Журналы и интеграции. Отметки времени в обменах удобнее хранить в 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-м числом.
Как это устроено в разных вариантах:
- 🧱 Обычное приложение (толстый клиент, конфигурации на обычных формах). Рабочая дата — параметр платформы, задаётся в
Сервис → Параметры, по умолчанию совпадает с датой компьютера. Читается функциейРабочаяДата(). - 🚕 Управляемое приложение (интерфейс «Такси»). Платформенной рабочей даты нет. Механизм реализован в БСП: администратор задаёт пользователям рабочие даты, а код получает её через
ОбщегоНазначения.ТекущаяДатаПользователя().
Отсюда правило выбора:
- Для даты нового документа —
ОбщегоНазначения.ТекущаяДатаПользователя(): так уважается выбор бухгалтера. - Для отметок времени, сроков, журналов —
ТекущаяДатаСеанса()или UTC: рабочая дата здесь только мешает. ТекущаяДата()на клиенте — ни для того, ни для другого.
Если после обновления типовой новые документы вдруг создаются «старой» датой, первым делом проверьте, не осталась ли у пользователя установленная рабочая дата.
Дата сервера через 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С развёрнут в облаке, его ОС работает в UTC. Пользователи в Москве (UTC+3). В 12:00 по Москве:
| Что вызвали | Результат |
|---|---|
ТекущаяДата() на сервере | 09:00 |
ТекущаяУниверсальнаяДата() | 09:00 |
ТекущаяДатаСеанса(), пояс базы не задан | 09:00 |
ТекущаяДатаСеанса(), пояс базы Europe/Moscow | 12:00 |
Вывод: если документы в облачной базе «съезжают» на 3 часа назад, а около полуночи попадают во вчерашний день — задайте пояс информационной базы.
Перевести время из UTC в местное и обратно помогают функции МестноеВремя(Дата, ЧасовойПояс) и УниверсальноеВремя(Дата, ЧасовойПояс):
ДатаUTC = ТекущаяУниверсальнаяДата();
ВремяНовосибирск = МестноеВремя(ДатаUTC, "Asia/Novosibirsk");
ВремяКалининград = МестноеВремя(ДатаUTC, "Europe/Kaliningrad");
Летнее время в России не переводят с 2011 года, а с октября 2014 года действует нынешнее поясное время. Проблемы возникают в двух случаях:
- 🕰️ на сервере стоит ОС без обновлений часовых поясов, и она «по старой памяти» сдвигает время на час;
- 🌐 часть пользователей работает из стран, где летнее время есть. Тогда для них пояс сеанса задают отдельно, а смещение проверяют функциями
СмещениеСтандартногоВремени()иСмещениеЛетнегоВремени().
Частые ошибки и как их найти
Ещё две ошибки на уровне кода:
- ❌
ТекущаяДата()в клиентской процедуре для даты документа или проверки срока. Исправление — серверный вызов илиОбщегоНазначенияКлиент.ДатаСеанса(). - ❌ Серверный вызов в цикле. 1000 строк табличной части — 1000 обращений к серверу ради одной и той же даты. Получите дату один раз до цикла.
Перед тем как менять часовой пояс базы в рабочей системе, учтите: уже записанные даты документов не пересчитаются, сдвинутся только новые. Делайте это в начале рабочего дня, а не посреди закрытия месяца.