English Deutsch 日本語
preview
Внедрение в MQL5 практических модулей из других языков (Часть 04): Модули time, date и datetime из Python

Внедрение в MQL5 практических модулей из других языков (Часть 04): Модули time, date и datetime из Python

MetaTrader 5Торговые системы |
275 0
Omega J Msigwa
Omega J Msigwa

Разделы


Введение

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

Когда речь заходит об алгоритмической торговле и торговле в целом, время по-прежнему остается важнейшим параметром. Мы часто принимаем торговые решения, основанные на временных рамках, и обычно оцениваем свои достижения ежедневно, ежемесячно и еженедельно.

Язык программирования MQL5 обладает множеством встроенных функций для управления и оценки времени, обеспечивая работу наших алгоритмических торговых систем с учетом реального времени. Однако выбранный нами язык программирования предлагает очень простой (базовый) и порой не очень подходящий человеку способ работы со временем, датами и т.п., по сравнению с другими языками наподобие языка Python, предлагающими богатый набор модулей для выполнения задач, таких как datetime, calendar, time, zoneinfo и т. д.

Источник: pexels.com

В этой статье мы собираемся реализовать в языке программирования MQL5 модули, аналогичные тем, которые предлагаются в Python для обработки времени.


Класс (модуль) time

Начнем с класса time, который содержит основные функции (методы) для работы со временем в языке программирования Python.

Согласно документации Python:

Объект time представляет собой (локальное) время суток, не привязанное к конкретной дате и подлежащее корректировке с помощью объекта tzinfo.

class datetime.time(hour=0, minute=0, second=0, microsecond=0, tzinfo=None, *, fold=0)

Все аргументы являются необязательными. tzinfo может быть None или экземпляром подкласса tzinfo. Остальные аргументы должны быть целыми числами в следующих диапазонах:

  • 0 <= час < 24,
  • 0 <= минута < 60,
  • 0 <= секунда < 60,
  • 0 <= микросекунда < 1000000

Если аргумент выходит за пределы указанных диапазонов, генерируется исключение ValueError. Все значения по умолчанию равны 0, за исключением tzinfo, для которого значение по умолчанию равно None.

Python предлагает такие классы, как datetime и date с использованием аналогичных методов для работы со временем. Однако класс, названный time, из модуля с именем time отвечает только за работу со временем (без учета даты). Точно так же, как вам хотелось бы узнать конкретное время суток.

Внутри класса с именем CTime нам необходимо проверить значения, чтобы убедиться, что пользователь указал соответствующие значения для создания объекта time в конструкторе класса, аналогично тому, как это делается на языке Python.

