#pragma twice

KAB-studio > プログラミング > #pragma twice > 261 Version 13.25 日時の計算

#pragma twice 261 Version 13.25 日時の計算

前のページへ 表紙・目次へ 次のページへ

 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 手作業撲滅! >
「につづく!」
『って、日時の話は!?』
「次回は番外編!」
 
del.icio.us 登録する
Yahoo!ブックマーク 詳細を表示 users
livedoorクリップ 詳細を表示 livedoorクリップ ブックマーク数
はてなブックマーク 詳細を表示 はてなブックマーク ブックマーク数
RSSに登録
del.icio.us 登録する
Yahoo!ブックマーク 詳細を表示 users
livedoorクリップ 詳細を表示 livedoorクリップ ブックマーク数
はてなブックマーク 詳細を表示 はてなブックマーク ブックマーク数
 
このページは、Visual C++ 6.0を用いた C++ 言語プログラミングの解説を行う#pragma twiceの一コンテンツです。
詳しい説明は#pragma twiceのトップページをご覧ください。