0003. 日付の計算(C言語)

戻る

2021年4月14日掲載、2026年3月10日更新

datecalc.cのソースコード】


/*
 * 日付の間の計算を行うプログラム
 * datecalc.c(date calculation)
 *
 * 1582年10月15日以降(グレゴリオ暦上)、9999年12月31日以前で動作する。
 * 西暦0年1月1日((*)拡張したグレゴリオ暦による)からの日数で計算する。
 */
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <string.h> /* strcspn, strcmp, strcat */
#include <ctype.h>  /* isdigit */

#define BUF_SIZE 256





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



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





/*
 * ユーザー定義関数の関数原型宣言
 */
void initialize(void);
void menu(char *sel_procp);
void input_enter(void);
void proc1_print_title(void);
bool proc1_input(struct date *d1p, struct date *d2p);
bool input_date(struct date *dp);
bool input_year(int *yearp);
bool is_valid_integer(char buf[]);
bool input_month(int *monthp);
bool input_day(int year, int month, int *dayp);
int get_days_of_the_month(int year, int month);
bool is_leap_year(int year);
int calc_date_diff(struct date d1, struct date d2);
int get_days_to_the_date(struct date d);
int get_days_to_the_year(int year);
int get_days_in_the_year(struct date d);
void proc1_output(struct date d1, struct date d2, int days);
void comma_sep(char *sp, int n);
void output_result_diff(struct date d1, struct date d2, char strdays[]);
void foutput_result_diff(struct date d1, struct date d2, char strdays[]);
void gen_filename(char filename[]);
bool retry(void);
void proc2_print_title(void);
bool proc2_input(struct date *d1p, int *days, char *opp);
struct date calc_addsub(struct date base, int days, char op);
bool is_out_of_range(struct date d);
void proc2_output(struct date d1, struct date d2, int days, char op);
void output_result_addsub(struct date d1, struct date d2,
    char strdays[], char addsub);
void foutput_result_addsub(struct date d1, struct date d2,
    char strdays[], char addsub);





/*
 * グローバル変数
 */
bool is_first_output = true;
char filename[BUF_SIZE];





/*
 * メイン関数
 */
int main(void)
{
    struct date date1, date2;
    int days;
    char addsub, sel_proc;
    
    /* 初期化 */
    initialize();
    
    while (1) {
        do_menu:
            /* メニュー */
            menu(&sel_proc);
        
        /* メニューからの処理の分岐 */
        switch (sel_proc) {
        
            /* 処理1(差の計算) */
            case '1':
                /* 処理1中の標題の表示 */
                proc1_print_title();
                
                /* 処理1中の入力 */
                if (!proc1_input(&date1, &date2))
                    /* qが押されたらメニューへ */
                    goto do_menu;
                
                /* 処理1中の計算 */
                days = calc_date_diff(date1, date2);
                
                /* 処理1中の出力 */
                proc1_output(date1, date2, days);
                
                /* 操作を繰り返すかどうかの選択 */
                if (!retry())
                    goto do_end;
                break;
            
            /* 処理2(日数の足し算引き算) */
            case '2':
                /* 処理2中の標題の表示 */
                proc2_print_title();
                
                while (1) {
                    /* 処理2中の入力 */
                    if (!proc2_input(&date1, &days, &addsub))
                        /* qが押されたらメニューへ */
                        goto do_menu;
                    
                    /* 処理2中の計算 */
                    date2 = calc_addsub(date1, days, addsub);
                    
                    /* 計算結果の日付が計算対象の範囲外かどうかで分岐 */
                    if (!is_out_of_range(date2))
                        break;
                }
                
                /* 処理2中の出力 */
                proc2_output(date1, date2, days, addsub);
                
                /* 操作を繰り返すかどうかの選択 */
                if (!retry())
                    goto do_end;
                break;
            
            /* 処理0(プログラムの終了) */
            case '0':
                goto do_end;
        }
    }
    do_end:
        printf("\nプログラムを終了します。\n");
        return EXIT_SUCCESS;
}





/*
 * 初期化を行う関数
 */
void initialize()
{
    /* 乱数系列の設定 */
    srand((unsigned int)time(NULL));
}



/*
 * メニューの関数
 * input_enter関数を使用する。
 */
void menu(char *sel_procp)
{
    char buf[BUF_SIZE], zz;
    
    /* 標題の表示 */
    printf("【日付に関する2通りの計算を行います】\n\n");
    input_enter();
    printf("次のいずれかを選択してください。\n\n");
    
    /* 処理の選択 */
    while (1) {
        printf("1.2つの日付の間の差を計算する。\n"
            "2.ある日付に日数を足したり引いたりする。\n"
            "0.終了する。\n\n0、1、2のどれですか? ");
        fgets(buf, sizeof buf, stdin);
        sscanf(buf, "%c%c", sel_procp, &zz);
        if ((*sel_procp != '0' && *sel_procp != '1' && *sel_procp != '2') ||
            zz != '\n') {
            printf("\n無効な入力です。\n\n");
            input_enter();
            printf("0、1、2のいずれかを入力してください。\n\n");
            continue;
        }
        break;
    }
}



