Cómo calcular la diferencia de tiempo en bash script?


Imprimo la hora de inicio y finalización usando date +"%T", lo que resulta en algo como:

10:33:56
10:36:10

¿Cómo podría calcular e imprimir la diferencia entre estos dos?

Me gustaría obtener algo como:

2m 14s
 176
Author: Misha Moroshko, 2012-01-18

18 answers

Bash tiene una útil variable incorporada SECONDS que rastrea el número de segundos que han pasado desde que se inició el shell. Esta variable conserva sus propiedades cuando se asigna a, y el valor devuelto después de la asignación es el número de segundos desde la asignación más el valor asignado.

Por lo tanto, solo puede establecer SECONDS a 0 antes de comenzar el evento cronometrado, simplemente lea SECONDS después del evento y haga la aritmética del tiempo antes de mostrarlo.

SECONDS=0
# do some work
duration=$SECONDS
echo "$(($duration / 60)) minutes and $(($duration % 60)) seconds elapsed."

Ya que esta solución no depende de date +%s (que es una extensión GNU), es portable a todos los sistemas soportados por Bash.

 347
Author: Daniel Kamil Kozar,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-12-12 01:12:25

Segundos

Para medir el tiempo transcurrido (en segundos) necesitamos: {[42]]}

  • un entero que representa la cuenta de segundos transcurridos y
  • una forma de convertir dicho entero a un formato utilizable.

Un valor entero de segundos transcurridos:

  • Hay dos formas internas de bash para encontrar un valor entero para el número de segundos transcurridos:

    1. Bash segundos variables (si SEGUNDOS no está configurado pierde su especial propiedad).

      • Establecer el valor de SEGUNDOS en 0:

        SECONDS=0
        sleep 1  # Process to execute
        elapsedseconds=$SECONDS
        
      • Almacenando el valor de la variable SECONDS al inicio:

        a=$SECONDS
        sleep 1  # Process to execute
        elapsedseconds=$(( SECONDS - a ))
        
    2. Opción Bash printf %(datefmt)T:

      a="$(TZ=UTC0 printf '%(%s)T\n' '-1')"    ### `-1`  is the current time
      sleep 1                                  ### Process to execute
      elapsedseconds=$(( $(TZ=UTC0 printf '%(%s)T\n' '-1') - a ))
      

Convertir dicho entero a un formato utilizable

El bash interno printf puede hacer eso directamente: {[42]]}

$ TZ=UTC0 printf '%(%H:%M:%S)T\n' 12345
03:25:45

Del mismo modo

$ elapsedseconds=$((12*60+34))
$ TZ=UTC0 printf '%(%H:%M:%S)T\n' "$elapsedseconds"
00:12:34

pero esto fallará por duraciones de más de 24 horas, ya que en realidad imprimimos una hora de wallclock, no realmente una duración:

$ hours=30;mins=12;secs=24
$ elapsedseconds=$(( ((($hours*60)+$mins)*60)+$secs ))
$ TZ=UTC0 printf '%(%H:%M:%S)T\n' "$elapsedseconds"
06:12:24

Para los amantes de los detalles, de bash-hackers.org :

%(FORMAT)T muestra la cadena de fecha y hora resultante de usar FORMAT como una cadena de formato para strftime(3). El argumento asociado es el número de segundos desde Época, o -1 (hora actual) o -2 (shell tiempo de inicio). Si no se proporciona ningún argumento correspondiente, el actual el tiempo se utiliza como predeterminado.

Así que es posible que desee llamar a textifyDuration $elpasedseconds donde textifyDuration es otra implementación de la impresión de duración:

textifyDuration() {
   local duration=$1
   local shiff=$duration
   local secs=$((shiff % 60));  shiff=$((shiff / 60));
   local mins=$((shiff % 60));  shiff=$((shiff / 60));
   local hours=$shiff
   local splur; if [ $secs  -eq 1 ]; then splur=''; else splur='s'; fi
   local mplur; if [ $mins  -eq 1 ]; then mplur=''; else mplur='s'; fi
   local hplur; if [ $hours -eq 1 ]; then hplur=''; else hplur='s'; fi
   if [[ $hours -gt 0 ]]; then
      txt="$hours hour$hplur, $mins minute$mplur, $secs second$splur"
   elif [[ $mins -gt 0 ]]; then
      txt="$mins minute$mplur, $secs second$splur"
   else
      txt="$secs second$splur"
   fi
   echo "$txt (from $duration seconds)"
}

