Время локальное и серверное

На платформе MetaTrader 5 всегда существует два типа времени: локальное (клиентское) и серверное (брокера).

Локальное время соответствует времени компьютера, на котором запущен терминал, и увеличивается непрерывно, с равной скоростью, как и в реальном мире.

Серверное время течет иначе. Основу для него задает время на компьютере у брокера, однако к клиенту информация о нем поступает только вместе с очередными изменениями цен, которые упаковываются в специальные структуры, называемые тиками (см. раздел про MqlTick), и передаются MQL-программам с помощью событий.

Таким образом, обновленное серверное время становится известно в терминале только в результате изменения цены хотя бы одного финансового инструмента на рынке, то есть из числа тех, что выбраны в окне "Обзор рынка". Последнее известное время сервера отображается в заголовке этого окна. Если тиков нет, серверное время в терминале стоит на месте. Это особенно заметно в выходные и праздничные дни, когда все биржи и площадки Forex закрыты.

В частности, в воскресенье серверное время будет, скорее всего, отображаться как вечер пятницы. Исключение составят лишь те экземпляры MetaTrader 5, в которых доступны непрерывно торгуемые инструменты, такие как криптовалюты. Однако и в этом случае, в периоды низкой волатильности серверное время может заметно отставать от локального.

Все функции данного раздела оперируют временем с точностью до секунды (точность представления времени в типе datetime).

Для получения локального и серверного времени MQL5 API предоставляет 3 функции: TimeLocal, TimeCurrent и TimeTradeServer. Все три функции имеют по два варианта прототипа: первый возвращает время как значение типа datetime, второй дополнительно принимает по ссылке и заполняет компонентами времени структуру MqlDateTime.

datetime TimeLocal()

datetime TimeLocal(MqlDateTime &dt)

Функция возвращает локальное компьютерное время в формате datetime.

Важно отметить, что время включает в себя поправку на летнее время, если она активирована. То есть TimeLocal равна стандартному времени часового пояса компьютера за вычетом поправки TimeDaylightSavings. Условно формулу можно представить так:

TimeLocalsummer() = TimeLocalwinter() - TimeDaylightSavings()

Здесь TimeDaylightSavings равно обычно -3600, то есть переводу часов на 1 час вперед (1 час пропадает). Таким образом, летнее значение TimeLocal больше зимнего (при равенстве астрономического времени суток) относительно UTC. Например, если зимой TimeLocal равен UTC+2, то летом UTC+3. Универсальное время UTC можно получить с помощью функции TimeGMT.

datetime TimeCurrent()

datetime TimeCurrent(MqlDateTime &dt)

Функция возвращает последнее известное время сервера в формате datetime. Это время прихода последней котировки из списка всех финансовых инструментов, подключенных в "Обзоре рынка". Единственное исключение: в обработчике события OnTick в экспертах данная функция вернет время обрабатываемого тика (даже если в обзоре рынка уже случились тики с более свежим временем).

Также отметим, что время на горизонтальной оси всех графиков в MetaTrader 5 соответствует времени сервера (на истории). Последний (текущий, самый правый) бар содержит в себе время TimeCurrent. Подробности см. в разделе Графики.

datetime TimeTradeServer()

datetime TimeTradeServer(MqlDateTime &dt)

Функция возвращает расчетное текущее время торгового сервера. В отличие от функции TimeCurrent, результаты которой могут не меняться в отсутствии новых котировок, TimeTradeServer позволяет получить оценку непрерывно увеличивающегося времени сервера. В основу расчета берется последняя известная разница "таймзон" клиента и сервера, которая прибавляется к текущему локальному времени.

В тестере значение TimeTradeServer всегда равно TimeCurrent.

Пример работы функций приведен в скрипте TimeCheck.mq5.

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

void OnStart()
{
   while(!IsStopped())
   {
      PRTF(TimeLocal());
      PRTF(TimeCurrent());
      PRTF(TimeTradeServer());
      PRTF(TimeTradeServerExact());
      Sleep(1000);
   }
}

