Intl.DateTimeFormat による明治以前の和暦の扱い


TL;DR

少なくとも Chrome, Firefox, Edge の実装において、明治改暦以前の日付について、 Intl.DateTimeFormat の返す和暦は多分に怪しいです。

  • 元号の始まりは、実際の改元日ではなく、和暦1における改元日の月日を西暦2(グレゴリオ暦3/ユリウス暦4)における月日と解釈した日となっている。
  • 南北朝時代の元号は、南朝の元号と北朝の元号とが混在しており、年の数値も正しくない。
  • 初期の元号については、通説と異なる扱いとなっている。

動作確認環境

本記事では以下のブラウザでの挙動について記載します。

  • Google Chrome 83.0.4103.116
  • Mozilla Firefox 78.0.1
  • Microsoft Edge 83.0.478.58

※ 因みに Internet Explorer 11 では挙動が異なりました(1867年以前の日付は常にエラー、1868年1月1日より明治元年)。本記事では Internet Explorer は無視します。

はじめに

現行の ECMAScript の標準では、ロケールを指定して日時をフォーマット化するには Intl.DateTimeFormat を使用します。
例えば以下のようにすることで、日本の和暦で日付を出力することができます。

var option = { era: "long", year: "numeric", month: "long", day: "numeric" };
var formatter = new Intl.DateTimeFormat("ja-JP-u-ca-japanese", option);
console.log(formatter.format(new Date("2020-07-06")));
// "令和2年7月6日" と出力される

Intl.DateTimeFormat の仕様の詳細については以下を参照してください。
https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Intl/DateTimeFormat

改元日の扱い

それでは、令和, 平成, 昭和, … の改元前後の日付を和暦で出力してみましょう。
ちゃんと改元の日から元号が変わっているようですね――

console.log(formatter.format(new Date("2019-05-01")));    // 令和1年5月1日
console.log(formatter.format(new Date("2019-04-30")));    // 平成31年4月30日
console.log(formatter.format(new Date("1989-01-08")));    // 平成1年1月8日
console.log(formatter.format(new Date("1989-01-07")));    // 昭和64年1月7日
console.log(formatter.format(new Date("1926-12-25")));    // 昭和1年12月25日
console.log(formatter.format(new Date("1926-12-24")));    // 大正15年12月24日
console.log(formatter.format(new Date("1912-07-30")));    // 大正1年7月30日
console.log(formatter.format(new Date("1912-07-29")));    // 明治45年7月29日
console.log(formatter.format(new Date("1868-10-23")));    // 明治1年10月23日
console.log(formatter.format(new Date("1868-10-22")));    // 明治1年10月22日
// 以上において "1年" とあるのは Firefox では "元年" と出力される

……いや、おかしいですね。
慶応から明治への改元は西暦1868年10月23日の筈なのに、その前日の1868年10月22日も "明治1年" になっていますね。

これはもしや、明治改元が立年改元(改元年の元日に遡って新元号を適用する)であったことを反映しているのか?
と思って明治元年1月1日(西暦1868年1月25日5)を表示してみると……

console.log(formatter.format(new Date("1868-01-25")));    // 慶応4年1月25日

そんなことは無かった。
はて、では慶応と明治との境目はどこにあるのだろう?
と思って探ってみると、結果は以下の通り。

console.log(formatter.format(new Date("1868-09-08")));    // 明治1年9月8日
console.log(formatter.format(new Date("1868-09-07")));    // 慶応4年9月7日

1868年9月8日だそうで。
確かに明治改元の日(西暦1868年10月23日)は和暦で言うと9月8日ですが、これはおかしな話です。
new Date("1868-09-08") というのは西暦の1868年9月8日のことであって、明治改元の日である和暦の9月8日とは全く異なる日です。

「もしや」と思って慶応以前の改元日についても調べてみると、案の定。

// 慶応改元は西暦1865年5月1日 和暦4月7日
console.log(formatter.format(new Date("1865-04-07")));    // 慶応1年4月7日
console.log(formatter.format(new Date("1865-04-06")));    // 元治2年4月6日

