Version 13.25
日時の計算
「今回は日時の計算をします」
『計算?』
「たとえば……」
・今日の3日後の日にち
・2004/04/01 から今日までの日にち
・今月の月末の日にち
・今月の第1月曜日
「などなど」
『ぱっと見、簡単そうだけど』
「そこが危険なんです。こういう日時の取得は簡単そうだけど、実際にプロ
グラムで求めるときには色々なコツが必要なんです」
『そういうものなの?』
「たとえば」
・今日の3日後の日にち
「これをプログラムで求めるとしたら?」
『 tm 構造体作って tm_wday に 3 足す……あ、繰り上がり……』
「そういうこと」
『 tm_wday が 29 とかだと月に繰り上がるし、その月が12月だった
ら……あ、そういえば、そういう計算はのべ秒でするって言ってたね』
「そう、計算はのべ秒、つまり time() の戻り値で計算すると、繰り上がり
を考えなくていいから簡単なんです」
void ThreeDaysLater()
{
setlocale( LC_ALL, "japanese" );
char pchTime[256];
tm *pstTm;
// 現在の日付を取得。
time_t lTime_t;
time( &lTime_t );
pstTm = localtime( &lTime_t );
strftime
( pchTime
, 255
, "%Y年%#m月%#d日%a曜日%H時%M分%S秒"
, pstTm
);
TRACE( "%s\n", pchTime );
// 2004年5月18日火曜日12時41分01秒
// 「3日後」を足します。
const int DAYS = 3;
lTime_t += DAYS * 24 * 60 * 60;
pstTm = localtime( &lTime_t );
strftime
( pchTime
, 255
, "%Y年%#m月%#d日%a曜日%H時%M分%S秒"
, pstTm
);
TRACE( "%s\n", pchTime );
// 2004年5月21日金曜日12時41分01秒
}
『ちゃんと3日後になってる!』
「ポイントはここ」
// 「3日後」を足します。
const int DAYS = 3;
lTime_t += DAYS * 24 * 60 * 60;
「3日後×24時間×60分×60秒、で〈3日間ののべ秒〉が出るから」
『それを足せばいいわけね』
「基本的にはこの要領でだいたい計算できるんだけど、ちょっと難しい場合
もあります。たとえば〈3ヶ月後〉とか」
『え? それって難しい? 3 * ……あ、1ヶ月の日数って変わる……』
「でしょう。一般的に〈3ヶ月後〉って言ったら、3ヶ月後の同じ日のこと
を差します。ところがのべ秒での計算の場合、1ヶ月の日数は月によって違
うから」
『普通に足せないね……』
「そういう場合には tm 構造体で計算します」
void ThreeMonthsLater()
{
setlocale( LC_ALL, "japanese" );
char pchTime[256];
tm *pstTm;
// 現在の日付を取得。
time_t lTime_t;
time( &lTime_t );
pstTm = localtime( &lTime_t );
strftime
( pchTime
, 255
, "%Y年%#m月%#d日%a曜日%H時%M分%S秒"
, pstTm
);
TRACE( "%s\n", pchTime );
// 2004年5月18日火曜日12時52分35秒
// 「3ヶ月後」を足します。
const int MONTHS = 3;
pstTm->tm_mon += MONTHS;
const int DECEMBER = 11;
if( DECEMBER < pstTm->tm_mon )
{
// 12月より大きい場合には繰り上がりを計算します。
pstTm->tm_year += 1;
pstTm->tm_mon -= 12;
}
strftime
( pchTime
, 255
, "%Y年%#m月%#d日%a曜日%H時%M分%S秒"
, pstTm
);
TRACE( "%s\n", pchTime );
// 2004年8月18日火曜日12時52分35秒
}
「まず」
// 「3ヶ月後」を足します。
const int MONTHS = 3;
pstTm->tm_mon += MONTHS;
「で月を足します」
『そのあとの if の中で繰り上がりの計算してるのね』
「ただし、このプログラムには重要なバグが潜んでいます」
『ええっ!?』
「このプログラムを11月30日に実行したら?」
『2月……30日なんてないし』
「そういうこと。〈一般的な三ヶ月後〉も、こういうイレギュラーな場合に
は、場面によってどうするか違ってくるでしょ」
『2月28日にしたり3月1日にしたり……あ、うるう年もあるし』
「まずは、そういう場合にどうするか決めて、それにあわせてプログラムを
作る、という形になります」
『……決める?』
「そう、決めるの」
『……なんか難しくない?』
「そうなんだよね、そういうルールって明確に決まってないことが多いか
ら。でもプログラムで、特にアルゴリズムで自動化する場合には」
『これを決めないとどうしようもない、と』
「そういうこと。それが決まったら、計算して2月の場合には処理を変えれ
ば大丈夫」
『2月28日の時は? うるう年の時は変えないと』
「そうだね。うるう年かどうかの判別は、次の法則を使って調べます」
1. 400 で割り切れる年はうるう年。
2. 1 に当てはまらず、 100 で割り切れる年はうるう年。
3. 2 に当てはまらず、 4 で割り切れる年はうるう年。
『へー、 3 だけじゃないんだ』
「ちょっと複雑だけど、プログラムにすればわかりやすいかも」
bool isLeapYear( int p_iYear )
{
if( p_iYear % 400 == 0 )
{
// 400 で割り切れたら閏年。
return true;
}
else if( p_iYear % 100 == 0 )
{
// 100 で割り切れたら違います。
return false;
}
else if( p_iYear % 4 == 0 )
{
// 4 で割り切れたら閏年。
return true;
}
// それ以外は違います。
return false;
}
『 % って?』
「 % は割り算の【余り】を計算する演算子」
『そか、割り切れたらあまり 0 だもんね』
「まぁ、普通に使う分には〈 100 で割り切れたら〉っていうのはまず関係
ないからあまり必要ないかもしれないけどね。あ、ちなみに使う時には」
if( isLeapYear( pstTm->tm_year + 1900 ) )
{
TRACE( "うるう年です。" );
}
else
{
TRACE( "うるう年ではありません。" );
}
「というふうに、 tm 構造体の tm_year を渡す時には 1900 を付けるのを
忘れないようにね」
『うわ、これ危ないね……』
「こうやって 1900 を加えるのも危険だし、〈 tm 構造体から年を取り出す
関数〉とか用意しておくのもいいかも」
『それはそれで面倒……』
「クラスにすれば、メンバ関数を呼ぶようにできるんだけどね……さて、最
後に月末の日付について」
『そだね、2月28日とか2月29日とか』
「って言っても、これはどの月かチェックして、月ごとに別の日付を返すよ
うにするしかないかな……」
『めんどい……』
/*
Preview Next Story!
*/
『月ごとに違うのとか、コーディングすんのめんどいね』
「ところが、実はそんなに面倒じゃないんです」
『え? ひとつずつ打ち込まなくていいの?』
「そう、だいたいの部分は自動化できるんです」
『というわけで次回』
< Version 13.26 手作業撲滅! >
「につづく!」
『って、日時の話は!?』
「次回は番外編!」