先頭へ戻る

日付と時間 解答ページ | Programming Place Plus C言語編 第51章

Programming Place Plus トップページ -- C言語編 -- 第51章

先頭へ戻る

問題①

問題① 標準入力から受け取った西暦年から、現在までの年数を求めるプログラムを作ってください。


たとえば、次のようになります。

#include <stdio.h>
#include <time.h>

int main(void)
{
    puts( "西暦年を入力してください。" );
    char buf[40];
    int input_year;
    fgets( buf, sizeof(buf), stdin );
    sscanf( buf, "%d", &input_year );

    // tm構造体の tm_yearメンバは 1900年からの経過年なので、それに合わせる
    int year = input_year - 1900;

    // 現在の時刻を取得
    time_t nowTime;
    time( &nowTime );
    const struct tm* nowTm = localtime( &nowTime );

    if( nowTm->tm_year <= year ){
        printf( "%d年まであと%d年\n", input_year, year - nowTm->tm_year );
    }
    else{
        printf( "%d年は%d年前\n", input_year, nowTm->tm_year - year );
    }

    return 0;
}

実行結果

西暦年を入力してください。
2030
2030年まであと12年

現在の時刻を取得するには、結局のところ、まずは time関数を使うしかありません。その後、localtime関数を使って、ローカル時間に変換します。

localtime関数が返した tm構造体のポインタから、tm_yearメンバを参照すれば、そこに 1900年からの経過年があります。

問題②

問題② プログラムの処理の進行を、5秒間停止させる方法を考えて、試してみてください。


time関数を使って、実現できます。

#include <stdio.h>
#include <time.h>

static const int STOP_SEC = 5;  // 停止させる秒数

int main(void)
{
    printf( "%d秒間停止します。\n", STOP_SEC );

    time_t begin;
    time( &begin );

    for( ;; ){
        time_t now;
        time( &now );

        if( difftime(now, begin) >= STOP_SEC ){
            break;
        }
    }

    puts( "終了します。" );

    return 0;
}

実行結果

5秒間停止します。
終了します。

停止させる直前で、time関数を呼び出して、その時点のカレンダー時間を取得しておきます。これがあれば、定期的に現在のカレンダー時間を取得して、difftime関数に渡せば、経過秒数を割り出せます。

clock関数を使っても、うまく動くかもしれませんが、clock関数の場合は、他のプログラムが同時に動いていると、正しく計測できない可能性があります。

問題③

問題③ 標準入力から受け取った西暦年・月・日から、その日の曜日を求めて表示するプログラムを作ってください。


曜日を直接求める手段はないので、いくつかの標準ライブラリ関数を組み合わせて実現します。

#include <locale.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <wchar.h>

static const wchar_t* const WEEK_DAY[] = {
    L"日曜日",
    L"月曜日",
    L"火曜日",
    L"水曜日",
    L"木曜日",
    L"金曜日",
    L"土曜日",
};

int main(void)
{
    // LC_CTYPE をネイティブロケールに変更
    if( setlocale( LC_CTYPE, "" ) == NULL ){
        fputs( "ロケールの設定に失敗しました。\n", stderr );
        return EXIT_FAILURE;
    }

    // 年・月・日 以外は関係しないので、まとめて 0 を入れておく
    struct tm tm_in;
    memset( &tm_in, 0, sizeof(tm_in) );

    // 年・月・日 を取得
    puts( "西暦年・月・日を、半角スペース区切りで入力してください。" );
    char buf[80];
    fgets( buf, sizeof(buf), stdin );
    sscanf( buf, "%d %d %d", &tm_in.tm_year, &tm_in.tm_mon, &tm_in.tm_mday );

    // 年は 1900年からの経過年にしておく
    tm_in.tm_year -= 1900;

    // 月は 0 が起点なので -1
    tm_in.tm_mon -= 1;


    // カレンダー時間を取得
    time_t t = mktime( &tm_in );
    if( t == (time_t)-1 ){
        fputs( "日時の変換に失敗しました。\n", stderr );
        return EXIT_FAILURE;
    }

    // 曜日を出力
    wprintf( L"%ls\n", WEEK_DAY[tm_in.tm_wday] );

    return 0;
}

実行結果

西暦年・月・日を、半角スペース区切りで入力してください。
2011 1 1
土曜日

曜日は、tm構造体に、メンバtm_wday として含まれています。本編で触れているようにmktime関数を使えば、tm_wday(とtm_yday)に情報を入れて貰えるので、これを使うのが簡単です。

まず、tm構造体の変数を用意して、西暦年、月、日を表すメンバに情報をセットします。西暦年が 1900年からの経過年、月が 0起点であることに注意してください。その他のメンバには興味がないので、事前にすべて 0 をセットしておきます。

こうして作成した tm構造体の変数を mktime関数に渡せば、その日時を表す time_t型の値が返され、tm構造体の tm_wday、tm_yday が書き換えられます。今回は、戻り値で得られる情報 (time_t型の値) は必要ありませんが、mktime関数は渡す情報の不備によって失敗しやすい関数なので、必ず、戻り値を調べて、エラーチェックを行うようにするべきです。

あとは、曜日の名前を適当に作って出力します。この辺りは、都合の良い標準ライブラリ関数がないので、自分で配列を作って対応しています。


参考リンク


------------------------------------------------------------------------

更新履歴

'2018/5/3 章のタイトルを変更(日付と時間、乱数 -> 日付と時間) 乱数に関する話題を、第54章へ移動。

'2018/3/26 全面的に文章を見直し、修正を行った。

'2016/10/8 問題④の解答例に無駄が多かったので修正し、これに合わせて解説も修正。

'2011/4/7 新規作成。



第51章のメインページへ

C言語編のトップページへ

Programming Place Plus のトップページへ



はてなブックマーク に保存 Pocket に保存 Facebook でシェア
Twitter でツイート Twitter をフォロー LINE で送る
rss1.0 取得ボタン RSS 管理者情報 プライバシーポリシー