English
preview
Implementierung von praktischen Modulen aus anderen Sprachen in MQL5 (Teil 04): Zeit-, Datums- und Datetime-Module aus Python

Implementierung von praktischen Modulen aus anderen Sprachen in MQL5 (Teil 04): Zeit-, Datums- und Datetime-Module aus Python

MetaTrader 5Handelssysteme |
17 0
Omega J Msigwa
Omega J Msigwa

Inhalt


Einführung

Zeit ist eine grundlegende Einheit in unserem Leben; alles, was in diesem Universum existiert, wird in Zeit gemessen. In unserem täglichen Leben messen wir unsere Ziele und die Zeit, die wir zum Erreichen dieser Ziele benötigen, und wir stellen sogar enge Zeitpläne auf, um Aufgaben mit Zeiterfassungsgeräten wie Smartphones und Uhren zu messen (zu überwachen).

Im Bereich der Algorithmen und des Handels im Allgemeinen ist die Zeit immer noch eine entscheidende Größe. Wir treffen oft zeitbasierte Handelsentscheidungen und bewerten unsere Erfolge in der Regel auf täglicher, monatlicher und wöchentlicher Basis.

Die Programmiersprache MQL5 verfügt über zahlreiche integrierte Funktionen zur Verwaltung und Auswertung der Zeit, die sicherstellen, dass unsere algorithmischen Handelssysteme die Zeit in der realen Welt kennen und berücksichtigen. Unsere bevorzugte Programmiersprache bietet jedoch eine sehr grundlegende (einfache) und manchmal nicht leicht lesbare Art, mit Zeit, Datum usw. zu arbeiten, im Vergleich zu anderen Sprachen wie Python, die reichhaltige Module für diese Aufgabe bieten, wie datetime, calendar, time, zoneinfo usw.

Quelle: pexels.com

In diesem Artikel werden wir ähnliche Module, wie sie in Python angeboten werden, für die Zeitverarbeitung in der Programmiersprache MQL5 implementieren.


Die Klasse Time (Modul)

Wir beginnen mit der Klasse der Zeit, die die grundlegenden Funktionen (Methoden) für die Arbeit mit der Zeit in der Programmiersprache Python enthält.

Laut einer Python-Dokumentation:

Ein Zeitobjekt stellt eine (lokale) Tageszeit dar, die unabhängig von einem bestimmten Tag ist und über ein tzinfo-Objekt angepasst werden kann.

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

Alle Argumente sind optional. tzinfo kann None oder eine Instanz einer tzinfo-Unterklasse sein. Die übrigen Argumente müssen ganze Zahlen in den folgenden Bereichen sein:

  • 0 <= Stunde < 24,
  • 0 <= Minute < 60,
  • 0 <= Sekunde < 60,
  • 0 <= Mikrosekunde < 1000000

Wenn ein Argument außerhalb dieser Bereiche angegeben wird, wird ValueError ausgelöst. Alle sind standardmäßig auf 0 gesetzt, außer tzinfo, das standardmäßig auf None gesetzt ist.

Python bietet Klassen wie datetime und date mit ähnlichen Methoden für die Arbeit mit der Zeit. Die Klasse CTime aus einem Modul namens CTime ist jedoch nur für die Arbeit mit der Zeit zuständig (ohne das Datum zu kennen). So wenn Sie nur eine bestimmte Tageszeit wissen möchten.

Innerhalb einer Klasse mit dem Namen CTime müssen wir die Überprüfung der Werte handhaben, um sicherzustellen, dass der Nutzer im Klassenkonstruktor die richtigen Werte für die Erstellung eines Zeitobjekts angegeben hat, ähnlich wie es in Python geschieht.

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;
  }

Wenn ein Klassenkonstruktor mit optionalen Parametern aufgerufen wird, füllt er die Variablen innerhalb der Klasse auf und erzeugt so ein CTime-Objekt.

Eine optionale Variable namens tzinfo speichert die Informationen über eine bestimmte Zeitzone, zu der ein Zeitobjekt gehört.

Im Folgenden werden einige Methoden der Klasse CTime beschrieben.

(a): fromisoformat

