Работа в разных часовых поясах

#std643

Область применения: управляемое приложение, обычное приложение.

1. Конфигурации должны быть рассчитаны на работу в условиях, когда часовой пояс на серверном компьютере не совпадает с реальным часовым поясом пользователей информационной базы. Например, с сервером, расположенным в Москве, работают сотрудники компании из Владивостока, и при этом все операции в системе должны выполняться по местному времени (Владивостока).

Такой сценарий работы часто востребован в клиент-серверных информационных базах и в прикладных решениях в модели сервиса (SaaS).

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

2.2. В тех случаях, когда требуется «универсальная» отметка времени, не зависящая от часового пояса текущего сеанса пользователя, в контексте которого выполняется серверный вызов, следует использовать функцию УниверсальноеВремя. Например, для определения момента перезаполнения закешированных данных, для получения времени последнего выполнения фонового задания и т.п.

2.3. При использовании методов платформы, возвращающих локальную дату серверного компьютера, следует приводить ее либо к универсальному времени, либо к времени пользовательского сеанса. Например:

ДатаАктуальностиУниверсальная = УниверсальноеВремя(ПолнотекстовыйПоиск.ДатаАктуальности());
ДатаАктуальности = МестноеВремя(ДатаАктуальностиУниверсальная, ЧасовойПоясСеанса());

3.1. В клиентском коде использование функции ТекущаяДата также недопустимо. Это требование обусловлено тем, что текущее время, вычисленное в клиентском и серверном коде, не должно различаться.

Например, с сервером, расположенным в Москве, работают пользователи из Киева. Функция ТекущаяДата для клиентского компьютера возвращает 10:00, а для сервера – 11:00. В то же время, функция ТекущаяДатаСеанса вернет на сервере 10:00, если в информационной базе установлено киевское время (с помощью метода УстановитьЧасовойПоясИнформационнойБазы).

Как правило, вместо вызова функции ТекущаяДата на клиенте необходимо 

Рассмотрим типовые случаи на примерах.

3.2. В алгоритме закрытия месяца с клиента на сервер передается дата, по которой далее определяется, какой месяц будет закрываться.

Неправильно:

&НаКлиенте
Процедура КомандаОткрытьЗакрытиеМесяца(Команда)
 ТекущиеДанные = Элементы.Список.ТекущиеДанные;
 Если ТекущиеДанные = Неопределено Тогда
  ТекДата  = ТекущаяДата();  // вызов ТекущаяДата() на клиенте
 Иначе
  ТекДата  = ТекущиеДанные.Дата;
 КонецЕсли;
 ПараметрыФормы = Новый Структура;
 ПараметрыФормы.Вставить("ПериодРегистрации", ТекДата);
 ОткрытьФорму("Обработка.ЗакрытиеМесяца.Форма.Форма", ПараметрыФормы, ЭтотОбъект);

а затем в форме обработки:

&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
 ЗаполнитьЗначенияСвойств(Объект, Параметры);
 Если Не ЗначениеЗаполнено(Объект.ПериодРегистрации) Тогда
  Объект.ПериодРегистрации = НачалоМесяца(ТекущаяДата());
 КонецЕсли;

Правильно

перенести получение текущей даты на сервер:

&НаКлиенте
Процедура КомандаОткрытьЗакрытиеМесяца(Команда)
 ТекущиеДанные = Элементы.Список.ТекущиеДанные;
 Если ТекущиеДанные = Неопределено Тогда
  ТекДата  = Неопределено; // нет вызова ТекущаяДата() на клиенте
 Иначе
  ТекДата  = ТекущиеДанные.Дата;
 КонецЕсли;
 ПараметрыФормы = Новый Структура;
 ПараметрыФормы.Вставить("ПериодРегистрации", ТекДата);
 ОткрытьФорму("Обработка.ЗакрытиеМесяца.Форма.Форма", ПараметрыФормы, ЭтотОбъект);

и в форме обработки использовать для этого функцию ТекущаяДатаСеанса:

&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
 ЗаполнитьЗначенияСвойств(Объект, Параметры);
 Если Не ЗначениеЗаполнено(Объект.ПериодРегистрации) Тогда
  Объект.ПериодРегистрации = НачалоМесяца(ТекущаяДатаСеанса());
 КонецЕсли;

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

Неправильно:

&НаКлиенте
Процедура ПодборТовары(Команда)
 ПараметрыПодбора = Новый Структура;
 ДатаРасчетов = ?(НачалоДня(Объект.Дата) = НачалоДня(ТекущаяДата()),
  Неопределено, Объект.Дата); // вызов ТекущаяДата() на клиенте
 ПараметрыПодбора.Вставить("ДатаРасчетов", ДатаРасчетов);
 ...
 ОткрытьФорму("Обработка.ПодборНоменклатуры.Форма.Форма", ПараметрыПодбора,
  ЭтотОбъект, УникальныйИдентификатор);

Правильно

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

&НаКлиенте
Процедура ПодборТовары(Команда)
 ПараметрыПодбора = Новый Структура;
 ПараметрыПодбора.Вставить("ДатаРасчетов", Объект.Дата);
 ...
 ОткрытьФорму("Обработка.ПодборНоменклатуры.Форма.Форма", ПараметрыПодбора,
  ЭтотОбъект, УникальныйИдентификатор);

Другой пример. При подборе документов для зачета аванса в форме подбора устанавливается отбор по условию «дата документов аванса не больше переданной в форму».

Неправильно:

&НаКлиенте
Процедура ЗачетАвансовДокументАвансаНачалоВыбора(Элемент, ДанныеВыбора, СтандартнаяОбработка)
 СтандартнаяОбработка = Ложь;
 ПараметрыФормы = Новый Структура;
  ПараметрыФормы.Вставить("КонецПериода",                                   
?(ЗначениеЗаполнено(Параметры.Ключ), Объект.Дата - 1, КонецДня(ТекущаяДата()))); // вызов ТекущаяДата() на клиенте
 ...
  ОткрытьФорму("Документ.ДокументРасчетовСКонтрагентом.ФормаВыбора",        
ПараметрыФормы, Элемент);
...

Правильно

вычислять параметр КонецПериода по дате документа:

&НаКлиенте
Процедура ЗачетАвансовДокументАвансаНачалоВыбора(Элемент, ДанныеВыбора,
СтандартнаяОбработка)
 СтандартнаяОбработка = Ложь;
 ПараметрыФормы = Новый Структура;
  ПараметрыФормы.Вставить("КонецПериода", ?(ЗначениеЗаполнено(Параметры.Ключ),
  Объект.Дата - 1, КонецДня(Объект.Дата)));
 ...
  ОткрытьФорму("Документ.ДокументРасчетовСКонтрагентом.ФормаВыбора", ПараметрыФормы, Элемент);

3.4. В остальных случаях при использовании Библиотеки стандартных подсистем рекомендуется использовать функцию ДатаСеанса общего модуля ОбщегоНазначенияКлиент.

4. Исключения из правил 2 и 3 возможны в отдельных, обоснованных случаях, когда требуется использовать действительно текущее время серверного компьютера. Такие исключения должны быть обоснованы в тексте комментария к вызову.

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

Неправильно

ДатаПоследнегоОповещения = ТекущаяДатаСеанса();
ДатаСледующегоОповещения = РассчитатьДату() + ТекущаяДатаСеанса();

Правильно

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

ДатаПоследнегоОповещения = ТекущаяДатаСеанса();
ДатаСледующегоОповещения = РассчитатьДату() + ДатаПоследнегоОповещения;