2018年5月4日金曜日

[MQL4] datetime を long にキャストすると 1970/1/1 からの秒になる

2018/6/12 追記
負数の扱いと最大値について加筆・修正しました。
> 参考 > > [Datetime Type - Integer Types - Data Types - Language Basics - MQL4 Reference](https://docs.mql4.com/basis/types/integer/datetime) 公式ドキュメントによると、`datetime` は 1970/1/1 からの秒を格納する 8 bytes の型だそうです。 これは `long` と同じサイズで、相互変換が可能です。 どちらを使っていても動作上問題はないのですが、以下のような特徴があるので使い分けると良いでしょう。 - `datetime`
日付文字列との相互変換が容易。実際の日付を表す場合に使用。 - `long`
純粋な秒を表す場合に使用。 ### キャストしてみる ```mq4 datetime date1 = D'2008.01.01'; long date1_sec = (long) date1; printf("%lld", date1_sec); // 結果 // 1199145600 ``` この値、[UNIX Time](https://ja.wikipedia.org/wiki/UNIX%E6%99%82%E9%96%93) として知られているUNIX Time は UTC 基準なので正確にはちょっと違うもので、Linux 等では簡単に調べられます。 ```console `gutter: false; $ date -u --date='2008/01/01' +%s 1199145600 ``` 一致しました。 datetime にはタイムゾーンという概念がないので、どのタイムゾーンを表しているのかは別途考えなければなりません。 ### 差分計算の結果は、long で受けた方が良い 上記定義より datetime と datetime の差分計算結果も datetime に格納することが出来ます。 ```mq4 datetime date1 = D'2008.01.01'; datetime date2 = D'2008.01.02'; datetime date3 = date2 - date1; Print(TimeToString(date3, TIME_DATE)); // 結果 // 1970.01.02 ``` 差分結果が `1970.01.02` という意味のないものになってしまいました。 `date3` に格納されている値は 1970/1/1 からの秒なので、使用時に `long` にキャストすれば実行上問題はありません。 しかし、せっかく相互変換が可能な型なのですから、実体を表した使い方をした方が良いだろうと思います。 つまり、以下のように差分は `long` で受ける方が自然です。 ```mq4 datetime date1 = D'2008.01.01'; datetime date2 = D'2008.01.02'; long time_delta = date2 - date1; printf("%lld", time_delta); // 結果 // 86400 (= 60 * 60 * 24) ``` なお、差分が4バイト大体68年を超えるような計算は、特殊なケースに限られるはずです。 つまり、`int` で受けても実害はないと思われますが、メモリに余裕がある系ではそういった最適化はしない方が無難です。 最近の PC でメモリが枯渇することはあまり無いですよね? ### 差分が負になっても datetime で扱えるが、やはり long で受けた方が良い 以下のように、負の値を `datetime` に入れることも可能ですが、[TimeToString()](https://docs.mql4.com/convert/timetostring) は正しく動作しません。 やはり、`long` で受けておく方が無難でしょう。 ```mq4 datetime date1 = D'2008.01.01'; datetime date2 = D'2008.01.02'; datetime date3 = date1 - date2; printf("%s : %lld", TimeToString(date3, TIME_DATE), date3); // 結果 // : -86400 ``` ### 最大値は 8bytes ではない 公式ドキュメントによれば、`datetime` の最大値は `3000/12/31` (≒ `0x7933FFF7F`時間まで書いてないので「ほぼ」にしました)だそうです。 実際は 5bytes 程度ということですね。 ところで、正確な時間まで調べてみたところ、本当の最大値は `3001/01/01 07:59:59` になりました。 ```mq4 datetime date1 = 0x0793406FFF; printf("%lld %s", date1, TimeToString(date1, TIME_DATE|TIME_MINUTES|TIME_SECONDS)); // 結果 // 32535244799 3001.01.01 07:59:59 datetime date2 = 0x0793407000; printf("%lld %s", date2, TimeToString(date2, TIME_DATE|TIME_MINUTES|TIME_SECONDS)); // 結果 // 32535244800 ``` 環境依存の可能性もありますし、1000年近い未来の数時間など誤差でしかありませんが。