はじめに
前回のVer.4では、登録した予定を日付順に並び替えられるようにした。
今回のVer.5では、予定と祝日・休日を連携させ、祝日・休日と重なる予定が一目で分かるようにする。
祝日・休日データは、以前、本ブログでご紹介した「マルチ言語(C言語、Go言語、Rust、Kotlin)で祝日対応のカレンダーをつくろう!」企画(multi-pg-lang-calendar)で使っているものと同じ形式をそのまま使う。内閣府の祝日CSV(内閣府の「国民の祝日・休日」のCSV)をUTF-8変換した holidays.csv を、このアプリのフォルダにコピーするだけで連携できる。
マルチ言語(C言語、Go言語、Rust、Kotlin)で祝日対応のカレンダーをつくろう!(第1日目)
対応するGitHubリポジトリは以下の通りである。
Rocky-Seven/c-language-studies 内の schedule-mini-app/ver5_holiday_link/
Ver.4からの変更点
- 起動時に
holidays.csvを読み込む。ファイルが無い場合はエラーにせず、祝日連携なしで続行する。 - 予定の追加・編集時、入力した日付が祝日・休日と一致する場合、参考情報として祝日名を表示する(入力を止めるものではない)。
- 一覧表示で、祝日・休日と重なる予定には祝日・休日名を添えて表示する。
祝日データの構造体
祝日は「年・月・日・名称」を持つ、Schedule とよく似た構造体として表現する。
/* 1件の祝日を表す構造体。祝日カレンダー企画(multi-pg-lang-calendar)と同じ形式。 */
typedef struct {
int year;
int month;
int day;
char name[MAX_HOLIDAY_NAME];
} Holiday;
Holiday holidays[MAX_HOLIDAYS];
int holiday_count = 0;holidays.csvの読み込み
内閣府の祝日CSVは、1行目がヘッダー行(項目名)で、2行目以降が 年/月/日,祝日名 という形式になっている。multi-pg-lang-calendar のC言語版と同じ考え方で、カンマの位置をstrchrで探し、その前後を日付・名称として読み取る。
/*
* 祝日データ(holidays.csv)を読み込む。
* 「マルチ言語で祝日対応のカレンダーをつくろう!」企画(multi-pg-lang-calendar)で
* 使っているものと同じ形式(内閣府CSV由来、1行目はヘッダー)を想定している。
* ファイルが無い場合は、祝日連携なしで続行する(エラー扱いにはしない)。
*/
void load_holidays(void) {
FILE *fp = fopen(HOLIDAY_FILE, "r");
if (fp == NULL) {
printf("祝日データ(%s)が見つかりませんでした。祝日連携なしで続行します。\n", HOLIDAY_FILE);
return;
}
char line[MAX_HOLIDAY_LINE];
/* 1行目はヘッダー行(項目名)のため読み飛ばす。 */
fgets(line, sizeof(line), fp);
while (fgets(line, sizeof(line), fp) != NULL && holiday_count < MAX_HOLIDAYS) {
line[strcspn(line, "\n")] = '\0';
line[strcspn(line, "\r")] = '\0';
char *comma = strchr(line, ',');
if (comma == NULL) {
continue;
}
*comma = '\0';
char *date_part = line;
char *name_part = comma + 1;
int year, month, day;
/* 内閣府CSVは"年/月/日"形式。念のため"年-月-日"にも対応しておく。 */
int result = sscanf(date_part, "%d/%d/%d", &year, &month, &day);
if (result != 3) {
result = sscanf(date_part, "%d-%d-%d", &year, &month, &day);
}
if (result != 3 || month < 1 || month > 12 || day < 1 || day > 31) {
continue;
}
Holiday *h = &holidays[holiday_count];
h->year = year;
h->month = month;
h->day = day;
strncpy(h->name, name_part, MAX_HOLIDAY_NAME - 1);
h->name[MAX_HOLIDAY_NAME - 1] = '\0';
holiday_count++;
}
fclose(fp);
printf("祝日データを読み込みました: %d件\n", holiday_count);
}
*comma = '\0'; で、読み込んだ1行の文字列をカンマの位置で2つに分断している。こうすると line の先頭からは日付部分だけの文字列として、comma + 1 からは名称部分だけの文字列として、それぞれ扱えるようになる。
日付部分は 年/月/日 形式を基本としつつ、sscanf が失敗した場合は 年-月-日 形式も試す。これは schedules.txt の読み込みと同様、想定外の行が混ざっていても、そこだけを読み飛ばして処理を続けられるようにするためである。
祝日の検索
ある年月日が祝日かどうかを調べる関数を用意する。
/*
* 指定した年月日が祝日であれば、その名称を返す。祝日でなければNULLを返す。
*/
const char *find_holiday_name(int year, int month, int day) {
for (int i = 0; i < holiday_count; i++) {
if (holidays[i].year == year && holidays[i].month == month && holidays[i].day == day) {
return holidays[i].name;
}
}
return NULL;
}
祝日の件数は多くても1年あたり20件前後、holidays.csv 全体でも1000件程度なので、先頭から順に探す単純な線形探索で十分な速度が出る。
予定の追加・編集時に祝日を知らせる
read_schedule_input の中で、日付の妥当性チェックが通った直後に find_holiday_name を呼び出し、該当すれば参考情報として表示する。
/* 祝日と重なる場合は、参考情報として知らせる(入力を止めるものではない)。 */
const char *holiday_name = find_holiday_name(s->year, s->month, s->day);
if (holiday_name != NULL) {
printf("この日は祝日です(%s)。\n", holiday_name);
}祝日だからといって予定を登録できなくする理由は無いため、あくまで「気づき」を与えるだけの表示にとどめている。
一覧表示に祝日名を添える
list_schedules でも同じ find_holiday_name を使い、祝日と重なる予定には [祝: 祝日名] を追加で表示する。
/* 祝日と重なる予定には、一覧表示の末尾に祝日名を添える。 */
void list_schedules(void) {
if (schedule_count == 0) {
printf("登録されている予定はありません。\n");
return;
}
printf("\n--- 予定一覧 ---\n");
for (int i = 0; i < schedule_count; i++) {
Schedule *s = &schedules[i];
const char *holiday_name = find_holiday_name(s->year, s->month, s->day);
if (holiday_name != NULL) {
printf("%d: %04d-%02d-%02d %s [祝: %s]\n", i + 1, s->year, s->month, s->day, s->title, holiday_name);
} else {
printf("%d: %04d-%02d-%02d %s\n", i + 1, s->year, s->month, s->day, s->title);
}
}
}サンプルのholidays.csv
このリポジトリには、動作確認用として、日付が年によって変わらない祝日(元日、建国記念の日、天皇誕生日、昭和の日、憲法記念日、みどりの日、こどもの日、山の日、文化の日、勤労感謝の日)だけを集めた小さなサンプル holidays.csv を同梱している。
国民の祝日・休日月日,国民の祝日・休日名称
2026/1/1,元日
2026/2/11,建国記念の日
2026/2/23,天皇誕生日
2026/4/29,昭和の日
2026/5/3,憲法記念日
2026/5/4,みどりの日
2026/5/5,こどもの日
2026/8/11,山の日
2026/11/3,文化の日
2026/11/23,勤労感謝の日成人の日や海の日のように年によって日付が変わるもの、春分の日・秋分の日のように年によって日付が変わるものは、サンプルには含めていない。実際に使う場合は、multi-pg-lang-calendar 側で用意した内閣府CSV(UTF-8変換済み)をこのフォルダにコピーすることで、全ての祝日と連携できる。
動作確認
holidays.csv を用意した状態で、憲法記念日(5/3)に予定を追加する。
番号を入力してください: 1
年月日を入力してください(例: 2026 8 1 または 20260801): 2026 5 3
この日は祝日です(憲法記念日)。
予定を入力してください: 家族で外出
予定を追加しました。一覧表示すると、祝日名が添えられて表示される。
--- 予定一覧 ---
1: 2026-05-03 家族で外出 [祝: 憲法記念日]holidays.csv が無い状態で起動すると、次のように表示され、祝日連携なし(Ver.4相当)の動作になる。
祝日データ(holidays.csv)が見つかりませんでした。祝日連携なしで続行します。次回予告
ここまででC言語版のVer.1〜Ver.5が完成し、追加・一覧表示・編集・削除・保存/読み込み・日付順ソート・祝日連携という一通りの機能が揃った。
次回以降は、Go・Kotlin版への移植を予定している。


コメント