I’m using timetagger to track my working times and I recently wanted to check if I’m over or under my contracted time. Usually I would write a small application in bash or java or nodejs or python. But because I’m using nushell more in the last months I wanted to try if I can achieve the same with my new favorite shell.

The script should do the following:

  • query a configurable date range
  • display working times grouped by date
  • display the overall sum of working times

Simple enough. The timetagger API supports querying a time range with two UNIX timestamps as parameters. In nushell you can convert a date into a UNIX timestamp with the format command:

date now | format date "%s"
# 1706081101

nushell also supports operations on dates with durations in a simple notation:

(date now) - 4wk
# Wed, 27 Dec 2023 08:27:12 +0100 (4 weeks ago)

(date now) - 4wk | format date '%s'
# 1703662037

Another function I use pretty often is downloading files or data via nushell:

http get https://www.kopis.de/bookmarks/bookmarks/759
╭───────────┬────────────────────────────────────────────────╮
│ ID        │ 759                                            │
│ CreatedAt │ 2024-01-18T08:47:01.045657Z                    │
│ UpdatedAt │ 2024-01-18T08:47:01.045657Z                    │
│ DeletedAt │                                                │
│ URL       │ https://some/host/on/the/internet              │
│ Title     │ Super interesting title goes here ...          │
│ Content   │ <div><div>                                     │
│           │                                                │
...
│           │                 </div></div>                   │
│ Public    │ true                                           │
╰───────────┴────────────────────────────────────────────────╯

That should be enough to write a simple script to query the timetagger API:

let base_url = 'https://hostname/path/to/timetagger'
let token = '1234567890'

# returns a datetime at the start of a given date
def start_of_day [date] {
  return ($date | format date '%Y-%m-%dT00:00:00Z' | into datetime)
}

# returns a datetime at the end of a given date
def end_of_day [date] {
  return ($date | format date '%Y-%m-%dT23:59:59Z' | into datetime)
}

# converts a UNIX timestamp into a datetime 
def convert_ts [ts] {
  return ($ts * 1_000_000_000 | into datetime)
}

# sends an authenticated request to the timetagger API and returns the records
def request [token t1 t2] {
  let url = [$base_url '/api/v2/records?timerange=' ($t1 | format date "%s") '-' ($t2 | format date "%s")] | str join
  let data = http get --headers [authtoken $token] $url | get records
  return $data
}

# returns records grouped by date and the difference to a default 8 hours working day added as a column for each day
def get_records [token duration] {
  let t1 = start_of_day ((date now) - ($duration | into duration))
  let t2 = end_of_day (date now)
  let data = request $token $t1 $t2 | update t1 {|row| convert_ts $row.t1} | update t2 {|row| convert_ts $row.t2}
  let result = $data | insert duration {|row| ($row.t2 - $row.t1)} | select key t1 t2 duration
  let grouped = $result | group-by --to-table {$in.t1 | format date '%Y-%m-%d'} | insert sum {|row| $row.items.duration | math sum} | insert diff {|row| $row.sum - ('8hr' | into duration)}| select group sum diff
  return $grouped
}

Now I can use this script to display all working times for the last 4 weeks:

source myscript.nu
get_records $token 4wk
#╭────┬────────────┬─────────────────┬──────────────────╮
#│  # │   group    │       sum       │       diff       │
#├────┼────────────┼─────────────────┼──────────────────┤
#│  0 │ 2024-12-27 │             8hr │             0sec │
#│  1 │ 2023-12-28 │             8hr │             0sec │
#│  2 │ 2023-12-29 │             8hr │             0sec │
#│  3 │ 2024-01-01 │             8hr │             0sec │
#...

Calculating the sum of all working times is also straight forward with nushell:

> get_records $token 4wk | get diff | math sum
# 1hr 2min 3sec

The command queries the last 4 weeks from the timetagger API, gets the diff column and calculates the sum of all rows. The more I work with nushell tables the more I like it.