GNU date.

Para obtener tiempo formalizado debemos usar una herramienta externa (GNU date) de varias maneras para obtener hasta casi un año de duración e incluyendo nanosegundos.

Math inside date.

No hay necesidad de aritmética externa, hazlo todo en un solo paso dentro date:

date -u -d "0 $FinalDate seconds - $StartDate seconds" +"%H:%M:%S"

Sí, hay un 0 cero en el cadena de órdenes. Es necesario.

Eso es asumiendo que podría cambiar el comando date +"%T" a un comando date +"%s" para que los valores se almacenen (impriman) en segundos.

Tenga en cuenta que el comando está limitado a:

  • Valores positivos de $StartDate y $FinalDate segundos.
  • El valor en $FinalDate es mayor (más tarde en el tiempo) que $StartDate.
  • Diferencia horaria inferior a 24 horas.
  • Acepta un formato de salida con Horas, Minutos y Segundos. Muy fácil de cambio.
  • Es aceptable usar-u UTC times. Para evitar correcciones" DST " y hora local.

Si debe usar la cadena 10:33:56, bueno, simplemente conviértala en segundos,
además, la palabra segundos podría abreviarse como sec:

string1="10:33:56"
string2="10:36:10"
StartDate=$(date -u -d "$string1" +"%s")
FinalDate=$(date -u -d "$string2" +"%s")
date -u -d "0 $FinalDate sec - $StartDate sec" +"%H:%M:%S"

Tenga en cuenta que la conversión de segundos de tiempo (como se presenta anteriormente) es relativa al inicio de "este" día (Hoy).


El concepto podría extenderse a nanosegundos, como esto:

string1="10:33:56.5400022"
string2="10:36:10.8800056"
StartDate=$(date -u -d "$string1" +"%s.%N")
FinalDate=$(date -u -d "$string2" +"%s.%N")
date -u -d "0 $FinalDate sec - $StartDate sec" +"%H:%M:%S.%N"

Si se requiere calcular diferencias de tiempo más largas (hasta 364 días), debemos usar el inicio de (algún) año como referencia y el valor de formato %j (el número de día en el año):

Similar a:

string1="+10 days 10:33:56.5400022"
string2="+35 days 10:36:10.8800056"
StartDate=$(date -u -d "2000/1/1 $string1" +"%s.%N")
FinalDate=$(date -u -d "2000/1/1 $string2" +"%s.%N")
date -u -d "2000/1/1 $FinalDate sec - $StartDate sec" +"%j days %H:%M:%S.%N"

Output:
026 days 00:02:14.340003400

Tristemente, en este caso, necesitamos restar manualmente 1 UNO del número de días. El comando date visualiza el primer día del año como 1. No es tan difícil ...