class CTime
  {
protected:

   uint              m_hour;
   uint              m_minute;
   uint              m_second;
   uint              m_millisecond;
   CTZInfo           *m_tzinfo;

public:
   //--- constructors
                     CTime(void);
                     CTime(const int hour, const int minute, const int second, CTZInfo *tzinfo=NULL, const int milliseconds=0);
                    ~CTime(void); //--- A destructor
CTime::CTime(const int hour, const int minute, const int second, CTZInfo *tzinfo=NULL, const int milliseconds=0)
  {
// --- Validate hour ---
   if(hour < MINHOUR || hour > MAXHOUR)
     {
      printf("CTime Error: hour (%d) out of range [%d..%d]. Defaulting to 0.", hour, MINHOUR, MAXHOUR);
      m_hour = 0;
     }
   else
      m_hour = hour;

// --- Validate minute ---
   if(minute < MINMINUTES || minute > MAXMINUTES)
     {
      printf("CTime Error: minute (%d) out of range [%d..%d]. Defaulting to 0.", minute, MINMINUTES, MAXMINUTES);
      m_minute = 0;
     }
   else
      m_minute = minute;

// --- Validate second ---
   if(second < MINSECOND || second > MAXSECOND)
     {
      printf("CTime Error: second (%d) out of range [%d..%d]. Defaulting to 0.", second, MINSECOND, MAXSECOND);
      m_second = 0;
     }
   else
      m_second = second;

// --- Validate millisecond ---
   if(milliseconds < MINMILLISECOND || milliseconds > MAXMILLISECOND)
     {
      printf("CTime Error: millisecond (%d) out of range [%d..%d]. Defaulting to 0.", milliseconds, MINMILLISECOND, MAXMILLISECOND);
      m_millisecond = 0;
     }
   else
      m_millisecond = milliseconds;

// --- Timezone info pointer ---

   m_tzinfo = tzinfo;
  }

При вызове конструктора класса с необязательными параметрами он заполняет переменные внутри класса, фактически создавая объект CTime.

Необязательная переменная с именем tzinfo хранит информацию о конкретном часовом поясе, к которому относится объект time.

Ниже приведены несколько методов, присутствующих в классе CTime.

(a) fromisoformat

Эта функция возвращает объект CTime, соответствующий переменной time_string в любом допустимом формате ISO 8601, за исключением следующих случаев:

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

CTime CTime::fromisoformat(string time_string)

Пример использования.

void OnStart()
  {
//---
   CTime time;
   
   Print("Time: ",time.fromisoformat("04:23:01").__str__());
   Print("Time: ",time.fromisoformat("T04:23:01").__str__());
   Print("Time: ",time.fromisoformat("T042301").__str__());
   Print("Time: ",time.fromisoformat("04:23:01.000384").__str__());
   Print("Time: ",time.fromisoformat("04:23:01,000384").__str__());
   Print("Time: ",time.fromisoformat("04:23:01+04:00").__str__());
   Print("Time: ",time.fromisoformat("04:23:01Z").__str__());
   Print("Time: ",time.fromisoformat("04:23:01+00:00").__datetime__());
 }

Результаты.

OL      0       20:44:38.611    Time testing (XAUUSD,H1)        Time: 04:23:01
IG      0       20:44:38.611    Time testing (XAUUSD,H1)        Time: 04:23:01
KO      0       20:44:38.611    Time testing (XAUUSD,H1)        Time: 04:23:01
EF      0       20:44:38.611    Time testing (XAUUSD,H1)        Time: 04:23:01
GQ      0       20:44:38.611    Time testing (XAUUSD,H1)        Time: 04:23:01
QI      0       20:44:38.611    Time testing (XAUUSD,H1)        Time: 04:23:01
CP      0       20:44:38.611    Time testing (XAUUSD,H1)        Time: 04:23:01
HJ      0       20:44:38.611    Time testing (XAUUSD,H1)        Time: 1970.01.01 04:23:01

Методы __str__() и __datetime__() преобразуют время, хранящееся в объекте CTime, в форматы string и datetime (переменные) соответственно.

(b) strptime

Эта функция преобразует заданное время в строковом формате в соответствующую переменную типа datetime.

datetime CTime::strptime(string timestr, string format)

Пример использования.

void OnStart()
  {
//---
  
   Print("Time: ", CTime::strptime("00:00:01", "%H:%M:%S"));
   Print("Time: ", CTime::strptime("00,00,01", "%H,%M,%S"));
   Print("Time: ", CTime::strptime("00-00-01", "%H-%M-%S"));
 }

Результаты.

MD      0       20:37:49.509    Time testing (XAUUSD,H1)        Time: 1970.01.01 00:00:01
IL      0       20:37:49.509    Time testing (XAUUSD,H1)        Time: 1970.01.01 00:00:01
EE      0       20:37:49.509    Time testing (XAUUSD,H1)        Time: 1970.01.01 00:00:01

(c) replace

Эта функция возвращает новый объект CTime с теми же значениями, но с обновленными указанными параметрами. Обратите внимание, что параметр tzinfo=NULL можно указать для создания относительного (naive) объекта времени из объекта абсолютного (aware) времени без преобразования данных о времени.

CTime CTime::replace(const int hour, const int minute=-1, const int second=-1, CTZInfo *tzinfo=NULL, const int millisecond=0)
  {
//--- Modify only specified values

   int n_hour = int(hour<=-1?this.m_hour:hour);
   int n_minute = int(minute<=-1?this.m_minute:minute);
   int n_second = int(second<=-1?this.m_second:second);
   int n_millisecond = int(millisecond<=-1?this.m_millisecond:millisecond);
   
//---

   m_tzinfo = tzinfo;

   return CTime(n_hour, n_minute, n_second, m_tzinfo, n_millisecond);
  }

Пример использования.

void OnStart()
  {
   CTime time(9, 48, 10, &tzinfo);
   Print(time.__str__());
   
   time = time.replace(22); //replace the hour and assign the new CTime object to the old one
   Print(time.__str__());
 }

Результаты.

FR      0       11:05:16.339    Time testing (XAUUSD,H1)        09:48:10 
IN      0       11:05:16.339    Time testing (XAUUSD,H1)        22:48:10

(c) isoformat

Эта функция возвращает строку, представляющую время в формате ISO 8601, в одном из следующих видов:

  • HH:MM:SS.ffffff, если количество микросекунд не равно 0
  • HH:MM:SS, если количество микросекунд равно 0
  • HH:MM:SS.ffffff+HH:MM[:SS[.ffffff]], если utcoffset() не возвращает None
  • HH:MM:SS+HH:MM[:SS[.ffffff]], если количество микросекунд равно 0, а utcoffset() не возвращает None

string CTime::isoformat(string timespec = "auto")
  {
   string hh = StringFormat("%02d", m_hour);
   string mm = StringFormat("%02d", m_minute);
   string ss = StringFormat("%02d", m_second);
   string ms = StringFormat("%03d", m_millisecond);

// ----- Timespec switch -----
   if(timespec == "hours")
      return hh;

   if(timespec == "minutes")
      return hh + ":" + mm;

   if(timespec == "seconds")
      return hh + ":" + mm + ":" + ss;

   if(timespec == "milliseconds")
      return hh + ":" + mm + ":" + ss + "." + ms;

// ----- AUTO -----
// Python rule: include .mmm only if non-zero
   if(timespec == "auto")
     {
      if(m_millisecond > 0)
         return hh + ":" + mm + ":" + ss + "." + ms;
      else
         return hh + ":" + mm + ":" + ss;
     }

// Invalid timespec -> fallback to full precision
   return hh + ":" + mm + ":" + ss + "." + ms;
  }

Необязательный аргумент timespec указывает количество дополнительных компонентов времени, которые следует включить (по умолчанию — auto). Возможные значения:

  • auto: такое же значение, как у seconds, если значение microseconds равно 0, в противном случае — то же значение, что и у microseconds.
  • hours: укажите час в двузначном формате HH.
  • minutes: укажите часы и минуты в формате HH:MM.
  • seconds: укажите часы, минуты и секунды в формате HH:MM:SS.
  • milliseconds: укажите время полностью, но отсекайте дробную часть секунды до миллисекунд. Формат HH:MM:SS.sss.
  • microseconds: укажите время полностью в формате HH:MM:SS.ffffff.

Пример использования.

void OnStart()
  {
//---
    
   CTime t(14, 30, 55, &tzinfo, 120);   // 14:30:55.120

   Print(t.isoformat());                       // AUTO -> "14:30:55.120"
   Print(t.isoformat("hours"));                // "14"
   Print(t.isoformat("minutes"));              // "14:30"
   Print(t.isoformat("seconds"));              // "14:30:55"
   Print(t.isoformat("milliseconds"));         // "14:30:55.120"
 }

(d): strftime

Возвращает строковое представление времени, контролируемое строкой (string) явно заданного формата.

string CTime::strftime(string format)
  {
   string result = "";
   for(int i = 0; i < StringLen(format); i++)
     {
      if(StringGetCharacter(format, i) == '%' && i + 1 < StringLen(format)) //Start obtaining the values after a % sign
        {
         i++;
         uchar spec = (uchar)StringGetCharacter(format, i);

         switch(spec)
           {
            case 'H': //Find the H for hour
               result += StringFormat("%02d", m_hour);
               break;
            case 'M': //Put minutes in a place of M
               result += StringFormat("%02d", m_minute);
               break;
            case 'S': //put seconds in a place of S
               result += StringFormat("%02d", m_second);
               break;
            default:
               result += "%";
               result += CharToString(spec);
               break;
           }
        }
      else
         result += CharToString((char)StringGetCharacter(format, i));
     }

   return result;
  }

(e) utcoffset

Если параметру tzinfo присвоено значение NULL (по умолчанию), эта функция возвращает INT_MAX; в противном случае она возвращает смещение в секундах для заданного часового пояса относительно времени UTC/GMT.

int  CTime::tzoffset()
  {
   if(m_tzinfo == NULL)
      return INT_MAX;

   return m_tzinfo.utcoffset();
  }

Пример ниже.

void OnStart()
  {
//---   
   
   CTZInfo tzinfo("America/New_York");
   
   CTime time(10, 22, 0, &tzinfo);
   printf("Tzoffset: %d hours",time.tzoffset()/3600); //we divide by 3600 to get the number of hours
 }

Класс CTZInfo рассматривается в следующем разделе.

Результаты.

KK      0       12:39:37.184    Time testing (XAUUSD,H1)        Tzoffset: -5 hours

Это правда, время в Соединенных Штатах (Нью-Йорк) — GMT-5 (на 5 часов отстает от UTC).

(f) dst

Возвращает INT_MAX, если значение tzinfo установлено на NULL (значение по умолчанию); в противном случае возвращает значение летнего времени.

int CTime::dst(void)
 {
   if(m_tzinfo == NULL)
      return INT_MAX;

   return m_tzinfo.dst();
 }


Класс информации о часовых поясах (tzinfo)

В MQL5 нет встроенного способа доступа к информации из разных часовых поясов. Это мешает нашим программам сохранять актуальность и соответствовать разным поясам мирового времени.

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

Согласно документации Python.

class datetime.tzinfo

Это абстрактный базовый класс, что означает: его не следует создавать напрямую. Зададим подкласс класса tzinfo для сбора информации о конкретном часовом поясе.

Экземпляр (конкретного подкласса) tzinfo может быть передан конструкторам объектов datetime и time. Эти последние объекты рассматривают свои атрибуты как относящиеся к местному времени, а объект tzinfo поддерживает методы, отображающие смещение местного времени относительно UTC, название часового пояса и смещение летнего времени, и все эти характеристики — относительно объекта date или time, переданного в них.

Вам необходимо создать конкретный подкласс и (как минимум) предоставить реализации стандартных методов tzinfo, необходимых для используемых вами методов datetime. Модуль datetime предоставляет time zone — простой конкретный подкласс класса tzinfo, который может представлять часовые зоны с фиксированным смещением относительно UTC, такие как само время UTC или североамериканские EST и EDT.

Конкретному подклассу tzinfo может потребоваться реализация следующих методов. Какие именно методы нужны, зависит от того, для каких целей используются известные объекты datetime. В случае сомнений просто реализуйте все методы.

Подобно классу в языке программирования Python, реализованный в MQL5 класс с именем CTZInfo предназначен для гармоничной работы с классами CDatetime и CTime, помогая этим двум классам получать информацию о часовых поясах.

Несмотря на то, что вы можете вызывать этот класс вне других классов, работающих с временными переменными, вам не следует использовать его для доступа к его значениям, таким как current time (текущее время), и т. д.

Для работы этого класса (модуля) нам необходима единая база данных со всей информацией о часовых поясах. Мне пришлось извлечь всю информацию из https://www.iana.org/time-zones и сохранить эти сведения в базе данных SQLite с именем timezonedb.sqlite (прикреплена в конце этого сообщения).

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

#include "sqlite3.mqh"
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
class CTZInfo
  {
private:
   CSqlite3          sqlite;
   string            m_zone_name;
   int               utcoffset(datetime utc_time);

public:

                     CTZInfo(const string zone_name);
                    ~CTZInfo(void);
  };
//+------------------------------------------------------------------+
//|      Constructor - open timezone database                        |
//+------------------------------------------------------------------+
CTZInfo::CTZInfo(const string zone_name):
 m_zone_name(zone_name)
  {
   string db_name = "timezonedb.sqlite";

   if(!sqlite.connect(db_name, true))
     {
      Print("Failed to open timezone DB");
      return;
     }
  }

Ниже приведены некоторые методы, предоставляемые классом, реализованным на языке MQL5, примерно как в языке Python, с небольшими изменениями.

(a) utcoffset

Возвращает смещение местного времени относительно UTC в секундах. Если местное время находится в часовом поясе к западу от UTC, возвращаемое значение должно быть отрицательным; в противном случае оно будет положительным.

int CTZInfo::utcoffset()
  {
   string query =
      "SELECT gmt_offset FROM time_zone "
      "WHERE zone_name='" + m_zone_name + "' AND time_start <= " + (string)(int)TimeGMT() + " "
      "ORDER BY time_start DESC LIMIT 1;";

   vector row = sqlite.execute(query).fetchone();

   if(row.Size() == 0)
      return 0;

   return (int)row[0]; // seconds offset
  }

Пример ниже.

void OnStart()
  {
//---
   
   CTZInfo tzinfo("America/New_York");
   printf("Utc offset: %d hours",tzinfo.utcoffset()/3600); 
 }

Результаты.

2025.11.19 17:32:08.699 Time testing (XAUUSD,H1)        Utc offset: -5 hours

(b) dst

Возвращает поправку на летнее время (DST) в виде объекта timedelta или None, если информация о летнем времени не найдена.

int CTZInfo::dst()
  {
   string query =
      "SELECT dst FROM time_zone\n"
      "WHERE zone_name = '" + m_zone_name + "' "
      "AND time_start <= " + (string)(int)TimeGMT() + " "
      "ORDER BY time_start DESC\n"
      "LIMIT 1;";
//---

   vector row = sqlite.execute(query).fetchone();

   if(row.Size() == 0)    // no result
      return 0;

   return (int)row[0];
  }

(c) fromutc

Функция принимает дату и время в формате UTC и возвращает локализованные дату и время (сдвиг UTC на величину смещения часового пояса для данного момента).

datetime CTZInfo::fromutc(datetime utc_time)
  {
   int offset = utcoffset(utc_time);
   return (datetime)((int)utc_time + offset);
  }

Пример ниже.

void OnStart()
  {
//---
   
   CTZInfo tzinfo("America/New_York");
   Print("From utc: ",tzinfo.fromutc(TimeGMT())); //converts utc time into New york's local time 
 }

Результаты.

2025.11.19 17:26:24.400 Time testing (XAUUSD,H1)        From utc: 2025.11.19 09:26:24


Класс timedelta

В Python объект timedelta представляет собой длительность, разницу или сумму двух значений экземпляров datetime или date.

class datetime.timedelta(days=0, seconds=0, microseconds=0, milliseconds=0, minutes=0, hours=0, weeks=0)

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

Внутри системы хранятся только дни, секунды и микросекунды. Аргументы преобразуются в следующие единицы:

  • Миллисекунда преобразуется в 1000 микросекунд.
  • Минута преобразуется в 60 секунд.
  • Час преобразуется в 3600 секунд.
  • Неделя преобразуется в 7 дней.

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

  • 0 <= микросекунды < 1000000
  • 0 <= секунд < 3600*24 (количество секунд в сутках)
  • -999999999 <= дни <= 999999999

Ниже приведён эквивалент модуля timedelta из языка Python в формате языка MQL5 с небольшими изменениями.

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

class CTimedelta
  {
public:
                     CTimedelta(void) {};
                    ~CTimedelta(void) {};

   //+------------------------------------------------------------------+
   //|                                                                  |
   //|   For crafting the desired time given the number of days, hours  |
   //|   minutes, seconds, and weeks                                    |
   //|                                                                  |
   //+------------------------------------------------------------------+

   template <typename T>
   static T          timedelta(uint days = 0, uint hours = 0, uint minutes = 0, uint seconds = 0, uint weeks = 0)
     {
      uint delta_seconds = ((days+(weeks*7)) * 86400) + (hours * 3600) + (minutes * 60) + seconds;
      return delta_seconds;
     }

   template <typename T>
   static T          days(uint days_)
     {
      return timedelta<T>(days_);
     }

   template <typename T>
   static T          hours(uint hours_)
     {
      return timedelta<T>(0, hours_);
     }
     
   template <typename T>
   static T          minutes(uint minutes_)
     {
      return timedelta<T>(0, 0, minutes_);
     }
     
   template <typename T>
   static T          seconds(uint seconds_)
     {
      return timedelta<T>(0, 0, 0, seconds_);
     }
     
   template <typename T>
   static T          weeks(uint weeks_)
     {
      return timedelta<T>(0, 0, 0, 0, weeks_);
     }
  };

Чтобы сделать этот класс гибким, как в Python, мы используем имя (имена) типа (typename), чтобы гарантировать возможность возврата времени, созданного в секундах, с использованием переменных типа long, int, ulong, double и т. д., а также datetime.

Пример использования:

#include <PyMQL5\datetime.mqh>
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
//---

   Print("10 minutes datetime: ",CTimedelta::minutes<datetime>(10));
   Print("10 minutes seconds: ",CTimedelta::minutes<int>(10));
   
   datetime now = TimeLocal();
   printf("Current time: %s 10 minutes ahead: %s",(string)now, string(now + CTimedelta::minutes<datetime>(10)));
   printf("Current time: %s 1 week, 2 days, 10 hours, and 5 minutes ahead: %s",string(now), string(now + CTimedelta::timedelta<datetime>(2,10,5,0,1)));
 }

