Como posso calcular e formatar uma duração de data usando ferramentas GNU, não uma data de resultado?

1

Todas as toneladas de artigos que encontrei até agora pareciam estar focados em obter uma data resultante, que ainda é útil, mas não o que eu quero neste caso.

Link de exemplo: Unix & Linux SE - Calcular rapidamente as diferenças de data .

Com datediff nesse outro Q & A, isso é mais do que eu quero referir a um resultado de duração de data / hora, e ele essencialmente usa a contagem de tempo do Unix com essas conversões anteriores, mas eu quero minha granularidade segundo, e é por isso que não divido por 86400.

Com a duração de segundos disponíveis, agora quero formatá-lo para algo como usar o comando GNU date desta maneira: date -d "@69600" "+ %Y years %m months %e days %H:%M:%S"

Eu sei que tudo é essencialmente uma data / hora da época Unix, mas agora eu tenho o problema com os números de dia, mês e ano não começando de 0, o que não representa corretamente o valor de duração que eu quero. / p>

Eu poderia cortar esse formato e aplicar mais comandos expr para essencialmente subtrair 1 ou 1970 de cada valor, mas estou imaginando se existe outra maneira mais fácil e simples de lidar com o cálculo de data / hora para alcançar um resultado de duração além de encadear os passos de matemática e formatação juntos. Talvez haja alguma outra opção no GNU date que eu possa aproveitar, ou outra ferramenta que aceite 2 argumentos de data / hora e imediatamente me dê o resultado que estou procurando.

Ter isso disponível no Homebrew para Macs seria uma vantagem:).

Link de data matemática simples: Walker News - Aritmética de data no Linux Scripts da Shell

Links de formatação de datas aleatórios:

por Pysis 18.05.2017 / 20:06

3 respostas

3

Com dateutils 's datediff (não é GNU desculpe), (anteriormente ddiff , dateutils.ddiff no Debian):

$ dateutils.ddiff -f '%Y years, %m months, %d days, %H:%0M:%0S' \
    '2012-01-23 15:23:01' '2017-06-01 09:24:00'
5 years, 4 months, 8 days, 18:00:59

(datas tomadas como UTC, adicione algo como --from-zone=Europe/London para as datas a serem tomadas como hora local no fuso horário correspondente. --from-zone=localtime pode funcionar para o fuso horário padrão no sistema; --from-zone="${TZ#:}" funcionaria o tempo todo como $TZ especifica o caminho de um arquivo de fuso horário da IANA (como TZ=:Europe/London , não TZ=GMT0BST,M3.5.0/1:00:00,M10.5.0/2:00:00 especificação TZ no estilo POSIX)).

Com ast-aberto date , então ainda não com ferramentas GNU desculpe (se estiver usando ksh93 como seu shell, que date pode estar disponível como um builtin) você pode usar date -E para obter a diferença entre duas datas em um formato que dê 2 números com unidades:

$ date -E 1970-01-01 2017-06-01
47Y04M
$ date -E 2017-01-01 2017-06-01
4M29d
$ date -E 12:12:01 23:01:43
10h49m

Observe que qualquer coisa acima de hora é ambígua, pois os dias têm um número variável de horas em locais com horário de verão (23, 24 ou 25) e meses e anos têm um número variável de dias. pode não fazer muito sentido ter mais precisão do que essas duas unidades acima.

Por exemplo, há exatamente um ano entre 2015-01-01 00:00:00 e 2016-01-01 00:00:00 ou entre 2016-01-01 00:00:00 e 2017-01- 01 00:00:00, mas essa não é a mesma duração (365 * 24 horas em um caso, 366 * 24 horas no outro)

Há exatamente um dia entre 2017-03-24 12:00:00 e 2017-03-25 12:00:00 ou entre 2017-03-25 12:00:00 e 2017-03-26 12:00 : 00, mas quando esses são hora local em países europeus (que mudaram para o horário de verão em 2017-03-26), que um dia é de 24 horas em um caso e 23 horas no outro. / p>

Em outras palavras, uma duração que mencione anos, meses, semanas ou dias é significativa apenas quando associada a um dos limites para os quais ela deve ser aplicada (e ao fuso horário). Assim, você não pode converter um número de segundos para tal duração sem saber a que hora (de ou até) ela deve ser aplicada (a menos que você queira usar definições aproximadas de dia (24 horas), mês (30 * 24 horas) ou ano (365 * 24 horas)).

Até certo ponto, até mesmo uma duração em segundos é ambígua. Para simplificação, os segundos no tempo Unix epoch são definidos como a parte 86400 de um determinado dia da Terra 1 . Esses segundos estão ficando cada vez mais longos à medida que a Terra desce.

Então, algo como (com o GNU date ):

