#!/bin/sh # # clock - record how long you work # # SPDX-FileCopyrightText: 2023 Daniel Kalak # SPDX-License-Identifier: GPL-3.0-or-later # # Inspired by my j script. The program logs clock-ins, clock-outs, and # comments to the file specified in the environment variable CLOCKLOG. # Every line in the log is preceded by the current time in the ISO 8601 # format as well as the UNIX time format, separated by spaces. Clock-ins # and clock-outs can also be logged under a specified time instead of # the current time; the line is then marked as "adjusted" and the action # is logged separately. Clock-ins and clock-outs under the current time # are only accepted if the log file isn't already in a clocked-in or # clocked-out state; this check is skipped for adjusted clock-ins and # clock-outs. # # The program returns 1 on bad usage, 0 otherwise. # # The program depends on GNU awk and the GNU coreutils. function usage_quit() { cat <<- EOF >&2 Usage: $(basename "$0") in|out $(basename "$0") note COMMENT [...] $(basename "$0") adjust in|out TIME $(basename "$0") tail $(basename "$0") status Environment variables: CLOCKLOG (needs to be a file, currently "$CLOCKLOG") Returns: 1 on bad usage 0 otherwise EOF exit 1 } function msg_quit() { printf '%s\n' "$1" >&2 exit 1 } function append() { local iso unix line iso="$(date -Iseconds -d "$1")" unix="$(date +%s -d "$1")" line="$2" printf '%s %s %s\n' "$iso" "$unix" "$line" >> "$CLOCKLOG" } function inouts() { LC_ALL=C sort "$CLOCKLOG" | awk '$3 == "in" || $3 == "out" { print $3 }' } function clockedin() { [ "$(inouts | tail -n1)" = 'in' ] } function outofbalance() { [ "$(inouts | head -n1)" = 'out' ] || [ "$(inouts | uniq -d)" ] } function hours() { awk -v "curr=$(date +%s)" ' $3 == "in" { s -= $2 } $3 == "out" { s += $2 } END { if (s < 0) print (s + curr) / 3600, "and counting"; else print s / 3600; }' < "$CLOCKLOG" } [ -f "$CLOCKLOG" ] || usage_quit curr="$(date -Iseconds)" case "$1" in 'in') [ "$#" -eq 1 ] || usage_quit clockedin && msg_quit 'Already clocked in' append "$curr" "$*" ;; 'out') [ "$#" -eq 1 ] || usage_quit clockedin || msg_quit 'Not clocked in' append "$curr" "$*" ;; 'note') [ "$#" -gt 1 ] || usage_quit append "$curr" "$*" ;; 'adjust') [ "$#" -eq 3 ] || usage_quit [ "$2" = 'in' ] || [ "$2" = 'out' ] || usage_quit adj="$(date -Iseconds -d "$3")" || exit 1 append "$curr" "$* ($adj)" append "$adj" "$2 adjusted" ;; 'tail') [ "$#" -eq 1 ] || usage_quit tail "$CLOCKLOG" ;; 'status') [ "$#" -eq 1 ] || usage_quit if clockedin then printf 'Currently clocked in\n' else printf 'Currently not clocked in\n' fi outofbalance && printf 'Log is out of balance! Might miscount hours\n' printf 'Hours clocked in: %s\n' "$(hours)" ;; *) usage_quit ;; esac