Результаты.

CI      0       17:39:37.748    Time testing (XAUUSD,H1)        10 minutes datetime: 1970.01.01 00:10:00
CL      0       17:39:37.748    Time testing (XAUUSD,H1)        10 minutes seconds: 600
HL      0       17:39:37.748    Time testing (XAUUSD,H1)        Current time: 2025.11.20 17:39:37 10 minutes ahead: 2025.11.20 17:49:37
HI      0       17:39:37.748    Time testing (XAUUSD,H1)        Current time: 2025.11.20 17:39:37 1 week, 2 days, 10 hours, and 5 minutes ahead: 2025.11.30 03:44:37

Этот простой класс важен для операций сложения и вычитания времени, которые могут представлять некоторую сложность в оригинальном MQL5.


Объект класса date

Объект date представляет собой дату (год, месяц и день) в идеализированном календаре, при этом текущий григорианский календарь бесконечно расширен в обоих направлениях. Рассматривайте этот класс как пользовательскую переменную (объект) типа date.

В языке Python функция с именем date возвращает объект, содержащий всю необходимую информацию о дате в заданный момент времени.

Начнем со способа вернуть сегодняшнюю дату.

CDate CDate::today() { m_datetime = TimeLocal(); return CDate(); }

Пример ниже.

#include <PyMQL5\datetime.mqh>

