PHP
日付

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

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

経緯

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

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

概要

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

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

仕様

対応する形式

フォーマットを決めます。日付と時刻の表現方法はISO 8601で規定されているのですが、日本ではこれに和暦の拡張仕様を追加したJIS X 0301で規定されています。

余談ですが、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]+)秒)?)?)?

可視化したのがこちら
image.png

改暦した日をどう扱うか

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

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

改暦をどう扱うか

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

JISでは明治、大正、昭和、平成が規定されているだけで、明治より昔の元号については規定されていません。理由は明治より前は太陰太陽暦が用いられていたからだと、仕様書29ページに記載されています。

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

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

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

実装

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

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

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

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のフォーマットってどうなるんだろう。