// 元治改元は西暦1864年3月27日 和暦2月20日
console.log(formatter.format(new Date("1864-02-20")));    // 元治1年2月20日
console.log(formatter.format(new Date("1864-02-19")));    // 文久4年2月19日

// 文久改元は西暦1861年3月29日 和暦2月19日
console.log(formatter.format(new Date("1861-02-19")));    // 文久1年2月19日
console.log(formatter.format(new Date("1861-02-18")));    // 万延2年2月18日

いずれも実際の改元日ではなく、和暦における改元日の月日を西暦における月日と解釈したものを改元日として扱うという謎の挙動になっています。

では、「和暦における改元日の月日を西暦における月日と解釈したもの」が存在しない場合はどうなるのかと言うと……

// 寛永改元は西暦1624年4月17日 和暦2月30日
console.log(formatter.format(new Date("1624-03-01")));    // 寛永1年3月1日
console.log(formatter.format(new Date("1624-02-29")));    // 元和10年2月29日

「1624年2月30日」なんてのは西暦には存在しない日付ですが、「2月29日までは元和、3月1日からは寛永」として扱われています。

ユリウス暦時代の日付

前節で明治以前の改元日の扱いが怪しいということが確認できた訳ですが、日付をもっと遡っていくと、1582年10月にまた違った挙動が出てきます。
ユリウス暦からグレゴリオ暦への改暦です。

console.log(formatter.format(new Date("1582-10-17")));    // 天正10年10月17日
console.log(formatter.format(new Date("1582-10-16")));    // 天正10年10月16日
console.log(formatter.format(new Date("1582-10-15")));    // 天正10年10月15日
console.log(formatter.format(new Date("1582-10-14")));    // 天正10年10月4日
console.log(formatter.format(new Date("1582-10-13")));    // 天正10年10月3日
console.log(formatter.format(new Date("1582-10-12")));    // 天正10年10月2日
console.log(formatter.format(new Date("1582-10-11")));    // 天正10年10月1日
console.log(formatter.format(new Date("1582-10-10")));    // 天正10年9月30日

Date オブジェクトのコンストラクタは先発グレゴリオ暦で表現することになっているので、 new Date("1582-10-15") の前日を表す Date オブジェクトは new Date("1582-10-14") です。
https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Date/Date

しかし乍ら、文字列表現の際には慣例に倣って1582年10月4日以前の日付はユリウス暦で表すことになります。
これはロケールが "ja-JP-u-ca-japanese" の場合でも同じのようですね。

そうなると気になるのが、ユリウス暦時代の改元日の扱い。結果は以下の通り。

// 天正改元は西暦1573年8月25日 和暦7月28日
console.log(formatter.format(new Date("1573-08-07")));    // 天正1年7月28日
console.log(formatter.format(new Date("1573-08-06")));    // 元亀4年7月27日

// 元亀改元は西暦1570年5月27日 和暦4月23日
console.log(formatter.format(new Date("1570-05-03")));    // 元亀1年4月23日
console.log(formatter.format(new Date("1570-05-02")));    // 永禄13年4月22日

// 永禄改元は西暦1558年3月18日 和暦2月28日
console.log(formatter.format(new Date("1558-03-10")));    // 永禄1年2月28日
console.log(formatter.format(new Date("1558-03-09")));    // 弘治4年2月27日

見ての通り、ユリウス暦時代においても和暦における改元日の月日を西暦(ユリウス暦)における月日と解釈したものを改元日として扱うという挙動となっています。

南北朝時代の元号

ここまでの時点で既に謎に満ちた挙動なのですが、南北朝時代まで遡ると更にカオスになってきます。

