【超入門】MQL5 EA講座 第117回「MqlDateTime構造体について」【EAの作り方】

MQL5でEA作ろう講座


前回datetime型を使って、日時データを操作する時の基本的な考え方についてお話しました。

datetime型、およびUNIXタイムの原理原則を改めてお伝えし、その特性を生かして2つの日時データを比較したり、「1日」「1時間」といった特定の期間を足したりすることによって、特定の日時情報を取得する方法を解説しました。

そして、datetime型文字列型との型変換を行える関数として、TimeToString関数StringToTime関数の使い方を紹介しました。

最後に復習も兼ねて、文字列をシングルクォーテーションで囲み、その前に大文字のDを付けることで、datetime型定数を作成する、という講座記事第16回で解説した内容を改めて紹介しました。

今回はMqlDateTime構造体について解説していきたいと思います。

構造体についての詳細は↓の記事をご参照ください

スポンサーリンク
スポンサーリンク

MqlDateTime構造体とは?

MqlDateTime構造体は日付と時刻に関連する情報を格納する構造体です。

MqlDateTime構造体には、年、月、日、時、分、秒などの情報を格納する各メンバが用意されており、プログラムがこれらMqlDateTime構造体体個々のメンバにアクセスする事によって日付と時刻の情報の部分的な要素を容易に取り出したり加工したりすることが出来るようになっています。

MqlDateTime構造体を使いこなせるようになると、日時データの扱いがぐっと楽になります。

たとえば、特定の取引時間を分析したり、統計的なデータを日時ごとに集計したりするときに、これらのプロセスが直感的に、そして効率的に行えるようになります。

今はピンと来ないかもしれませんが、この後直感的に理解できるように解説していきます。

次回以降追加していくクラス関数を作成する際にもMqlDateTime構造体は登場しますので、是非今回も楽しんで読んでいただければと思います。

MqlDateTime構造体のメンバ構成について

MqlDateTime構造体メンバ構成は以下のようになっています。

struct MqlDateTime
 {
  int year;           // 年
  int mon;           // 月
  int day;           // 日
  int hour;           // 時間
  int min;           // 分
  int sec;           // 秒
  int day_of_week;   // 曜日(日曜が 0、月曜が 1、...土曜が 6 )
  int day_of_year;   // 年の日番号(1月1日には、ゼロの数の値が割り当てられる)
 };

メンバについて簡単に説明をしたいと思います。

year

year メンバは、年を表す整数値を格納します。例えば、2024 ならば、 2024年 として表します。

mon

mon メンバは月を表す整数値を格納します。格納されるのは1 から 12 の値で、1 は1月、2 は2月というように月を表します。

day

day メンバはその月の日を表す整数値を格納します。1 から月の最後の日(28〜31)までの値を取り得ます。

hour

hourメンバは時間を表す整数値を格納します。格納される値は0 から 23 の値で、0 は0時(深夜の12時)、23 は23時(夜の11時)を表します。

min

minメンバは分を表す整数値を格納します。格納される値は0 から 59 の値で、各時間の分を表します。

sec

secメンバは秒を表す整数値を格納します。0 から 59 の値で、各分の秒を表します。

day_of_week

day_of_weekメンバは曜日を表す整数値を格納します。0 から 6 の値で、0 は日曜日、1 は月曜日、…、6 は土曜日を表します。

day_of_year

day_of_year メンバは、その年の日番号を表す整数値を格納します。1月1日は 0 から始まり、12月31日までの値を取り得ます。

day_of_week」と「day_of_yearは、それぞれ日曜が0,1月1日が0から始まっているので注意が必要ですね。

他のメンバは我々が普段理解している形式で日時情報を把握できるような初期値設定になっていますので混乱する事はないかと思います(例えば、その月の日を表す整数値を格納する「dayは、その初期値が1から始まっていますね)

MqlDateTime構造体についての注意点

MqlDateTime構造体に限った話ではないのですが、構造体はそもそもが変数配列などをワンパッケージでまとめ、使いやすくしたものにすぎません。

