2018-01-08

Scheme: 和暦表記

連休で暇だったから、思いつきで、グレゴリオ暦の日付から和暦表記を作る関数を作った。訳あって最初は C言語で作ったけど、それをまた別の訳あって最近触り始めた Lua に翻訳して、最後に Scheme に翻訳したんだ。ここには Scheme のやつを載せとく。

グレゴリオ暦の日付から和暦表記の文字列を作る関数

書式はいろいろ考えられるけど、よく使われそうな三つの書式の関数を作ってみた。

関数 1989年2月1日 1990年2月1日 元号表記 1年目 月日の桁数
Calendar.JapaneseImperial.GGG_0M_0D "平成元年02月01日" "平成2年02月01日" 正式、漢字 "元年" "0"埋めで二桁
Calendar.JapaneseImperial.GGG_SM_SD "平成元年 2月 1日" "平成2年 2月 1日" 正式、漢字 "元年" " "埋めで二桁
Calendar.JapaneseImperial.GGG_M_D "平成元年2月1日" "平成2年2月1日" 正式、漢字 "元年" 最小桁数

どの関数も、明治5年12月2日以前(1972年12月31日以前)はグレゴリオ暦表記を返んだ。この日より前は太陰太陽暦を使ってて日付が合わないんだ。そこも何とかできればいいのかもだけど、今の自分の知識ぢゃダメ。(´・ω・`)

あと、平成31年5月1日以降(2019年5月1日以降)もグレゴリオ暦表記を返んだ。元号が分からないから。

コード

; ==============================================================================
; Calendar.JapaneseImperial
; ==============================================================================

(define (Calendar.JapaneseImperial.GGG_0M_0D year month day)
  (let*
    (
    (GnGyMD (Calendar.JapaneseImperial._GnGyMD year month day))
    (Gn (car GnGyMD)) (Gy (cadr GnGyMD)) (M (caddr GnGyMD)) (D (cadddr GnGyMD))
    )
    (string-append
      (Calendar.JapaneseImperial._Gn->Hani Gn)
      (if (= 1 Gy) "元" (number->string Gy))
      "年"
      (string-append (if (< M 10) "0" "") (number->string M))
      "月"
      (string-append (if (< D 10) "0" "") (number->string D))
      "日"
    )
  )
) ; Calendar.JapaneseImperial.GGG_0M_0D

(define (Calendar.JapaneseImperial.GGG_SM_SD year month day)
  (let*
    (
    (GnGyMD (Calendar.JapaneseImperial._GnGyMD year month day))
    (Gn (car GnGyMD)) (Gy (cadr GnGyMD)) (M (caddr GnGyMD)) (D (cadddr GnGyMD))
    )
    (string-append
      (Calendar.JapaneseImperial._Gn->Hani Gn)
      (if (= 1 Gy) "元" (number->string Gy))
      "年"
      (string-append (if (< M 10) " " "") (number->string M))
      "月"
      (string-append (if (< D 10) " " "") (number->string D))
      "日"
    )
  )
) ; Calendar.JapaneseImperial.GGG_SM_SD

(define (Calendar.JapaneseImperial.GGG_M_D year month day)
  (let*
    (
    (GnGyMD (Calendar.JapaneseImperial._GnGyMD year month day))
    (Gn (car GnGyMD)) (Gy (cadr GnGyMD)) (M (caddr GnGyMD)) (D (cadddr GnGyMD))
    )
    (string-append
      (Calendar.JapaneseImperial._Gn->Hani Gn)
      (if (= 1 Gy) "元" (number->string Gy))
      "年"
      (number->string M)
      "月"
      (number->string D)
      "日"
    )
  )
) ; Calendar.JapaneseImperial.GGG_M_D

; ------------------------------------------------------------------------------

(define (Calendar.JapaneseImperial._GnGyMD year month day)
  (let*
    (
    (ymd (+ (* 10000 year) (* 100 month) day))
    (eraName
      (cond
        ((<= 18730101 ymd 19120729) 'meiji  )
        ((<= 19120730 ymd 19261224) 'taishou)
        ((<= 19261225 ymd 19890107) 'shouwa )
        ((<= 19890108 ymd 20190430) 'heisei )
        (else #f)
      )
    )
    (eraYear
      (case eraName
        ((meiji  ) (+ year -1873 +1 +5))
        ((taishou) (+ year -1912 +1   ))
        ((shouwa ) (+ year -1926 +1   ))
        ((heisei ) (+ year -1989 +1   ))
        (else year)
      )
    )
    )
    (list eraName eraYear month day)
  )
) ; Calendar.JapaneseImperial._GnGyMD

; ------------------------------------------------------------------------------

(define (Calendar.JapaneseImperial._Gn->Hani Gn)
  (case Gn ((meiji) "明治") ((taishou) "大正") ((shouwa) "昭和") ((heisei) "平成") (else ""))
)

(define (Calendar.JapaneseImperial._Gn->Hani.short Gn)
  (case Gn ((meiji) "明") ((taishou) "大") ((shouwa) "昭") ((heisei) "平") (else ""))
)

(define (Calendar.JapaneseImperial._Gn->Latn Gn)
  (case Gn ((meiji) "Meiji") ((taishou) "Taishou") ((shouwa) "Shouwa") ((heisei) "Heisei") (else ""))
)

(define (Calendar.JapaneseImperial._Gn->Latn.short Gn)
  (case Gn ((meiji) "M") ((taishou) "T") ((shouwa) "S") ((heisei) "H") (else ""))
)

; ==============================================================================
; [EOF]

おためし

(let
  ((test (lambda (y m d)
    (display y) (display "/")
    (display m) (display "/")
    (display d) (display ": ")
    (display (Calendar.JapaneseImperial.GGG_M_D y m d))
    (newline)
  )))
  (display "明治6年初日") (newline)
  (test 1872 12 31)
  (test 1873 01 01)
  (test 1873 01 02)
  (newline)
  (display "明治末日") (newline)
  (test 1912 07 28)
  (test 1912 07 29)
  (test 1912 07 30)
  (newline)
  (display "大正初日") (newline)
  (test 1912 07 29)
  (test 1912 07 30)
  (test 1912 07 31)
  (newline)
  (display "大正末日") (newline)
  (test 1926 12 23)
  (test 1926 12 24)
  (test 1926 12 25)
  (newline)
  (display "昭和初日") (newline)
  (test 1926 12 24)
  (test 1926 12 25)
  (test 1926 12 26)
  (newline)
  (display "昭和末日") (newline)
  (test 1989 01 06)
  (test 1989 01 07)
  (test 1989 01 08)
  (newline)
  (display "平成初日") (newline)
  (test 1989 01 07)
  (test 1989 01 08)
  (test 1989 01 09)
  (newline)
  (display "平成末日") (newline)
  (test 2019 04 29)
  (test 2019 04 30)
  (test 2019 05 01)
  (newline)
)

実行結果として画面に出てくる文字列。

明治6年初日
1872/12/31: 1872年12月31日
1873/1/1: 明治6年1月1日
1873/1/2: 明治6年1月2日

明治末日
1912/7/28: 明治45年7月28日
1912/7/29: 明治45年7月29日
1912/7/30: 大正元年7月30日

大正初日
1912/7/29: 明治45年7月29日
1912/7/30: 大正元年7月30日
1912/7/31: 大正元年7月31日

大正末日
1926/12/23: 大正15年12月23日
1926/12/24: 大正15年12月24日
1926/12/25: 昭和元年12月25日

昭和初日
1926/12/24: 大正15年12月24日
1926/12/25: 昭和元年12月25日
1926/12/26: 昭和元年12月26日

昭和末日
1989/1/6: 昭和64年1月6日
1989/1/7: 昭和64年1月7日
1989/1/8: 平成元年1月8日

平成初日
1989/1/7: 昭和64年1月7日
1989/1/8: 平成元年1月8日
1989/1/9: 平成元年1月9日

平成末日
2019/4/29: 平成31年4月29日
2019/4/30: 平成31年4月30日
2019/5/1: 2019年5月1日

うん、いい感じっ。(*´∀`*)

感想

あらかじめ特定の書式の関数を作り込むのもいいけど、日付の書式が色々ありえるから、こんな感じのことできればいいな。

(Calendar.JapaneseImperial "{GGG}年{0M}月{0D}日" 2018 1 8)
 平成300108

(Calendar.JapaneseImperial "{GGG:Hani}年{M:Hani}月{D:Hani}日" 2018 10 20)
 平成三十年十月二十日

(Calendar.JapaneseImperial "{GGG:Hani/0}年{M:Hani/0}月{D:Hani/0}日" 2018 10 20)
 平成三〇年一〇月二〇日

(Calendar.JapaneseImperial "{GGG:Hani}年、{M:Hani-JP}月、{D:Hani}日" 2018 10 20)
 平成三十年、神無月、二十日

(Calendar.JapaneseImperial "{GG}/{M}/{D}" 2018 1 8)
 30/1/8

(Calendar.JapaneseImperial "{M:Latn-JP} {D}, {GGG:Latn}" 2018 1 8)
 Mutsuki 1, Heisei 30

一般的で上手なまとめ方がよく分からない。