Diese Funktion gibt ein CTime-Objekt zurück, das der Variablen time_string in einem beliebigen gültigen ISO 8601-Format entspricht, mit den folgenden Ausnahmen:

  • Zeitzonenverschiebungen können Bruchteile von Sekunden haben.
  • Das führende T, das normalerweise in Fällen erforderlich ist, in denen ein Datum und eine Uhrzeit nicht eindeutig zuzuordnen sind, ist nicht erforderlich.
  • Bruchteile von Sekunden können eine beliebige Anzahl von Nachkommastellen haben (alles über 6 wird abgeschnitten).
  • Bruchteile von Stunden und Minuten werden nicht unterstützt.

CTime CTime::fromisoformat(string time_string)

Beispiel für die Verwendung:

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__());
 }

Ausgabe:

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

Die Methoden __str__() und __datetime__() konvertieren die im CTime-Objekt gespeicherte Zeit in ein String- bzw. Datetime-Format (Variablen).

(b): strptime

Diese Funktion wandelt eine gegebene Zeit in einem String-Format in eine entsprechende Datetime-Variable um.

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

Beispiel für die Verwendung:

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"));
 }

Ausgabe:

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

Diese Funktion gibt ein neues CTime-Objekt mit denselben Werten zurück, wobei die angegebenen Parameter aktualisiert wurden. Beachten Sie, dass tzinfo=NULL angegeben werden kann, um eine naive Zeit aus einer bekannten Zeit zu erstellen, ohne die Zeitdaten zu konvertieren.

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);
  }

Beispiel für die Verwendung:

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__());
 }

Ausgabe:

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

Diese Funktion gibt eine Zeichenkette zurück, die die Zeit im ISO 8601-Format darstellt, eine wie:

  • HH:MM:SS.ffffff, wenn die Mikrosekunde nicht 0 ist
  • HH:MM:SS, wenn die Mikrosekunde 0 ist
  • HH:MM:SS.ffffff+HH:MM[:SS[.ffffff]], wenn utcoffset() nicht None zurückgibt
  • HH:MM:SS+HH:MM[:SS[.ffffff]], wenn die Mikrosekunde 0 ist und utcoffset() nicht None zurückgibt

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;
  }

Das optionale Argument timespec gibt die Anzahl der zusätzlichen Komponenten der Zeit an, die einbezogen werden sollen (die Vorgabe ist 'auto'). Es kann einer der folgenden Punkte sein:

  • 'auto': Wie 'auto' wenn Mikrosekunden 0 ist, sonst wie 'microseconds'.
  • 'hours': Geben Sie die Stunde im zweistelligen HH-Format an.
  • 'minutes': Geben Sie Stunde und Minute im Format HH:MM an.
  • 'seconds': Geben Sie Stunde, Minute und Sekunde im Format HH:MM:SS an.
  • 'milliseconds': Geben Sie die volle Zeit an, aber kürzen Sie die Sekundenbruchteile auf Millisekunden ab. HH:MM:SS.sss-Format.
  • 'microseconds': Geben Sie die Vollzeit im Format HH:MM:SS.ffffff an.

Beispiel für die Verwendung:

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

Gibt eine Zeichenkette zurück, die die Zeit darstellt, gesteuert durch eine explizite Formatzeichenkette.

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

Wenn tzinfo auf NULL gesetzt ist (Standardeinstellung), gibt diese Funktion INT_MAX zurück; andernfalls gibt sie die Abweichung einer bestimmten Zeitzone von der UTC/GMT-Zeit in Sekunden zurück.

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

   return m_tzinfo.utcoffset();
  }

Beispiel.

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
 }

Die Klasse CTZInfo wird im nächsten Abschnitt behandelt.

Ausgabe:

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

Die Zeit der Vereinigten Staaten (New York) ist GMT-5 (5 Stunden hinter UTC).

(f): dst

Gibt INT_MAX zurück, wenn tzinfo auf NULL gesetzt ist (Standardwert); andernfalls wird die Sommerzeit zurückgegeben.

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

   return m_tzinfo.dst();
 }


Klasse Zeitzoneninformationen (tzinfo)

