Skip to main content

Measuring Command Execution Time and Duration in zsh

·3 mins·
Makoto Morinaga
Author
Makoto Morinaga
A personal notebook for tech notes, coding, and system experiments.
Table of Contents

When using zsh, you may sometimes want to check:

  • When a command was executed
  • When it finished, and how long it took

This article summarizes methods to record timing information directly in your terminal using the following two mechanisms:

  • Record the execution timestamp in your prompt each time you press Enter
  • Automatically measure execution duration of commands and notify only when they take a long time

Enter timing: Record the execution timestamp in the prompt
#

Add the following code to your ~/.zshrc:

zsh
function _date_exec {
    PROMPT="[%D{%Y/%m/%d} %*] %~ %# "
    zle .reset-prompt
    zle .accept-line
}

zle -N accept-line _date_exec

How it works
#

  1. Press Enter
  2. The prompt is updated to a specified format (prefixing the timestamp at the left)
  3. The typed command is executed immediately afterward

Example
#

text
[2025/12/30 12:46:54] ~ % cd scripts
[2025/12/30 12:46:57] ~ % git pull

Now, simply scrolling the terminal history allows you to check when each command was executed.

Note: if you already customize your PROMPT
#

_date_exec overwrites the prompt earch time. If you have an existing prompt configuration, merge it into _date_exec to avoid losing it.

Example:

zsh
# Existing PROMPT (example)
PROMPT="%n@%m:%~ %# "

# Merged prompt in _date_exec (timestamp + existing prompt)
function _date_exec {
    PROMPT="[%D{%Y/%m/%d} %*] %n@%m:%~ %# "
    zle .reset-prompt
    zle .accept-line
}

Measure execution time and notify when it takes long
#

Add the following to your ~/.zshrc:

zsh
function format_duration() {
    local total_seconds=$1
    local hours=$(( total_seconds / 3600 ))
    local minutes=$(( (total_seconds % 3600) / 60 ))
    local seconds=$(( total_seconds % 60 ))

    if (( hours > 0 )); then
        echo "${hours}h ${minutes}m ${seconds}s"
    elif (( minutes > 0 )); then
        echo "${minutes}m ${seconds}s"
    else
        echo "${seconds}s"
    fi
}

function _time_and_date_precmd() {
    local timer_end=$SECONDS
    local duration=$(( timer_end - TIMER_START ))
    local current_time=$(date +"%Y-%m-%d %H:%M:%S")

    if [[ -n "$TIMER_START" && "$duration" -gt 300 ]]; then
        local formatted_duration=$(format_duration $duration)
        printf "\nCommand finished at %s, took %s\n" "$current_time" "$formatted_duration"
    fi

    TIMER_START=""
}

function _time_and_date_preexec() {
    TIMER_START=$SECONDS
}

add-zsh-hook preexec _time_and_date_preexec
add-zsh-hook precmd _time_and_date_precmd

How it works
#

  1. Run any command (pressing Enter triggers preexec)
  2. preexec records the start time in TIMER_START
  3. When command execution finishes, precmd is called and calculates duration
  4. If execution time is over 5 minutes (300 seconds), notify with start time, end time, and duration
  5. Reset TIMER_START to prepare for the next command

Example
#

text
[2025/12/30 10:35:22] ~ % make

Command finished at 2025-12-30 12:46:35, took 2h 11m 13s

Even if you step away from the terminal, you can later check when the command started, when it finished, and how long it took just by looking at the terminal buffer.

Related