構造体はデータを入れる箱を作った段階ではなにも情報がはいっておらず、MqlDateTime構造体も作っただけでは、各メンバに日時情報が格納されるわけではないので、その大前提をまずは注意しましょう

MqlDateTime構造体の使い方

MqlDateTime構造体の各メンバがどのような情報を格納するのか、を紹介し終わったので次はサンプルコードを使って具体的な使い方を見ていきましょう↓

void OnStart() 
{
  // 'TimeCurrent()' 関数を使用して現在のサーバー時間を取得
  datetime current_time = TimeCurrent(); 

  // 'MqlDateTime' 構造体の変数を宣言
  MqlDateTime mt_time;

  // 'TimeToStruct()' 関数を使用して 'datetime' 型の 'current_time' を
  // 'MqlDateTime' 型の 'mt_time' 構造体に変換
  // これにより年月日時分秒などの個別の情報に分けることができる
  TimeToStruct(current_time, mt_time); 

  // 分解した年月日時分秒などの情報を 'Print()' 関数で出力
  Print("Year: ", mt_time.year);            // 現在の年
  Print("Month: ", mt_time.mon);            // 現在の月
  Print("Day: ", mt_time.day);              // 現在の日
  Print("Hour: ", mt_time.hour);            // 現在の時間(時)
  Print("Minute: ", mt_time.min);           // 現在の時間(分)
  Print("Second: ", mt_time.sec);           // 現在の時間(秒)
  // 'day_of_week' は曜日を整数で表し、日曜日が0、月曜日が1...となる
  Print("Day of the Week: ", mt_time.day_of_week); 
  // 'day_of_year' は年始からの経過日数を表す
  Print("Day of the Year: ", mt_time.day_of_year);
}

手順1:まずはMqlDateTime型に変換したいdatetime型の値を用意する

上記のサンプルコードは、

TimeCurrent関数を利用して、現在のサーバー時間を取得し、その情報をMqlDateTime構造体の各メンバに振り分け、その後振り分けた各メンバのデータをPrint関数で出力される・・・という挙動になっています。

TimeCurrent関数は現在のサーバー時間を取得する関数です。

TimeCurrent関数についての詳細は↓の記事をご参照ください

TimeCurrent関数が獲得した値をdatetime型変数「current_time」に代入しています。

  // 'TimeCurrent()' 関数を使用して現在のサーバー時間を取得
  datetime current_time = TimeCurrent(); 

【広告リンク↓】

手順2:MqlDateTime構造体のインスタンスを宣言する

MqlDateTime構造体は、言うまでもなく構造体なのでインスタンスを宣言しないと使えません。

ここではmt_timeというインスタンスを宣言しました

// 'MqlDateTime' 構造体のインスタンスを宣言
  MqlDateTime mt_time;

構造体についての詳細は↓の記事をご参照ください

インスタンスについての詳細は↓の記事をご参照ください

手順3:TimeToStruct関数を使い、MqlDateTime構造体の各メンバにデータを振り分ける

そして、datetime型の日時情報を、MqlDateTime構造体の各メンバに振り分ける作業を担っているのがTimeToStruct関数になります。

TimeToStruct関数は第1引数に記述したdatetime型の日時情報を、値をより詳細な成分に分解し

MqlDateTime構造体型のインスタンスに格納する、という処理をしてくれる関数です。

TimeToStruct関数についての詳細は↓の記事をご参照ください

TimeToStruct関数の第1引数datetime型の ‘current_time’ を記述し、第2引数にはMqlDateTime型の ‘mt_time’を記述します。

この記述によって、’mt_time’の各メンバに年月日時分秒などの個別の情報を振り分ける事ができました。

// 'TimeToStruct()' 関数を使用して 'datetime' 型の 'current_time' を
  // 'MqlDateTime' 型の 'mt_time' 構造体に変換
  // これにより年月日時分秒などの個別の情報に分けることができる
  TimeToStruct(current_time, mt_time); 

【広告↓】

手順4:格納したデータをログ出力して確認してみる

‘mt_time’の各メンバにデータがどのように格納されているのか、Print関数を使ってログ出力してみましょう。