void OnStart()
  {
//---
   CDate date;
   
   Print("Today's date: ",date.today().__str__());
  }

Результаты.

2025.11.20 18:14:46.217 Time testing (XAUUSD,H1)        Today's date: 2025-11-20

Ниже приведена таблица, содержащая все необходимые функции в классе с именем CDate.

Функция Описание
CDate::CDate(uint year, uint month, uint day)
  {      
   if(year < MINYEAR || year > MAXYEAR)
      Print("ValueError: year out of range");

   if(month < 1 || month > 12)
      Print("ValueError: month out of range");

   if(day < 1 || (int)day > DaysInMonth(year, month))
      Print("ValueError: day out of range");

   m_year  = year;
   m_month = month;
   m_day   = day;
  }
Конструктор, который получает год, месяц и день для создания пользовательской даты.
CDate(void); 
Конструктор класса по умолчанию, который при вызове устанавливает сегодняшнюю дату в значение класса.
CDate::CDate(const datetime time)
  {
   MqlDateTime t;
   TimeToStruct(time, t);
   
   m_year = t.year;
   m_month = t.mon;
   m_day = t.day;
  }
Пользовательский конструктор, извлекающий дату из переменной datetime, например: 18.10.2025 00:00.
CDate fromtimestamp(datetime ts);
Конвертирует метку времени UNIX в соответствующую местную дату.
CDate CDate::fromisoformat(string s)
Возвращает дату, соответствующую заданной строке date_string в любом допустимом формате ISO 8601. В отличие от функции, присутствующей в модуле (модулях) Python с именем datetime, наша функция в MQL5 в настоящее время поддерживает только два формата ISO, используя эти формулы.
  1. "ГГГГ-ММ-ДД" (10 символов с дефисами)
  2. "ГГГГММДД" (8 цифр)
