LoginSignup
2
1

More than 5 years have passed since last update.

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

Last updated at Posted at 2019-04-01

はじめに

今日,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

元号がテーブルになっているので,次回の更新時には簡単に対応できるはず.

2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1