a=( $(date -u -d "2000/1/1 $FinalDate sec - $StartDate sec" +"%j days %H:%M:%S.%N") )
a[0]=$((10#${a[0]}-1)); echo "${a[@]}"



El uso de largo número de segundos es válido y documentada here:
https://www.gnu.org/software/coreutils/manual/html_node/Examples-of-date.html#Examples-of-date


Fecha de Busybox

Una herramienta utilizada en dispositivos más pequeños (un ejecutable muy pequeño para instalar): Busybox.

O bien hacer un enlace a busybox llamado fecha:

$ ln -s /bin/busybox date

Utilícelo entonces llamando a este date (colóquelo en un directorio PATH included).

O hacer un alias como:

$ alias date='busybox date'

La fecha de Busybox tiene un buena opción: - D para recibir el formato del tiempo de entrada. Eso abre una gran cantidad de formatos para ser utilizados como tiempo. Usando la opción-D podemos convertir la hora 10:33:56 directamente:

date -D "%H:%M:%S" -d "10:33:56" +"%Y.%m.%d-%H:%M:%S"

Y como se puede ver en la salida del Comando anterior, se asume que el día es "hoy". Para obtener la hora que comienza en epoch:

$ string1="10:33:56"
$ date -u -D "%Y.%m.%d-%H:%M:%S" -d "1970.01.01-$string1" +"%Y.%m.%d-%H:%M:%S"
1970.01.01-10:33:56

Busybox date puede incluso recibir la hora (en el formato anterior) sin-D:

$ date -u -d "1970.01.01-$string1" +"%Y.%m.%d-%H:%M:%S"
1970.01.01-10:33:56

Y el formato de salida podría incluso ser segundos desde época.

$ date -u -d "1970.01.01-$string1" +"%s"
52436

Para ambas veces, y un poco de matemáticas bash (busybox no puede hacer las matemáticas, todavía):

string1="10:33:56"
string2="10:36:10"
t1=$(date -u -d "1970.01.01-$string1" +"%s")
t2=$(date -u -d "1970.01.01-$string2" +"%s")
echo $(( t2 - t1 ))

O formateado:

$ date -u -D "%s" -d "$(( t2 - t1 ))" +"%H:%M:%S"
00:02:14
 63
Author: David Tonhofer,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-06-03 09:13:13

Así es como lo hice:

START=$(date +%s);
sleep 1; # Your stuff
END=$(date +%s);
echo $((END-START)) | awk '{print int($1/60)":"int($1%60)}'

Realmente simple, tome el número de segundos al principio, luego tome el número de segundos al final e imprima la diferencia en minutos:segundos.

 30
Author: Dorian,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-11-27 17:45:26

Otra opción es usar datediff desde dateutils (http://www.fresse.org/dateutils/#datediff):

$ datediff 10:33:56 10:36:10
134s
$ datediff 10:33:56 10:36:10 -f%H:%M:%S
0:2:14
$ datediff 10:33:56 10:36:10 -f%0H:%0M:%0S
00:02:14

También puedes usar gawk. mawk 1.3.4 también tiene strftime y mktime pero las versiones anteriores de mawk y nawk no lo hacen.

$ TZ=UTC0 awk 'BEGIN{print strftime("%T",mktime("1970 1 1 10 36 10")-mktime("1970 1 1 10 33 56"))}'
00:02:14

O aquí hay otra forma de hacerlo con GNU date:

$ date -ud@$(($(date -ud'1970-01-01 10:36:10' +%s)-$(date -ud'1970-01-01 10:33:56' +%s))) +%T
00:02:14
 12
Author: user4669748,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-04-08 02:36:31

Me gustaría proponer otra forma de evitar recordar date comando. Puede ser útil en caso de que ya haya recopilado marcas de tiempo en formato de fecha %T:

ts_get_sec()
{
  read -r h m s <<< $(echo $1 | tr ':' ' ' )
  echo $(((h*60*60)+(m*60)+s))
}

start_ts=10:33:56
stop_ts=10:36:10

START=$(ts_get_sec $start_ts)
STOP=$(ts_get_sec $stop_ts)
DIFF=$((STOP-START))

echo "$((DIFF/60))m $((DIFF%60))s"

Incluso podemos manejar milisegundos de la misma manera.

ts_get_msec()
{
  read -r h m s ms <<< $(echo $1 | tr '.:' ' ' )
  echo $(((h*60*60*1000)+(m*60*1000)+(s*1000)+ms))
}

start_ts=10:33:56.104
stop_ts=10:36:10.102

START=$(ts_get_msec $start_ts)
STOP=$(ts_get_msec $stop_ts)
DIFF=$((STOP-START))

min=$((DIFF/(60*1000)))
sec=$(((DIFF%(60*1000))/1000))
ms=$(((DIFF%(60*1000))%1000))

echo "${min}:${sec}.$ms"
 11
Author: Zskdan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-05-21 17:00:52

Aquí hay algo de magia:

time1=14:30
time2=$( date +%H:%M ) # 16:00
diff=$(  echo "$time2 - $time1"  | sed 's%:%+(1/60)*%g' | bc -l )
echo $diff hours
# outputs 1.5 hours

sed reemplaza un : con una fórmula para convertir a 1/60. Entonces el cálculo del tiempo que se hace por bc

 6
Author: redolent,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-03-19 20:37:11

A partir de la fecha (GNU coreutils) 7.4 ahora puede usar-d para hacer aritmética:

$ date -d -30days
Sat Jun 28 13:36:35 UTC 2014

$ date -d tomorrow
Tue Jul 29 13:40:55 UTC 2014

Las unidades que puedes usar son días, años, meses, horas, minutos y segundos :

$ date -d tomorrow+2days-10minutes
Thu Jul 31 13:33:02 UTC 2014
 3
Author: rags,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-07-28 13:43:25

Siguiendo la respuesta de Daniel Kamil Kozar, para mostrar horas / minutos / segundos:

echo "Duration: $(($DIFF / 3600 )) hours $((($DIFF % 3600) / 60)) minutes $(($DIFF % 60)) seconds"

Así que el script completo sería:

date1=$(date +"%s")
date2=$(date +"%s")
diff=$(($date2-$date1))
echo "Duration: $(($DIFF / 3600 )) hours $((($DIFF % 3600) / 60)) minutes $(($DIFF % 60)) seconds"
 2
Author: mcaleaa,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-03-03 08:33:23

O envuélvelo un poco

alias timerstart='starttime=$(date +"%s")'
alias timerstop='echo seconds=$(($(date +"%s")-$starttime))'

Entonces esto funciona.

timerstart; sleep 2; timerstop
seconds=2
 2
Author: user3561136,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-08-05 18:32:50
% start=$(date +%s)
% echo "Diff: $(date -d @$(($(date +%s)-$start)) +"%M minutes %S seconds")"
Diff: 00 minutes 11 seconds
 1
Author: Joseph,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-12-21 15:39:55

Con GNU units:

$ units
2411 units, 71 prefixes, 33 nonlinear units
You have: (10hr+36min+10s)-(10hr+33min+56s)
You want: s
    * 134
    / 0.0074626866
You have: (10hr+36min+10s)-(10hr+33min+56s)
You want: min
    * 2.2333333
    / 0.44776119
 1
Author: user5207022,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-08-08 21:10:09

date puede darle la diferencia y formatearlo para usted (se muestran las opciones de OS X)

date -ujf%s $(($(date -jf%T "10:36:10" +%s) - $(date -jf%T "10:33:56" +%s))) +%T
# 00:02:14

date -ujf%s $(($(date -jf%T "10:36:10" +%s) - $(date -jf%T "10:33:56" +%s))) \
    +'%-Hh %-Mm %-Ss'
# 0h 2m 14s

Algún procesamiento de cadenas puede eliminar esos valores vacíos

date -ujf%s $(($(date -jf%T "10:36:10" +%s) - $(date -jf%T "10:33:56" +%s))) \
    +'%-Hh %-Mm %-Ss' | sed "s/[[:<:]]0[hms] *//g"
# 2m 14s

Esto no funcionará si colocas primero la hora anterior. Si necesita manejar eso, cambie $(($(date ...) - $(date ...))) a $(echo $(date ...) - $(date ...) | bc | tr -d -)

 1
Author: Mr. Dave,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-04-21 12:32:24

Aquí hay una solución usando solo las capacidades de comandos date usando "ago", y no usando una segunda variable para almacenar el tiempo de finalización:

#!/bin/bash

# save the current time
start_time=$( date +%s.%N )

# tested program
sleep 1

# the current time after the program has finished
# minus the time when we started, in seconds.nanoseconds
elapsed_time=$( date +%s.%N --date="$start_time seconds ago" )

echo elapsed_time: $elapsed_time

Esto da:

$ ./time_elapsed.sh 
elapsed_time: 1.002257120
 1
Author: Zoltan K.,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-09-24 15:55:48

Me doy cuenta de que este es un post anterior, pero lo encontré hoy mientras trabajaba en un script que tomaría fechas y horas de un archivo de registro y calcularía el delta. El script a continuación es ciertamente excesivo, y recomiendo encarecidamente revisar mi lógica y matemáticas.

#!/bin/bash

dTime=""
tmp=""

#firstEntry="$(head -n 1 "$LOG" | sed 's/.*] \([0-9: -]\+\).*/\1/')"
firstEntry="2013-01-16 01:56:37"
#lastEntry="$(tac "$LOG" | head -n 1 | sed 's/.*] \([0-9: -]\+\).*/\1/')"
lastEntry="2014-09-17 18:24:02"

# I like to make the variables easier to parse
firstEntry="${firstEntry//-/ }"
lastEntry="${lastEntry//-/ }"
firstEntry="${firstEntry//:/ }"
lastEntry="${lastEntry//:/ }"

# remove the following lines in production
echo "$lastEntry"
echo "$firstEntry"

# compute days in last entry
for i in `seq 1 $(echo $lastEntry|awk '{print $2}')`; do {
  case "$i" in
   1|3|5|7|8|10|12 )
    dTime=$(($dTime+31))
    ;;
   4|6|9|11 )
    dTime=$(($dTime+30))
    ;;
   2 )
    dTime=$(($dTime+28))
    ;;
  esac
} done