CDate             fromordinal(int ordinal);
Преобразует порядковую дату в объект CDate. 
const int         weekday();
Возвращает день недели в виде целого числа, где понедельник равен 0, а воскресенье — 6, аналогично функции MqlDateTime.day.
const int         isoweekday();
Возвращает день недели в виде целого числа, где понедельник равен 1, а воскресенье — 7.
static int        DaysInMonth(int year, int month);
Возвращает количество дней в заданном месяце.
static bool       IsLeapYear(int year); 
Возвращает true, если указанный год является високосным, в противном случае — false. 
CDate             replace(int year=-1, int month=-1, int day=-1) const
Заменяет одно или несколько значений заданного объекта date новыми значениями. 

Пример использования:

void OnStart()
  {
//---

   CDate date = py_datetime.date(D'29.02.2024');
     
   Print("date: ", date.isoformat());               
   Print("Weekday: ", date.weekday());            
   Print("ISO Weekday: ", date.isoweekday());     
   Print("Ordinal: ", date.toordinal());          
   Print("Leap year 2024? ", date.IsLeapYear(2024));
   Print("__str__: ",date.__str__());
   
   CDate d2 = py_datetime.date().today();
   Print("Today: ", d2.isoformat());            
   Print("From ISO: ", d2.isoformat());         

   d2 = d2.replace(-1, -1, 30);
   Print("Replaced: ", d2.isoformat());   
      
//--- from timestamps

   CDate date3 = date.fromtimestamp(1672531199);
   Print("Date From timestamps: ",date3.isoformat());
   
   datetime time = py_datetime.fromtimestamp(1672531199);
   Print("time timestamps: ",time);

//---

   Print(date_m.fromisoformat("2019-12-04").__str__());
   Print(date_m.fromisoformat("20191204").__str__());
   
//---
   
   CDate today = date.today();
   Print(today.ctime());
  }

