0017. 万年カレンダー(C言語)

戻る

mannen.cのソースコード】


/*
 * 万年カレンダー mannen.c
 * グレゴリオ暦上(1582年10月15日以降)、9999年12月31日以前で動作する。
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h> /* strlen()  */
#include <ctype.h>  /* isdigit() */

#define BUF_SIZE 256





/*
 * 自作のbool型の定義
 */
typedef enum {
    false, /* 0 */
    true   /* 1 */
} bool;





/*
 * ユーザー定義関数のプロトタイプ宣言
 */
void print_title(void);
void input_ym(void);
bool is_valid_number(char buf[]);
void calc_var(void);
int get_days_of_the_month(int year, int month);
bool is_leap_year(int year);
void print_calendar(void);
bool retry(void);





/*
 * グローバル変数
 */
int year, month,
    y, m, d, c, b, /* ツェラーの公式用の変数 */
    day = 1,       /* 日は当月の1日に固定    */
    wd;            /* 当月の1日の曜日[0,6]   */





/*
 * メイン関数
 */
int main(void)
{
    while (1) {
        
        /* 表題の表示 */
        print_title();
        
        /* 年、月の入力 */
        input_ym();
        
        /* 暦の各変数の計算 */
        calc_var();
        
        /* カレンダーの表示 */
        print_calendar();
        
        /* 操作を続けるかどうかの選択 */
        if (!retry())
            break;
    }
    
    return EXIT_SUCCESS;
}





/*
 * 表題を表示する関数
 */
void print_title(void)
{
    printf("【万年カレンダー】\n");
    printf("グレゴリオ暦上(1582年10月15日以降)、9999年12月31日以前で\n");
    printf("カレンダーを表示します。\n\n");
}



/*
 * 年、月を入力する関数
 * is_valid_number()関数を使用する。
 */
void input_ym(void)
{
    char buf[BUF_SIZE], zz;
    
    while (1) {
        
        while (1) {
            printf("年 = ?(西暦で) ");
            
            fgets(buf, sizeof buf, stdin);
            sscanf(buf, "%d%c", &year, &zz);
            buf[strlen(buf) - 1] = '\0';
            
            if (!is_valid_number(buf)) {
                printf("無効な入力です。\n");
                printf("半角整数値を入力してください。\n\n");
                continue;
            } else if (year < 1582) {
                printf("%d年はグレゴリオ暦の範囲外です。\n", year);
                printf("1582年以降の年を入力してください。\n\n");
                continue;
            } else if (year > 9999) {
                printf("無効な入力です。\n");
                printf("9999年以前の年を入力してください。\n\n");
                continue;
            } else
                break;
        }
        
        while (1) {
            printf("月 = ? ");
            
            fgets(buf, sizeof buf, stdin);
            sscanf(buf, "%d%c", &month, &zz);
            buf[strlen(buf) - 1] = '\0';
            
            if (!is_valid_number(buf)) {
                printf("無効な入力です。\n");
                printf("半角整数値を入力してください。\n\n");
                continue;
            } else if ((month < 1) || (12 < month)) {
                printf("無効な入力です。\n");
                printf("1から12の整数値を入力してください。\n\n");
                continue;
            } else
                break;
        }
        
        /* 入力結果がグレゴリオ暦の範囲外となった場合の処理 */
        if (100*year+month < 158210) {
            printf("%d年%d月はグレゴリオ暦の範囲外です。\n", year, month);
            printf("1582年10月以降の年月を入力してください。\n\n");
            continue;
        } else
            break;
    }
}



/*
 * 文字列が正しい形式の数値かどうかを判定する関数
 */
bool is_valid_number(char buf[])
{
    int i = 0;
    
    /* 最初は符号(省略可) */
    if ((buf[i] == '+') || (buf[i] == '-'))
        i++;
    /* 次に数字(省略可) */
    if (!isdigit(buf[i]))
        return false;
    i++;
    /* 次に数字列(省略可) */
    while (isdigit(buf[i]))
        i++;
    
    /* ほかに余計な文字がなければ真を返す */
    return buf[i] == '\0';
}



/*
 * 暦の各変数を計算する関数
 * get_days_of_the_month()関数を使用する。
 */
void calc_var(void)
{
    d = get_days_of_the_month(year, month);
    
    /* うるう月を最後に回すために、3月を1月、4月を2月、…と */
    /* 数え直し、1月、2月はそれぞれ前年の11月、12月とする。 */
    y = year;
    m = month - 2;
    if (m < 1) {
        m += 12;
        y--;
    }
    
    /* ツェラーの公式で曜日を求める。 */
    c = y / 100; /* 年(西暦)の上2桁 */
    b = y % 100; /* 年(西暦)の下2桁 */
    wd = ((26 * m - 2) / 10 + day + b + b / 4 /* ツェラーの公式 */
     + 5 * c + c / 4) % 7;
}



/*
 * year年month月に含まれる日数を返す関数
 * is_lear_year()関数を使用する。
 */
