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

トップページ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 now_time;
    time(&now_time);
    const struct tm* now_tm = localtime(&now_time);

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

実行結果:

西暦年を入力してください。
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("終了します。");
}

実行結果:

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]);
}

実行結果:

西暦年・月・日を、半角スペース区切りで入力してください。
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関数は渡す情報の不備によって失敗しやすい関数なので、必ず、戻り値を調べて、エラーチェックを行うようにするべきです。

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


参考リンク 🔗


更新履歴 🔗

≪さらに古い更新履歴≫

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

 全面的に文章を見直し、修正を行った。

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

 新規作成。



第51章のメインページへ

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

Programming Place Plus のトップページへ



はてなブックマーク に保存 Pocket に保存 Facebook でシェア
X で ポストフォロー LINE で送る noteで書く
rss1.0 取得ボタン RSS 管理者情報 プライバシーポリシー
先頭へ戻る