# do leap year calculations for all years between first and last entry
for i in `seq $(echo $firstEntry|awk '{print $1}') $(echo $lastEntry|awk '{print $1}')`; do {
  if [ $(($i%4)) -eq 0 ] && [ $(($i%100)) -eq 0 ] && [ $(($i%400)) -eq 0 ]; then {
    if [ "$i" = "$(echo $firstEntry|awk '{print $1}')" ] && [ $(echo $firstEntry|awk '{print $2}') -lt 2 ]; then {
      dTime=$(($dTime+1))
    } elif [ $(echo $firstEntry|awk '{print $2}') -eq 2 ] && [ $(echo $firstEntry|awk '{print $3}') -lt 29 ]; then {
      dTime=$(($dTime+1))
    } fi
  } elif [ $(($i%4)) -eq 0 ] && [ $(($i%100)) -ne 0 ]; then {
    if [ "$i" = "$(echo $lastEntry|awk '{print $1}')" ] && [ $(echo $lastEntry|awk '{print $2}') -gt 2 ]; then {
      dTime=$(($dTime+1))
    } elif [ $(echo $lastEntry|awk '{print $2}') -eq 2 ] && [ $(echo $lastEntry|awk '{print $3}') -ne 29 ]; then {
      dTime=$(($dTime+1))
    } fi
  } fi
} done