Помимо стандартных функций здесь применена пользовательская функция TimeTradeServerExact.

datetime TimeTradeServerExact()
{
   enum LOCATION
   {
      LOCAL,
      SERVER,
   };
   static datetime now[2] = {}, then[2] = {};
   static int shiftInHours = 0;
   static long shiftInSeconds = 0;
   
   // постоянно засекаем 2 последних метки времени там и тут 
   then[LOCAL] = now[LOCAL];
   then[SERVER] = now[SERVER];
   now[LOCAL] = TimeLocal();
   now[SERVER] = TimeCurrent();
   
   // при первом вызове еще не имеем 2-х меток,
   // необходимых для расчета стабильной разницы
   if(then[LOCAL] == 0 && then[SERVER] == 0return 0;
 
   // когда ход времени одинаков на клиенте и на сервере,
   // и сервер не "заморожен" из-за выходных/праздников,
   // обновляем разницу   
   if(now[LOCAL] - now[SERVER] == then[LOCAL] - then[SERVER]
   && now[SERVER] != then[SERVER])
   {
      shiftInSeconds = now[LOCAL] - now[SERVER];
      shiftInHours = (int)MathRound(shiftInSeconds / 3600.0);
      // отладочная печать
      PrintFormat("Shift update: hours: %d; seconds: %lld"shiftInHoursshiftInSeconds);
   }
   
   // NB: встроенная функция TimeTradeServer вычисляет так:
   //                TimeLocal() - shiftInHours * 3600
   return (datetime)(TimeLocal() - shiftInSeconds);
}

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

В идеале "бортовые" часы всех компьютеров должны быть синхронизированы с глобальным временем, но на практике случаются отклонения, и достаточно небольшого сдвига на одной из сторон, как TimeTradeServer перестанет максимально точно повторять время на сервере.

В нашей реализации этой же функции на MQL5 мы не округляем разницу между временем клиента и сервера до часовых "таймзон". Вместо этого в расчете используется точная разница в секундах. Поэтому TimeTradeServerExact возвращает время, в котором минуты и секунды тикают так же, как на сервере.

Вот пример журнала, генерируемого скриптом.

TimeLocal()=2021.09.02 16:03:34 / ok
TimeCurrent()=2021.09.02 15:59:39 / ok
TimeTradeServer()=2021.09.02 16:03:34 / ok
TimeTradeServerExact()=1970.01.01 00:00:00 / ok

Видно, что часовые пояса клиента и сервера совпадают, но есть рассинхронизация в несколько минут (для наглядности). При первом вызове TimeTradeServerExact вернула 0. Далее данные для расчета разницы уже поступят, и мы увидим все 4 "типа" времени, размеренно "шагающие" с секундным интервалом.

TimeLocal()=2021.09.02 16:03:35 / ok
TimeCurrent()=2021.09.02 15:59:40 / ok
TimeTradeServer()=2021.09.02 16:03:35 / ok
Shift update: hours: 0; seconds: 235
TimeTradeServerExact()=2021.09.02 15:59:40 / ok
TimeLocal()=2021.09.02 16:03:36 / ok
TimeCurrent()=2021.09.02 15:59:41 / ok
TimeTradeServer()=2021.09.02 16:03:36 / ok
Shift update: hours: 0; seconds: 235
TimeTradeServerExact()=2021.09.02 15:59:41 / ok
TimeLocal()=2021.09.02 16:03:37 / ok
TimeCurrent()=2021.09.02 15:59:41 / ok
TimeTradeServer()=2021.09.02 16:03:37 / ok
TimeTradeServerExact()=2021.09.02 15:59:42 / ok
TimeLocal()=2021.09.02 16:03:38 / ok
TimeCurrent()=2021.09.02 15:59:43 / ok
TimeTradeServer()=2021.09.02 16:03:38 / ok
TimeTradeServerExact()=2021.09.02 15:59:43 / ok