0020. 年齢から生年月日を求める(C言語)

戻る

getbirthday.cのソースコード】


/*
 * 年齢から生年月日(の範囲)を求めるプログラム getbirthday.c
 *
 * 日本の民法および年齢計算に関する法律の規定に従い、
 * 誕生日の前日で加齢する。
 * 1582年10月15日以降(グレゴリオ暦の範囲内)、9999年12月31日以前で動作する。
 */

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <string.h> /* strlen(), strcmp(), strcpy(), strcat() */
#include <ctype.h>  /* isdigit() */

#define BUF_SIZE 256





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





/*
 * 構造体dateの定義
 */
struct date {
    int year;
    int month;
    int day;
};





/*
 * ユーザー定義関数の関数原型宣言
 */
void print_title(void);
void input(void);
void input_date(struct date *some_date);
bool is_valid_number(char buf[]);
int get_days_of_the_month(int year, int month);
bool is_leap_year(int year);
void sel_outputto(void);
void calc(void);
bool is_invalid_case(void);
void output(void);
void output_result(void);
void foutput_result(char filename[], char oldfilename[]);
void gen_filename(char filename[]);
bool retry(void);





/*
 * グローバル変数
 */
char outputto,
     filename[BUF_SIZE] = {1},    /* filenameとoldfilenameの初期値を      */
     oldfilename[BUF_SIZE] = {0}; /* 異なる物に設定する。                 */
                                  /* (ファイルへの追記の判断に使用する。) */
struct date refdate, start_birthdate, end_birthdate;
int age, refmonthday;





int main(void)
{
    
    /* 乱数系列の設定 */
    srand((unsigned int)time(NULL));
    
    while (1) {
        
        /* 表題・説明の表示 */
        print_title();
        
        while (1) {
            
            /* 年齢計算日および年齢の入力 */
            input();
            
            /* 計算 */
            calc();
            
            /* 例外処理 */
            if (is_invalid_case())
                continue;
            else
                break;
        }
        
        /* 出力先の選択 */
        sel_outputto();
        
        /* 出力 */
        output();
        
        /* 操作を続けるかどうかの選択 */
        if (!retry())
            break;
    }
    return EXIT_SUCCESS;
}





/*
 * 標題およびプログラムの説明を表示する関数
 */
void print_title(void)
{
    printf("【年齢から生年月日(の範囲)を求める】\n");
    printf("任意の年齢計算日における満年齢から、生年月日の範囲を求めます。\n");
    printf("(*1)日本では、民法および年齢計算に関する法律の規定により、\n");
    printf("    誕生日の前日に加齢されます。\n");
    printf("(*2)1582年10月15日以降(グレゴリオ暦の範囲内)、かつ、\n");
    printf("    9999年12月31日以前で動作します。\n\n");
}



/*
 * 年齢計算日および年齢を入力する関数
 * input_date()関数とis_valid_number()関数を使用する
 */
void input(void)
{
    char buf[BUF_SIZE], zz;
    
    while (1) {
        
        /* 年齢計算日の入力 */
        printf("年齢計算日の日付を入力してください。\n");
        
        input_date(&refdate);
        refmonthday = 100*refdate.month + refdate.day;
        
        /* 年齢の入力 */
        printf("満年齢を入力してください。\n");
        while (1) {
            printf("満年齢 = ? ");
            
            fgets(buf, sizeof buf, stdin);
            sscanf(buf, "%d%c", &age, &zz);
            buf[strlen(buf) - 1] = '\0';
            
            if (!is_valid_number(buf)) {
                printf("無効な入力です。\n");
                printf("半角整数値を入力してください。\n\n");
                continue;
            } else if (age < 0) {
                printf("負の年齢の入力は受け付けません。\n");
                printf("0以上の整数を入力してください。\n");
                continue;
            } else
                goto do_exit;
        }
    }
    do_exit:
        ;
}



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



/*
 * 文字列が正しい形式の数値かどうかを判定する関数
 * true: 正しい形式である、false: 無効な形式である。
 */