console.log(formatter.format(new Date("1329-09-05")));    // 嘉暦4年8月28日
console.log(formatter.format(new Date("1329-09-06")));    // 元徳1年8月29日
console.log(formatter.format(new Date("1331-08-16")));    // 元徳3年8月8日
console.log(formatter.format(new Date("1331-08-17")));    // 元弘1年8月9日
console.log(formatter.format(new Date("1334-02-05")));    // 元弘4年1月28日
console.log(formatter.format(new Date("1334-02-06")));    // 建武1年1月29日
console.log(formatter.format(new Date("1336-03-07")));    // 建武3年2月28日
console.log(formatter.format(new Date("1336-03-08")));    // 延元1年2月29日
console.log(formatter.format(new Date("1340-05-05")));    // 延元5年4月27日
console.log(formatter.format(new Date("1340-05-06")));    // 興国1年4月28日
console.log(formatter.format(new Date("1346-12-15")));    // 興国7年12月7日
console.log(formatter.format(new Date("1346-12-16")));    // 正平1年12月8日
console.log(formatter.format(new Date("1370-07-31")));    // 正平25年7月23日
console.log(formatter.format(new Date("1370-08-01")));    // 建徳1年7月24日
console.log(formatter.format(new Date("1372-04-08")));    // 建徳3年3月31日
console.log(formatter.format(new Date("1372-04-09")));    // 文中1年4月1日
console.log(formatter.format(new Date("1375-06-03")));    // 文中4年5月26日
console.log(formatter.format(new Date("1375-06-04")));    // 天授1年5月27日
console.log(formatter.format(new Date("1379-03-29")));    // 天授5年3月21日
console.log(formatter.format(new Date("1379-03-30")));    // 康暦1年3月22日
console.log(formatter.format(new Date("1381-02-17")));    // 康暦3年2月9日
console.log(formatter.format(new Date("1381-02-18")));    // 弘和1年2月10日
console.log(formatter.format(new Date("1384-05-05")));    // 弘和4年4月27日
console.log(formatter.format(new Date("1384-05-06")));    // 元中1年4月28日
console.log(formatter.format(new Date("1387-08-29")));    // 元中4年8月21日
console.log(formatter.format(new Date("1387-08-30")));    // 至徳1年8月22日
console.log(formatter.format(new Date("1387-08-31")));    // 嘉慶1年8月23日
console.log(formatter.format(new Date("1389-02-16")));    // 嘉慶3年2月8日
console.log(formatter.format(new Date("1389-02-17")));    // 康応1年2月9日
console.log(formatter.format(new Date("1390-04-02")));    // 康応2年3月25日
console.log(formatter.format(new Date("1390-04-03")));    // 明徳1年3月26日
console.log(formatter.format(new Date("1394-07-12")));    // 明徳5年7月4日
console.log(formatter.format(new Date("1394-07-13")));    // 応永1年7月5日

これだけ見ても何がどうカオスなのかわかりづらいのですが、よく見ると南朝の元号北朝の元号とが混在しています。
南北両朝の元号と Intl.DateTimeFormat による扱いを表に纏めると、次のようになります。6

日付 南朝(大覚寺統) 北朝(持明院統) Intl.DateTimeFormat
1326年4月26日~ 嘉暦 嘉暦
1329年8月29日~ 元徳 元徳 元徳
1331年8月9日~ 元弘 元弘
1332年4月28日~ 正慶
1333年5月25日~ (元弘)
1334年1月29日~ 建武 建武 建武
1336年2月29日~ 延元 延元
1338年8月28日~ 暦応
1340年4月28日~ 興国 興国
1342年4月27日~ 康永
1345年10月21日~ 貞和
1346年12月8日~ 正平 正平
1350年2月27日~ 観応
1352年9月27日~ 文和
1356年3月28日~ 延文
1361年3月29日~ 康安
1362年9月23日~ 貞治
1368年2月18日~ 応安
1370年7月24日~ 建徳 建徳
1372年4月1日~ 文中 文中
1375年2月27日~ 永和
1375年5月27日~ 天授 天授
1379年3月22日~ 康暦 康暦
1381年2月10日~ 弘和 弘和
1381年2月24日~ 永徳
1384年2月27日~ 至徳
1384年4月28日~ 元中 元中
1387年8月22日 至徳1年8月22日
1387年8月23日~ 嘉慶 嘉慶
1389年2月9日~ 康応 康応
1390年3月26日~ 明徳 明徳
1392年閏10月5日~ 明徳
1394年7月5日~ 応永 応永

