date コマンドが辛かったので、自分的最強日付時刻取り扱いコマンドを作りました。

以前、以下のようなぼやきを書きました。

bearmini.hatenablog.com

もう 5 年以上前になるんですね。

その後も、ときどき date コマンドの辛さを味わっては何とかしたいと思い続けて来ました。

一方、今年の夏、私は fq というコマンドに出会いました。

github.com

この fq 自体は、date コマンドとは何の関係もありません。jq のようなフィルタ構文を使ってバイナリファイルの解析ができるという超便利なツールです。

<自慢>

ちなみに私はこの fq で WASM のバイナリが解析できるようにするためのプルリクエストを送ってマージされました。😤

</自慢>

jq の構文を JSON の操作以外のことに使ってもいいんだということに fq のおかげで気付くことができて、しかもそれがかなり強力なものであるということがわかりました。

ちなみに fq の作者の wader さんは、jq で jq を実装するという意味のわからないことをしている人でもありますw

github.com

で、この fqjq っぽいことができているのは gojq というパッケージのおかげだということを知りました。

github.com

gojq はライブラリとして Go のプログラムに比較的かんたんに組み込むことができそうでした。

ここまで来たときに、「もしかして、日付や時刻を表すさまざまな表現を jq のような構文でゴニョゴニョできたらめっちゃ便利だったりしないかな?」という妄想が私の脳の片隅に浮かびました。

たとえばこんな感じです。

$ echo 1669872594 | dq 'from_unix | add(72 | hours) | to_rfc3339'
"2022-12-04T14:29:54+09:00"

すなわち、数値や文字列など何らかの形式で日付時刻を表すデータが標準入力から与えられた時に、それを元に「日付時刻オブジェクト」のようなものを内部的に生成し(上の例では Unix 時刻 1669872594 を与えているので from_unixUnix 時刻から「日付時刻オブジェクト」を生成している)、必要に応じて計算を行って(上の例では add(72 | hours) で 72 時間後の時刻を計算)、それを任意の形式で出力(上の例では RFC 3339 の形式の文字列に変換)する、というようなことが慣れ親しんだ jq の構文でできるようになりそうです。

そんなわけで、開発したのがこちらです:

github.com

dq コマンドをインストールすると、上の例がそのまま実行できます。

詳しい使い方は README を見ていただければと思いますが、我ながらとてつもなく便利なものを作ってしまったなと思っています。

たとえば、date コマンドつらい に書いた以下のようなこともクロスプラットフォームで同じ書き方ができるようになっています。

  1. 単に date コマンドを実行したときと同じ出力を得たい

     $ dq -r tounixdate
     Thu Dec  1 22:24:24 JST 2022
    

    -r オプションは jq と同じで raw output すなわち文字列の周りのダブルクオーテーション "〜" を省略します。

    tounixdate という関数の名前は to unix date という具合に分解されて、つまり date コマンドの標準のフォーマットに変換するものです。

    このコマンドには標準入力に何も与えていないので、デフォルト動作として現在時刻(コマンドを実行した瞬間の時刻)を表す time オブジェクトに対して関数が適用されます。

  2. 現在の日時を指定のフォーマット(ここでは %Y-%m-%d)で出力したい

     $ dq -r '.unix | strftime("%Y-%m-%d")'
     2022-12-01
    

    strftime 関数は実は jq に元から備わっている関数で、gojq を使っていると自動的に使えるようになっている関数の一つです。内部的には C 言語のライブラリ関数の strftime() を呼び出しているようです。なので strftime() 関数と同じフォーマット指定子を使うことができます。strftime() 関数には Unix time を入力する必要があるので、.unix で現在時刻の Unix time を取得して与えています。

  3. 昨日の日付を出力したい

     $ dq -r 'add_date(0;0;-1) | tounixdate'
     Wed Nov 30 22:27:38 JST 2022
    

    日時の加減は add_date 関数、もしくは add 関数を使います。(減算も add_dateadd に負の値を与えて行います) 年月日を加減算したい場合は add_date を使うのが便利です。 時分秒を加減算したい場合は add を使うのが便利です。

    add_date 関数には 3 つの引数で「年」「月」「日」のそれぞれの差分を指定します。jq の関数は引数の区切りがカンマではなくセミコロンです。(カンマは JSON の中などで使うので紛らわしいのでセミコロンになっているのかな?) つまり add_date(0;0;-1) は、年は変更なし、月も変更なし、日は -1 すなわち 1 日前を表します。

    add 関数には duration (期間)オブジェクトを渡す必要があります。 duration オブジェクトは hours minutes seconds milliseconds microseconds nanoseconds のいずれかの関数(いずれも整数を入力して duration を出力する関数)を使って、3 | hours のように生成します。

最低限自分が必要とした基本的な機能は実装してあるつもりですが、まだまだ機能的には足りていないと思うので今後も継続的に育てていこうと思います。

皆様からのフィードバックもお待ちしております!GitHub の Issue や Pull Request はもちろん歓迎ですし、bitbears-dev 名義は英語で活動しておりますが「日本語じゃないと無理ー」という方は Twitter で @bearmini 宛にメンションしていただければと!