challenge 西暦 to 和暦

西暦を和暦に変換するプログラムを書いてください。元号の切り替わる日など、複数の表記が可能な場合には両方表示し、西暦が無効な日付の場合には「範囲外」と表示するようにしてください。対応すべき日付は明治元年以降とします。

>a.py 1868/12/2
明治1年12月2日

>a.py 1926/12/24
大正15年12月24日

>a.py 2007/12/01
平成19年12月1日

>a.py 1926/12/25
大正15年12月25日 昭和1年12月25日

>a.py 1868/1/2
範囲外

>a.py 1868/100/2
範囲外

Posted feedbacks - Flatten

Nested Hidden
最初の例はどう解釈するのでしょう。
1868/12/2は明治1年12月2日ではないと思います。
問題を明治6年1月1日以降に限った方がいいのでは?

また、カレンダーが正しく実装されていればできるのですから、
「無効と表示しなければならない」
ではなく、
「無効と表示してもよい」
としたほうがいいのでは?

とりあえず、太陰暦への変換は諦めて書いてみました。
(私も問題を明治6年1月1日以降に限った方が良いと思います。)

結果
-----------
明治1年12月2日
大正15年12月24日
平成19年12月1日
大正15年12月25日 昭和1年12月25日
範囲外
範囲外
-----------
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;

public class JapaneseImperialCalendarTest {
    public static String convert(String aYYYYMMDD) throws ParseException {
        aYYYYMMDD = aYYYYMMDD.replaceAll("^0+|(?<=/)0", "");
        Date tDate = new SimpleDateFormat("y/M/d").parse(aYYYYMMDD);
        if (!new SimpleDateFormat("yyyy/M/d").format(tDate).equals(aYYYYMMDD)) {
            return "範囲外";
        } else if (tDate.getTime() < -3197178000000L) {
            return "範囲外";
        } else if (tDate.getTime() == -1812186000000L) {
            return "明治45年7月30日 大正1年7月30日";
        } else if (tDate.getTime() == -1357635600000L) {
            return "大正15年12月25日 昭和1年12月25日";
        }
        return new SimpleDateFormat("GGGGy年MMMMd日", new Locale("ja", "JP", "JP")).format(tDate);
    }

    public static void main(String[] args) throws ParseException {
        System.out.println(convert("1868/12/2"));
        System.out.println(convert("1926/12/24"));
        System.out.println(convert("2007/12/01"));
        System.out.println(convert("1926/12/25"));
        System.out.println(convert("1868/1/2"));
        System.out.println(convert("1868/100/2"));
    }
}

暫定で

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
    class Program
    {
        static void Main(string[] args)
        {
            try
            {
                CultureInfo culture = new CultureInfo("ja-JP", true);
                culture.DateTimeFormat.Calendar = new JapaneseCalendar();
                DateTime datetime = DateTime.Parse(args[0]);
                Console.WriteLine(datetime.ToString("ggyy年M月d日", culture));
            }
            catch
            {
                Console.WriteLine("範囲外です");
            }
        }
    }

とりあえず、明治6年1月1日以前を範囲外としてあります。

>trans.exe 1868/12/2
範囲外

>trans.exe 1926/12/24
大正15年12月24日

>trans.exe 2007/12/01
平成19年12月1日

>trans.exe 1926/12/25
大正15年12月25日 昭和1年12月25日

>trans.exe 1868/100/2
範囲外

>trans.exe 1868/1/2
範囲外
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
using System;
using System.Globalization;

class Program
{
    static void Main(string[] args)
    {
        CultureInfo culture = new CultureInfo("ja-JP", true);
        DateTime date;
        string[] tmp;
        int y, m, d;

        culture.DateTimeFormat.Calendar = new JapaneseCalendar();

        try {
            tmp = args[0].Split('/');
            y = int.Parse(tmp[0]);
            m = int.Parse(tmp[1]);
            d = int.Parse(tmp[2]);
            date = new DateTime(y, m, d);

            if (DateTime.Compare(date, new DateTime(1873, 1, 1)) < 0)
                throw new Exception();

            if (y == 1912 && m == 7 && d == 30)
                Console.Write("明治45年7月30日 ");
            else if (y == 1926 && m == 12 && d == 25)
                Console.Write("大正15年12月25日 ");

            Console.WriteLine(date.ToString("ggy年M月d日", culture));

        }
        catch {
            Console.WriteLine("範囲外");
            return;
        }

    }
}