/*
 * エンターキーの入力を受け付ける関数
 */
void input_enter(void)
{
    char buf[BUF_SIZE], zz;
    
    while (1) {
        printf("エンターキーを押してください・・・");
        fgets(buf, sizeof buf, stdin);
        sscanf(buf, "%c", &zz);
        if (zz != '\n')
            continue;
        break;
    }
    printf("\n");
}



/*
 * 処理1中の標題を表示する関数
 * input_enter関数を使用する。
 */
void proc1_print_title(void)
{

    printf("\n【2つの日付の間の差を計算します】\n\n");
    input_enter();
    printf("「日付1」をより古い日付、「日付2」をより新しい日付とします。\n\n");
    input_enter();
}



/*
 * 処理1中の入力処理をする関数
 * true: 次に進む、false: 中断してメニューに戻る。
 * input_date関数およびinput_enter関数を使用する。
 */
bool proc1_input(struct date *d1p, struct date *d2p)
{
    while (1) {
        printf("まず、日付1を入力してください。\n"
            "入力を中断したい場合は、qを入力してください。\n\n");
        if (!input_date(d1p)) {
            printf("\n+++++\n\n");
            return false;
        }
        
        printf("\n次に、日付2を入力してください。\n"
            "入力を中断したい場合は、qを入力してください。\n\n");
        if (!input_date(d2p)) {
            printf("\n+++++\n\n");
            return false;
        }
        
        if (10000*d1p->year + 100*d1p->month + d1p->day >
            10000*d2p->year + 100*d2p->month + d2p->day) {
            printf("日付1と日付2の順序が逆です。\n\n");
            input_enter();
            printf("日付1がより古い日付、日付2がより新しい日付となるように"
                "入力してください。\n\n");
            continue;
        }
        break;
    }
    return true;
}



/*
 * 日付を入力する関数
 * true: 次に進む、false: 中断してメニューに戻る。
 * input_year関数、input_month関数、input_day関数および
 * input_enter関数を使用する。
 */
bool input_date(struct date *dp)
{
    while (1) {
        /* 年の入力 */
        if (!input_year(&dp->year))
            return false;
        
        /* 月の入力 */
        if (!input_month(&dp->month))
            return false;
        
        /* 日の入力 */
        if (!input_day(dp->year, dp->month, &dp->day))
            return false;
        
        /* 入力結果がグレゴリオ暦の範囲内かどうかで分岐 */
        if (10000*dp->year + 100*dp->month + dp->day
            < 15821015) {
            printf("\n無効な入力です。\n\n");
            input_enter();
            printf("%d年%d月%d日はグレゴリオ暦の範囲外です。\n"
                "本プログラムは、グレゴリオ暦上でしか動作しません。\n"
                "1582年10月15日以降の日付を入力してください。\n"
                "入力を中断したい場合は、qを入力してください。\n\n",
                dp->year, dp->month, dp->day);
            continue;
        }
        break;
    }
    return true;
}



/*
 * 年を入力する関数
 * true: 次に進む、false: 中断してメニューに戻る。
 * is_valid_integer関数およびinput_enter関数を使用する。
 */
bool input_year(int *yearp)
{
    char buf[BUF_SIZE], zz;
    
    while (1) {
        printf("年 = ?(西暦で) ");
        fgets(buf, sizeof buf, stdin);
        sscanf(buf, "%d%c", yearp, &zz);
        buf[strcspn(buf, "\n")] = '\0';
        
        /* 中断判定 */
        if (strcmp(buf, "q") == 0 || strcmp(buf, "Q") == 0)
            return false;
        
        if (!is_valid_integer(buf)) {
            printf("\n無効な入力です。\n\n");
            input_enter();
            printf("半角整数値を入力してください。\n"
                "入力を中断したい場合は、qを入力してください。\n\n");
            continue;
        }
        if (*yearp < 0) {
            printf("\n無効な入力です。\n\n");
            input_enter();
            printf("負の値は受け付けません。\n"
                "入力をやり直してください。\n"
                "入力を中断したい場合は、qを入力してください。\n\n");
            continue;
        }
        if (*yearp < 1582) {
            printf("\n無効な入力です。\n\n");
            input_enter();
            printf("西暦%d年はグレゴリオ暦の範囲外です。\n"
                "1582年以降の年を入力してください。\n"
                "入力を中断したい場合は、qを入力してください。\n\n",*yearp);
            continue;
        }
        if (9999 < *yearp) {
            printf("\n無効な入力です。\n\n");
            input_enter();
            printf("本プログラムは9999年までにしか対応していません。\n"
                "9999年以前の年を入力してください。\n"
                "入力を中断したい場合は、qを入力してください。\n\n");
            continue;
        }
        break;
    }
    return true;
}



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