構造体メンバ呼び出し方は覚えていますでしょうか?

「mt_time.year」「mt_time.mon」のように、インスタンスの後ろにドット(.)をつけて、その後に呼び出したいメンバ)の名前を記述します

 // 分解した年月日時分秒などの情報を 'Print()' 関数で出力
  Print("Year: ", mt_time.year);            // 現在の年
  Print("Month: ", mt_time.mon);            // 現在の月
  Print("Day: ", mt_time.day);              // 現在の日
  Print("Hour: ", mt_time.hour);            // 現在の時間(時)
  Print("Minute: ", mt_time.min);           // 現在の時間(分)
  Print("Second: ", mt_time.sec);           // 現在の時間(秒)
  // 'day_of_week' は曜日を整数で表し、日曜日が0、月曜日が1...となる
  Print("Day of the Week: ", mt_time.day_of_week); 
  // 'day_of_year' は年始からの経過日数を表す
  Print("Day of the Year: ", mt_time.day_of_year);

出力結果は以下のようになりました↓

今回のサンプルコードではdatetime型の値はTimeCurrent関数を使って用意しましたが、自分自身で定数を下記のように用意しても当然構いません。

 datetime dtTime= D'2024.03.18 22:30:00';

StructToTime関数を使えば、MqlDateTime型→datetime型への変換も可能

MQL5には、StructToTime関数というMqlDateTime構造体の形式で保存された日時を、dateTime形式(1970年1月1日からの秒数=UNIXタイム)に変換する関数もあります。

StructToTime関数引数MqlDateTime構造体インスタンスを記述すれば、TimeToStruct関数とは逆に、MqlDateTime構造体型→dateTime型への変換も行えます。

void OnStart()
  {
// 日時構造体のサンプルデータ
   MqlDateTime myDateStruct;
   myDateStruct.year = 2024;
   myDateStruct.mon = 3;
   myDateStruct.day = 17;
   myDateStruct.hour = 12;
   myDateStruct.min = 30;
   myDateStruct.sec = 0;

// StructToTime関数で datetime 形式に変換
   datetime myDateTime = StructToTime(myDateStruct);

// 変換された datetime 形式の日時を出力
   Print("The datetime representation is: ", myDateTime);
  }

上記のサンプルコードは、StructToTime関数を使って、MqlDateTime構造体の各メンバに格納されている値をdateTime型に変換して、Print関数にてその結果をログ出力するまでの挙動を記述したものになります。

まずは、インスタンス「myDateStruct」を宣言し、各メンバに任意の日時情報を示す値を代入しています↓

// 日時構造体のサンプルデータ
MqlDateTime myDateStruct;
myDateStruct.year = 2024;
myDateStruct.mon = 3;
myDateStruct.day = 17;
myDateStruct.hour = 12;
myDateStruct.min = 30;
myDateStruct.sec = 0;

次は、StructToTime関数引数に「myDateStruct」を記述し、その戻り値datetime型変数 「myDateTime」に代入します↓

// StructToTime関数で datetime 形式に変換
datetime myDateTime = StructToTime(myDateStruct);

これでMqlDateTime構造体型として格納されていた日時情報がdatetime型に変換されている筈です。最後にログ出力して確認をします。

出力結果は

「The datetime representation is: 2024.03.17 12:30:00」

となりました。

StructToTime関数についての詳細は↓の記事をご参照ください

MqlDateTime構造体に値を代入しなかった場合、または無効な値を代入した場合

MqlDateTime構造体インスタンスに何も値を代入しなかった場合や、無効な値を代入してしまった場合はどうなるのか?ということについて触れたいと思います。

MqlDateTime構造体のyearメンバに値を代入しなかった場合

MqlDateTime構造体の「yearメンバに値を代入しなかった場合、StructToTime関数を使っても、datetime型への値変換が行われません

先程のサンプルコードで「yearメンバだけをコメントアウトしてプログラムを実行した場合↓