Результаты.

OR      0       19:10:29.522    Time testing (XAUUSD,H1)        date: 2024-02-29
CD      0       19:10:29.522    Time testing (XAUUSD,H1)        Weekday: 3
OQ      0       19:10:29.522    Time testing (XAUUSD,H1)        ISO Weekday: 4
CD      0       19:10:29.522    Time testing (XAUUSD,H1)        Ordinal: 738945
RD      0       19:10:29.522    Time testing (XAUUSD,H1)        Leap year 2024? true
HQ      0       19:10:29.522    Time testing (XAUUSD,H1)        __str__: 2024-02-29
LJ      0       19:10:29.522    Time testing (XAUUSD,H1)        Today: 2025-11-17
LR      0       19:10:29.522    Time testing (XAUUSD,H1)        From ISO: 2025-11-17
DD      0       19:10:29.522    Time testing (XAUUSD,H1)        Replaced: 2025-11-30
DO      0       19:10:29.522    Time testing (XAUUSD,H1)        Date From timestamps: 2023-01-01
EP      0       19:10:29.522    Time testing (XAUUSD,H1)        time timestamps: 2023.01.01 02:59:59
QK      0       19:10:29.522    Time testing (XAUUSD,H1)        2019-12-04
OR      0       19:10:29.522    Time testing (XAUUSD,H1)        2019-12-04
CH      0       19:10:29.523    Time testing (XAUUSD,H1)        Sun Nov 17 19:10:29 2025


Модуль datetime

Модуль datetime предоставляет классы для работы с датами и временем. Этот класс (модуль) объединяет два предыдущих модуля (date и time) с несколькими новыми внедренными методами.

Начнем с конструкторов классов. Одни используют переменную типа datetime, а другие — переменные для создания даты и точного времени этой даты.

class CDatetime
  {
protected:

   CTZInfo           *m_tzinfo;
   MqlDateTime       m_datetime_struct;
   int               weekday(const datetime time);

public:
                     CDatetime();
                     CDatetime(const datetime dt, CTZInfo *tzinfo=NULL);
                     CDatetime(uint year, uint month, uint day, uint hour, uint minutes, uint seconds, CTZInfo *tzinfo=NULL);

                    ~CDatetime(void);

   //--- custom constructor

   CDatetime         datetime_(uint year, uint month, uint day, uint hour, uint minutes, uint seconds, CTZInfo *tzinfo=NULL);
   CDatetime         combine(CDate &date, CTime &time, CTZInfo *tzinfo=NULL)

Метод с именем combine объединяет объекты date и time для создания объекта datetime.

(a) функция now

Эта функция возвращает текущий объект CDatetime в зависимости от указанного часового пояса.

CDatetime CDatetime::now(CTZInfo *tzinfo)
  {
   return CDatetime(tzinfo.now(), tzinfo);
  }

Пример ниже.

#include <PyMQL5\datetime.mqh>
CDatetime py_datetime;
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
//--- Datetime module testing
   
   CTZInfo tzinfo("Africa/Nairobi");
   
   CDatetime now = py_datetime.now(&tzinfo);
   Print("ctime: ", now.ctime());
  }

Результаты.

2025.11.21 20:10:03.116 Time testing (XAUUSD,H1)        ctime: Fri Nov 21 20:10:03 2025

(b) fromisoformat

Возвращает объект datetime, соответствующий объекту date_string в любом допустимом формате ISO 8601, за исключением следующих случаев.

  • Смещение часовых поясов может составлять доли секунды.
  • Разделитель T можно заменить любым символом Unicode.
  • Дробные части часов и минут не поддерживаются.
  • В настоящее время даты с пониженной точностью (ГГГГ-ММ, ГГГГ) не поддерживаются.
  • Расширенные представления дат в настоящее время не поддерживаются (±ГГГГГ-ММ-ДД).
  • В настоящее время порядковые даты не поддерживаются (ГГГГ-ППП).

