これはなに?
Microsoft の DATE 型のドキュメントの内容が思いがけなさ過ぎて変な声がでるぐらいだったので、この驚きを可視化してシェアしようと思って書いた記事。
その文書
下記リンクがその文書。
びっくりポイントは
日付は、1899 年 12 月 30 日の真夜中を 0 として始まる、整数の増分で表されます。 時間の値は、数値の小数部分の絶対値で表されます。
日付値の整数部分は符号付きとして扱われ、小数部分は符号なしとして扱われる
の部分。(強調引用者)
計算して可視化する
文書だとよくわからないので、実際に計算してみる。
#include <iostream>
#include <ATLComTime.h>
int main()
{
for (int i = -48; i <= 48; ++i) {
auto d = COleDateTime(1899, 12, 30, 0, 0, 0)
+ COleDateTimeSpan(0, i, 0, 0);
std::cout << i << "," << d << "\n";
}
return 0;
}
i は、1899年 12月 30日 午前0時 からの経過時間。単位は時間。つまり i=-12 で 基準時刻の半日前、i=24 で一日後。横軸。
d は、DATE
型 の中に入っている double
型の値。縦軸。
グラフにすると、下図。
日付が -2日 から -1日に 変わるときに以下のようになる。
下表は 日付が変わる瞬間の プラスマイナス 1万分の1 のふたつの時刻。
整数部 | 小数部 |
DATE の値 |
|
---|---|---|---|
変わる直前 | -2 | 0.9999 | -2.9999 |
変わった直後 | -1 | 0.0001 | -1.0001 |
のようなことになる。小数部が繰り上がって整数部が増える、というところだけみるとまあそうかなと思うかもしれないけど、 DATE
の値 の欄を見ると分かる通り、極めて思いがけない動きとなっている。
困るポイント
DATE
型だから実質 double
だよね。と思って double
として整列すると順序通りにならない。
時刻の差を見るために double
だと思って引き算をするのもだめ。
対策
DATE
型の中身を「どうせ double
だよね」と思って操作してはいけない。
感想
Windows アプリケーションを作る仕事もだいぶやったことがあるんだけど、全然気づかなかった。
百年前の時間を扱うことがないので発現しなかったんだと思う。
いにしえのミスによりこうなってしまって、それを前提としたソフトがたくさんあるので直せないまま 30年が過ぎた、とかだと思う。
ミスとしてはわりと酷いものだと思うけど、後方互換性にかける執着心みたいなものの凄みを感じる。