void OnStart()
  {
// 日時構造体のサンプルデータ
   MqlDateTime myDateStruct;

   //myDateStruct.year = 2024;
   myDateStruct.mon = 3;
   myDateStruct.day = 17;
   myDateStruct.hour = 12;
   myDateStruct.min = 30;
   myDateStruct.sec = 0;

// StructToTime関数で datetime 形式に変換
   datetime myDateTime = StructToTime(myDateStruct);

// 変換された datetime 形式の日時を出力
   Print("The datetime representation is: ", myDateTime);
  }

出力結果は「The datetime representation is: wrong datetime」

となります。

【広告↓】

メンバ「mon」や「day」に1より小さい値が入っている場合

例えば、メンバmonや「dayに1より小さい値が入っている場合

すなわち、月または日を格納している値が 1 より小さい場合、デフォルトの値は 1 となります。

void OnStart()
  {
// 日時構造体のサンプルデータ
   MqlDateTime myDateStruct;
 
   myDateStruct.year = 2024;
   myDateStruct.mon = 0;//1より小さい値が入っている
   myDateStruct.day = 0;//1より小さい値が入っている
   myDateStruct.hour = 12;
   myDateStruct.min = 30;
   myDateStruct.sec = 0;

// StructToTime関数で datetime 形式に変換
   datetime myDateTime = StructToTime(myDateStruct);

// 変換された datetime 形式の日時を出力
   Print("The datetime representation is: ", myDateTime);
  }

メンバmonと「dayの値を0に変更しました。このサンプルコードの出力結果は

The datetime representation is: 2024.01.01 12:30:00

となります。

メンバ「mon」に12より大きい値が入っている場合

メンバmonに12より大きい値が入っている場合、デフォルトの値が12(月)に修正されます

void OnStart()
  {
// 日時構造体のサンプルデータ
   MqlDateTime myDateStruct;
 
   myDateStruct.year = 2024;
   myDateStruct.mon = 13;//12より大きい値が入っている
   myDateStruct.day = 15;
   myDateStruct.hour = 12;
   myDateStruct.min = 30;
   myDateStruct.sec = 0;

// StructToTime関数で datetime 形式に変換
   datetime myDateTime = StructToTime(myDateStruct);

// 変換された datetime 形式の日時を出力
   Print("The datetime representation is: ", myDateTime);
  }

メンバmonの値を13に変更してみました。

出力結果は「The datetime representation is: 2024.12.15 12:30:00」になります

メンバ「day」にその月の日数より大きい値が入っている場合

メンバdayにその月の日数より大きい値が入っている場合、その月が31日ある場合は、デフォルト値が31(日)となります。その月が31日ない場合は、超過している日数分翌月にずれ込む仕様になっています。

   myDateStruct.mon = 1;//1月は31日ある
   myDateStruct.day = 33;//31より大きい値がはいっている

上記のサンプルコードについてですが、1月は31日まであるので、メンバdayに33のような数字が入っている場合、出力結果は

The datetime representation is: 2024.01.31 12:30:00」となります

一方で例えば2月の場合、

void OnStart()
  {
// 日時構造体のサンプルデータ
   MqlDateTime myDateStruct;
 
   myDateStruct.year = 2024;
   myDateStruct.mon = 2;//2月は28日あるいは29日しかない
   myDateStruct.day = 33;//28あるいは29より大きい値がはいっている
   myDateStruct.hour = 12;
   myDateStruct.min = 30;
   myDateStruct.sec = 0;

// StructToTime関数で datetime 形式に変換
   datetime myDateTime = StructToTime(myDateStruct);

// 変換された datetime 形式の日時を出力
   Print("The datetime representation is: ", myDateTime);
  }day = 33;//28あるいは29より大きい値がはいっている

2月は28日、あるいは閏年の場合29日しかありません。

この場合、メンバdayに格納できる最大値「31」-2月の本来の最大値「28あるいは29」の差分、すなわち「3日(28日の場合)」あるいは「2日(29日)」分、翌月に日付がずれ込む事になります。

上記のサンプルコードを実行した場合、出力結果は

The datetime representation is: 2024.03.02 12:30:00」となります。

2024年は閏年で2月が29日まであるので、2日分日付が3月にずれ込んだ形です。