CDatetime CDatetime::fromisoformat(string iso)
  {
   string orig = iso;

//--- Split date & time
   int sep = StringFind(iso, "T");
   if(sep == -1)
      sep = StringFind(iso, " ");

   if(sep == -1)
     {
      Print("Invalid ISO datetime: ", orig);
      return CDatetime();
     }

   string date_part = StringSubstr(iso, 0, sep);
   string time_part = StringSubstr(iso, sep + 1);

//--- Parse date YYYY-MM-DD

   string dparts[];
   if(StringSplit(date_part, '-', dparts) != 3)
     {
      Print("Invalid ISO date: ", orig);
      return CDatetime();
     }

   int year = (int)StringToInteger(dparts[0]);
   int mon  = (int)StringToInteger(dparts[1]);
   int day  = (int)StringToInteger(dparts[2]);

//--- Extract timezone part

   string tz = "";
   int tz_pos = StringFind(time_part, "+");
   if(tz_pos == -1)
      tz_pos = StringFind(time_part, "-");
   if(tz_pos == -1)
      tz_pos = StringFind(time_part, "Z");

   if(tz_pos != -1)
     {
      tz = StringSubstr(time_part, tz_pos);
      time_part = StringSubstr(time_part, 0, tz_pos);
     }

//--- Parse time HH:MM:SS(.fff)

   int hour=0, minute=0, second=0, millisecond=0;

   string tparts[];
   int n = StringSplit(time_part, ':', tparts);

   if(n < 2)
     {
      Print("Invalid ISO time: ", orig);
      return CDatetime();
     }

   hour   = (int)StringToInteger(tparts[0]);
   minute = (int)StringToInteger(tparts[1]);

   if(n >= 3)
     {
      int dot = StringFind(tparts[2], ".");
      if(dot != -1)
        {
         second = (int)StringToInteger(StringSubstr(tparts[2], 0, dot));
         string frac = StringSubstr(tparts[2], dot + 1);
         millisecond = (int)(StringToInteger(frac) / MathPow(10, StringLen(frac) - 3));
        }
      else
        {
         second = (int)StringToInteger(tparts[2]);
        }
     }

//--- Timezone if provided

   CTZInfo *tzinfo = NULL;
   if(tz == "Z")
      tzinfo = new CTZInfo("UTC");
   else
      if(StringLen(tz) > 0)
        {
         string id = "UTC" + tz;   // Example: "UTC+03:00"
         tzinfo = new CTZInfo(id);
        }

   return CDatetime(year, mon, day, hour, minute, second, tzinfo);
  }

Функция с похожим именем есть в классах CDate и CTime, но эта функция заходит дальше, поскольку учитывает и дату, и время в одной отформатированной строке.

Пример ниже.

   CDatetime time = py_datetime.fromisoformat("2011-11-04T00:05:23Z");
   
   Print("datetime: ",time.__str__());

Результаты.

2025.11.22 12:47:18.047 Time testing (XAUUSD,H1)        datetime: 2011.11.04 00:05:23

(c) isoformat

Преобразует информацию datetime, хранящуюся в объекте CDatetime, в строковый формат времени в соответствии со стандартом ISO 8601.

string CDatetime::isoformat(string sep="T", string timespec="auto")
  {
   datetime dt = this.__datetime__();
   MqlDateTime s;
   TimeToStruct(dt, s);

   string hh = StringFormat("%02d", s.hour);
   string mm = StringFormat("%02d", s.min);
   string ss = StringFormat("%02d", s.sec);

   string time_str;

   if(timespec == "hours")
      time_str = hh;
   else
      if(timespec == "minutes")
         time_str = hh + ":" + mm;
      else
         if(timespec == "seconds")
            time_str = hh + ":" + mm + ":" + ss;
         else
            if(timespec == "milliseconds")
               time_str = hh + ":" + mm + ":" + ss;
            else
               if(timespec == "auto")
                  time_str = hh + ":" + mm + ":" + ss;
               else
                  time_str = hh + ":" + mm + ":" + ss;

   string tz = _tz_offset_str();

   return StringFormat("%04d-%02d-%02d", s.year, s.mon, s.day) + sep + time_str + tz;
  }

Пример использования.

   CDatetime time = py_datetime.fromisoformat("2011-11-04T00:05:23Z");
   
   Print("Iso format: ", time.isoformat());

Результаты.

2025.11.22 12:47:18.047 Time testing (XAUUSD,H1)        Iso format: 2011-11-04T00:05:23

(d) strftime

Преобразует сохраненные данные datetime в классе в желаемый строковый формат, обычно для демонстрационных целей.

Пример ниже.

   CDatetime now = py_datetime.now(&tzinfo);
   string formatted_time = now.strftime("%Y/%M/%d %H:%M:%S");
   
   Print("formatted time: ", formatted_time);

Результаты.

2025.11.22 19:55:55.680 Time testing (XAUUSD,H1)        formatted time: 2025/55/22 19:55:55