bool is_valid_number(char buf[])
{
    int i = 0;
    
    /* 最初は符号(省略可) */
    if (buf[i] == '+' || buf[i] == '-')
        i++;
    /* 次に数字(省略不可) */
    if (!isdigit(buf[i]))
        return 0;
    i++;
    /* 次に数日(省略可) */
    while (isdigit(buf[i]))
        i++;
    
    return buf[i] == '\0';
}



/*
 * year年month月に含まれる日数を返す関数
 * is_leap_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) ||
     (month == 9) || (month == 11))                 /* 4、6、9、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;
    else {                                          /* 上記以外                */
        printf("月の日数の計算ができません。処理を終了します。\n");
        exit(EXIT_FAILURE);
    }
    return days;
}



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



/*
 * 出力先(outputto)を選択する関数
 */
void sel_outputto(void)
{
    char buf[BUF_SIZE], zz;
    
    while (1) {
        printf("結果の出力先を1、2、3から選んでください。\n");
        printf("1. 画面だけに出力する。\n");
        printf("2. ファイルだけに出力する。\n");
        printf("3. 画面とファイルの両方に出力する。\n");
        
        fgets(buf, sizeof buf, stdin);
        sscanf(buf, "%c%c", &outputto, &zz);
        
        if (((outputto != '1') && (outputto != '2') && (outputto != '3'))
         || (zz != '\n')) {
            printf("無効な入力です。\n");
            printf("1、2、3のいずれかを入力してください。\n\n");
            continue;
        } else
            break;
    }
}



/*
 * 計算をする関数
 * get_days_of_the_month()関数を使用する。
 */
void calc(void)
{
    /* 年齢が0歳か(【A】)それ以外か(【B】)で場合分けする */
    switch (age) {
     
     /* 【A】年齢が0歳の場合 */
     case 0:
        
        /* 【A-1】年齢計算日が12月31日の場合 */
        if (refmonthday == 1231) {
            end_birthdate.year  = refdate.year - age;
            end_birthdate.month = 12;
            end_birthdate.day   = 31;
            
            start_birthdate.year  = refdate.year - age;
            start_birthdate.month = 1;
            start_birthdate.day   = 2;
        }
        
        /* 【A-2】年齢計算日が12月31日以外の月末の場合 */
        else if
         (
          refmonthday != 1231 &&
          refdate.day ==
           get_days_of_the_month(refdate.year, refdate.month)
         )
        {
            end_birthdate.year  = refdate.year - age;
            end_birthdate.month = refdate.month;
            end_birthdate.day
             = get_days_of_the_month(refdate.year, refdate.month);
            
            start_birthdate.year  = refdate.year - age - 1;
            start_birthdate.month = refdate.month + 1;
            start_birthdate.day   = 2;
        }
        
        /* 【A-3】年齢計算日が12月30日の場合 */
        else if (refmonthday == 1230)
        {
            end_birthdate.year  = refdate.year - age;
            end_birthdate.month = 12;
            end_birthdate.day   = 30;
            
            start_birthdate.year  = refdate.year - age;
            start_birthdate.month = 1;
            start_birthdate.day   = 1;
        }
        
        /* 【A-4】年齢計算日が月末の前日かつ12月30日以外の場合 */
        else if
         (
          refdate.day ==
           get_days_of_the_month(refdate.year, refdate.month) - 1 &&
          refmonthday != 1230
         )
        {
            end_birthdate.year  = refdate.year - age;
            end_birthdate.month = refdate.month;
            end_birthdate.day
             = get_days_of_the_month(refdate.year, refdate.month) - 1;
            
            start_birthdate.year  = refdate.year - age - 1;
            start_birthdate.month = refdate.month + 1;
            start_birthdate.day   = 1;
        }
        
        /* 【A-5】年齢計算日が1~4以外の場合 */
        else
        {
            end_birthdate.year  = refdate.year - age;
            end_birthdate.month = refdate.month;
            end_birthdate.day   = refdate.day;
            
            start_birthdate.year  = refdate.year - age - 1;
            start_birthdate.month = refdate.month;
            start_birthdate.day   = refdate.day + 2;
        }
        break;
      
      /* 【B】年齢が0歳以外の場合 */
      default:
        
        /* 【B-1】年齢計算日が12月31日の場合 */
        if (refmonthday == 1231)
        {
            end_birthdate.year  = refdate.year - age + 1;
            end_birthdate.month = 1;
            end_birthdate.day   = 1;
            
            start_birthdate.year  = refdate.year - age;
            start_birthdate.month = 1;
            start_birthdate.day   = 2;
        }
        
        /* 【B-2】年齢計算日が12月31日以外の月末の場合 */
        else if
         (
          refmonthday != 1231 &&
          refdate.day ==
           get_days_of_the_month(refdate.year, refdate.month)
         )
        {
            end_birthdate.year  = refdate.year - age;
            end_birthdate.month = refdate.month + 1;
            end_birthdate.day   = 1;
            
            start_birthdate.year  = refdate.year - age - 1;
            start_birthdate.month = refdate.month + 1;
            start_birthdate.day   = 2;
        }
        
        /* 【B-3】年齢計算日が12月30日の場合 */
        else if (refmonthday == 1230)
        {
            end_birthdate.year  = refdate.year - age;
            end_birthdate.month = 12;
            end_birthdate.day   = 31;
            
            start_birthdate.year  = refdate.year - age;
            start_birthdate.month = 1;
            start_birthdate.day   = 1;
        }
        
        /* 【B-4】年齢計算日が月末の前日かつ12月30日以外の場合 */
        else if
         (
          refdate.day ==
           get_days_of_the_month(refdate.year, refdate.month) - 1 &&
          refmonthday != 1230
         )
        {
            end_birthdate.year  = refdate.year - age;
            end_birthdate.month = refdate.month;
            end_birthdate.day
             = get_days_of_the_month(refdate.year, refdate.month);
            
            start_birthdate.year  = refdate.year - age - 1;
            start_birthdate.month = refdate.month + 1;
            start_birthdate.day   = 1;
        }
        
        /* 【B-5】年齢計算日が1~4以外の場合 */
        else
        {
            end_birthdate.year  = refdate.year - age;
            end_birthdate.month = refdate.month;
            end_birthdate.day   = refdate.day + 1;
            
            start_birthdate.year  = refdate.year - age - 1;
            start_birthdate.month = refdate.month;
            start_birthdate.day   = refdate.day + 2;
        }
        break;
    }
}