/*
 * 月を入力する関数
 * true: 次に進む、false: 中断してメニューに戻る。
 * is_valid_integer関数およびinput_enter関数を使用する。
 */
bool input_month(int *monthp)
{
    char buf[BUF_SIZE], zz;
    
    while (1) {
        printf("月 = ? ");
        fgets(buf, sizeof buf, stdin);
        sscanf(buf, "%d%c", monthp, &zz);
        buf[strcspn(buf, "\n")] = '\0';
        
        /* 中断判定 */
        if (strcmp(buf, "q") == 0 || strcmp(buf, "Q") == 0)
            return false;
        
        if (!is_valid_integer(buf)) {
            printf("\n無効な入力です。\n\n");
            input_enter();
            printf("半角整数値を入力してください。\n"
                "入力を中断したい場合は、qを入力してください。\n\n");
            continue;
        }
        if (*monthp < 1 || 12 < *monthp) {
            printf("\n無効な入力です。\n\n");
            input_enter();
            printf("月は1から12までです。\n"
                "1から12の整数値を入力してください。\n"
                "入力を中断したい場合は、qを入力してください。\n\n");
            continue;
        }
        break;
    }
    return true;
}



/*
 * 日を入力する関数
 * true: 次に進む、false: 中断してメニューに戻る。
 * get_days_of_the_month関数、is_valid_integer関数および
 * input_enter関数を使用する。
 */
bool input_day(int year, int month, int *dayp)
{
    char buf[BUF_SIZE], zz;
    int days = get_days_of_the_month(year, month);
    
    while (1) {
        printf("日 = ? ");
        fgets(buf, sizeof buf, stdin);
        sscanf(buf, "%d%c", dayp, &zz);
        buf[strcspn(buf, "\n")] = '\0';
        
        /* 中断判定 */
        if (strcmp(buf, "q") == 0 || strcmp(buf, "Q") == 0)
            return false;
        
        if (!is_valid_integer(buf)) {
            printf("\n無効な入力です。\n\n");
            input_enter();
            printf("半角整数値を入力してください。\n"
                "入力を中断したい場合は、qを入力してください。\n\n");
            continue;
        }
        if (*dayp < 1 || days < *dayp) {
            printf("\n無効な入力です。\n\n");
            input_enter();
            printf("%d年%d月は、1日から%d日までです。\n"
                "1から%dの整数値を入力してください。\n"
                "入力を中断したい場合は、qを入力してください。\n\n",
                year, month, days, days);
            continue;
        }
        break;
    }
    return true;
}



/*
 * ある月に含まれる日数を返す関数
 * is_leap_year関数を使用する。
 */
int get_days_of_the_month(int year, int month)
{
    int days;
    
    switch (month) {
        /* 2月 */
        case 2:
            if (is_leap_year(year))
                days = 29;
            else
                days = 28;
            break;
        
        /* 4、6、9、11月 */
        case 4:
        case 6:
        case 9:
        case 11:
            days = 30;
            break;
        
        /* 1、3、5、7、8、10、12月 */
        case 1:
        case 3:
        case 5:
        case 7:
        case 8:
        case 10:
        case 12:
            days = 31;
            break;
    }
    return days;
}



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



/*
 * 処理1中の計算をする関数
 * get_days_to_the_date関数を使用する。
 */
int calc_date_diff(struct date d1, struct date d2)
{
    return
        get_days_to_the_date(d2) -
        get_days_to_the_date(d1);
}



/*
 * 西暦0年1月1日(*)からその日までの日数を返す関数
 * ((*)拡張したグレゴリオ暦による)
 * get_days_to_the_year関数とget_days_in_the_year関数を使用する。
 */
int get_days_to_the_date(struct date d)
{
    return
        get_days_to_the_year(d.year) +
        get_days_in_the_year(d);
}



/*
 * 西暦0年1月1日(*)からその年の1月1日までの日数を返す関数
 * ((*)拡張したグレゴリオ暦による)
 * is_leap_year関数を使用する。
 */
int get_days_to_the_year(int year)
{
    int numleap = 0, i;
    
    for (i = 0; i < year; i++)
        if (is_leap_year(i))
            numleap++;
    return (365 * year + numleap);
}



/*
 * その年(*)の1月1日からその日までの日数を返す関数
 * ((*)拡張したグレゴリオ暦による)
 * get_days_of_the_month関数を使用する。
 */
int get_days_in_the_year(struct date d)
{
    int i, temp_days = d.day - 1;
    
    for (i = d.month - 1; i > 0; i--)
        temp_days += get_days_of_the_month(d.year, i);
    return temp_days;
}



/*
 * 処理1中の出力処理をする関数
 * comma_sep関数、output_result_diff関数、foutput_result_diff関数および
 * input_enter関数を使用する。
 */