In MQL5 haben wir keine integrierte Möglichkeit, auf Informationen aus verschiedenen Zeitzonen zuzugreifen. Dies macht es für unsere Programme zu einer Herausforderung, sich den verschiedenen Zeiten in der Welt bewusst zu sein und relevant zu bleiben.

Python bietet eine integrierte Klasse namens tzinfo, die den Zugriff auf die Zeit aus allen Regionen der Welt ermöglicht.

Gemäß der Dokumentation in Python.

Die Klasse datetime.tzinfo

Dies ist eine abstrakte Basisklasse, was bedeutet, dass diese Klasse nicht direkt instanziiert werden sollte. Definieren Sie eine Unterklasse von tzinfo, um Informationen über eine bestimmte Zeitzone zu erfassen.

Eine Instanz von (einer konkreten Unterklasse von) tzinfo kann an die Konstruktoren für datetime- und time-Objekte übergeben werden. Die letztgenannten Objekte sehen ihre Attribute als in lokaler Zeit, und das tzinfo-Objekt unterstützt Methoden, die den Offset der lokalen Zeit von UTC, den Namen der Zeitzone und den DST-Offset, alle relativ zu einem Datum oder Zeitobjekt, das ihnen übergeben wird, anzeigen.

Sie müssen eine konkrete Unterklasse ableiten und (zumindest) Implementierungen der Standard-Tzinfo-Methoden bereitstellen, die von den von Ihnen verwendeten Datetime-Methoden benötigt werden. Das datetime-Modul bietet time zone, eine einfache konkrete Unterklasse von tzinfo, die Zeitzonen mit festem Offset von UTC wie UTC selbst oder nordamerikanische EST und EDT darstellen kann.

Eine konkrete Unterklasse von tzinfo muss möglicherweise die folgenden Methoden implementieren. Welche Methoden genau benötigt werden, hängt von der Verwendung der bewussten datetime-Objekte ab. Im Zweifelsfall sollten Sie einfach alle umsetzen.

Ähnlich wie die Klasse in der Programmiersprache Python soll die implementierte Klasse CTZInfo in MQL5 harmonisch mit den Klassen CDatetime und CTime zusammenarbeiten und diese beiden Klassen bei der Beschaffung von Informationen über Zeitzonen unterstützen.

Auch wenn Sie die Klasse außerhalb anderer zeitbasierter Klassen aufrufen können, sollten Sie sie nicht für den Zugriff auf ihre Werte, wie die aktuelle Zeit usw., verwenden.

Damit diese Klasse (Modul) funktioniert, brauchen wir eine einheitliche Datenbank mit allen Zeitzoneninformationen. Ich musste alle Informationen aus https://www.iana.org/time-zones extrahieren und in einer Sqlite-Datenbank namens timezonedb.sqlite speichern (am Ende dieses Beitrags angehängt).

Im Klassenkonstruktor wird die obige Datenbank gelesen und eine Verbindung in einer Variablen für die spätere Verwendung innerhalb der Klasse offen gehalten.

#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;
     }
  }

Nachfolgend finden Sie einige der in der Klasse bereitgestellten Methoden, die in MQL5 wie in Python implementiert sind, mit leichten Änderungen.

(a): utcoffset

Rückgabe der Abweichung der Ortszeit von UTC in Sekunden. Wenn die Ortszeit westlich von UTC liegt, sollte der zurückgegebene Wert negativ sein; andernfalls ist er positiv.

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
  }

Beispiel.

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

Ausgabe:

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

(b): dst

Rückgabe der Sommerzeitanpassung in Form eines Timedelta-Objekts oder None, wenn keine Sommerzeitinformationen gefunden werden.

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

Sie nimmt eine UTC-Datumszeit und gibt die lokalisierte Datumszeit zurück (UTC verschoben um den Zeitzonen-Offset für diesen Zeitpunkt).

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

Beispiel.

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

Ausgabe:

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


Die Klasse Timedelta

In Python stellt das Objekt timedelta eine Dauer, die Differenz oder Summe zwischen zwei datetime- oder date-Instanzen dar.

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

