Calcular rapidamente diferenças de datas

74

Muitas vezes, quero fazer alguns cálculos de data rápida, como:

  • Qual é a diferença entre essas duas datas?
  • Qual é a data n semanas após essa outra data?

Eu normalmente abro um calendário e conto os dias, mas acho que deveria haver um programa / script que eu possa usar para fazer esse tipo de cálculo. Alguma sugestão?

    
por daniel kullmann 15.11.2011 / 12:46

13 respostas

97

A "n semanas após uma data" é fácil com a data GNU (1):

$ date -d 'now + 3 weeks'
Tue Dec  6 23:58:04 EST 2011
$ date -d 'Aug 4 + 3 weeks'
Thu Aug 25 00:00:00 EST 2011
$ date -d 'Jan 1 1982 + 11 weeks'
Fri Mar 19 00:00:00 EST 1982

Eu não sei de uma maneira simples de calcular a diferença entre duas datas, mas você pode envolver uma pequena lógica em volta da data (1) com uma função de shell.

datediff() {
    d1=$(date -d "$1" +%s)
    d2=$(date -d "$2" +%s)
    echo $(( (d1 - d2) / 86400 )) days
}
$ datediff '1 Nov' '1 Aug'
91 days

Troque d1 e d2 se você quiser o cálculo da data da outra maneira, ou seja um pouco mais extravagante para fazer isso não importa. Além disso, caso haja uma transição não-DST para DST no intervalo, um dos dias será de apenas 23 horas longo; você pode compensar adicionando ½ dia à soma.

echo $(( (((d1-d2) > 0 ? (d1-d2) : (d2-d1)) + 43200) / 86400 )) days
    
por 15.11.2011 / 14:05
36

Para um conjunto de ferramentas portáteis, experimente os meus dateutils . Seus dois exemplos resumir-se-iam aos one-liners:

ddiff 2011-11-15 2012-04-11
=>
  148

ou em semanas e dias:

ddiff 2011-11-15 2012-04-11 -f '%w %d'
=>
  21 1

e

dadd 2011-11-15 21w
=>
  2012-04-10
    
por 11.04.2012 / 08:37
28

Um exemplo de python para calcular o número de dias que eu andei no planeta:

$ python
>>> from datetime import date as D
>>> print (D.today() - D(1980, 6, 14)).days
11476
    
por 15.11.2011 / 13:25
9

Eu geralmente prefiro ter a data / hora no formato unix utime (número de segundos desde a época, quando os anos setenta começaram, UTC). Dessa forma, sempre se resume a subtração simples ou adição de segundos.

O problema geralmente torna-se transformar uma data / hora neste formato.

Se você tem o GNU date , você pode obtê-lo com date '+%s' No momento da escrita, a hora atual é 1321358027 .

Para comparar com 2011-11-04 (meu aniversário), date '+%s' -d 2011-11-04 , rendendo 1320361200 . Subtrair: expr 1321358027 - 1320361200996827 segundos, o que é expr 996827 / 86400 = 11 dias atrás.

O problema é converter de utime (formato 1320361200) em uma data. Eu não sei de uma ferramenta prontamente disponível para fazer isso, mas é muito simples de fazer, por exemplo, C ou perl.

    
por 15.11.2011 / 13:02
5

Se uma ferramenta gráfica é boa para você, eu recomendo de coração qalculate (uma calculadora com ênfase em conversões de unidade, ela vem com uma interface GTK e KDE, IIRC). Lá você pode dizer, por exemplo,

days(1900-05-21, 1900-01-01)

para obter o número de dias (140, desde 1900 não foi um ano bissexto) entre as datas, mas é claro que você também pode fazer o mesmo nos horários:

17:12:45 − 08:45:12

rende 8.4591667 horas ou, se você definir a saída para a formatação de hora, 8:27:33 .

    
por 29.08.2013 / 09:30
5

Isso surgiu quando usamos date -d "$death_date - $y years - $m months - $d days" para obter uma data de nascimento (para genealogia). Esse comando é errado. Meses não são todos do mesmo tamanho, então (date + offset) - offset != date . Idades, ano / mês / dia, são medidas em andamento desde a data de nascimento.

$ date --utc -d 'mar 28 1867 +72years +11months +2days'
Fri Mar  1 00:00:00 UTC 1940

$ date --utc -d 'mar 1 1940 -72years -11months -2days'
Sat Mar 30 00:00:00 UTC 1867
# (2 days later than our starting point)

A data fornece a saída correta em ambos os casos, mas no segundo caso você estava fazendo a pergunta errada. É importante que 11 meses do ano o +/- 11 cubra, antes de adicionar / subtrair dias. Por exemplo:

$ date --utc -d 'mar 31 1939  -1month'
Fri Mar  3 00:00:00 UTC 1939
$ date --utc -d 'mar 31 1940  -1month' # leap year
Sat Mar  2 00:00:00 UTC 1940
$ date --utc -d 'jan 31 1940  +1month' # leap year
Sat Mar  2 00:00:00 UTC 1940

Para subtrair a operação inversa de adição, a ordem de operações teria que ser invertida. Adicionando adiciona anos, então meses, depois de dias. Se a subtração usasse a ordem oposta, você retornaria ao seu ponto de partida. Isso não acontece, assim você não faz, se o dia deslocado cruza um limite de mês em um mês de comprimento diferente.