void proc1_output(struct date d1, struct date d2, int days)
{
    char strdays[BUF_SIZE], buf[BUF_SIZE], ch, zz;
    
    /* 日数を3桁ずつコンマで区切った文字列に変換 */
    comma_sep(strdays, days);
    
    /* 画面への出力 */
    output_result_diff(d1, d2, strdays);
    
    /* ファイルに出力するかどうかの分岐 */
    while (1) {
        printf("\nファイルに出力しますか?(y/n) ");
        fgets(buf, sizeof buf, stdin);
        sscanf(buf, "%c%c", &ch, &zz);
        if ((ch == 'y' || ch == 'Y') && zz == '\n') {
            foutput_result_diff(d1, d2, strdays);
            break;
        }
        else if ((ch == 'n' || ch == 'N') && zz == '\n')
            break;
        printf("\n無効な入力です。\n\n");
        input_enter();
        printf("yまたはnを入力してください。\n\n");
    }
}



/*
 * 数値を3桁ずつコンマで区切った文字列に変換する関数
 */
void comma_sep(char *sp, int n)
{
    char *p = sp, c;
    int digit = 0, m = n, len = 0;
    
    /* nの桁数をlenに取得 */
    if (m == 0)
        len = 1;
    while (m > 0) {
        m /= 10;
        len++;
    }
    
    /* nが0の場合は"0"を格納 */
    if (n == 0) {
        *p++ = '0';
        *p-- = '\0';
    }
    /* n > 0の場合は1の位からコンマで区切って格納 */
    else {
        while (n > 0) {
            *p++ = '0' + (n % 10);
            n /= 10;
            digit++;
            if (digit % 3 == 0 && digit != len)
                *p++ = ',';
        }
        *p-- = '\0';
    }
    
    /* 文字列を逆順に並べ替え */
    while (sp < p) {
        c = *sp;
        *sp++ = *p;
        *p-- = c;
    }
}



/*
 * 差の計算の結果を画面に出力する関数
 */
void output_result_diff(struct date d1, struct date d2, char strdays[])
{
    printf("%d年%d月%d日と%d年%d月%d日の差は、%s日です。\n",
        d1.year, d1.month, d1.day, d2.year, d2.month, d2.day, strdays);
}



/*
 * 差の計算の結果をファイルに出力する関数
 * gen_filename関数を使用する。
 */
void foutput_result_diff(struct date d1, struct date d2, char strdays[])
{
    FILE *fp;
    
    /* 新規ファイル出力か追記かの分岐 */
    if (is_first_output) {
        /* 初めてのファイル出力のときだけファイル名を生成して新規作成 */
        gen_filename(filename);
        fp = fopen(filename, "w");
        if (fp == NULL) {
            printf("ファイルを開けません。ファイル出力を中断します。\n");
            return;
        }
        is_first_output = false; /* 以降は追記状態になる */
    }
    else {
        /* 2回目以降のファイル出力は追記 */
        fp = fopen(filename, "a");
        if (fp == NULL) {
            printf("ファイルを開けません。ファイル出力を中断します。\n");
            return;
        }
    }
    
    fprintf(fp, "%d年%d月%d日と%d年%d月%d日の差は、%s日です。\n",
        d1.year, d1.month, d1.day, d2.year, d2.month, d2.day, strdays);
    fclose(fp);
}



/*
 * 現在日付時刻からファイル名を生成する関数
 */