/*
 * 計算結果が無効な場合かどうかの判定
 *
 * 戻り値がtrue : 計算結果がグレゴリオ暦の範囲外(無効な場合)であり、処理をやり直す。
 * 戻り値がfalse: 計算結果が有効な日付であり、次に進む。
 */
bool is_invalid_case(void)
{
    int start_birthdate_val =
         10000*start_birthdate.year  +
           100*start_birthdate.month +
               start_birthdate.day,
        end_birthdate_val   =
         10000*end_birthdate.year  +
           100*end_birthdate.month +
               end_birthdate.day;
    int ret = false;
    
    if (start_birthdate_val < 15821015 || end_birthdate_val < 15821015) {
        printf("計算結果がグレゴリオ暦の範囲外です。");
        printf("入力をやり直してください。\n");
        ret = true;
    }
    
    return ret;
}



/*
 * 出力をする関数
 * output_result()関数とfoutput_result()関数を使用する。
 */
void output(void)
{
    switch (outputto) {
     case '1':
        output_result();
        break;
     case '2':
        foutput_result(filename, oldfilename);
        break;
     case '3':
        output_result();
        foutput_result(filename, oldfilename);
        break;
     default:
        break;
    }
}



/*
 * 結果を画面に出力する関数
 */
void output_result(void)
{
    printf("%d年%d月%d日現在で", refdate.year, refdate.month, refdate.day);
    printf("%d歳の人の生年月日は、", age);
    printf("%d年%d月%d日から%d年%d月%d日の間です。\n",
     start_birthdate.year, start_birthdate.month, start_birthdate.day,
     end_birthdate.year, end_birthdate.month, end_birthdate.day);
}



/*
 * 結果をファイルに出力する関数
 * gen_filename()関数を使用する。
 */
