ステータスモニターを作成していて、グラフの表示は現在の時刻から線画してという処理になっているのですが、時々誤差が出ます。
リアルタイムに受信しながらテストを行っているので、余り深く考えていなかったのですが、なんか妙におかしなデータになります。
どっかにバグがあるのかと思って、あれこれテストしていたのですが、ふと「これって日付関連の浮動小数点における誤差で出てる?」と思い出して、今までちゃんと確認したことがなかったので適当にテストコードを書いてみる。
var
NowDateTime,WDT:TDateTime;
i:Integer;
Year,Month,Day,Hour,Minute,Second,MSecond:WORD;
WSL:TStringList;
begin
WDT:=Now;
DecodeDate(WDT,Year,Month,Day);
DecodeTime(WDT,Hour,Minute,Second,MSecond);
NowDateTime:=EncodeDateTime(Year,Month,Day,15,0,0,0);
WDT:=EncodeDateTime(Year,Month,Day,14,0,0,0);
WSL:=TStringList.Create;
for i:=0 to 20 do
begin
WSL.Add(DateTimeToAllStr(NowDateTime)+' / '+DateTimeToAllStr(WDT)+' diff '+Inttostr(MinutesBetween(NowDateTime,WDT)));
WDT:=IncMinute(WDT,-1);
end;
WSL.SaveToFile('time.txt');
WSL.Free;
end;
DateTimeToAllStr() は yyyy/mm/dd hh:mm:ss と出力するだけの別関数です。
現在の日付の 15:00:00.000 から 14:00:00.000 までの差(MinutesBetween)を最初の20分だけ出力します。
んで結果はこれ。
2015/01/16 15:00:00 / 2015/01/16 14:00:00 diff 59
2015/01/16 15:00:00 / 2015/01/16 13:59:00 diff 60
2015/01/16 15:00:00 / 2015/01/16 13:58:00 diff 62
2015/01/16 15:00:00 / 2015/01/16 13:57:00 diff 63
2015/01/16 15:00:00 / 2015/01/16 13:56:00 diff 64
(以下略)
最初の2行がおかしいですね。
14:00:00.000 から 15:00:00.000 の差は 60分ジャストですが、誤差が出ています。
次の行も同様に誤差が出ていて3行目から正しい結果が出ています。(SecondsBetween でも同様に出る)
***Between を使うことは滅多に無い(あったとしても前回から60分経過しているかなー?程度の精度でしか使わない)のですが、今回は1分単位で集計を行うようなコードが必要だった為、深く考えずにコーディングしてしまった落とし穴でした。
(MinuteSpan などを使って端数も合わせて処理すればよさげですが。先に思い出したのが Between関数だったので)
まるめ処理かなんかの内部的な仕様かなと思いますが、こんなんで半日潰れてしまいましたよ。
Ps.
Delphi の MinutesBetween() のヘルプには
MinutesBetween 関数を呼び出すと,2 つの TDateTime 値の差(分数)を取得できます。MinutesBetween は,完全な分(60 秒)だけをカウントします。したがって,MinutesBetween は午前 9 時と午前 9 時 0 分 59 秒 999 ミリ秒の差を 0 として報告します。この場合,1 としてカウントされるには 1 ミリ秒足りません。
15:00:00 と 14:00:00 は完全な60秒でカウントされる60分じゃないんかい!と突っ込みを入れたくなりましたが(笑)