In its simplest form, this sounds like what you're after.
now(){
date -u +%s.%N
}
measure_time(){
local elapsed="$(bc <<<"$end_time-$start_time")"
echo "Total of $elapsed seconds elapsed for process"
}
start_time="$(now)"
sleep 5
end_time="$(now)"
measure_time
You could make the now function accept a named variable and adapt the measure_time function to handle that, but it would be an unnecessary complication if your use case is to track one process at a time.
Having said that, here's an iteration in that direction. start and end are arbitrary labels, so you could call them what ever suits, if you were tracking multiple events.
stamp(){
time[$1]="$(date -u +%s.%N)"
}
measure_time(){
local elapsed="$(bc <<<"${time[$2]}-${time[$1]}")"
echo "Total of $elapsed seconds elapsed for process"
}
declare -A time=()
stamp start
sleep 5
stamp end
measure_time start end
EDIT:
Caveat
As the figures in roaima's answer show, this is not going to be high precision, because each action (particularly so sub shells and external processes) will pad the reported time out by some fraction of a second. This probably doesn't matter for multi-minute processes (in which case why measure nano seconds?) but will become more pronounced at lower run times, to the point of being almost meaningless. Though the problem was rather less severe in my tests as compared to roaima's. It rather depends on what you are using that data for.
ANOTHER EDIT:
Responding to QuartzCrystal's fair comment, herein lies a further iteration which, it turns out, is converging on roaima's answer -- probably indicating they are on the "best" track.
_stamp(){
time[${1:?}]="$(date -u +%s.%N)"
}
_start() {
_stamp "start${1:+_${1}}"
}
_end() {
_stamp "end${1:+_${1}}"
}
measure_time(){
local elapsed="$(bc <<<"${time[end${1:+_${1}}]}-${time[start${1:+_${1}}]}")"
echo "Total of $elapsed seconds elapsed${1:+ for process ${1}}"
}
declare -A time=()
_start
sleep 5
_end
measure_time
_start foo
sleep 1
_start bar
sleep 2
_end foo
_end bar
measure_time foo
measure_time bar