Segundos
Para medir o tempo decorrido (em segundos), precisamos:
- um número inteiro que representa a contagem de segundos decorridos e
- uma maneira de converter esse número inteiro em um formato utilizável.
Um valor inteiro de segundos decorridos:
Converta esse número inteiro em um formato utilizável
O bash interno printf
pode fazer isso diretamente:
$ TZ=UTC0 printf '%(%H:%M:%S)T\n' 12345
03:25:45
similarmente
$ elapsedseconds=$((12*60+34))
$ TZ=UTC0 printf '%(%H:%M:%S)T\n' "$elapsedseconds"
00:12:34
mas isso falhará por durações de mais de 24 horas, pois na verdade imprimimos um tempo de relógio de parede, não exatamente uma duração:
$ 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 os amantes dos detalhes, em bash-hackers.org :
%(FORMAT)T
gera a string de data e hora resultante do uso de FORMAT como uma string de formato para strftime(3)
. O argumento associado é o número de segundos desde a época , ou -1 (hora atual) ou -2 (hora de inicialização do shell). Se nenhum argumento correspondente for fornecido, o horário atual será usado como padrão.
Portanto, convém ligar para textifyDuration $elpasedseconds
onde textifyDuration
está mais uma implementação da impressão por duração:
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)"
}
Data GNU.
Para obter um tempo formatado, devemos usar uma ferramenta externa (data GNU) de várias maneiras para atingir quase um ano e incluir nanossegundos.
Data interna da matemática.
Não há necessidade de aritmética externa, faça tudo em uma única etapa date
:
date -u -d "0 $FinalDate seconds - $StartDate seconds" +"%H:%M:%S"
Sim, existe um 0
zero na cadeia de comando. É necessário.
Isso pressupõe que você possa alterar o date +"%T"
comando para um date +"%s"
comando para que os valores sejam armazenados (impressos) em segundos.
Observe que o comando está limitado a:
- Valores positivos de
$StartDate
e $FinalDate
segundos.
- O valor em
$FinalDate
é maior (mais tarde) que $StartDate
.
- Diferença horária menor que 24 horas.
- Você aceita um formato de saída com horas, minutos e segundos. Muito fácil de mudar.
- É aceitável usar -u horas UTC. Para evitar "DST" e correções de hora local.
Se você deve usar a 10:33:56
sequência, basta convertê-la em segundos,
também, a palavra segundos pode ser abreviada 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"
Observe que a conversão de segundos em tempo (como apresentada acima) é relativa ao início deste dia "hoje" (hoje).
O conceito pode ser estendido para nanossegundos, assim:
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"
Se for necessário calcular diferenças de tempo mais longas (até 364 dias), devemos usar o início de (alguns) anos como referência e o valor do formato %j
(o número do dia no ano):
Igual 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
Infelizmente, nesse caso, precisamos subtrair manualmente 1
UM do número de dias. O comando date exibe o primeiro dia do ano como 1. Não é tão 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[@]}"
O uso de um número longo de segundos é válido e documentado aqui:
https://www.gnu.org/software/coreutils/manual/html_node/Examples-of-date.html#Examples-of-date
Data do Busybox
Uma ferramenta usada em dispositivos menores (um executável muito pequeno para instalar): Busybox.
Crie um link para o busybox chamado data:
$ ln -s /bin/busybox date
Use-o e chame-o date
(coloque-o em um diretório incluído no PATH).
Ou crie um alias como:
$ alias date='busybox date'
A data do Busybox tem uma boa opção: -D para receber o formato do horário de entrada. Isso abre muitos formatos para serem usados com o tempo. Usando a opção -D, podemos converter o tempo 10:33:56 diretamente:
date -D "%H:%M:%S" -d "10:33:56" +"%Y.%m.%d-%H:%M:%S"
E como você pode ver na saída do comando acima, presume-se que o dia seja "hoje". Para começar o tempo na é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
A data do Busybox pode até receber a hora (no formato acima) sem -D:
$ date -u -d "1970.01.01-$string1" +"%Y.%m.%d-%H:%M:%S"
1970.01.01-10:33:56
E o formato de saída pode até demorar alguns segundos desde a época.
$ date -u -d "1970.01.01-$string1" +"%s"
52436
Nas duas vezes e um pouco de matemática bash (o busybox ainda não pode fazer as contas):
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 ))
Ou formatado:
$ date -u -D "%s" -d "$(( t2 - t1 ))" +"%H:%M:%S"
00:02:14
time
comando?