Alle Argumente sind optional und stehen standardmäßig auf 0. Die Argumente können ganze Zahlen oder Gleitkommazahlen sein, die positiv oder negativ sein können.

Intern werden nur Tage, Sekunden und Mikrosekunden gespeichert. Argumente werden in diese Einheiten umgerechnet:

  • Eine Millisekunde wird in 1000 Mikrosekunden umgerechnet.
  • Eine Minute wird in 60 Sekunden umgerechnet.
  • Eine Stunde wird in 3600 Sekunden umgerechnet.
  • Eine Woche wird in 7 Tage umgerechnet.

und Tage, Sekunden und Mikrosekunden werden dann so normalisiert, dass die Darstellung eindeutig ist, mit

  • 0 <= Mikrosekunden < 1000000
  • 0 <= Sekunden < 3600*24 (die Anzahl der Sekunden eines Tages)
  • -999999999 <= Tage <= 999999999

Nachfolgend finden Sie das MQL5-Äquivalent des Moduls Timedelta aus Python, mit leichten Änderungen.

Keine Millisekunden und Mikrosekunden in den Funktionsargumenten, da diese beiden nicht in der datetime-Variable gespeichert (verfolgt) werden können.

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_);
     }
  };

Um diese Klasse wie in Python flexibel zu gestalten, verwenden wir den/die Typenamen, um sicherzustellen, dass wir die Möglichkeit haben, die in Sekunden erstellte Zeit mit Hilfe von Variablen wie (long, int, ulong, double, usw.) und datetime zurückzugeben.

Beispiel für die Verwendung:

#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)));
 }

Ausgabe:

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

Diese einfache Klasse ist wichtig für zeitliche Additions- und Subtraktionsoperationen, die in nativem MQL5 ein wenig schwierig sein können.


Das Objekt der Klasse Datum

Ein Datumsobjekt stellt ein Datum (Jahr, Monat und Tag) dar in einem idealisierten Kalender, dem aktuellen gregorianischen Kalender, der in beide Richtungen unbegrenzt erweitert ist. Betrachten wir diese Klasse als eine nutzerdefinierte Variable (Objekt) „date“.

Von datetime in Python gibt die Funktion „date“ ein Objekt zurück, das alle notwendigen Informationen über das Datum einer bestimmten Zeit enthält.

Beginnend mit einer Möglichkeit, das heutige Datum zurückzugeben.

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

Beispiel.

#include <PyMQL5\datetime.mqh>

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

Ausgabe:

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

Nachfolgend finden Sie eine Tabelle mit allen notwendigen Funktionen innerhalb der Klasse CDate.

Funktion Beschreibung
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;
  }
Ein Konstruktor, dem Jahr, Monat und Tag übergeben wird, um ein nutzerdefiniertes Datum zu erstellen.
CDate(void); 
Standardklassenkonstruktor, der beim Aufruf das heutige Datum in die Klasse setzt.
CDate::CDate(const datetime time)
  {
   MqlDateTime t;
   TimeToStruct(time, t);
   
   m_year = t.year;
   m_month = t.mon;
   m_day = t.day;
  }
Ein nutzerdefinierter Konstruktor, der das Datum aus einer Datetime-Variablen extrahiert, z. B. 18.10.2025 00:00
CDate fromtimestamp(datetime ts);
Konvertiert UNIX-Zeitstempel in ein entsprechendes lokales Datum.
CDate CDate::fromisoformat(string s)
Gibt ein Datum zurück, das einem date_string in einem beliebigen gültigen ISO 8601-Format entspricht. Im Gegensatz zu den Funktionen in den Python datetime-Modulen unterstützt unsere MQL5-Funktion derzeit nur zwei Iso-Formate, die diese Formeln verwenden.
  1. „JJJJ-MM-TT“ (10 Zeichen mit Bindestrichen)
  2. „JJJJMMTT“ (8 Ziffern)