追記
明治6年1月1日以降のみを対象とするという意見には私も賛成です(そうすると出題の意図から離れてしまうのかもしれませんが…)。

とりあえず、太陰暦には、対応してないです…。
実行結果:
(to-japanese-date "1926/12/25") 〜

;1868/12/2 => 明治1年12月2日
;1926/12/24 => 大正15年12月24日
;2007/12/01 => 平成19年12月1日
;1926/12/25 => 大正15年12月25日 昭和1年12月25日
;1868/1/2 => 範囲外
;1868/100/2 => 範囲外
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
(defpackage #:doukaku-122 (:use #:cl))
(in-package #:doukaku-122)

(defconstant +meiji-end+     396802800)
(defconstant +taisho-start+  +meiji-end+)
(defconstant +taisho-end+    851353200)
(defconstant +showa-start+   +taisho-end+)
(defconstant +showa-end+     2809090800)
(defconstant +heisei-start+  2809177200)
(defconstant +meiji-offset+  1867)
(defconstant +taisho-offset+ 1911)
(defconstant +showa-offset+  1925)
(defconstant +heisei-offset+ 1988)

(defun to-japanese-date (date-string)
  (or (ppcre:register-groups-bind (y m d)
          ("([0-9]{4})/([01]*[0-9])/([0-3]*[0-9])$" date-string)
        (destructuring-bind (y m d) (mapcar #'parse-integer `(,y ,m ,d))
          (when (and (<= y 1868) (<= m 1) (< d 25))
            (return-from to-japanese-date (princ "範囲外")))
          (let ((utime (if (>= y 1900)
                           (encode-universal-time 0 0 0 d m y -9)
                           0))
                gengo toshi)
            (when (<= +heisei-start+ utime)
              (push "平成" gengo)
              (push (- y +heisei-offset+) toshi))
            (when (<= +showa-start+ utime +showa-end+)
              (push "昭和" gengo)
              (push (- y +showa-offset+) toshi))
            (when (<= +taisho-start+ utime +taisho-end+)
              (push "大正" gengo)
              (push (- y +taisho-offset+) toshi))
            (when (<= 0 utime +meiji-end+)
              (push "明治" gengo)
              (push (- y +meiji-offset+) toshi))
            (format nil #'multiple-gengo-print gengo toshi m d))))
      (princ "範囲外")))   ;日付のパターンにマッチしない場合は範囲外

(defun multiple-gengo-print (stream gengo y &rest args)
  (do ((yy y (cdr yy)) (g gengo (cdr g)))
      ((endp g))
    (apply #'format stream "~A~D年~D月~D日" (car g) (car yy) args)
    (and (cdr g) (princ " " stream))))

すいません、余計なプリント文が混じってましたので修正します。

1
2
3
4
5
6
7
8
--- 5234.txt    2008-01-09 01:43:00.000000000 +0900
+++ 5234.txt.diff       2008-01-09 01:42:50.000000000 +0900
@@ -20 +20 @@
-            (return-from to-japanese-date (princ "範囲外")))
+            (return-from to-japanese-date "範囲外"))
@@ -38 +38 @@
-      (princ "範囲外")))   ;日付のパターンにマッチしない場合は範囲外
+      "範囲外"))   ;日付のパターンにマッチしない場合は範囲外

Squeak Smalltalk で。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
| 入力 変換 境界 年号 日付 出力 |
入力 := '1926/12/25'.
変換 := [:文字列 |
    | 年月日 |
    年月日 := (文字列 subStrings: '/') collect: [:各々 | 各々 asInteger].
    [DateAndTime year: 年月日 first month: 年月日 second day: 年月日 third]
        ifError: [^'範囲外']].
境界 := #('1873/1/1' '1912/7/30' '1926/12/25' '1989/1/8') collect: [:各々 | 変換 value: 各々].
境界 := 境界 readStream.
年号 := #(明治 6 大正 1 昭和 1 平成 1).
(日付 := 変換 value: 入力) < 境界 peek ifTrue: [^'範囲外'].
出力 := OrderedCollection new.
年号 pairsDo: [:号 :年 |
    | 開始 |
    開始 := 境界 next.
    (日付 between: 開始 and: (境界 peek ifNil: [開始 max: 日付])) ifTrue: [
        出力 add: (号, '{1}年{2}月{3}日'
            format: {日付 year - 開始 year + 年. 日付 month. 日付 dayOfMonth})]].
^出力 asArray   "=> #('大正15年12月25日' '昭和1年12月25日') "

すみません、明治5年以前は太陰暦であったとは・・・知りませんでした。「明治6年以降」にお題を修正します。


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
<html>
<head>
<title>西暦 to 和暦</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<script type="text/javascript">
<!--
//*******************************************************
// 西暦 to 和暦
//*******************************************************
function toWareki(numYear, numMonth, numDay) {
    var today = new Date();
    numCurYear = today.getFullYear();
    numCurMon = today.getMonth() + 1;
    numCurDay = today.getDate();
    
    strDate = '' + numYear + ((numMonth < 10) ? '0' : '') + numMonth + ((numDay < 10) ? '0' : '') + numDay;
    strCurDate = '' + numCurYear + ((numCurMon < 10) ? '0' : '') + numCurMon + ((numCurDay < 10) ? '0' : '') + numCurDay;

    var arrWareki = [
        {'name': '明治', 'start': '18680908', 'end': '19120730'},
        {'name': '大正', 'start': '19120730', 'end': '19261225'},
        {'name': '昭和', 'start': '19261225', 'end': '19890107'},
        {'name': '平成', 'start': '19890108', 'end': strCurDate}
    ];
    
    var strName = '';
    for(var i=0; i<arrWareki.length; i++) {
        if(arrWareki[i]['start'] <= strDate && strDate <= arrWareki[i]['end']) {
            var strAnd = (strName == '') ? '' : '年&nbsp;';
            var numBaseYear = parseInt(arrWareki[i]['start'].substring(0,4));
            strName += strAnd + arrWareki[i]['name'] + (numYear - numBaseYear + 1);
        }
    }

    return (strName == '') ? '範囲外' : strName + '年' + numMonth + '月' + numDay + '日';
}
//-->
</script>
</head>
<body>
西暦:<br/>
<input type="text" id="year" value="2008" size="4" />
<input type="text" id="month" value="1" size="2" />
<input type="text" id="day" value="9" size="2" />
<input type="button" value="変換" onclick="document.getElementById('wareki').innerHTML=toWareki(document.getElementById('year').value, document.getElementById('month').value, document.getElementById('day').value);" />
<br/>
和暦:<br>
<div id="wareki">&nbsp;</div>
</body>
</html>

Java SE 6の和暦機能を使用しました。
結果は以下のとおりで、太陰暦云々の話でもエラーなしで
出力するところに注意が必要です。

 明治01年 12月 2日
 大正15年 12月 24日
 平成19年 12月 1日
 昭和01年 12月 25日
 明治01年 1月 2日
 明治09年 4月 2日

ちゃんと「範囲外」を出力しなければならないため、
お題を解いたとは言えないのですが、
和暦を使う際には気をつけてくださいねという
意味も込めて投稿の投稿です。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;

public class Wareki {

    public static void main(String[] args) throws ParseException {
        System.out.println(convert("1868/12/2"));
        System.out.println(convert("1926/12/24"));
        System.out.println(convert("2007/12/01"));
        System.out.println(convert("1926/12/25"));
        System.out.println(convert("1868/1/2"));
        System.out.println(convert("1868/100/2"));
    }
    
    public static String convert(String dateStr) throws ParseException {
        Locale waLocale = new Locale("ja", "JP", "JP");
        DateFormat sei = new SimpleDateFormat("yyyy/MM/dd");
        Date date = sei.parse(dateStr);
        
        DateFormat wa = new SimpleDateFormat("GGGGyy年 MMMM d日", waLocale);
            
        return wa.format(date);
    }

}

「なでしこ」とかそーいう機能のあってもよさそうな...と調べてる間に....(まさかJavaのほうとは)。そこがオイシイ言語やライブラリもあるんじゃないの?もったいない:-)

といいつつ、太陰暦付近はあいまいな出力をするプログラム。(対応する範囲が5年程しかないし、ゴリ押しでできそうな気もする)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
#include <stdio.h>
#include <iostream>

bool check_date(int yy, int mm, int dd) {
    if ((mm < 1) || (mm > 12))
        return false;
    int DATE_MAX[] = {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
    if (dd < 1 || dd > DATE_MAX[mm - 1])
        return false;
    if ((mm != 2) || (dd < 29))
        return true;
    return ((yy % 4) == 0 && (yy % 100) != 0) || ((yy % 400) == 0);
}

void wareki(int yy, int mm, int dd, std::ostream& s) {
    if ((yy < 1868) || (check_date(yy, mm, dd) == false)) { s << "範囲外"; }
    else {
        int ymd = yy * 10000 + mm * 100 + dd;
        if      (ymd <  18680125) { s << "範囲外"; }
        else if (ymd == 19120730) { s << "明治45年7月30日 大正1年7月30日";    }
        else if (ymd == 19261225) { s << "大正15年12月25日 昭和1年12月25日";  }
        else {
            if (ymd < 18730101) {    // 1873/1/1より前((明治5年以前)は太陰暦
                if      (ymd < 18690101) { s << "明治1年9月8日~11月18日のどこか"; }
                else if (ymd < 18700101) { s << "明治1年11月19日~明治2年11月29日のどこか"; }
                else if (ymd < 18710101) { s << "明治2年11月30日~明治3年11月10日のどこか"; }
                else if (ymd < 18720101) { s << "明治3年11月11日~明治4年11月20日のどこか"; }
                else                     { s << "明治4年11月21日~明治5年12月2日のどこか";  }
            }
            else {
                if      (ymd <= 19120730) { s << "明治" << (yy - 1869) << "年"; }
                else if (ymd <= 19261225) { s << "大正" << (yy - 1911) << "年"; }
                else if (ymd <= 19890107) { s << "昭和" << (yy - 1925) << "年"; }
                else                      { s << "平成" << (yy - 1988) << "年"; }
                s << mm << "月" << dd << "日";
            }
        }
    }
}

void wareki(const char* p) {
    int yy, mm, dd;
    sscanf(p, "%d/%d/%d", &yy, &mm, &dd);
    wareki(yy, mm, dd, std::cout);
    std::cout << std::endl;
}

int main(int argc, char* argv[]) {
    wareki("1868/12/2");    // ... orz
    wareki("1926/12/24");   // 大正15年12月24日
    wareki("2007/12/01");   // 平成19年12月1日
    wareki("1912/7/30");    // 明治45年7月30日 大正1年7月30日
    wareki("1926/12/25");   // 大正15年12月25日 昭和1年12月25日 
    wareki("1868/1/2");     // 範囲外 
    wareki("1868/100/2");   // 範囲外 
    return 0;
}

明治 6 年より前は範囲外で。あまり美しくないですが。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
import std.stdio;
import std.date;
import std.string;

void printWareki(string date){
    auto d = parse(date);
    if(d == d_time_nan){
        writefln("範囲外");
        return;
    }

    auto meiji6 = parse("1873/1/1");
    auto taisho = parse("1912/7/30");
    auto showa = parse("1926/12/25");
    auto heisei = parse("1989/1/8");

    auto dateParts = date.split("/");
    auto year = atoi(dateParts[0]);
    auto month = atoi(dateParts[1]);
    auto day = atoi(dateParts[2]);

    void output(char eraNameCap){
        auto diff = (['M' : 1867, 'T' : 1911, 'S' : 1925, 'H' : 1988]);
        auto eraName = (['M' : "明治", 'T' : "大正", 'S' : "昭和", 'H' : "平成"]);
        writef("%s%d年%d月%d日", eraName[eraNameCap],
                    year - diff[eraNameCap], month, day);
    }

    if(meiji6 <= d && d < taisho){
        output('M');
    }
    else if(d == taisho){
        output('M'); writef(" "); output('T');
    }
    else if(taisho < d && d < showa){
        output('T');
    }
    else if(d == showa){
        output('T'); writef(" "); output('S');
    }
    else if(showa < d && d < heisei){
        output('S');
    }
    else if(heisei <= d){
        output('H');
    }
    else{
        writef("範囲外");
    }
    writefln();
}

void main(){
    printWareki("1868/12/2");   // 範囲外
    printWareki("1926/12/24");  // 大正15年12月24日
    printWareki("2007/12/01");  // 平成19年12月1日
    printWareki("1926/12/25");  // 大正15年12月25日 昭和1年12月25日
    printWareki("1868/1/2");    // 範囲外
    printWareki("1868/100/2");  // 範囲外
    printWareki("1904/02/06");  // 明治37年2月6日
}

お察しの通りなでしこには「和暦変換」という関数が標準でありますが、すべてグレゴリオ暦で処理するようです。

1
2
3
4
5
6
7
「1868/12/2」を和暦変換して表示
「1926/12/25」を和暦変換して表示
/*
 * 結果
 明治元年12月2日
 昭和元年12月25日
*/

たしかに、Java6で導入された和暦に関する機能は明治⇔大正⇔昭和の境界になっている日の扱いが新しい方の年号になっているので、厳密ではありませんね。 とはいえ、Javaに限らずほとんどの言語の組み込みAPIは異なる暦(たとえば西暦と和暦)の日付が1対1対応すると仮定したインタフェースになっているため、きれいな解決方法はなさそうです。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
import java.text.{SimpleDateFormat, ParseException}
import java.util.{Date, Locale, Calendar}

object west2japanese {
  val inf    = new SimpleDateFormat("y/MM/dd")
  val parse = {inf.setLenient(false);inf}.parse _:String => Date
  val format = new SimpleDateFormat("GGGGy年MMMMd日", 
                    new Locale("ja", "JP", "JP")).format _:Date => String
  def apply(d:String) = try { 
    (format compose parse)(d) match {
      case "大正1年7月30日" => "明治45年7月30日 大正1年7月30日"
      case "昭和1年12月25日" => "大正15年12月25日 昭和1年12月25日"
      case s if s.matches("^(西暦|明治[1-5]{1}年).*") => "範囲外"
      case s => s
    }
  }catch{
    case e:ParseException => "範囲外"
  }
}

明治6年以降対応版を自前で。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
●Date2Wareki(date)
    tmpとは文字列
    dとは整数=Date2Int(date)
    もし(d<18730101)ならば
        "範囲外"で戻る
    もし(d<=19120730)ならば
        もし(d=19120730)ならば
            "明治45年7月30日 大正元年7月30日"で戻る
        Date2JADate("明治",1867,date)
    もし(d>=19120731&&d<=19261225)ならば
        もし(d=19261225)ならば
            "大正15年12月25日 昭和元年12月25日"で戻る
        Date2JADate("大正",1911,date)
    もし(d>=19261226&&d<=19890107)ならば
        Date2JADate("昭和",1925,date)
    もし(d>=19890108)ならば
        Date2JADate("平成",1988,date)
●Date2JADate(nengou,sa,date)
    date=dateを"/"で区切る
    "{nengou}{date[0]-sa}年{TOINT(date[1])}月{TOINT(date[2])}日"で戻る
●Date2Int(date)
    date=dateを"/"で区切る
    TOSTR(date[0]を4でゼロ埋め)&TOSTR(date[1]を2でゼロ埋め)&TOSTR(date[2]を2でゼロ埋め)で戻る

Date2Wareki("1926/12/25")を表示

Pythonのがないみたいなので投稿。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# coding: shift_jis

import datetime
import sys

def main():
    year, month, day = [int(s) for s in sys.argv[1].split("/")]
    def f():
        try:
            date = datetime.date(year, month, day)
        except ValueError:
            return
        if datetime.date(1873, 1, 1) <= date <= datetime.date(1912, 7, 30):
            yield u"明治", 1867
        if datetime.date(1912, 7, 30) <= date <= datetime.date(1926, 12, 25):
            yield u"大正", 1911
        if datetime.date(1926, 12, 25) <= date <= datetime.date(1989, 1, 7):
            yield u"昭和", 1925
        if datetime.date(1989, 1, 8) <= date:
            yield u"平成", 1988
    print " ".join(u"%s%d%d%d日" % (gengo, year - offset, month, day) \
        for gengo, offset in f()) or u"範囲外"

if __name__ == '__main__':
    main()

お題のサンプル出力では元年は前年号の最終年と併記していますが, 1年を元年としたことが同義だと考えてその記述は省略しました.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
require 'date'
require 'kconv'
WAREKI = [['明治', '1868/9/8'], ['大正', '1912/7/30'], 
  ['昭和', '1926/12/25'], ['平成', '1989/1/8']]
DATES = %w('1868/12/2' '1926/12/24' '2007/12/01' '1926/12/25'
           '1868/1/2' '1868/100/2')
wareki = WAREKI.sort{|a, b| 
  -1*(Date.parse(a[1]).year <=> Date.parse(b[1]).year)
} # => 新しい年代からマッチさせるためにソート 
DATES.each{|date_str|
  begin
    date = Date.parse(date_str)
    y0, m0, d0 = date.year, date.month, date.day
    era = wareki.each{|nengo, date_str|
      date = Date.parse(date_str)
      y1, m1, d1 = date.year, date.month, date.day
      evaluation = y0 >= y1 && (m0 > m1 or m0 == m1 && d0 >= d1)
      break [nengo, y0 - y1] if evaluation
    }
    puts "#{era[0]}#{era[1] == 0 ? '元' : era[1] + 1}#{m0}#{d0}日".tosjis
  rescue
    puts '範囲外'.tosjis
    next
  end
}

seireki2wareki("1868/9/8")
>>範囲外

seireki2wareki("1873/1/1")
>>明治6年1月1日

seireki2wareki("1926/12/24")
>>大正15年12月24日

seireki2wareki("2007/12/01")
>>大正15年12月24日

seireki2wareki("1926/12/25")
>>大正15年12月24日

seireki2wareki("1868/1/2")
>>範囲外

seireki2wareki("1873/100/2")
>>範囲外
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
function seireki2wareki(y){
    var c = new Date(y);
    
    if(c.getFullYear()<1873) return "範囲外";
    
    var wareki = {
        '明治' : [new Date("1868/9/8"), new Date("1912/7/30")],
        '大正' : [new Date("1912/7/30"), new Date("1926/12/25")],
        '昭和' : [new Date("1926/12/25"), new Date("1989/1/7")],
        '平成' : [new Date("1989/1/8"), new Date()]
    }
    var a = [];

    for(w in wareki){
        var from = wareki[w][0];
        var to = wareki[w][1];
        
        if((c>=from && c<=to)){
            var n1 = (to.getFullYear()- from.getFullYear())+1;
            var n2 = to.getFullYear()- c.getFullYear();
            
            a.push(w+ (n1-n2)+ "年"+ (c.getMonth()+1)+ "月"+ c.getDate()+ "日");
        }
    }
    
    return a.join(" ") || "範囲外";
}

明治元年~明治5年の範囲で太陰暦にも対応しました。
(黄道の計算などはしておらず、5年分のテーブルを持っているだけです。)
漢字での出力に対応していますが、utf-8専用です。

% ./dk122 1868/12/2
明治元年10月19日
% ./dk122 1926/12/24
大正15年12月24日
% ./dk122 2007/12/01
平成19年12月1日
% ./dk122 1926/12/25
大正15年12月25日  昭和元年12月25日
% ./dk122 1868/1/2
範囲外
% ./dk122 1868/100/2
範囲外

【追加テスト】
% ./dk122 1873/1/1
明治6年1月1日
% ./dk122 1872/12/31
明治5年12月2日
% ./dk122 1868/10/23
明治元年9月8日
% ./dk122 1870/10/25
明治3年10月1日
% ./dk122 1870/11/23
明治3年閏10月1日
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
open String
open Printf

let meiji   = "\xE6\x98\x8E\xE6\xB2\xBB"
let taishou = "\xE5\xA4\xA7\xE6\xAD\xA3"
let showa   = "\xE6\x98\xAD\xE5\x92\x8C"
let heisei  = "\xE5\xB9\xB3\xE6\x88\x90"
let gan     = "\xE5\x85\x83"
let nen     = "\xE5\xB9\xB4"
let gatsu   = "\xE6\x9C\x88"
let nichi   = "\xE6\x97\xA5"
let uruu    = "\xE9\x96\x8F"

        (* M1   Meiji 2      Meiji 3       Meiji 4      Meiji 5    *)
let tab = "9009 000990990900 900909090jj09 009009090990 909009009092"

let errmsg = "\xE7\xAF\x84\xE5\x9B\xB2\xE5\xA4\x96"
let mtab = [| 0; 31; 29; 31; 30; 31; 30; 31; 31; 30; 31; 30; 31 |]

let prt1 era y m d =
    let ystr = if y = 1 then gan else string_of_int y in
    let leap = if m mod 2 = 1 then uruu else "" in
    sprintf "%s%s%s%s%d%s%d%s" era ystr nen leap (m/2) gatsu d nichi

let prt2 era1 y1 era2 y2 m d = sprintf "%s  %s" (prt1 era1 y1 m d) (prt1 era2 y2 m d)

let dec i (year, month) = if month - 2 >= i then year    , month - i
                                            else year - 1, month - i + 24

let rec search target i r (year, month as date) =
    if r < target then year, month, target - r
        else let r', d' = match tab.[i] with
            | '0' -> 30, 2 | '9' -> 29, 2 | '2' -> 2, 2 | 'j' -> 29, 1 | _ -> 0, 0 in
            search target (i - 1) (r - r') (dec d' date)

let taiin year month day =
    let y, m = if month < 3 then year - 1, month + 12 else year, month in
    let w = y * 365 + y / 4 - y / 100 + y / 400