English Русский Deutsch
preview
他言語の実用モジュールをMQL5で実装する(第04回):Pythonのtime、date、datetimeモジュール

他言語の実用モジュールをMQL5で実装する(第04回):Pythonのtime、date、datetimeモジュール

MetaTrader 5トレーディングシステム |
39 0
Omega J Msigwa
Omega J Msigwa

内容


はじめに

時間は私たちの生活における基本的な単位であり、この宇宙に存在するあらゆるものは時間によって測定されています。日常生活においては、目標の達成までの時間を測定したり、スマートフォンや時計などの時間管理デバイスを用いてタスクを監視し、管理しています。

アルゴリズム取引や取引全般においても、時間は依然として重要な要素となっています。私たちはしばしば時間に基づいて取引判断をおこない、その成果を日次、週次、月次といった単位で評価します。

MQL5には、時間の管理や評価をおこなうための組み込み関数が豊富に用意されており、アルゴリズム取引システムが現実の時間と同期できるようになっています。しかしながら、MQL5における時間や日付の扱いは比較的基本的で、場合によっては人間にとって直感的とは言えないことがあります。一方で、Pythonは、datetimecalendartimezoneinfoといった豊富なモジュールを提供しており、より高度で扱いやすい時間処理が可能です。

出典:pexels.com

本記事では、こうしたPythonの機能に類似した時間処理モジュールを、MQL5で実装していきます。


timeクラス(モジュール)

まず、timeというクラスから始めます。このクラスは、Pythonにおいて時間を扱うための基本的な機能(メソッド)を提供します。

Pythonのドキュメントによると、

timeオブジェクトは、特定の日付に依存しない「1日の中のローカル時間」を表し、tzinfoオブジェクトによって調整されます。

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

すべての引数は省略可能です。tzinfoはNone、またはtzinfoのサブクラスのインスタンスである必要があります。他の引数は以下の範囲の整数でなければなりません。

  • 0 <= hour < 24
  • 0 <= minute < 60
  • 0 <= second < 60
  • 0 <= microsecond < 1000000

これらの範囲外の値が指定された場合、ValueErrorが発生します。デフォルト値は、tzinfoがNoneであることを除き、すべて0です。

Pythonにはdatetimedateなど、時間を扱うためのクラスが他にも存在します。しかし、timeモジュール内のtimeクラスは、日付を考慮せずに純粋に「時刻のみ」を扱うことに特化しています。これは、「その日の特定の時刻」を知りたい場合に対応するものです。

CTimeクラスでは、コンストラクタ内でユーザーが適切な値を渡しているかどうかを検証する必要があります。これは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というオプション変数は、その時刻が属するタイムゾーン情報を保持します。

以下はCTimeクラスに含まれる主なメソッドです。

(a) fromisoformat

この関数は、有効なISO 8601形式のtime_string変数に対応するCTimeオブジェクトを返します。ただし、以下の例外があります。

  • タイムゾーンのオフセットには小数秒を含めることができる
  • 通常、日付と時刻の曖昧さを避けるために必要とされる先頭の「T」は必須ではない
  • 小数秒は任意の桁数を取ることができる(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オブジェクトに格納されている時刻をそれぞれ文字列と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を指定することで、タイムゾーン情報を持つ時刻から、変換をおこなわずにナイーブな時刻を生成することも可能です。

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(UTCオフセットが存在する場合)
  • HH:MM:SS+HH:MM(マイクロ秒が0かつUTCオフセットが存在する場合)

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:マイクロ秒が0ならseconds、それ以外は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 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(UTCより5時間遅れ)です。

(f) dst

tzinfoがNULLに設定されている場合(デフォルト値)、INT_MAXを返します。そうでなければ、サマータイム(DST)を返します。

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からのオフセット、タイムゾーン名、DSTオフセットを返すメソッドをサポートします。

tzinfoの具体的なサブクラスを作成する場合、使用するdatetimeオブジェクトに応じて、標準のtzinfoメソッドを少なくとも実装する必要があります。datetimeモジュールには簡易的な具体的なタイムゾーンサブクラスがあり、UTCや北米のESTやEDTなど、固定オフセットのタイムゾーンを表現できます。

tzinfoの具体的なサブクラスは、必要に応じて以下のメソッドを実装することが求められます。どのメソッドを実装するかは、aware datetimeオブジェクトの利用方法に依存します。迷った場合は、すべてのメソッドを実装しておくと安全です。

Pythonのクラスと同様に、MQL5で実装したCTZInfoクラスは、CDatetimeやCTimeクラスと連携して動作することを目的としており、これらのクラスがタイムゾーン情報を取得する際に役立ちます。

CTZInfoクラスを単独で呼び出すことは可能ですが、現在の時刻などの値に直接アクセスするために使うべきではありません。

このクラス(モジュール)が機能するためには、すべてのタイムゾーン情報を統一したデータベースにまとめておく必要があります。私は https://www.iana.org/time-zonesからすべての情報を抽出し、timezonedb.sqliteという名前の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;
     }
  }

以下は、Pythonでの実装を参考にしつつ、MQL5で実装したクラスで提供されている主なメソッドのいくつかです。若干の変更があります。

