日本語や漢字の日付をPythonのdatetimeに変換するライブラリを作った
日本語文章を解析しているときに地味に困る物の一つに漢字等の日本語で書かれた年月日があります。
今回は、日本語で書かれた日付のデータをPythonのdatetime、timedeltaに変換するライブラリを作成したので紹介します。
※ datetimeは日時、timedeltaは時間(時間差)を取り扱うPython標準オブジェクトです
kanji_to_timeという名前のライブラリです。
https://github.com/corkborg/kanji_to_time
https://pypi.org/project/kanji-to-time/
こんな感じでインストールできます。
pip install kanji_to_time
こんな変換ができます
半角、漢字混じり、漢数字、全角数字などを変換できます。
>>> import kanji_to_time as ktt
>>> text = "2024年4月5日22時30分4秒"
>>> ktt.to_datetime(text)
datetime.datetime(2024, 4, 5, 22, 30, 4)
>>> ktt.to_timedelta("マイナス七〇億分")
datetime.timedelta(days=-4861112, seconds=76800)
旧字の漢数字などにも対応しています。
>>> ktt.to_datetime("弐零弐参年伍月肆日")
datetime.datetime(2023, 5, 4, 0, 0)
今のところ億の位まで対応しています。
>>> ktt.to_timedelta("3億二三万千7十1秒間")
datetime.timedelta(days=3474, seconds=77471)
機能ポイント
- 数値部分の文字数が可変でも変換可能です(ゼロ埋めの必要が無い)
- 半角全角漢数字が混じっていても変換することができます。
- 20秒、20秒間のように位の後ろに間が付いていても同様に変換することができます(timdelta変換のみ)。
どうやって作ったか?
当初、そんなに難しいコードを書かなくても解決できるだろうと考えていました。
しかし、単純そうに見えて時間の変換はルールが多くてシンプルなコードでは実現できないことがわかりました。
このようなルールがある文章を処理する場合、一番手軽なのは正規表現です。私も当初はreを使って解決しようと思ったのですが、実装している途中で正規表現で実現すると大変複雑で自分ですら理解できないようなコードになってしまうことに気づきました。
手間を惜しまなければ、ルールが複雑な文章(文法)を相手にする際にはパーサージェネレータを使ったほうが良いです。Pythonでおすすめのパーサージェネレータを調べた所Larkというものがヒットしたのでこれを使用して作成しました。
今回は、kanji_to_timeのパーサの中でも数字の周りの文法ルールだけを抜き出して紹介します。
number: (MINUS | PLUS)? mixed_number_with_unit
mixed_number_with_unit: unit_oku? unit_man? unit_sen? unit_juu? mixed_number?
MINUS: "-" | "ー" | "マイナス" | "ひく" | "引く"
PLUS: "+" | "+" | "プラス" | "たす" | "足す"
unit_oku: mixed_number "億" // 億は数値が必須
unit_man: mixed_number "万" // 万は数値が必須
unit_sen: mixed_number? "千"
unit_juu: mixed_number? "十"
mixed_number: (ZENKAKU_DIGIT | KANJI_DIGIT | DIGIT)+
KANJI_DIGIT: "一" | "壱"
| "二" | "弐"
| "三" | "参"
| "四" | "肆"
| "五" | "伍"
| "六" | "陸"
| "七" | "漆"
| "八" | "捌"
| "九" | "玖"
| "〇" | "ゼロ" | "零"
| "○" | "◯" // 一応丸もサポートしておく
ZENKAKU_DIGIT: "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9"
KANJI_DIGITという漢数字を読むだけの文法のルールをがありますが、このルールはmixed_numberというルールに参照されています。そして、mixed_numberはkanji_digitの他に全角、半角数値も参照していてそれらを統合しています。
このように局所的な文法ルールが大域的な文法ルールで参照されることで、文法を木構造で表現することができます。
上記のルールでは最終的にnumberという一番大域的なルールにそれぞれのルールが参照されている形になっています。
Larkの文法ルールを用意したら、Python側でルールを実際に変換するコードを書いてやれば複雑なルールの文章を構造的にプログラミング言語の型に変換することができます。大変便利ですね。
全体のkanji_to_timeの文法ルール全体はここから見れます。
https://github.com/corkborg/kanji_to_time/blob/main/kanji_to_time/grammer/kanji_to_time.lark
一応漢数字の変換もできる
Larkの文法定義を見てもらうとわかるのですが、数値変換だけの文法ルールが存在するので数値変換だけを行うこともできます。
一応調査用に漢数字から数値に変換する機能もあります。
>>> ktt.to_number("八千七十二")
8072
ただ、漢数字の数値変換についてはKanjizeという優れた先行ライブラリが存在するのでこの用途だけであればこちらを使ったほうが良いと思います。
https://github.com/nagataaaas/Kanjize
サポート
このような日付文字列を変換する際にはぜひ使ってみてください。
不具合などに気づいたらissuesに投稿してもらえると嬉しいです。
https://github.com/corkborg/kanji_to_time/issues