Se você precisar trabalhar para trás a partir de uma data e idade de término, poderá fazê-lo com várias chamadas de date . Primeiro, subtraia os dias, depois os meses, depois os anos. (Não acho seguro combinar os anos e meses em uma única invocação de date , devido a anos bissextos que alteram a duração de fevereiro.)

    
por 31.01.2015 / 04:39
3

Eu freqüentemente uso SQL para cálculos de data. Por exemplo MySQL , PostgreSQL ou SQLite :

bash-4.2$ mysql <<< "select datediff(current_date,'1980-06-14')"
datediff(current_date,'1980-06-14')
11477

bash-4.2$ psql <<< "select current_date-'1980-06-14'"
 ?column? 
----------
    11477
(1 row)

bash-4.2$ sqlite2 <<< "select julianday('now')-julianday('1980-06-14');"
11477.3524537035

Outras vezes, sinto-me com disposição para JavaScript. Por exemplo, SpiderMonkey , WebKit , Seed ou Node.js :

bash-4.2$ js -e 'print((new Date()-new Date(1980,5,14))/1000/60/60/24)'
11477.477526192131

bash-4.2$ jsc-1 -e 'print((new Date()-new Date(1980,5,14))/1000/60/60/24)'
11477.47757960648

bash-4.2$ seed -e '(new Date()-new Date(1980,5,14))/1000/60/60/24'
11477.4776318287

bash-4.2$ node -pe '(new Date()-new Date(1980,5,14))/1000/60/60/24'
11624.520061481482

(Cuidado ao passar o mês para o construtor do objeto JavaScript Date . Começa com 0.)

    
por 16.11.2011 / 09:35
3

Outra maneira de calcular a diferença entre duas datas do mesmo ano pode usar isso:

date_difference.sh
1  #!/bin/bash
2  DATEfirstnum='date -d "2014/5/14" +"%j"'
3  DATElastnum='date -d "12/31/14" +"%j"'
4  DAYSdif=$(($DATElastnum - $DATEfirstnum))
5  echo "$DAYSdif"
  • A linha 1 declara para o shell qual interpretador usar.
  • A linha 2 atribui o valor de fora de date à variável DATAfirstnum. O -d flag exibe a string em um formato de hora em Neste caso, 14 de maio de 2014 e +"%j" informa date para formatar a saída para apenas o dia do ano (1-365).
  • A linha 3 é a mesma que a Linha 2, mas com uma data diferente e formato diferente para a string, 31 de dezembro de 2014.
  • A linha 4 atribui o valor DAYSdif à diferença dos dois dias.
  • A linha 5 exibe o valor de DAYSdif .

Isso funciona com a versão GNU de date , mas não na versão PC-BSD / FreeBSD. Instalei coreutils da árvore de ports e usei o comando /usr/local/bin/gdate .

    
por 31.12.2014 / 18:32
2

Com a ajuda das soluções dannas, isso pode ser feito em uma linha com o seguinte código:

python -c "from datetime import date as d; print(d.today() - d(2016, 7, 26))"

(Funciona no Python 2.xe no Python 3.)

    
por 17.03.2017 / 14:59
1

Há também cálculos de tempo da unidade GNU combinados com a data do GNU:

$ gunits $(gdate +%s)sec-$(gdate +%s -d -1234day)sec 'yr;mo;d;hr;min;s'
        3 yr + 4 mo + 16 d + 12 hr + 37 min + 26.751072 s
$ gunits $(gdate +%s -d '2015-1-2 3:45:00')sec-$(gdate +%s -d '2013-5-6 7:43:21')sec 'yr;mo;d;hr;min;s'
        1 yr + 7 mo + 27 d + 13 hr + 49 min + 26.206759 s

(gunits é unidades no Linux, gdate é data)

    
por 14.11.2015 / 21:14
1

datediff.sh no github: gist

#!/bin/bash
#Simplest calculator two dates difference. By default in days

# Usage:
# ./datediff.sh first_date second_date [-(s|m|h|d) | --(seconds|minutes|hours|days)]

first_date=$(date -d "$1" "+%s")
second_date=$(date -d "$2" "+%s")

case "$3" in
"--seconds" | "-s") period=1;;
"--minutes" | "-m") period=60;;
"--hours" | "-h") period=$((60*60));;
"--days" | "-d" | "") period=$((60*60*24));;
esac

datediff=$(( ($first_date - $second_date)/($period) ))
echo $datediff
    
por 11.09.2016 / 15:16
1

date e bash podem fazer diferenças de datas (opções do OS X mostradas). Coloque a última data primeiro.

echo $((($(date -jf%D "04/03/16" +%s) - $(date -jf%D "03/02/16" +%s)) / 86400))
# 31
    
por 21.04.2017 / 14:54
0

Você pode usar a biblioteca de Velud do awk:

velour -n 'print t_secday(t_utc("2017-4-12") - t_utc("2017-4-5"))'

Ou:

velour -n 'print t_secday(t_utc(ARGV[1]) - t_utc(ARGV[2]))' 2017-4-12 2017-4-5

Resultado:

7
    
por 28.12.2016 / 03:28

Tags