CDate             fromordinal(int ordinal);
Konvertiert ein ordinales Datum in ein CDate-Objekt. 
const int         weekday();
Gibt den Wochentag als Ganzzahl zurück, wobei Montag 0 und Sonntag 6 ist, ähnlich wie bei MqlDateTime.day.
const int         isoweekday();
Gibt den Wochentag als Ganzzahl zurück, wobei Montag gleich 1 und Sonntag gleich 7 ist.
static int        DaysInMonth(int year, int month);
Gibt die Anzahl der Tage in einem bestimmten Monat zurück.
static bool       IsLeapYear(int year); 
Sie gibt true zurück, wenn ein bestimmtes Jahr ein Schaltjahr ist, andernfalls gibt sie false zurück. 
CDate             replace(int year=-1, int month=-1, int day=-1) const
Es ersetzt einen oder mehrere Werte eines gegebenen Datumsobjekts durch einen oder mehrere neue Wert(e). 

Beispiel für die Verwendung:

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());
  }

Ausgabe:

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


Das datetime-Modul

Das datetime-Modul liefert Klassen zur Bearbeitung von Datums- und Zeitangaben. Diese Klasse (Modul) kombiniert die beiden vorangegangenen Module (Datum und Uhrzeit) mit einigen neu eingeführten Methoden.

Beginnend mit den Klassenkonstruktoren. Einige nehmen die datetime-Variable, andere nehmen Variablen für die Erstellung des Datums und eine bestimmte Uhrzeit dieses Datums.

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)

Die Methode combine kombiniert sowohl Datums- als auch Zeitobjekte, um ein datetime-Objekt zu erstellen.

(a): Die Funktion now

Diese Funktion gibt das aktuelle CDatetime-Objekt in Abhängigkeit von einer bestimmten Zeitzone zurück.

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

Beispiel.

#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());
  }

Ausgabe:

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

(b): fromisoformat

Gibt eine Datumszeit zurück, die einem date_string in einem beliebigen gültigen ISO 8601-Format entspricht, mit den folgenden Ausnahmen.

  • Zeitzonenverschiebungen können Bruchteile von Sekunden haben.
  • Das Trennzeichen T kann durch ein beliebiges Unicode-Zeichen ersetzt werden.
  • Bruchteile von Stunden und Minuten werden nicht unterstützt.
  • Daten mit reduzierter Genauigkeit werden derzeit nicht unterstützt (JJJJ-MM, JJJJ).
  • Erweiterte Datumsdarstellungen werden derzeit nicht unterstützt (±YYYYYY-MM-DD).
  • Ordinale Daten werden derzeit nicht unterstützt (JJJJ-OOO).

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);
  }

Es gibt eine Funktion mit einem ähnlichen Namen in CDate und CTime, aber diese geht noch weiter, da sie sowohl Datum als auch Uhrzeit in einer einzigen formatierten Zeichenkette berücksichtigt.

Beispiel.

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

Ausgabe:

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

(c): isoformat

Konvertiert die im CDatetime-Objekt gespeicherten „datetime“-Informationen in eine als String formatierte Zeit im ISO 8601-Format.

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;
  }

Beispiel für die Verwendung:

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

Ausgabe:

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

(d): strftime

Formatiert eine in der Klasse gespeicherte Datumsangabe in ein gewünschtes String-Format, in der Regel zu Demonstrationszwecken.

Beispiel.

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

Ausgabe:

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

(e): strptime

Diese Methode bewirkt das Gegenteil der Methode strftime, sie kehrt die formatierte Zeit von einem ISO 1806 String-Format in ein Datetime-Objekt um, das in der Klasse CDatetime gespeichert ist.

Beispiel.

   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__());

Ausgabe:

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


Abschließende Überlegungen

Die Neuerstellung von Pythons Modulen für Datum, Uhrzeit und Kalender in MQL5 ist mehr als eine Übung im Umschreiben von Code; sie schließt eine echte Lücke im MQL5-Ökosystem. Durch die Implementierung von Klassen wie CTimedelta, CTime und CDatetime erhalten wir Zugang zu ausdrucksstarken High-Level-Tools zur Zeitmanipulation, die in MetaTrader 5 nicht nativ vorhanden sind.

Diese Ergänzungen ermöglichen zuverlässige Zeitstempelkonvertierungen, den korrekten Umgang mit Zeitzonen und den Aufbau anspruchsvoller Backtesting- oder zeitgesteuerter Systeme.