int get_days_of_the_month(int year, int month)
{
    int days;
    
    if (is_leap_year(year) && (month == 2))       /* うるう年の2月           */
        days = 29;
    else if (!is_leap_year(year) && (month == 2)) /* うるう年でない2月       */
        days = 28;
    else if ((month == 4) || (month == 6) ||      /* 4、6、9、11月           */
     (month == 9) || (month == 11))
        days = 30;
    else if ((month == 1) || (month == 3) ||      /* 1、3、5、7、8、10、12月 */
     (month == 5) || (month == 7) ||
     (month == 8) || (month == 10) ||
     (month == 12))
        days = 31;
    
    return days;
}



/*
 * うるう年の判定をする関数
 */
bool is_leap_year(int year)
{
    /* 400で割り切れればうるう年                        */
    /* 400で割り切れず、100で割り切れればうるう年でない */
    /* 100で割り切れず、4で割り切れればうるう年         */
    /* 4で割り切れればうるう年でない                    */
    
    return ((year % 400 == 0) || ((year % 100) && (year % 4 == 0)));
}



/*
 * カレンダーを表示する関数
 */
void print_calendar(void)
{
    int i;
    
    if ((year == 1582) && (month == 10)) {
        
        /* ツェラーの公式で1582年10月15日の曜日を求める。 */
        c = y / 100; /* 年(西暦)の上2桁 */
        b = y % 100; /* 年(西暦)の下2桁 */
        wd = ((26 * m - 2) / 10 + 15 + b + b / 4 /* ツェラーの公式でday==15とする */
         + 5 * c + c / 4) % 7;
        
        printf("%14d年%2d月\n", year, month);
        printf("  日  月  火  水  木  金  土\n");
        for (i = 0; i < wd; i++)
            printf("    ");
        for (i = 15; i <= d; i++) { /* 15日以降を表示する */
            printf("%4d", i);
            if ((i + wd) % 7 == 0)
                printf("\n");
        }
        printf("\n");
    } else {
        printf("%14d年%2d月\n", year, month);
        printf("  日  月  火  水  木  金  土\n");
        for (i = 0; i < wd; i++)
            printf("    ");
        for (i = 1; i <= d; i++) {
            printf("%4d", i);
            if ((i + wd) % 7 == 0)
                printf("\n");
        }
        printf("\n");
    }
}



/*
 * 操作を続けるかどうかを選択する関数
 * true: 繰り返す、false: 終了する
 */
bool retry(void)
{
    bool ret;
    char buf[BUF_SIZE], ch, zz;
    
    while (1) {
        printf("続けますか?(y/n) ");
        
        fgets(buf, sizeof buf, stdin);
        sscanf(buf, "%c%c", &ch, &zz);
        
        if (((ch == 'y') || (ch == 'Y')) && (zz == '\n')) {
            printf("\n+++++\n\n");
            ret = true;
            break;
        } else if (((ch == 'n') || (ch == 'N')) && (zz == '\n')) {
            ret = false;
            break;
        }
        printf("無効な入力です。\n");
        printf("yまたはnを入力してください。\n\n");
    }
    
    return ret;
}


mannenの実行時画面表示】赤字はキーボードからの入力を表す。

C:\Users\skonishi\Documents>mannen
【万年カレンダー】
グレゴリオ暦上(1582年10月15日以降)、9999年12月31日以前で
カレンダーを表示します。

年 = ?(西暦で) a
無効な入力です。                                  ←正しい形式の数値でなければ無効。
半角整数値を入力してください。

年 = ?(西暦で) 1
1年はグレゴリオ暦の範囲外です。                   ←1,581年以前の年は無効。
1582年以降の年を入力してください。

年 = ?(西暦で) 10000
無効な入力です。                                  ←10,000年以降の年は無効。
9999年以前の年を入力してください。

年 = ?(西暦で) 1582
月 = ? 9
1582年9月はグレゴリオ暦の範囲外です。             ←年月はグレゴリオ暦の範囲内に限る。
1582年10月以降の年月を入力してください。

年 = ?(西暦で) 1582
月 = ? 10
          1582年10月
  日  月  火  水  木  金  土
                      15  16
  17  18  19  20  21  22  23
  24  25  26  27  28  29  30
  31
続けますか?(y/n) y

+++++

【万年カレンダー】
グレゴリオ暦上(1582年10月15日以降)、9999年12月31日以前で
カレンダーを表示します。

年 = ?(西暦で) 2023
月 = ? 7
          2023年 7月
  日  月  火  水  木  金  土
                           1
   2   3   4   5   6   7   8
   9  10  11  12  13  14  15
  16  17  18  19  20  21  22
  23  24  25  26  27  28  29
  30  31
続けますか?(y/n) n


【参考文献】
・玉井浩、「新・演習と応用 プログラミング言語=1 演習と応用C」、(株)サイエンス社・1999年発行。

戻る