(e) strptime

Этот метод действует в противоположность методу strftime: он преобразует отформатированное время из строкового формата ISO 8601 в объект datetime, хранящийся в классе CDatetime.

Пример ниже.

   CDatetime now = py_datetime.now(&tzinfo);
   string format = "%Y/%M/%d %H:%M:%S";
   
   string formatted_time = now.strftime(format);
   
   Print("formatted time: ", formatted_time);
   Print("Original time: ", now.strptime(formatted_time, format).__datetime__());

Результаты.

OL      0       20:00:47.220    Time testing (XAUUSD,H1)        formatted time: 2025/00/22 20:00:47
FF      0       20:00:47.220    Time testing (XAUUSD,H1)        Original time: 2025.11.22 20:00:47


Заключительные мысли

Воссоздание утилит для работы с датами, временем и календарем из модулей Python на языке MQL5 — это не просто переписывание кода; это заполнение реального пробела в экосистеме MQL5. Внедрение таких классов, как CTimedelta, CTime и CDatetime, открывает нам доступ к выразительным инструментам высокоуровневой обработки времени, которые по умолчанию отсутствуют в MetaTrader 5.

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

Репозиторий, содержащий весь код, рассмотренный в этой серии статей, можно найти здесь: https://github.com/MegaJoctan/PyMQL5 за дополнения и исправление ошибок.

С наилучшими пожеланиями.


Таблица вложений

Имя файла Описание и использование
Include\errordescription.mqh  Содержит функции для преобразования кодов ошибок, генерируемых MetaTrader 5 и MQL5, в удобочитаемый формат. 
Include\PyMQL5\datetime.mqh Содержит классы CDate и CDateTime для обработки дат и времени.
Include\PyMQL5\SQLite3.mqh Модуль похож на sqlite3 в Python, содержащий функции для чтения баз данных SQLITE в MetaTrader 5.
Include\time.mqh Содержит класс CTime для работы со временем.
Include\TZInfo.mqh В ней есть класс CTZInfo для чтения информации о часовых поясах из универсальной базы данных time.
Common\Files\timezonedb.sqlite База данных SQLite3, содержащая информацию обо всех часовых поясах, включая смещение UTC, названия часовых поясов и т. д.
Scripts\Time testing.mq5 Площадка для тестирования всех методов и функций, рассмотренных в этой статье. 

Перевод с английского произведен MetaQuotes Ltd.
Оригинальная статья: https://www.mql5.com/en/articles/19035

Прикрепленные файлы |
Attachments.zip (1281.22 KB)
Статистический арбитраж на основе коинтегрированных акций (Часть 1): Tесты Энгла — Грейнджера и Йохансена на коинтеграцию Статистический арбитраж на основе коинтегрированных акций (Часть 1): Tесты Энгла — Грейнджера и Йохансена на коинтеграцию
Эта статья призвана стать понятным и дружелюбным введением для трейдеров в наиболее распространенные тесты на коинтеграцию, а также простым руководством по интерпретации их результатов. Тесты Энгла — Грейнджера и Йохансена позволяют выявлять статистически значимые пары или группы активов, обладающие общей долгосрочной динамикой. Тест Йохансена особенно полезен для портфелей из трех и более активов, так как он рассчитывает силу коинтеграционных векторов для всех инструментов одновременно.
Торговые инструменты на MQL5 (Часть 15): Эффекты размытия canvas, рендеринг теней и плавная прокрутка колесом мыши Торговые инструменты на MQL5 (Часть 15): Эффекты размытия canvas, рендеринг теней и плавная прокрутка колесом мыши
В этой статье мы выполняем улучшение панели холста на MQL5 с помощью новейших визуальных эффектов, включая градиенты размытия для эффекта наложения тумана, рендеринг теней для заголовков и рисование со сглаживанием для получения более плавных линий и кривых. Мы добавим плавную прокрутку колесом мыши на текстовой панели, которая не влияет на масштаб графика, что технически является улучшением.
Нейросети в трейдинге: Многодоменная архитектура анализа финансовых данных (Окончание) Нейросети в трейдинге: Многодоменная архитектура анализа финансовых данных (Окончание)
В статье завершается построение фреймворка MDL и его интеграция в среду MQL5/OpenCL. Реализован объект верхнего уровня, объединяющий признаки, сценарии и задачи в единый вычислительный процесс. Проведено тестирование на исторических данных, показавшее устойчивую работу модели и её способность адаптироваться к изменяющимся рыночным условиям.
Эволюционный отбор LLM-агентов в MetaTrader 5 Эволюционный отбор LLM-агентов в MetaTrader 5
Статья описывает архитектуру торговой системы из 20 LLM-агентов на базе Grok (xAI), каждый из которых несёт уникальную торговую философию — от чистого моментума до статистического z-score. Система применяет генетический алгоритм прямо в ходе торговли: каждые 20 сделок автоматически убивает слабых агентов, клонирует сильных с мутацией промпта и публикует лидерборд на графике MetaTrader 5 — без остановки торговли и без единого SDK.