d1='2016-01-01 00:00:00' d2='2017-01-01 00:00:00'
eval "$(date -d "@$(($(date -d "$d2" +%s) - $(date -d "$d1" +%s)))" +'
  delta="$((%-Y - 1970)) years, $((%-m - 1)) months, $((%-d - 1)) days, %T"')"
echo "$delta"

Ou em fish :

date -d@(expr (date -d $d2 +%s) - (date -d $d1 +%s)) +'%Y %-m %-d %T' | \
  awk '{printf "%s years, %s months, %s days, %s\n", $1-1970, $2-1, $3-1, $4, $5}'

Geralmente, não daria a você algo correto, pois o tempo delta é aplicado a 1970, em vez da data de início real de 2016.

Por exemplo, acima, isso me dá (em um fuso horário Europa / Londres):

1 years, 0 months, 1 days, 01:00:00

em vez de

1 years, 0 months, 0 days, 00:00:00

(isso ainda pode ser uma boa aproximação para você).

1 Tecnicamente, enquanto um dia tem 86400 segundos de Unix, os sistemas conectados à rede normalmente sincronizam seu relógio com os relógios atômicos usando SI segundos. Quando um relógio atômico diz 12: 00: 00.00, esses sistemas Unix também dizem 12: 00: 00.00. A exceção é apenas na introdução de segundos bissextos, em que ou há um segundo Unix que dura 2 segundos ou alguns segundos que duram um pouco mais para manchar esse segundo extra em um período mais longo.

Assim, é possível saber a duração exata (em termos de segundos atômicos) entre dois timestamps Unix (emitidos por sistemas sincronizados ao relógio atômico): obter a diferença do tempo Unix epoch entre os dois timestamps e adicione o número de segundos que foram adicionados entre eles.

datediff também tem -f %rS para obter o número de segundos reais entre duas datas:

$ dateutils.ddiff -f %S '1990-01-01 00:00:00' '2010-01-01 00:00:00'
631152000
$ dateutils.ddiff -f %rS '1990-01-01 00:00:00' '2010-01-01 00:00:00'
631152009

A diferença é para os 9 segundos de salto que foram adicionados entre 1990 e 2010.

Agora, esse número de segundos real não ajudará você a calcular o número de dias, já que os dias não têm um número constante de real segundos. Existem exatamente 20 anos entre essas 2 datas e aqueles 9 saltos segundos, em vez de atrapalhar o caminho para chegar a esse valor. Observe também que ddiff do %rS só funciona quando não é combinado com outros especificadores de formato. Além disso, os segundos bissextos futuros não são conhecidos com bastante antecedência, mas também as informações sobre segundos bissextos recentes podem não estar disponíveis. Por exemplo, o meu sistema não sabe quais foram depois de 2012-07-01.

    
por 18.05.2017 / 21:50
1

Ok, digamos que você tenha duas strings date -compatible definindo dois pontos no tempo que você deseja calcular a duração entre:

echo $(($(date +%s -d "3 days ago")-$(date +%s -d "4 weeks 2 hours 1 minutes 33 seconds ago"))) | sed 's/-//'

Mas vamos dizer ainda que você quer mais legível:

convertsecs() {
    hours=$(($1/3600))
    minutes=$(($1%3600/60))
    seconds=$(($1%60))
}
totalduration="$(($(date +%s -d "3 days ago")-$(date +%s -d "4 weeks 2 hours 1 minutes 33 seconds ago"))) | sed 's/-//'"
convertsecs "$totalduration"
echo "Duration was ${hours} hours, ${minutes} minutes, and ${seconds} seconds."
    
por 18.05.2017 / 20:17
0

O tempo é uma coisa complicada e é importante gerenciar suas expectativas ao calculá-lo. Eu não acredito que seja tecnicamente possível fazer o que você pede com precisão e exatidão no caso geral, já que um determinado número de segundos pode corresponder a um número diferente de minutos, uma vez que você considera coisas como segundos bissextos, que não ocorrem em uma base totalmente previsível.

Por outras palavras, enquanto dizemos que um dia é de 24 horas, nem todos os dias são exatamente 24 horas, então se você quiser saber quantos dias mais minutos mais segundos uma determinada duração foi, você necessidade de ancorar essa duração em algum lugar em tempo real, de modo a saber se havia ou não um segundo bissexto lá, ou mesmo se existia ou não 29 de abril em algum lugar na duração.

Ainda mais exótico, nem todo mês tem o mesmo número de dias.

Se isso não for uma preocupação, basta executar sua contagem de segundos por meio da aritmética básica; use date para converter em segundos desde a época, divida por 86400 para obter dias, o restante por 3600 para obter horas, o restante para obter minutos e assim por diante.

    
por 19.05.2017 / 05:24