🐍
ZXで雑に勤務時間を集計するツールをサクッと作る
どういうものが欲しいのか
worktime.txt
12/1 3:14
12/2 4:12
12/3 2:20
こんな内容に対して
cat worktime.txt | aggtime
# total: 586min (= 9h46min)
これを返してくれるものをサクッと作りたかった。
実装
直接スクリプトを実行できるようにする
shebangを記述して、直接ファイルを実行できるようにします。
aggtime
#!/usr/bin/env zx
入力の処理
zxでは標準入力をstdin()
で簡単に受け取れます。
aggtime
const lines = (await stdin())
/* ① */ .split('\n')
/* ② */ .filter(i => i !== '')
/* ③ */ .filter(i => !i.match(/^[#|\/\/].*$/g))
入力を①改行で分けて行ごとの配列に変換し、②空行を削除して③コメント行(// または # で始まる行)を削除しています。
正規表現の選択
雑に時間を集計することを目的とするので、次のいずれかの形式で(統一して)記述されていれば受理できるようにします。
- 3h14min
- 3:14
- 12/1 3h14min
- 12/1 3:14
aggtime
const regExp = [
'^(\\d+):(\\d+)',
'^(\\d+)h(\\d+)min',
'^\\d+/\\d+\\s+(\\d+):(\\d+)',
'^\\d+/\\d+\\s+(\\d+)h(\\d+)min',
].map(i => new RegExp(i))
.find(i => lines.every(l => l.match(i)))
時間の取得
正規表現の選択時に、グループ化を行っているので、時間と分をそのまま取り出すことができます。
aggtime
const getHM = (s, regExp) => {
const match = regExp.exec(s)
const [hours, minutes] = [Number(match[1]), Number(match[2])]
return [hours, minutes]
}
集計結果の表示
aggtime
const totalMinutes = lines.reduce((sum, li) => {
const [h, m] = getHM(li, regExp)
return sum + 60 * h + m
}, 0)
const hours = Math.floor(totalMinutes / 60)
const minutes = totalMinutes - 60 * hours
console.log(`total: ${totalMinutes}min (= ${hours}h${minutes}min)`)
完成
以上の実装によって、冒頭の方法で時間を集計できます。
著者はMacを使っているので、pbpasteからパイプして使っています。
おわりに
一部エラー処理を追加した全体のコードはこちらに置いてあります。
zxを使えばサクッとコマンドラインツールが作れます。
Discussion