南朝・北朝いずれの元号を採用しているのか、基準が全くの謎です。

特に怪しいのがこれ。

console.log(formatter.format(new Date("1387-08-30")));    // 至徳1年8月22日

どういう訳か、この1日だけ「至徳」という元号になっています。
しかも、1387年は正しくは "至徳4年" であるべきなのに、何故か "至徳1年" と出力されます

初期の元号

更に更に遡って飛鳥時代。

console.log(formatter.format(new Date("0701-03-25")));    // 大宝1年3月21日
console.log(formatter.format(new Date("0701-03-24")));    // 朱鳥16年3月20日
console.log(formatter.format(new Date("0686-07-23")));    // 朱鳥1年7月20日
console.log(formatter.format(new Date("0686-07-22")));    // 白鳳15年7月19日
console.log(formatter.format(new Date("0672-01-04")));    // 白鳳1年1月1日
console.log(formatter.format(new Date("0672-01-03")));    // 白雉22年12月31日
console.log(formatter.format(new Date("0650-02-18")));    // 白雉1年2月15日
console.log(formatter.format(new Date("0650-02-17")));    // 大化6年2月14日
console.log(formatter.format(new Date("0645-01-04")));    // 大化1年1月1日
console.log(formatter.format(new Date("0645-01-03")));    // 大化0年12月31日
console.log(formatter.format(new Date("0644-01-03")));    // 大化-1年12月31日
console.log(formatter.format(new Date("0643-01-03")));    // 大化-2年12月31日

これもだいぶ通説と異なります。

  • 「朱鳥」は朱鳥元年9月9日(天武天皇崩御)を以て廃止されたとされているが、次の「大宝」制定までの間が全て「朱鳥」として扱われている。
  • 「白鳳」は「白雉」の別称とされる私年号(公文書に現れない元号)だが、672年~686年の間が「白鳳」として扱われている。
  • 「白雉」は白雉5年10月10日(孝徳天皇崩御)を以て廃止されたとされているが、671年までの間が全て「白雉」として扱われている。
  • 「大化」は大化元年6月19日(孝徳天皇即位)を以て制定されたとされているが、それ以前の期間が全て「大化」として扱われている。

大化より前の期間や元号の空白期間については仕方無いとしても、「白鳳」の扱いはかなり謎です。

まとめ

そんな訳で、少なくとも Chrome, Firefox, Edge の実装において、明治改暦以前の日付について、 Intl.DateTimeFormat の返す和暦は多分に怪しいということがわかりました。
明治以前の和暦を正しく扱いたい場合は、自分で実装し直した方が良いでしょう。


  1. 本稿において和暦とは、日本の元号紀年法に、明治5年以前は天保暦等の太陰太陽暦(所謂旧暦)を、明治6年以降はグレゴリオ暦を組み合わせたものを指す。 

  2. 本稿において西暦とは、キリスト紀年法に、1582年10月4日以前はユリウス暦を、1582年10月15日以降はグレゴリオ暦を組み合わせたものを指す。 

  3. グレゴリオ暦: 我々が普段使っている暦。平年の日数は365日で、400年に97回の閏日が挿入される。 

  4. ユリウス暦: グレゴリオ暦採用以前のヨーロッパで採用されていた暦。1582年10月15日(ユリウス暦10月5日)にローマ・カトリック教会で採用された後、順次広まっていった。 

  5. 日本におけるグレゴリオ暦の採用は1873年(明治6年)なので、明治元年時点では和暦と西暦の月日は異なる。 

  6. 表中の日付は日本の旧暦による年にキリスト紀年法を当て嵌めたもの。ユリウス暦の年とは必ずしも一致しない。