(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オブジェクトとして返します。DST情報が見つからない場合はNone(または0)を返します。

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の日時を受け取り、その時点におけるタイムゾーンのオフセットを適用したローカル日時を返します。

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オブジェクトは期間を表し、2つのdatetimeまたはdateインスタンスの差や和を表現します

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

すべての引数は省略可能でデフォルトは0です。引数は整数または浮動小数点数で、正または負の値を指定できます。

内部的には、days、seconds、microsecondsのみが保存されます。他の引数は次のように変換されます。

  • ミリ秒は1000マイクロ秒に変換される
  • 分は60秒に変換される
  • 時間は3600秒に変換される
  • 週は7日に変換される

その後、days、seconds、microsecondsは正規化され、表現が一意になるように調整されます。条件は次の通りです。

  • 0 <= microseconds < 1000000
  • 0 <= seconds < 3600*24(1日の秒数)
  • -999999999 <= days <= 999999999

MQL5でのtimedeltaモジュールの対応クラスはCTimedeltaであり、Python版に近い機能を持ちますが、若干の変更があります。

関数引数にミリ秒やマイクロ秒は含まれません。これは、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と同様に汎用性を持たせるため、テンプレート型を用いて、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での日時の加算や減算の操作を簡単にする上で重要です。ネイティブのMQL5では、このような時間演算は少し複雑になりがちだからです。


日付クラスオブジェクト

日付オブジェクトは、理想化された暦上の日付(年、月、日)を表します。現在のグレゴリオ暦が両方向に無限に延長されたものと考えてください。このクラスは、カスタムの「日付」変数(オブジェクト)として扱えます。

Pythonのdatetimeから、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)
有効なISO 8601形式の文字列から日付を返します。Python datetimeモジュールにある関数とは異なり、MQL5版では現在、次の2種類の形式をサポートしています。
  1. YYYY-MM-DD(ダッシュを含む10文字)
  2. YYYYMMDD(8桁の数字)
CDate             fromordinal(int ordinal);
順序付き日付をCDateオブジェクトに変換します。 
const int         weekday();
曜日を整数で返します。月曜が0、日曜が6。MqlDateTime.dayと同様です。
const int         isoweekday();
ISO形式の曜日を整数で返します。月曜が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
指定した日付オブジェクトの値を新しい値に置き換えます。 

使用例

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

有効なISO 8601形式の文字列からdatetimeオブジェクトを返します。ただし、以下の例外があります。

  • タイムゾーンのオフセットには小数秒を含めることができる
  • T区切りを任意の単一Unicode文字に置き換えることができる
  • 時および分の小数表現はサポートされていない
  • 精度を落とした日付(YYYY-MM、YYYY)は現在サポートされていない
  • 拡張日付表現(±YYYYYY-MM-DD)は現在サポートされていない
  • 順序付き日付(YYYY-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);
  }

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

CDatetimeオブジェクトに格納されているdatetime情報を、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

クラスに格納されている日時を、任意の文字列形式にフォーマットします。 通常は表示用に使用されます。

   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形式の文字列から、フォーマットされた時刻情報を解析し、CDatetimeクラス内に格納されるdatetimeオブジェクトに変換します。

   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モジュールのdate、time、およびカレンダー関連ユーティリティを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 Pythonのsqlite3に類似したモジュールで、MetaTrader 5でSQLiteデータベースを読み取る関数を提供する
Include\time.mqh 時間操作用のCTimeクラスを含む
Include\TZInfo.mqh 統一タイムゾーンデータベースからタイムゾーン情報を読み取るCTZInfoクラスを含む
Common\Files\timezonedb.sqlite すべてのタイムゾーン情報(UTCオフセット、タイムゾーン名など)を含むSQLite3データベース
Scripts\Time testing.mq5 この記事で紹介したすべてのメソッドや関数を試すためのテスト用スクリプト 

MetaQuotes Ltdにより英語から翻訳されました。
元の記事: https://www.mql5.com/en/articles/19035

添付されたファイル |
Attachments.zip (1281.22 KB)
MQL5での取引戦略の自動化(第43回):適応型線形回帰チャネル戦略 MQL5での取引戦略の自動化(第43回):適応型線形回帰チャネル戦略
本記事では、ユーザー定義期間にわたって回帰直線と標準偏差チャネルを自動的に計算し、明確なトレンドを確認するために傾きが最小閾値を超えた場合にのみ有効化され、さらに価格がチャネル幅の設定可能な割合を超えてブレイクアウトした際にチャネルを動的に再生成または延長する、適応型リニア回帰チャネルシステムをMQL5で実装します。
古典的な戦略を再構築する(第13回):クロスオーバー戦略を新たな次元へ(その2) 古典的な戦略を再構築する(第13回):クロスオーバー戦略を新たな次元へ(その2)
本記事では、移動平均クロスオーバー戦略に対してさらなる改善を加え、ラグをより実用的で信頼性の高い水準まで低減する方法について検討します。データサイエンスの知見を活用しながら議論を進めます。一般に、データを高次元へ射影することで、機械学習モデルの性能が向上する場合があることはよく知られています。本記事では、この考え方がトレーダーにとって実際に何を意味するのかを示し、MetaTrader 5ターミナルを用いてどのように活用できるかを説明します。
MQL5におけるARIMA予測指標 MQL5におけるARIMA予測指標
本記事では、MQL5でARIMA予測インジケーターを実装する方法について説明します。ARIMAモデルがどのように予測を生成するのか、またそれが外国為替市場や株式市場全般にどのように適用できるのかを解説します。さらに、自己回帰(AR)とは何か、自己回帰モデルがどのように予測に利用されるのか、その仕組みについても説明します。
MQL5入門(第29回):MQL5のAPIとWebRequest関数の習得(III) MQL5入門(第29回):MQL5のAPIとWebRequest関数の習得(III)
本記事では、MQL5におけるAPIおよびWebRequestの理解をさらに深め、外部サービスからローソク足データを取得する方法を解説します。サーバーレスポンスの分割、データのクレンジング、そして複数の日足に対する始値時刻やOHLC値などの主要要素の抽出に焦点を当て、後続の分析に利用可能な形へと整形していきます。