# substract days in first entry
for i in `seq 1 $(echo $firstEntry|awk '{print $2}')`; do {
  case "$i" in
   1|3|5|7|8|10|12 )
    dTime=$(($dTime-31))
    ;;
   4|6|9|11 )
    dTime=$(($dTime-30))
    ;;
   2 )
    dTime=$(($dTime-28))
    ;;
  esac
} done

dTime=$(($dTime+$(echo $lastEntry|awk '{print $3}')-$(echo $firstEntry|awk '{print $3}')))

# The above gives number of days for sample. Now we need hours, minutes, and seconds
# As a bit of hackery I just put the stuff in the best order for use in a for loop
dTime="$(($(echo $lastEntry|awk '{print $6}')-$(echo $firstEntry|awk '{print $6}'))) $(($(echo $lastEntry|awk '{print $5}')-$(echo $firstEntry|awk '{print $5}'))) $(($(echo $lastEntry|awk '{print $4}')-$(echo $firstEntry|awk '{print $4}'))) $dTime"
tmp=1
for i in $dTime; do {
  if [ $i -lt 0 ]; then {
    case "$tmp" in
     1 )
      tmp="$(($(echo $dTime|awk '{print $1}')+60)) $(($(echo $dTime|awk '{print $2}')-1))"
      dTime="$tmp $(echo $dTime|awk '{print $3" "$4}')"
      tmp=1
      ;;
     2 )
      tmp="$(($(echo $dTime|awk '{print $2}')+60)) $(($(echo $dTime|awk '{print $3}')-1))"
      dTime="$(echo $dTime|awk '{print $1}') $tmp $(echo $dTime|awk '{print $4}')"
      tmp=2
      ;;
     3 )
      tmp="$(($(echo $dTime|awk '{print $3}')+24)) $(($(echo $dTime|awk '{print $4}')-1))"
      dTime="$(echo $dTime|awk '{print $1" "$2}') $tmp"
      tmp=3
      ;;
    esac
  } fi
  tmp=$(($tmp+1))
} done

echo "The sample time is $(echo $dTime|awk '{print $4}') days, $(echo $dTime|awk '{print $3}') hours, $(echo $dTime|awk '{print $2}') minutes, and $(echo $dTime|awk '{print $1}') seconds."

Obtendrá la salida de la siguiente manera.

2012 10 16 01 56 37
2014 09 17 18 24 02
The sample time is 700 days, 16 hours, 27 minutes, and 25 seconds.

He modificado un poco el script para hacerlo independiente (es decir. simplemente establezca valores de variables), pero tal vez la idea general también aparezca. Es posible que desee algún error adicional comprobación de valores negativos.

 0
Author: Daniel Blackmon,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-09-18 16:09:51

Necesitaba un script de diferencia de tiempo para usar con mencoder (su --endpos es relativo), y mi solución es llamar a un script Python:

