🐍

ZXで雑に勤務時間を集計するツールをサクッと作る

2022/12/17に公開

どういうものが欲しいのか

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))

入力を①改行で分けて行ごとの配列に変換し、②空行を削除して③コメント行(// または # で始まる行)を削除しています。

正規表現の選択

雑に時間を集計することを目的とするので、次のいずれかの形式で(統一して)記述されていれば受理できるようにします。

  1. 3h14min
  2. 3:14
  3. 12/1 3h14min
  4. 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