Das Repository, das den gesamten in dieser Artikelserie besprochenen Code enthält, ist hier zu finden: https://github.com/MegaJoctan/PyMQL5 für Beiträge und Fehlerkorrekturen.

Mit freundlichen Grüßen.


Tabelle der Anhänge

Dateiname Beschreibung und Verwendung
Include\errordescription.mqh  Enthält Funktionen zur Umwandlung der von MetaTrader 5 und MQL5 erzeugten Fehlercodes in ein für den Menschen lesbares Format. 
Include\PyMQL5\datetime.mqh Enthält sowohl die CDate- als auch die CDateTime-Klassen zur Handhabung von Datum und Uhrzeit.
Include\PyMQL5\SQLite3.mqh Ein ähnliches Modul wie sqlite3 in Python, das Funktionen zum Lesen von SQLITE-Datenbanken in MetaTrader 5 bietet.
Include\time.mqh Enthält die Klasse CTime für die Arbeit mit der Zeit.
Include\TZInfo.mqh Sie verfügt über die Klasse CTZInfo zum Lesen der Zeitzoneninformationen aus einer Universalzeitdatenbank.
Common\Files\timezonedb.sqlite Eine SQLite3-Datenbank, die Informationen aus allen Zeitzonen enthält, einschließlich utcoffset, Zeitzonennamen usw.
Scripts\Time testing.mq5 Eine Spielwiese zum Testen aller in diesem Artikel behandelten Methoden und Funktionen. 

Übersetzt aus dem Englischen von MetaQuotes Ltd.
Originalartikel: https://www.mql5.com/en/articles/19035

Beigefügte Dateien |
Attachments.zip (1281.22 KB)
Die Übertragung der Trading-Signale in einem universalen Expert Advisor. Die Übertragung der Trading-Signale in einem universalen Expert Advisor.
In diesem Artikel wurden die verschiedenen Möglichkeiten beschrieben, um die Trading-Signale von einem Signalmodul des universalen EAs zum Steuermodul der Positionen und Orders zu übertragen. Es wurden die seriellen und parallelen Interfaces betrachtet.
Analytical Volume Profile Trading (AVPT): Liquiditätsarchitektur, Marktgedächtnis und algorithmische Ausführung Analytical Volume Profile Trading (AVPT): Liquiditätsarchitektur, Marktgedächtnis und algorithmische Ausführung
Analytical Volume Profile Trading (AVPT) untersucht, wie die Liquiditätsarchitektur und das Marktgedächtnis das Preisverhalten beeinflussen, und ermöglicht so einen tieferen Einblick in die institutionelle Positionierung und die volumengesteuerte Struktur. Durch die Zuordnung von POC, HVNs, LVNs und Value Areas können Händler Annahme-, Ablehnungs- und Ungleichgewichtszonen präzise identifizieren.
Eine alternative Log-datei mit der Verwendung der HTML und CSS Eine alternative Log-datei mit der Verwendung der HTML und CSS
In diesem Artikel werden wir eine sehr einfache, aber leistungsfähige Bibliothek zur Erstellung der HTML-Dateien schreiben, dabei lernen wir auch, wie man eine ihre Darstellung einstellen kann (nach seinem Geschmack) und sehen wir, wie man es leicht in seinem Expert Advisor oder Skript hinzufügen oder verwenden kann.
Kagi-Charts in MQL5 beherrschen (Teil I): Erstellen des Indikators Kagi-Charts in MQL5 beherrschen (Teil I): Erstellen des Indikators
Lernen Sie, wie man eine komplette Kagi-Chart-Engine in MQL5 aufbaut – Preisumkehrungen konstruieren, dynamische Liniensegmente erzeugen und Kagi-Strukturen in Echtzeit aktualisieren. In diesem ersten Teil lernen Sie, wie Sie Kagi-Charts direkt auf dem MetaTrader 5 rendern können, sodass Händler einen klaren Überblick über Trendverschiebungen und Marktstärke erhalten. Gleichzeitig bereiten Sie sich auf die automatisierte Kagi-basierte Handelslogik in Teil 2 vor.