和暦、日本語表記の日付と時刻をパースしてみた。

正直なところ需要はないかもしれないが、作ってみた。

# 経緯

なぜ作ったかというと、以前[ハイフン無しの電話番号をハイフン付の形式に変換してみる](http://qiita.com/tomgoodsun/items/f3050689ac84827d5af9)で、総務省のデータを見たときに__(平成29年8月1日現在)__とか記載されていたので、これから西暦に変換するための処理を作ってみようと思いました。

（正直、今後仕事で使う機会もたぶんないので、微妙な感じです）
データはすべからく西暦で記載すべきです。

# 概要

今回の目的は年の部分が和暦の場合に西暦に変換することが大きな目的ですが、せっかくなので、平成29年1月1日午前1時1分1秒みたいな形式をパース出来るように正規表現を頑張ってみました。

なお、当たり前かもしれませんが、世界的にも西暦を採用する例が多いようです。
また、以前聞いた話では、給与計算や経理のソフトには和暦が使われることがあるらしいのですが、改暦には対応しているそうです。
そういえば、銀行の通帳も年のところは和暦を使ってますね。

# 仕様

## 対応する形式

フォーマットを決めます。日付と時刻の表現方法は[ISO 8601](https://ja.wikipedia.org/wiki/ISO_8601)で規定されているのですが、日本ではこれに和暦の拡張仕様を追加した[JIS X 0301](http://www.jisc.go.jp/pdfa7/PDFView/ShowPDF/dQAAAPNq_sxTwbKt1twy)で規定されています。

余談ですが、JISのページって使いにくいですね。
ウェブからもっと参照されやすい作りにしておけばいいのに。

これを見ていてちょっと驚いたのは、和暦表記は「平」や「H」のフォーマットしかなく、「平成」は規定されていない。
しょうがないので、Excelの日本語ロケールで例に上がっている表記を参考にすることにしました。

ということで以下のような感じです。

| フォーマット                 | 備考                |
|:-----------------------------|:--------------------|
| 平02年05月07日               | JIS X 0301          |
| H02.05.07                    | JIS X 0301          |
| 平成元年5月7日               | Excelまたは独自仕様 |
| 平成1年5月7日                | Excelまたは独自仕様 |
| 平成02年05月07日             | Excelまたは独自仕様 |
| 平成2年5月7日午前1時15分30秒 | Excelまたは独自仕様 |
| 平成2年5月7日13時15分30秒    | Excelまたは独自仕様 |
| 平成2年5月7日13時15分        | Excelまたは独自仕様 |
| H1.5.7                       | Excelまたは独自仕様 |

## 正規表現

数日試行錯誤してみたんですが、最終的に上記のように仕様を決めてしまえば、正規表現1発でパースできることがわかりました。
またREGEXPERというサイトで正規表現を可視化することで、かなり助けられた面があります。

ちなみに正規表現は以下のような感じです。（「元号」の部分はプログラムで変化します。）

```
(元号(元|[0-9]+)[\.年]([0-9]+)[\.月](([0-9]+)[\.日]?)?)((午前|午後)?([0-9]+)時(([0-9]+)分(([0-9]+)秒)?)?)?
```

可視化したのが[こちら](https://regexper.com/#(%E5%85%83%E5%8F%B7(%E5%85%83%7C%5B0-9%5D%2B)%5B%5C.%E5%B9%B4%5D(%5B0-9%5D%2B)%5B%5C.%E6%9C%88%5D((%5B0-9%5D%2B)%5B%5C.%E6%97%A5%5D%3F)%3F)((%E5%8D%88%E5%89%8D%7C%E5%8D%88%E5%BE%8C)%3F(%5B0-9%5D%2B)%E6%99%82((%5B0-9%5D%2B)%E5%88%86((%5B0-9%5D%2B)%E7%A7%92)%3F)%3F)%3F)。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/131218/b7fc86cd-89eb-2a3a-1bed-d87ed2aa2838.png)

## 改暦した日をどう扱うか

昭和は1926年12月25日0時0分から昭和64年1月7日23時59分、平成は1989年1月8日0時0分からとなっています。
今回は和暦は入力なので、このへんはあまり気にしないことにします。

年を計算する上では、各元号の起点となった西暦と入力された元号の年が重要なのです。
その他の日付はそのまま扱います。

## 改暦をどう扱うか

この見出し、前の項目と何が違うかというと、基本的に元号は日本独特の表記で月日が経つと追加されるからです。

JISでは明治、大正、昭和、平成が規定されているだけで、明治より昔の元号については規定されていません。理由は明治より前は[太陰太陽暦](https://ja.wikipedia.org/wiki/%E5%A4%AA%E9%99%B0%E5%A4%AA%E9%99%BD%E6%9A%A6)が用いられていたからだと、仕様書29ページに記載されています。

また、この記事を書いている時点では天皇譲位が特例法で決まって、それに合わせて新元号が制定されることがわかっています。（元号法という法律で決まっているようです）
参考：[【 気になる平成の次は？ 】天皇譲位による新元号の定め方とは](https://rekijin.com/?p=21695)

このあたりはソフトウェアをアップデートするような感じで追加していくしかないでしょう。

なので、今回はJISに合わせて、明治、大正、昭和、平成を対象とします。

# 実装

PHPのDateTimeクラスを継承する形で、パースするプログラムはパブリックな静的メソッドに切り出したました。
各元号の改暦日時の実装については以下の記事を参考にさせて頂きました。

- [PHPで和暦・日本語曜日を使う](http://qiita.com/chiyoyo/items/da32649b0e04957856c1)
- [Wikipedia 和暦](https://ja.wikipedia.org/wiki/%E5%92%8C%E6%9A%A6)
- [Wikipedia 元号](https://ja.wikipedia.org/wiki/%E5%85%83%E5%8F%B7)

今回もプログラムはGitHubに公開しています。
https://github.com/tomgoodsun/parse_ja_datetime

実行すると以下のような感じになります。

```php
echo (new DateTimeJp('平成2年5月7日午前1時15分30秒'))->format('Y-m-d H:i:s');
// 1990-05-07 12:15:30

var_export(DateTimeJp::parse('平成2年5月7日午後1時15分30秒'));
// array (
//   'year' => 1990,
//   'month' => '5',
//   'day' => '7',
//   'hour' => 13,
//   'minute' => '15',
//   'second' => '30',
// )
```

# まとめ

利用シーンはかなり少ないかなと言う感じです。（自分自身、仕事で使うことはまずないだろうなと思ってます。）
個人的には日本の公文書でも西暦を採用すればいいのに、、、と思っています。
ちなみにあり得ないとは思いますが、和暦が100年以上続いたら後述のJISのフォーマットってどうなるんだろう。