void gen_filename(char filename[])
{
    char strnumrand[10];
    time_t tt = time(NULL);
    struct tm *t = localtime(&tt);
    
    sprintf(filename, "datecalc%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(strnumrand, "%04d", rand() % 10000);
    strcat(filename, strnumrand);
    strcat(filename, ".txt");
}



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



/*
 * 処理2中の標題を表示する関数
 */
void proc2_print_title(void)
{
    printf("\n【日付に日数を足したり引いたりします】\n\n");
}



/*
 * 処理2中の入力処理をする関数
 * true: 次に進む、false: 中断してメニューに戻る。
 * input_date関数、input_enter関数およびis_valid_integer関数を使用する。
 */
bool proc2_input(struct date *d1p, int *daysp, char *opp)
{
    char buf[BUF_SIZE], zz;
    
    /* 日付の入力 */
    printf("日付を入力してください。\n"
        "入力を中断したい場合は、qを入力してください。\n");
    if (!input_date(d1p)) {
        printf("\n+++++\n\n");
        return false;
    }
    
    /* +か-かの入力 */
    while (1) {
        printf("足し算(+)か引き算(-)かを入力してください。\n"
            "入力を中断したい場合は、qを入力してください。\n"
            "「+」ですか、「-」ですか? ");
        fgets(buf, sizeof buf, stdin);
        sscanf(buf, "%c%c", opp, &zz);
        buf[strcspn(buf, "\n")] = '\0';
        
        /* 中断判定 */
        if (strcmp(buf, "q") == 0 || strcmp(buf, "Q") == 0) {
            printf("\n+++++\n\n");
            return false;
        }
        
        if ((*opp != '+' && *opp != '-') || zz != '\n') {
            printf("\n無効な入力です。\n\n");
            input_enter();
            printf("半角の「+」または「-」を入力してください。\n\n");
            continue;
        }
        break;
    }
    
    /* 日数の入力 */
    while (1) {
        printf("日数を入力してください。\n"
            "入力を中断したい場合は、qを入力してください。\n"
            "日数 = ? ");
        fgets(buf, sizeof buf, stdin);
        sscanf(buf, "%d%c", daysp, &zz);
        buf[strcspn(buf, "\n")] = '\0';
        
        /* 中断判定 */
        if (strcmp(buf, "q") == 0 || strcmp(buf, "Q") == 0) {
            printf("\n+++++\n\n");
            return false;
        }
        
        if (!is_valid_integer(buf)) {
            printf("\n無効な入力です。\n\n");
            input_enter();
            printf("半角整数値を入力してください。\n\n");
            continue;
        }
        if (*daysp < 0) {
            printf("\n無効な入力です。\n\n");
            input_enter();
            printf("負の日数の入力は受け付けません。\n"
                "0以上の整数を入力してください。\n\n");
            continue;
        }
        break;
    }
    return true;
}



/*
 * 日付に日数を足したり引いたりする関数
 * get_days_of_the_month関数を使用する。
 */
struct date calc_addsub(struct date base, int days, char op)
{
    struct date result = base;
    
    /* 足し算か引き算か */
    switch (op) {
    
        /* 足し算の場合 */
        case '+':
            result.day += days;
            while (result.day >
                get_days_of_the_month(result.year, result.month)) {
                
                result.day -=
                    get_days_of_the_month(result.year, result.month);
                
                result.month++;
                
                /* 年をまたぐ場合 */
                if (result.month > 12) {
                    result.year++;
                    result.month = 1;
                }
            }
            break;
        
        /* 引き算の場合 */
        case '-':
            result.day -= days;
            while (result.day < 1) {
                
                result.month--;
                
                /* 年をまたぐ場合 */
                if (result.month < 1) {
                    result.year--;
                    result.month = 12;
                }
                
                result.day +=
                    get_days_of_the_month(result.year, result.month);
            }
            break;
    }
    return result;
}



/*
 * 計算結果の日付が計算対象の範囲外かどうかを判定する関数
 * true: 範囲外である、false: 範囲内である。
 * input_enter関数を使用する。
 */
bool is_out_of_range(struct date d)
{
    bool flag = true;
    int dvalue = 10000*d.year + 100*d.month + d.day;
    
    /* グレゴリオ暦の範囲外の場合 */
    if (dvalue < 15821015) {
        printf("\n無効な入力です。\n\n");
        input_enter();
        printf("1582年10月15日より古い日付は計算できません。\n"
            "(グレゴリオ暦の範囲外です。)\n入力をやり直してください。\n\n");
    }
    
    /* 9999年12月31日より新しい日付の場合 */
    else if (99991231 < dvalue) {
        printf("\n無効な入力です。\n\n");
        input_enter();
        printf("9999年12月31日より新しい日付は計算できません。\n"
            "入力をやり直してください。\n\n");
    }
    
    /* 計算対象の範囲内の場合 */
    else
        flag = false;
    
    return flag;
}



/*
 * 処理2中の出力処理をする関数
 * comma_sep関数、output_result_addsub関数、foutput_result_addsub関数および
 * input_enter関数を使用する。
 */
void proc2_output(struct date d1, struct date d2, int days, char op)
{
    char strdays[BUF_SIZE], buf[BUF_SIZE], ch, zz;
    
    /* 日数を3桁ずつコンマで区切った文字列に変換 */
    comma_sep(strdays, days);
    
    /* 画面への出力 */
    output_result_addsub(d1, d2, strdays, op);
    
    /* ファイルに出力するかどうかの分岐 */
    while (1) {
        printf("ファイルに出力しますか?(y/n) ");
        fgets(buf, sizeof buf, stdin);
        sscanf(buf, "%c%c", &ch, &zz);
        if ((ch == 'y' || ch == 'Y') && zz == '\n') {
            foutput_result_addsub(d1, d2, strdays, op);
            break;
        }
        if ((ch == 'n' || ch == 'N') && zz == '\n')
            break;
        printf("\n無効な入力です。\n\n");
        input_enter();
        printf("yまたはnを入力してください。\n\n");
    }
}



/*
 * 足し算引き算の結果を画面に出力する関数
 */
void output_result_addsub(struct date d1, struct date d2,
    char strdays[], char addsub)
{
    switch (addsub) {
        case '+':
            printf("%d年%d月%d日足す%s日は、",
                d1.year, d1.month, d1.day, strdays);
            break;
        case '-':
            printf("%d年%d月%d日引く%s日は、",
                d1.year, d1.month, d1.day, strdays);
            break;
    }
    printf("%d年%d月%d日です。\n", d2.year, d2.month, d2.day);
}



/*
 * 足し算引き算の結果をファイルに出力する関数
 * gen_filename関数を使用する。
 */
void foutput_result_addsub(struct date d1, struct date d2,
    char strdays[], char addsub)
{
    FILE *fp;
    
    /* 新規ファイル出力か追記かの分岐 */
    if (is_first_output) {
        /* 初めてのファイル出力のときだけファイル名を生成して新規作成 */
        gen_filename(filename);
        fp = fopen(filename, "w");
        if (fp == NULL) {
            printf("ファイルを開けません。ファイル出力を中断します。\n");
            return;
        }
        is_first_output = false; /* 以降は追記状態になる */
    }
    else {
        /* 2回目以降のファイル出力は追記 */
        fp = fopen(filename, "a");
        if (fp == NULL) {
            printf("ファイルを開けません。ファイル出力を中断します。\n");
            return;
        }
    }
    
    switch (addsub) {
        case '+':
            fprintf(fp, "%d年%d月%d日足す%s日は、",
                d1.year, d1.month, d1.day, strdays);
            break;
        case '-':
            fprintf(fp, "%d年%d月%d日引く%s日は、",
                d1.year, d1.month, d1.day, strdays);
            break;
    }
    fprintf(fp, "%d年%d月%d日です。\n", d2.year, d2.month, d2.day);
    fclose(fp);
}


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

C:\Users\skonishi\Documents>datecalc
【日付に関する2通りの計算を行います】

エンターキーを押してください・・・1
エンターキーを押してください・・・[エンター]キー                                ←[エンター]キー以外の入力なら入力繰り返し。

次のいずれかを選択してください。

1.2つの日付の間の差を計算する。
2.ある日付に日数を足したり引いたりする。
0.終了する。

0、1、2のどれですか? 3

無効な入力です。                                                                ←0、1、2のいずれでもなければ入力繰り返し。

エンターキーを押してください・・・[エンター]キー

0、1、2のいずれかを入力してください。

1.2つの日付の間の差を計算する。
2.ある日付に日数を足したり引いたりする。
0.終了する。

0、1、2のどれですか? 2

【日付に日数を足したり引いたりします】

日付を入力してください。
入力を中断したい場合は、qを入力してください。
年 = ?(西暦で) 1

無効な入力です。                                                                ←1581年以前の年は無効。

エンターキーを押してください・・・[エンター]キー

西暦1年はグレゴリオ暦の範囲外です。
1582年以降の年を入力してください。
入力を中断したい場合は、qを入力してください。

年 = ?(西暦で) -1

無効な入力です。                                                                ←負の値の入力は無効。

エンターキーを押してください・・・[エンター]キー

負の値は受け付けません。
入力をやり直してください。
入力を中断したい場合は、qを入力してください。

年 = ?(西暦で) 1582y

無効な入力です。                                                                ←数値以外は無効。

エンターキーを押してください・・・[エンター]キー

半角整数値を入力してください。
入力を中断したい場合は、qを入力してください。

年 = ?(西暦で) 15820

無効な入力です。                                                                ←10000年以降の年は無効。

エンターキーを押してください・・・[エンター]キー

本プログラムは9999年までにしか対応していません。
9999年以前の年を入力してください。
入力を中断したい場合は、qを入力してください。

年 = ?(西暦で) 1582
月 = ? 1
日 = ? 1

無効な入力です。                                                                ←日付はグレゴリオ暦の範囲内に限る。

エンターキーを押してください・・・[エンター]キー

1582年1月1日はグレゴリオ暦の範囲外です。
本プログラムは、グレゴリオ暦上でしか動作しません。
1582年10月15日以降の日付を入力してください。
入力を中断したい場合は、qを入力してください。

年 = ?(西暦で) 1582
月 = ? 10
日 = ? 15
足し算(+)か引き算(-)かを入力してください。
入力を中断したい場合は、qを入力してください。
「+」ですか、「-」ですか? -
日数を入力してください。
入力を中断したい場合は、qを入力してください。
日数 = ? 1

無効な入力です。                                                                ←計算結果がグレゴリオ暦から外れる場合のエラー処理。

エンターキーを押してください・・・[エンター]キー

1582年10月15日より古い日付は計算できません。
(グレゴリオ暦の範囲外です。)
入力をやり直してください。

日付を入力してください。
入力を中断したい場合は、qを入力してください。
年 = ?(西暦で) 1582
月 = ? 10
日 = ? 15
足し算(+)か引き算(-)かを入力してください。
入力を中断したい場合は、qを入力してください。
「+」ですか、「-」ですか? -
日数を入力してください。
入力を中断したい場合は、qを入力してください。
日数 = ? -1

無効な入力です。                                                                ←負の日数が入力された場合のエラー処理。

エンターキーを押してください・・・[エンター]キー

負の日数の入力は受け付けません。
0以上の整数を入力してください。

日数を入力してください。
入力を中断したい場合は、qを入力してください。
日数 = ? 0
1582年10月15日引く0日は、1582年10月15日です。
ファイルに出力しますか?(y/n) yy

無効な入力です。                                                                ←yでもnでもなければ入力繰り返し。

エンターキーを押してください・・・[エンター]キー

yまたはnを入力してください。

ファイルに出力しますか?(y/n) y

繰り返しますか?(y/n) x

無効な入力です。                                                                ←yでもnでもなければ入力繰り返し。

エンターキーを押してください・・・[エンター]キー

yまたはnを入力してください。

繰り返しますか?(y/n) y

+++++

【日付に関する2通りの計算を行います】

エンターキーを押してください・・・[エンター]キー

次のいずれかを選択してください。

1.2つの日付の間の差を計算する。
2.ある日付に日数を足したり引いたりする。
0.終了する。

0、1、2のどれですか? 2

【日付に日数を足したり引いたりします】

日付を入力してください。
入力を中断したい場合は、qを入力してください。
年 = ?(西暦で) 9999
月 = ? 12
日 = ? 31
足し算(+)か引き算(-)かを入力してください。
入力を中断したい場合は、qを入力してください。
「+」ですか、「-」ですか? +
日数を入力してください。
入力を中断したい場合は、qを入力してください。
日数 = ? 1

無効な入力です。                                                                ←計算結果が9999年12月31日より新しい日付になる場合のエラー処理。

エンターキーを押してください・・・[エンター]キー

9999年12月31日より新しい日付は計算できません。
入力をやり直してください。

日付を入力してください。
入力を中断したい場合は、qを入力してください。
年 = ?(西暦で) 9999
月 = ? 12
日 = ? 31
足し算(+)か引き算(-)かを入力してください。
入力を中断したい場合は、qを入力してください。
「+」ですか、「-」ですか? +
日数を入力してください。
入力を中断したい場合は、qを入力してください。
日数 = ? 0
9999年12月31日足す0日は、9999年12月31日です。
ファイルに出力しますか?(y/n) y

繰り返しますか?(y/n) y

+++++

【日付に関する2通りの計算を行います】

エンターキーを押してください・・・[エンター]キー

次のいずれかを選択してください。

1.2つの日付の間の差を計算する。
2.ある日付に日数を足したり引いたりする。
0.終了する。

0、1、2のどれですか? 2000

無効な入力です。                                                                ←間違えて日付を入力しようとしてもエラー処理される。

エンターキーを押してください・・・[エンター]キー

0、1、2のいずれかを入力してください。

1.2つの日付の間の差を計算する。
2.ある日付に日数を足したり引いたりする。
0.終了する。

0、1、2のどれですか? 2

【日付に日数を足したり引いたりします】

日付を入力してください。
入力を中断したい場合は、qを入力してください。
年 = ?(西暦で) 2000
月 = ? 13

無効な入力です。                                                                ←「13月」はありえない。

エンターキーを押してください・・・[エンター]キー

月は1から12までです。
1から12の整数値を入力してください。
入力を中断したい場合は、qを入力してください。

月 = ? 2
日 = ? 30

無効な入力です。                                                                ←「2000年2月30日」はありえない。

エンターキーを押してください・・・[エンター]キー

2000年2月は、1日から29日までです。
1から29の整数値を入力してください。
入力を中断したい場合は、qを入力してください。

日 = ? q                                                                        ←qを入力すると入力操作が中断される。

+++++

【日付に関する2通りの計算を行います】

エンターキーを押してください・・・[エンター]キー

次のいずれかを選択してください。

1.2つの日付の間の差を計算する。
2.ある日付に日数を足したり引いたりする。
0.終了する。

0、1、2のどれですか? 2

【日付に日数を足したり引いたりします】

日付を入力してください。
入力を中断したい場合は、qを入力してください。
年 = ?(西暦で) 2021
月 = ? 7
日 = ? 23
足し算(+)か引き算(-)かを入力してください。
入力を中断したい場合は、qを入力してください。
「+」ですか、「-」ですか? -
日数を入力してください。
入力を中断したい場合は、qを入力してください。
日数 = ? 100
2021年7月23日引く100日は、2021年4月14日です。                                   ←「東京オリンピック開幕の100日前」。
ファイルに出力しますか?(y/n) y

繰り返しますか?(y/n) y

+++++

【日付に関する2通りの計算を行います】

エンターキーを押してください・・・[エンター]キー

次のいずれかを選択してください。

1.2つの日付の間の差を計算する。
2.ある日付に日数を足したり引いたりする。
0.終了する。

0、1、2のどれですか? 1

【2つの日付の間の差を計算します】

エンターキーを押してください・・・[エンター]キー

「日付1」をより古い日付、「日付2」をより新しい日付とします。

エンターキーを押してください・・・[エンター]キー

まず、日付1を入力してください。
入力を中断したい場合は、qを入力してください。

年 = ?(西暦で) 2021
月 = ? 7
日 = ? 23

次に、日付2を入力してください。
入力を中断したい場合は、qを入力してください。

年 = ?(西暦で) 2021
月 = ? 4
日 = ? 14
日付1と日付2の順序が逆です。                                                    ←古い日付から順番に入力するように促される。

エンターキーを押してください・・・[エンター]キー

日付1がより古い日付、日付2がより新しい日付となるように入力してください。

まず、日付1を入力してください。
入力を中断したい場合は、qを入力してください。

年 = ?(西暦で) 2021
月 = ? 4
日 = ? 14

次に、日付2を入力してください。
入力を中断したい場合は、qを入力してください。

年 = ?(西暦で) 2021
月 = ? 7
日 = ? 23
2021年4月14日と2021年7月23日の差は、100日です。                                 ←日数の足し算引き算と日付の差の計算が同じ結果となっている。

ファイルに出力しますか?(y/n) y

繰り返しますか?(y/n) y

+++++

【日付に関する2通りの計算を行います】

エンターキーを押してください・・・[エンター]キー

次のいずれかを選択してください。

1.2つの日付の間の差を計算する。
2.ある日付に日数を足したり引いたりする。
0.終了する。

0、1、2のどれですか? 1

【2つの日付の間の差を計算します】

エンターキーを押してください・・・[エンター]キー

「日付1」をより古い日付、「日付2」をより新しい日付とします。

エンターキーを押してください・・・[エンター]キー

まず、日付1を入力してください。
入力を中断したい場合は、qを入力してください。

年 = ?(西暦で) 1582
月 = ? 10
日 = ? 15

次に、日付2を入力してください。
入力を中断したい場合は、qを入力してください。

年 = ?(西暦で) 9999
月 = ? 12
日 = ? 31
1582年10月15日と9999年12月31日の差は、3,074,323日です。                         ←数値が3桁ずつコンマで区切って出力されている。

ファイルに出力しますか?(y/n) y

繰り返しますか?(y/n) y

+++++

【日付に関する2通りの計算を行います】

エンターキーを押してください・・・[エンター]キー

次のいずれかを選択してください。

1.2つの日付の間の差を計算する。
2.ある日付に日数を足したり引いたりする。
0.終了する。

0、1、2のどれですか? 0

プログラムを終了します。


【出力されたファイルdatecalc20260308_075039_8928.txtの内容】 ←現在日付時刻から取得されたファイル名となっている。

1582年10月15日引く0日は、1582年10月15日です。
9999年12月31日足す0日は、9999年12月31日です。               ←2行目以降が同じファイルに追記されている。
2021年7月23日引く100日は、2021年4月14日です。
2021年4月14日と2021年7月23日の差は、100日です。
1582年10月15日と9999年12月31日の差は、3,074,323日です。


【日数の計算例】

【datecalcの計算例】

●阪神タイガース・藤浪晋太郎投手、1,450日ぶりに甲子園白星
2017年4月27日~2021年4月16日

●大谷翔平、1,072日ぶり勝利
2018年5月20日~2021年4月26日

●ソフトバンクホークス、巨人に708日ぶり黒星
2019年6月22日~2021年5月30日

●阪神タイガース、2,860日ぶりに敵地で中日・大野雄大に黒星つけた
2013年8月23日~2021年6月22日

●上野由岐子、4,717日ぶりの五輪でのピッチング
2008年8月21日~2021年7月21日

●明仁上皇陛下が歴代最長寿に並ぶ32,031日
明仁上皇:1933年12月23日の前日~2021年9月2日
昭和天皇:1901年4月29日の前日~1989年1月7日

●大阪・関西万博まで1,000日
2022年7月18日~2025年4月13日

●阪神タイガース・才木浩人投手、1,159日ぶり勝利
2019年5月1日~2022年7月3日

●阪神・淡路大震災から10,000日
1995年1月17日~2022年6月4日

●中日ドラゴンズ、2,891日振りに単独首位
2016年5月10日~2024年4月9日

●阪神タイガース・湯浅京己投手、977日ぶりに2イニング目のピッチング
2022年9月21日~2025年5月25日

●LAドジャース・大谷翔平、663日ぶりに「二刀流」復帰
2023年8月24日~2025年6月17日

●LAドジャース・大谷翔平、749日ぶりに勝利
2023年8月9日~2025年8月27日

●嵐、1,768日ぶりに5人そろって生配信
2020年12月31日~2025年11月3日

●嵐、1,948日ぶりに新作ミュージックビデオ公開
2020年11月3日~2026年3月5日


戻る

©2017 KONISHI, Shoichiro.