Segundos
Para medir el tiempo transcurrido (en segundos) necesitamos:
- un entero que representa el recuento de segundos transcurridos y
- una forma de convertir dicho número entero a un formato utilizable.
Un valor entero de segundos transcurridos:
Convierta dicho entero a un formato utilizable
El bash interno printf
puede hacer eso directamente:
$ TZ=UTC0 printf '%(%H:%M:%S)T\n' 12345
03:25:45
similar
$ elapsedseconds=$((12*60+34))
$ TZ=UTC0 printf '%(%H:%M:%S)T\n' "$elapsedseconds"
00:12:34
pero esto fallará por más de 24 horas, ya que imprimimos un tiempo de reloj de pared, 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
genera 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 Epoch , o -1 (hora actual) o -2 (hora de inicio del shell). Si no se proporciona ningún argumento correspondiente, la hora actual se usa como predeterminada.
Por lo tanto, puede llamar a textifyDuration $elpasedseconds
donde textifyDuration
hay otra implementación de 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)"
}
Fecha GNU.
Para obtener tiempo formateado, debemos usar una herramienta externa (fecha GNU) de varias maneras para obtener una duración de casi un año e incluir nanosegundos.
Matemáticas dentro de la fecha.
No hay necesidad de aritmética externa, hágalo todo en un solo paso date
:
date -u -d "0 $FinalDate seconds - $StartDate seconds" +"%H:%M:%S"
Sí, hay un 0
cero en la cadena de comando. Es necesario.
Eso supone que podría cambiar el date +"%T"
comando a un date +"%s"
comando para que los valores se almacenen (impriman) en segundos.
Tenga en cuenta que el comando se limita a:
- Valores positivos de
$StartDate
y $FinalDate
segundos.
- El valor en
$FinalDate
es mayor (más tarde) que $StartDate
.
- Diferencia horaria menor a 24 horas.
- Acepta un formato de salida con Horas, Minutos y Segundos. Muy facil de cambiar.
- Es aceptable usar -u UTC veces. Para evitar "DST" y correcciones de hora local.
Si debe usar la 10:33:56
cadena, bueno, simplemente conviértala a segundos,
también, 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 del tiempo de segundos (como se presentó anteriormente) es relativa al comienzo del día "este" (Hoy).
El concepto podría extenderse a nanosegundos, como este:
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 (algunos) años 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
Lamentablemente, en este caso, necesitamos restar 1
UNO manualmente del número de días. El comando de fecha ve 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 una gran cantidad de segundos es válido y está documentado aquí:
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.
Haga un enlace a busybox llamado fecha:
$ ln -s /bin/busybox date
Úselo luego llamando a esto date
(colóquelo en un directorio incluido PATH).
O haga un alias como:
$ alias date='busybox date'
La fecha de Busybox tiene una buena opción: -D para recibir el formato de la hora de entrada. Eso abre muchos formatos para ser utilizados como tiempo. Usando la opción -D podemos convertir el tiempo 10:33:56 directamente:
date -D "%H:%M:%S" -d "10:33:56" +"%Y.%m.%d-%H:%M:%S"
Y como puede ver en la salida del Comando anterior, se supone que el día es "hoy". Para comenzar el tiempo en época:
$ 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
La fecha de Busybox 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 ser incluso segundos desde la época.
$ date -u -d "1970.01.01-$string1" +"%s"
52436
Para ambas veces, y un poco de matemática bash (busybox todavía no puede hacer las matemáticas):
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
time
comando?