はじめに
今日,2019年4月1日に新元号「令和(れいわ)」が発表された.
西暦→元号の変換プログラムはこれまで散々作られてきたと思うが,自身の整理の為に,記録を残す.
(当初投稿の際に,旧元号変換の機能が参照サイトのソースコードで実装されていたため,そのコードを入替ました.)
元号のルール
昭和→平成,平成→令和は,まだ最近なので,身近であるが,それ以前となるとなかなかイメージが付かない.
こういう時はWikipediaの元号のページが役に立つので,参考にした(さっそく更新されている!).
元号 | 始期 | 終期 |
---|---|---|
明治 | 1868/09/08 | 1912/07/29 |
大正 | 1912/07/30 | 1926/12/24 |
昭和 | 1926/12/25 | 1989/01/07 |
平成 | 1989/01/08 | 2019/04/30 |
令和 | 2019/05/01 |
西暦→元号変換の方法
先人がいろいろ作ってくれているので,今回は西暦を和暦に変換するプログラムを参考にして,令和版を作った.
西暦・月・日を1つの整数として扱って比較する方式で,簡単に表現するには良い方法だと思う.
比較の為に,令和対応していないプログラムもコンパイルできるようにして,また,チェックもできるようにテストコードも入れてみた.
ソースコード "gengo.c"
/*
gengo.c
Copyright (c) 2019 Tetsuo Furuichi (@furufuru)
This software is released under the MIT License.
http://opensource.org/licenses/mit-license.php
*/
# include <stdio.h>
# include <string.h>
# include <stdlib.h>
/*
元号期間
明治 1868/09/08 〜 1912/07/29
大正 1912/07/30 〜 1926/12/24
昭和 1926/12/25 〜 1989/01/07
平成 1989/01/08 〜 2019/04/30
令和 2019/05/01 〜
*/
char *AD2gengo_reiwa(char *in_date, char *out_date) {
struct GENGO {
char *name;
int y0,m0,d0, y1,m1,d1;
} g_tbl[] = {
{"明治以前", 1,0,0, 1868,9,7},
{"明治", 1868, 9, 8, 1912, 7,29},
{"大正", 1912, 7,30, 1926,12,24},
{"昭和", 1926,12,25, 1989, 1, 7},
{"平成", 1989, 1, 8, 2019, 4,30},
{"令和", 2019, 5, 1, (-1), 0, 0}
};
long y, m, d, target, ad, index;
sscanf(in_date, "%ld/%ld/%ld", &y, &m, &d);
target = y * 10000 + m * 100 + d;
ad = y;
index = 0;
do {
if ((g_tbl[index].y1 == (-1))
|| (target <= g_tbl[index].y1 * 10000 + g_tbl[index].m1 * 100 + g_tbl[index].d1)) {
y -= (g_tbl[index].y0 - 1);
break;
}
} while(g_tbl[index++].y1 != (-1));
if (index == 0) {
sprintf(out_date, "%ld(%s)/%02ld/%02ld", ad, g_tbl[index].name, m, d);
} else {
if (y == 1) {
sprintf(out_date, "%ld(%s元年)/%02ld/%02ld", ad, g_tbl[index].name, m, d);
} else {
sprintf(out_date, "%ld(%s%ld年)/%02ld/%02ld", ad, g_tbl[index].name, y, m, d);
}
}
return out_date;
}
char *AD2gengo(char *in_date, char *out_date) {
struct GENGO {
char *name;
int y0,m0,d0, y1,m1,d1;
} g_tbl[] = {
{"明治以前", 1,0,0, 1868,9,7},
{"明治", 1868, 9, 8, 1912, 7,29},
{"大正", 1912, 7,30, 1926,12,24},
{"昭和", 1926,12,25, 1989, 1, 7},
{"平成", 1989, 1, 8, (-1), 0, 0}
};
long y, m, d, target, ad, index;
sscanf(in_date, "%ld/%ld/%ld", &y, &m, &d);
target = y * 10000 + m * 100 + d;
ad = y;
index = 0;
do {
if ((g_tbl[index].y1 == (-1))
|| (target <= g_tbl[index].y1 * 10000 + g_tbl[index].m1 * 100 + g_tbl[index].d1)) {
y -= (g_tbl[index].y0 - 1);
break;
}
} while(g_tbl[index++].y1 != (-1));
if (index == 0) {
sprintf(out_date, "%ld(%s)/%02ld/%02ld", ad, g_tbl[index].name, m, d);
} else {
if (y == 1) {
sprintf(out_date, "%ld(%s元年)/%02ld/%02ld", ad, g_tbl[index].name, m, d);
} else {
sprintf(out_date, "%ld(%s%ld年)/%02ld/%02ld", ad, g_tbl[index].name, y, m, d);
}
}
return out_date;
}
int test_gengo(char *target, char *right_answer) {
char *ans;
char out_date[64];
# ifdef REIWA
ans = AD2gengo_reiwa(target, out_date);
# else
ans = AD2gengo(target, out_date);
# endif
printf("test_gengo(%s, %s)", target, out_date);
if (strcmp(ans, right_answer)) {
printf(" NG -> O:%s\n", right_answer);
return 1;
} else {
printf(" OK\n");
return 0;
}
}
void self_test() {
int test_cnt = 0;
int err_cnt = 0;
test_cnt++; if (test_gengo("0/1/1", "0(明治以前)/01/01")) err_cnt++;
test_cnt++; if (test_gengo("1868/09/07", "1868(明治以前)/09/07")) err_cnt++;
test_cnt++; if (test_gengo("1868/09/08", "1868(明治元年)/09/08")) err_cnt++;
test_cnt++; if (test_gengo("1912/07/29", "1912(明治45年)/07/29")) err_cnt++;
test_cnt++; if (test_gengo("1912/07/30", "1912(大正元年)/07/30")) err_cnt++;
test_cnt++; if (test_gengo("1926/12/24", "1926(大正15年)/12/24")) err_cnt++;
test_cnt++; if (test_gengo("1926/12/25", "1926(昭和元年)/12/25")) err_cnt++;
test_cnt++; if (test_gengo("1989/01/07", "1989(昭和64年)/01/07")) err_cnt++;
test_cnt++; if (test_gengo("1989/01/08", "1989(平成元年)/01/08")) err_cnt++;
test_cnt++; if (test_gengo("2019/04/30", "2019(平成31年)/04/30")) err_cnt++;
test_cnt++; if (test_gengo("2019/05/01", "2019(令和元年)/05/01")) err_cnt++;
test_cnt++; if (test_gengo("2020/01/01", "2020(令和2年)/01/01")) err_cnt++;
test_cnt++; if (test_gengo("3000/05/01", "3000(令和982年)/05/01")) err_cnt++;
printf("OK Result %d / %d\n", test_cnt - err_cnt, test_cnt);
}
int main(int argc, char *argv[])
{
char *in_date;
char out_date[64];
if (argc > 1) {
if (!strcmp(argv[1], "--selftest")) {
self_test();
exit(0);
}
in_date = argv[1];
} else {
printf("引数は,YYYY/MM/DD で入力してください\n");
exit(1);
}
# ifdef REIWA
printf("%s\n", AD2gengo_reiwa(in_date, out_date));
# else
printf("%s\n", AD2gengo(in_date, out_date));
# endif
exit(0);
}
Makefile
CC = cc
PROGRAM = gengo gengo_reiwa
all: $(PROGRAM)
gengo: gengo.c
$(CC) -o gengo gengo.c
gengo_reiwa: gengo.c
$(CC) -DREIWA -o gengo_reiwa gengo.c
clean:; rm -f gengo gengo_reiwa
ビルド方法
Mac、Linuxの場合は,Makefileを使って実行するだけです.
$ make
cc -o gengo gengo.c
cc -DREIWA -o gengo_reiwa gengo.c
実行例
自分自身のチェックオプションも入れているので,検証の参考にしてください.
$ ./gengo --selftest
test_gengo(0/1/1, 0(明治以前)/01/01) OK
test_gengo(1868/09/07, 1868(明治以前)/09/07) OK
test_gengo(1868/09/08, 1868(明治元年)/09/08) OK
test_gengo(1912/07/29, 1912(明治45年)/07/29) OK
test_gengo(1912/07/30, 1912(大正元年)/07/30) OK
test_gengo(1926/12/24, 1926(大正15年)/12/24) OK
test_gengo(1926/12/25, 1926(昭和元年)/12/25) OK
test_gengo(1989/01/07, 1989(昭和64年)/01/07) OK
test_gengo(1989/01/08, 1989(平成元年)/01/08) OK
test_gengo(2019/04/30, 2019(平成31年)/04/30) OK
test_gengo(2019/05/01, 2019(平成31年)/05/01) NG -> O:2019(令和元年)/05/01
test_gengo(2020/01/01, 2020(平成32年)/01/01) NG -> O:2020(令和2年)/01/01
test_gengo(3000/05/01, 3000(平成1012年)/05/01) NG -> O:3000(令和982年)/05/01
OK Result 10 / 13
$ ./gengo_reiwa --selftest
test_gengo(0/1/1, 0(明治以前)/01/01) OK
test_gengo(1868/09/07, 1868(明治以前)/09/07) OK
test_gengo(1868/09/08, 1868(明治元年)/09/08) OK
test_gengo(1912/07/29, 1912(明治45年)/07/29) OK
test_gengo(1912/07/30, 1912(大正元年)/07/30) OK
test_gengo(1926/12/24, 1926(大正15年)/12/24) OK
test_gengo(1926/12/25, 1926(昭和元年)/12/25) OK
test_gengo(1989/01/07, 1989(昭和64年)/01/07) OK
test_gengo(1989/01/08, 1989(平成元年)/01/08) OK
test_gengo(2019/04/30, 2019(平成31年)/04/30) OK
test_gengo(2019/05/01, 2019(令和元年)/05/01) OK
test_gengo(2020/01/01, 2020(令和2年)/01/01) OK
test_gengo(3000/05/01, 3000(令和982年)/05/01) OK
OK Result 13 / 13
$ ./gengo_reiwa 2019/04/30
2019(平成31年)/04/30
$ ./gengo_reiwa 2019/05/01
2019(令和元年)/05/01
$ ./gengo_reiwa 2020/01/01
2020(令和2年)/01/01
元号がテーブルになっているので,次回の更新時には簡単に対応できるはず.