例えばメンバyearを2023にした場合、2月は28日までなので、出力結果は日付が3月に3日分ずれ込み、

The datetime representation is: 2023.03.03 12:30:00」となります。

【広告↓】

メンバ「hour」「min」に無効な値が入っている場合

メンバhour」「min」に無効な最小値(例えば「-1」などのマイナス値)が入っている場合は「hour」「min」のデフォルト最小値である「0」が格納されるようになっています。

void OnStart()
  {
// 日時構造体のサンプルデータ
   MqlDateTime myDateStruct;
 
   myDateStruct.year = 2024;
   myDateStruct.mon = 3;
   myDateStruct.day = 18;
   myDateStruct.hour = -1;//hourに無効な最小値を代入
   myDateStruct.min = -1;//minに無効な最小値を代入
   myDateStruct.sec = 0;

// StructToTime関数で datetime 形式に変換
   datetime myDateTime = StructToTime(myDateStruct);

// 変換された datetime 形式の日時を出力
   Print("The datetime representation is: ", myDateTime);
  }

この場合の出力結果は

The datetime representation is: 2024.03.18 00:00:00」となります。

一方でメンバhour」「min」に無効な最大値が入っている場合は、「hour」のデフォルト最大値「23」、「min」のデフォルト最大値「59」がそれぞれ格納されるようになっています。

void OnStart()
  {
// 日時構造体のサンプルデータ
   MqlDateTime myDateStruct;
 
   myDateStruct.year = 2024;
   myDateStruct.mon = 3;
   myDateStruct.day = 18;
   myDateStruct.hour = 28;//hourに無効な最大値を代入
   myDateStruct.min = 67;//minに無効な最大値を代入
   myDateStruct.sec = 0;

// StructToTime関数で datetime 形式に変換
   datetime myDateTime = StructToTime(myDateStruct);

// 変換された datetime 形式の日時を出力
   Print("The datetime representation is: ", myDateTime);
  }

この場合の出力結果は

The datetime representation is: 2024.03.18 23:59:00」となります。

おまけ:オーバーフローについて

今回説明したような形を含め、変数のデータ型が扱える範囲を超えた値を入力した結果、正しい結果が得られなくなる事」をプログラミングの領域においてオーバーフローといいます。

オーバーフローによって予期しない結果や誤った値が生成される可能性がありますので、改めてデータ型とその許容できる最大値、というものに意識を払いオーバーフローを発生させる事のない様にしましょう

まとめ

今回はMqlDateTime構造体に関して解説をしました。

MqlDateTime構造体が日付と時刻に関連する情報を格納する構造体である、という事をまず説明し、MqlDateTime構造体の各メンバがどのような役割をもっているのかを説明しました。

詳細は当記事の

MqlDateTime構造体とは?」セクション及び、

MqlDateTime構造体のメンバ構成について」セクションをご覧ください。

そして具体的にMqlDateTime構造体をどうやって使っていくのか、という部分をサンプルコードを見ながら解説を加えました。その過程で、TimeCurrent関数TimeToStruct関数StructToTime関数といった今回初出の関数についても紹介しました

※詳細は当記事の「MqlDateTime構造体の使い方」セクションをご覧ください。

そして最後に、MqlDateTime構造体の各メンバに、無効な値を代入した場合、どのような結果が生じるのか、という点について補足させて頂きました。

※詳細は当記事の「MqlDateTime構造体に値を代入しなかった場合、または無効な値を代入した場合」セクションをご覧ください。

今回は以上とさせていただきます。次回から、前回と今回解説した日時情報に関する知識を使って、特定の日時を簡単に生成できる関数を実装していきます。

最後までお読みいただきありがとうございました。

第116回「DateTime型を使って、日時データを操作する時の基本的な考え方」

→ 第118回「特定の日時を簡単に生成できる関数を実装する」

<参照>

TimeToString関数/StringToTime関数/UNIXタイム/タイムスタンプ/datetime型/iTime関数/ENUM_TIMEFRAMES/TimeToStruct関数/StructToTime関数/構造体/サーバー時間


タイトルとURLをコピーしました