void foutput_result(char filename[], char oldfilename[])
{
    FILE *fp;
    
    /* ifによる分岐は、2行目以降を同一ファイルに追記出力させるため                      */
    if (strcmp(filename, oldfilename) == 0) { /* ファイル名が同じならば(2行目以降なら)  */
        fp = fopen(oldfilename, "a");         /* oldfilenameを開く。                    */
        if (fp == NULL) {
            printf("ファイルを開けません。ファイル出力を中断します。\n");
            return;
        }
    } else {                                  /* ファイル名が違うなら(初めての出力なら) */
        gen_filename(filename);               /* 新規にファイル名を生成して             */
        fp = fopen(filename, "a");            /* そのファイル(filename)を開いて         */
        strcpy(oldfilename, filename);        /* oldfilenameを更新する                  */
        if (fp == NULL) {
            printf("ファイルを開けません。ファイル出力を中断します。");
            return;
        }
    }
    fprintf(fp, "%d年%d月%d日現在で", refdate.year, refdate.month, refdate.day);
    fprintf(fp, "%d歳の人の生年月日は、", age);
    fprintf(fp, "%d年%d月%d日から%d年%d月%d日の間です。\n",
     start_birthdate.year, start_birthdate.month, start_birthdate.day,
     end_birthdate.year, end_birthdate.month, end_birthdate.day);
}



/*
 * 現在日付時刻からファイル名を生成する関数
 */
void gen_filename(char filename[])
{
    struct tm *t;
    time_t tt;
    char snumrand[5];
    
    time(&tt);
    t = localtime(&tt);
    
    sprintf(filename, "getbirthday%04d%02d%02d_%02d%02d%02d_",
     1900+(t->tm_year), 1+(t->tm_mon), t->tm_mday,
     t->tm_hour, t->tm_min, t->tm_sec);
    sprintf(snumrand, "%04d", rand() % 10000);
    filename = strcat(filename, snumrand);
    filename = strcat(filename, ".txt");
}



/*
 * 操作を続けるかどうかを選択する関数
 * 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;
}


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

C:\Users\skonishi\Documents>getbirthday
【年齢から生年月日(の範囲)を求める】
任意の年齢計算日における満年齢から、生年月日の範囲を求めます。
(*1)日本では、民法および年齢計算に関する法律の規定により、
    誕生日の前日に加齢されます。
(*2)1582年10月15日以降(グレゴリオ暦の範囲内)、かつ、
    9999年12月31日以前で動作します。

年齢計算日の日付を入力してください。
年 = ?(西暦で) 2024
月 = ? 10
日 = ? 27
満年齢を入力してください。
満年齢 = ? 17
結果の出力先を1、2、3から選んでください。
1. 画面だけに出力する。
2. ファイルだけに出力する。
3. 画面とファイルの両方に出力する。
2
続けますか?(y/n) y

+++++

【年齢から生年月日(の範囲)を求める】
任意の年齢計算日における満年齢から、生年月日の範囲を求めます。
(*1)日本では、民法および年齢計算に関する法律の規定により、
    誕生日の前日に加齢されます。
(*2)1582年10月15日以降(グレゴリオ暦の範囲内)、かつ、
    9999年12月31日以前で動作します。

年齢計算日の日付を入力してください。
年 = ?(西暦で) 2024
月 = ? 10
日 = ? 27
満年齢を入力してください。
満年齢 = ? 18
結果の出力先を1、2、3から選んでください。
1. 画面だけに出力する。
2. ファイルだけに出力する。
3. 画面とファイルの両方に出力する。
2
続けますか?(y/n) n


【出力されたファイルgetbirthday20241209_225328_7994.txtの内容】

2024年10月27日現在で17歳の人の生年月日は、2006年10月29日から2007年10月28日の間です。
2024年10月27日現在で18歳の人の生年月日は、2005年10月29日から2006年10月28日の間です。


【解説】
 上の結果は、先の2024年10月27日に投開票が行われた衆議院議員選挙の、投票資格の年齢要件にかかわるものである。すなわち、投票日の2024年10月27日現在で満18歳以上の者に投票資格があり、満18歳未満の者には資格がない、という規定にかかわる。現在の日本の法令(民法および年齢計算に関する法律)では、誕生日の前日に加齢されると規定されており、2006年(平成18年)10月28日以前に出生した者が「投票日現在で18歳以上」という要件を満たして投票資格を有し、2006年(平成18年)10月29日以降に出生した者はその要件を満たさないので資格を有さない、ということを表している。

戻る