#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のトップページをご覧ください。