西暦を元号に変換するプログラム(C言語版，「令和」対応)

#はじめに
今日，2019年4月1日に新元号「令和（れいわ）」が発表された．
西暦→元号の変換プログラムはこれまで散々作られてきたと思うが，自身の整理の為に，記録を残す．
(当初投稿の際に，旧元号変換の機能が参照サイトのソースコードで実装されていたため，そのコードを入替ました．)

#元号のルール
昭和→平成，平成→令和は，まだ最近なので，身近であるが，それ以前となるとなかなかイメージが付かない．
こういう時はWikipediaの[元号のページ](https://ja.wikipedia.org/wiki/%E5%85%83%E5%8F%B7%E4%B8%80%E8%A6%A7_(%E6%97%A5%E6%9C%AC))が役に立つので，参考にした（さっそく更新されている！)．

|元号|始期|終期|
|:----|:----:|:----:|
|明治|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||

# 西暦→元号変換の方法
先人がいろいろ作ってくれているので，今回は[西暦を和暦に変換するプログラム](https://tondol.hatenablog.jp/entry/20070829/1188314937)を参考にして，令和版を作った．
西暦・月・日を１つの整数として扱って比較する方式で，簡単に表現するには良い方法だと思う．
比較の為に，令和対応していないプログラムもコンパイルできるようにして，また，チェックもできるようにテストコードも入れてみた．

#ソースコード "gengo.c"

```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

``` 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
```
元号がテーブルになっているので，次回の更新時には簡単に対応できるはず．