$ ./timediff.py 1:10:15 2:12:44
1:02:29

También se admiten fracciones de segundos:

$ echo "diff is `./timediff.py 10:51.6 12:44` (in hh:mm:ss format)"
diff is 0:01:52.4 (in hh:mm:ss format)

Y puede decirte que la diferencia entre 200 y 120 es 1h 20m:

$ ./timediff.py 120:0 200:0
1:20:0

Y puede convertir cualquier (probablemente fraccional) número de segundos o minutos u horas a hh:mm:ss

$ ./timediff.py 0 3600
1:00:0
$ ./timediff.py 0 3.25:0:0
3:15:0

timediff.py:

#!/usr/bin/python

import sys

def x60(h,m):
    return 60*float(h)+float(m)

def seconds(time):
    try:
       h,m,s = time.split(':')
       return x60(x60(h,m),s)
    except ValueError:
       try:
          m,s = time.split(':')
          return x60(m,s)
       except ValueError:
          return float(time)

def difftime(start, end):
    d = seconds(end) - seconds(start)
    print '%d:%02d:%s' % (d/3600,d/60%60,('%02f' % (d%60)).rstrip('0').rstrip('.'))

if __name__ == "__main__":
   difftime(sys.argv[1],sys.argv[2])
 0
Author: 18446744073709551615,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-07-09 15:14:42

Generalizando la solución de @nisetama usando GNU date (trusty Ubuntu 14.04 LTS):

start=`date`
# <processing code>
stop=`date`
duration=`date -ud@$(($(date -ud"$stop" +%s)-$(date -ud"$start" +%s))) +%T`

echo $start
echo $stop
echo $duration

Rendimiento:

Wed Feb 7 12:31:16 CST 2018
Wed Feb 7 12:32:25 CST 2018
00:01:09
 0
Author: Bill Gale,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-02-07 19:02:13

No puedo comentar la respuesta de mcaleaa, de ahí que publique esto aquí. La variable "diff" debe estar en caso pequeño. He aquí un ejemplo.

[root@test ~]# date1=$(date +"%s"); date
Wed Feb 21 23:00:20 MYT 2018
[root@test ~]# 
[root@test ~]# date2=$(date +"%s"); date
Wed Feb 21 23:00:33 MYT 2018
[root@test ~]# 
[root@test ~]# diff=$(($date2-$date1))
[root@test ~]# 

La variable anterior se declaró en minúsculas. Esto es lo que sucedió cuando se usa mayúscula.

[root@test ~]# echo "Duration: $(($DIFF / 3600 )) hours $((($DIFF % 3600) / 60)) minutes $(($DIFF % 60)) seconds"
-bash: / 3600 : syntax error: operand expected (error token is "/ 3600 ")
[root@test ~]# 

Entonces, la solución rápida sería así

[root@test ~]# echo "Duration: $(($diff / 3600 )) hours $((($diff % 3600) / 60)) minutes $(($diff % 60)) seconds"
Duration: 0 hours 0 minutes 13 seconds
[root@test ~]# 
 0
Author: Sabrina,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-02-21 15:09:43

Aquí está mi implementación de bash (con bits tomados de otros SO; -)

function countTimeDiff() {
    timeA=$1 # 09:59:35
    timeB=$2 # 17:32:55

    # feeding variables by using read and splitting with IFS
    IFS=: read ah am as <<< "$timeA"
    IFS=: read bh bm bs <<< "$timeB"

    # Convert hours to minutes.
    # The 10# is there to avoid errors with leading zeros
    # by telling bash that we use base 10
    secondsA=$((10#$ah*60*60 + 10#$am*60 + 10#$as))
    secondsB=$((10#$bh*60*60 + 10#$bm*60 + 10#$bs))
    DIFF_SEC=$((secondsB - secondsA))
    echo "The difference is $DIFF_SEC seconds.";

    SEC=$(($DIFF_SEC%60))
    MIN=$((($DIFF_SEC-$SEC)%3600/60))
    HRS=$((($DIFF_SEC-$MIN*60)/3600))
    TIME_DIFF="$HRS:$MIN:$SEC";
    echo $TIME_DIFF;
}

$ countTimeDiff 2:15:55 2:55:16
The difference is 2361 seconds.
0:39:21

No probado, puede tener errores.

 -1
Author: Ondra Žižka,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-10-08 03:42:59