Rust で時刻文字列をパースしたくなったのでどんなクレートがあるかを調べました。strptime
のようなことが出来れば満足です。
適当にググると Rustで日時を扱う - Qiita や Rustの日付時刻処理(stdtime, time, chrono) - 簡潔なQ などがヒットしました。これらは共通して Chrono を使いましょうと主張しています。Chrono が Rust で時刻表現を取り扱うデファクトスタンダードのようです。
ただ、2021 年 10 月の記事 No Time for Chrono - Félix Saparelli によると、Chrono にはセグメンテーションフォルトが発生する脆弱性があり1、修正される予定がないので time 0.3 に移行する提案をしています。気になったので、この問題を少し深掘ってみました。
Twitter で Chrono の評判を探してみると、Chrono と time のどちらを使うべきなのか迷っているツイート2を見つけることができました。このツイートでは Chrono の方が time より使いやすい一方で、脆弱性が対処されていないために迷っているようです。この脆弱性はきっと先の記事で指摘されている脆弱性のことだろうと思います。
RUSTSEC-2020-0159 1 を確認すると、どうやら Chrono 0.4.20 で問題が修正されたようです。0.4.20 のリリース は 2022 年 8 月 4 日です。かなり最近ですね。当該の脆弱性を理由に time クレートを使いましょうという記事は、8 月 4 日を境に古い情報となっていることが分かりました。
Chrono にはもう脆弱性問題がないので(将来はもちろん分からない)、純粋に使いやすさの観点で選定することにします。今回やりたいのは「2022-08-17T16:43:01+09:00」というような文字列をパースして、年や時などの情報を得ることです。
Chrono 0.4.22 のドキュメントの Formatting and Parsing に載っているパース例
"2014-11-28T21:00:09+09:00".parse::<DateTime<Utc>>()
を見ると、かなり簡単にパースできそうなことが分かりました。今回パースしたい文字列は Chrono のデフォルトフォーマットと一致しているので非常に楽ですが、もし別の時刻フォーマットにしたくなっても %Y-%m-%d %H:%M:%S
のように慣れ親しんだ書式指定子を使えるようです。
time はどうでしょうか。time 0.3.13 の OffsetDateTime.parse のドキュメントにあるサンプルコードは次の通りです。
let format = format_description::parse(
"[year]-[month]-[day] [hour]:[minute]:[second] [offset_hour \
sign:mandatory]:[offset_minute]:[offset_second]",
)?;
assert_eq!(
OffsetDateTime::parse("2020-01-02 03:04:05 +06:07:08", &format)?,
datetime!(2020-01-02 03:04:05 +06:07:08)
);
Chrono とは異なり、まず時刻フォーマットを表すオブジェクト format
を構築し、それを用いて時刻文字列をパースする方式です。文字列を何度もパースするのであれば、一旦フォーマット指定オブジェクトを構築するのは良い方式だと思います。
今回の用途だと大量の時刻文字列をパースするわけではないので、メリットはあまり無さそうです。また、フォーマット指定に慣れ親しんだ書式指定子が使えないのは学習コストが高いです。
以上から、今回は Chrono を使ってみようかなと思います。