Calcola rapidamente le differenze di data


84

Spesso voglio fare alcuni rapidi calcoli della data, come ad esempio:

  • Qual è la differenza tra queste due date?
  • Qual è la data n settimane dopo l'altra data?

Di solito apro un calendario e conto i giorni, ma penso che ci dovrebbe essere un programma / script che posso usare per fare questo tipo di calcoli. Eventuali suggerimenti?


3
Vedi anche Strumento in UNIX per sottrarre date per quando la data GNU non è disponibile.
Gilles,

Risposte:


107

"N settimane dopo una data" è facile con la 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

Non conosco un modo semplice per calcolare la differenza tra due date, ma puoi avvolgere un po 'di logica attorno a date (1) con una funzione shell.

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

Scambia d1e d2se vuoi il calcolo della data nell'altro modo, o diventa un po 'più elaborato per renderlo irrilevante. Inoltre, nel caso in cui vi sia una transizione non da DST a DST nell'intervallo, uno dei giorni durerà solo 23 ore; puoi compensare aggiungendo ½ giornata alla somma.

echo $(( (((d1-d2) > 0 ? (d1-d2) : (d2-d1)) + 43200) / 86400 )) days

42

Per un set di strumenti portatili prova le mie dateutils . I tuoi due esempi si riducono a una riga:

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

o in settimane e giorni:

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

e

dadd 2011-11-15 21w
=>
  2012-04-10

3
Fai +1 sui tuoi strumenti rock (anche dateadd -i '%m%d%Y' 01012015 +1dse non sembra funzionare, si blocca lì indefinitamente ... funziona se le specifiche della data sono separate da un carattere, un carattere qualsiasi ...
hai

2
@don_crissti Il parser non è riuscito a distinguere tra date e durate solo numerali, è stato risolto nell'attuale master (d0008f98)
hroptatyr,

Hai una distribuzione binaria di dateutils?
mosh

@mosh no, e non ho le strutture per provare.
hroptatyr,

In ritardo alla festa qui, ma dateutils è su Cygwin se è necessario utilizzarlo su Windows.
gregb212,

30

Un esempio di pitone per calcolare il numero di giorni in cui ho camminato sul pianeta:

$ python
>>> from datetime import date as D
>>> print (D.today() - D(1980, 6, 14)).days
11476

2
Nel caso in cui qualcuno desideri che si comporti esattamente come un singolo comando, invece di digitare un interprete interattivo: ychaouche@ychaouche-PC ~ $ python -c "from datetime import date as d; print (d.today() - d(1980, 6, 14)).days" 12813 ychaouche@ychaouche-PC ~ $
ychaouche

1
questo funziona per me python3 -c "dalla data di importazione datetime come d; print (d.today () - d (2016, 1, 9))" non sono richiesti giorni alla fine
Kiran K Telukunta

13

Di solito preferisco avere l'ora / la data nel formato unime utime (numero di secondi dall'epoca, quando sono iniziati gli anni settanta, UTC). In questo modo si riduce sempre alla semplice sottrazione o aggiunta di secondi.

Il problema di solito diventa trasformare una data / ora in questo formato.

In un ambiente / script di shell è possibile ottenerlo con date '+%s' Al momento in cui scrivo, l'ora corrente è 1321358027.

Per confrontare con 2011-11-04 (il mio compleanno) date '+%s' -d 2011-11-04,, cedendo 1320361200. Sottrai: expr 1321358027 - 1320361200996827secondi, ovvero expr 996827 / 8640011 giorni fa.

La conversione da utime (formato 1320361200) a una data è molto semplice da fare, ad esempio in C, PHP o perl, usando librerie standard. Con GNU date, l' -dargomento può essere anteposto @per indicare il formato "Secondi dall'epoca".


7
Con la data GNU, date -d @1234567890converte da pochi secondi dall'epoca in qualsiasi formato data specificato.
Gilles,

1
@Gilles: è geniale. Non sono riuscito a trovarlo sulla manpage. Dove l'hai imparato?
MattBianco,

1
@MattBianco, vedi info date, in particolare i Secondi dal nodo Epoch : "Se si precede un numero con" @ ", rappresenta un timestamp interno come conteggio dei secondi."
manatwork

8

Se uno strumento grafico è OK per te, lo consiglio vivamente qalculate(una calcolatrice con enfasi sulle conversioni di unità, viene fornita con un'interfaccia GTK e KDE, IIRC). Lì puoi dire ad es

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

per ottenere il numero di giorni (140, dal 1900 non è stato un anno bisestile) tra le date, ma ovviamente puoi fare lo stesso anche per i tempi:

17:12:45 − 08:45:12

produce 8.4591667ore o, se si imposta l'uscita per la formattazione del tempo, 8:27:33.


3
Che è grande, e ancora di più a causa Qalculate fa avere una CLI. Prova qalcallora help days.
Sparhawk,

qcalcesempio: qalc -t 'days(1900-05-21, 1900-01-01)'-> 140
sierrasdetandil

8

Ciò è emerso quando si utilizzava date -d "$death_date - $y years - $m months - $d days"per ottenere una data di nascita (per la genealogia). Questo comando è SBAGLIATO. I mesi non sono tutti della stessa durata, quindi (date + offset) - offset != date. Le età, in anno / mese / giorno, sono misure che vanno avanti dalla data di nascita.

$ 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)

La data fornisce l'output corretto in entrambi i casi, ma nel secondo caso stavi facendo la domanda sbagliata. È importante QUANTO 11 mesi all'anno la copertina di +/- 11, prima di aggiungere / sottrarre giorni. Per esempio:

$ 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

Affinché la sottrazione sia l'operazione inversa dell'aggiunta, l'ordine delle operazioni dovrebbe essere invertito. L'aggiunta aggiunge anni, POI mesi, POI giorni. Se la sottrazione usasse l'ordine opposto, torneresti al punto di partenza. Non lo è, quindi non lo fai, se l'offset dei giorni attraversa il limite di un mese in un mese di diversa lunghezza.

Se devi lavorare a ritroso a partire da una data e un'età di fine, puoi farlo con più invocazioni di date. Prima sottrai i giorni, poi i mesi, poi gli anni. (Non penso che sia sicuro combinare anni e mesi in un'unica dateinvocazione, a causa degli anni bisestili che alterano la lunghezza di febbraio.)


4

Un altro modo per calcolare la differenza tra due date dello stesso anno solare è possibile utilizzare questo:

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"
  • La riga 1 dichiara alla shell quale interprete usare.
  • La riga 2 assegna il valore da out of datealla variabile DATEfirstnum. Il -dflag mostra la stringa in un formato orario in questo caso il 14 maggio 2014 e +"%j"indica datedi formattare l'output solo il giorno dell'anno (1-365).
  • La riga 3 è uguale alla riga 2 ma con una data e un formato diversi per la stringa, 31 dicembre 2014.
  • La riga 4 assegna il valore DAYSdifalla differenza dei due giorni.
  • La riga 5 mostra il valore di DAYSdif.

Funziona con la versione GNU di date, ma non con la versione PC-BSD / FreeBSD. Ho installato coreutilsdall'albero delle porte e /usr/local/bin/gdateinvece ho usato il comando .


1
Questo script non verrà eseguito. C'è un errore di battitura sull'ultima riga e spazi attorno alle assegnazioni delle variabili, quindi bash sta tentando di eseguire un programma chiamato DATE primo nome con due argomenti. Prova questo: DATEfirstnum=$(date -d "$1" +%s) DATElastnum=$(date -d "$2" +%s) Inoltre, questo script non sarà in grado di calcolare la differenza tra due anni diversi. +%jsi riferisce al giorno dell'anno (001..366), quindi le ./date_difference.sh 12/31/2001 12/30/2014uscite -1. Come notato da altre risposte, è necessario convertire entrambe le date in secondi dal 1970-01-01 00:00:00 UTC.
Sei

Non hai bisogno di $un'espressione aritmetica interna: $((DATElastnum - DATEfirstnum))funzionerà anche.
Ruslan,

3

Uso spesso SQL per i calcoli della data. Ad esempio MySQL , PostgreSQL o 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

Altre volte mi sento in vena di JavaScript. Ad esempio SpiderMonkey , WebKit , Seed o 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

(Fai attenzione quando passi il mese al Datecostruttore dell'oggetto JavaScript . Inizia con 0.)


3

Con l'aiuto di soluzioni dannas questo può essere fatto in una riga con il seguente codice:

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

(Funziona in Python 2.xe Python 3.)


1
Puoi modificare il tuo post invece di commentare
Stephen Rauch,

3

datee bash può fare differenze di data (mostrate le opzioni di OS X). Inserire prima quest'ultima data.

echo $((($(date -jf%D "04/03/16" +%s) - $(date -jf%D "03/02/16" +%s)) / 86400))
# 31

1

C'è anche il calcolo del tempo dell'unità GNU combinato con la data 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 è unità in Linux, gdate è data)


1

datiff.sh su 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

1

la risposta di Camh si occupa della maggior parte di essa, ma possiamo migliorare per gestire arrotondamenti, fusi orari, ecc. Inoltre abbiamo una maggiore precisione e la capacità di scegliere le nostre unità:

datediff() {
#convert dates to decimal seconds since 1970-01-01 00:00:00 UTC
date1seconds=$(date +%s.%N -d "$date1")
date2seconds=$(date +%s.%N -d "$date2")

#Calculate time difference in various time units
timeseconds=$(printf "%0.8f\n" $(bc <<<"scale=9; ($date2sec-$date1sec)"))
timeminutes=$(printf "%0.8f\n" $(bc <<<"scale=9; ($date2sec-$date1sec)/60"))
  timehours=$(printf "%0.8f\n" $(bc <<<"scale=9; ($date2sec-$date1sec)/3600"))
   timedays=$(printf "%0.8f\n" $(bc <<<"scale=9; ($date2sec-$date1sec)/86400"))
  timeweeks=$(printf "%0.8f\n" $(bc <<<"scale=9; ($date2sec-$date1sec)/604800"))
}

-ddice dateche stiamo fornendo una data-ora per la conversione. +%s.%Ncambia la data-ora in secondi.nanosecondi dal 1970-01-01 00:00:00 UTC. bccalcola la differenza tra i due numeri e il fattore di divisione ci fornisce le diverse unità. printfaggiunge uno 0 prima del punto decimale, se necessario ( bcnon lo fa) e assicura che l'arrotondamento vada al più vicino ( bcsolo tronca). Puoi anche usare awk.

Ora eseguiamo questo con un caso di test funky,

date1='Tue Jul  9 10:18:04.031 PST 2020'
date2='Wed May  8 15:19:34.447 CDT 2019'
datediff "$date1" "$date2"

echo $timeseconds seconds
-36971909.584000000 seconds

echo $timeminutes minutes
-616198.493066667 minutes

echo $timehours hours
-10269.974884444 hours

echo $timedays days
-427.915620185 days

echo $timeweeks weeks
-61.130802884 weeks

Si noti che poiché la durata di un mese o di un anno non è sempre la stessa, non esiste un'unica risposta "corretta", sebbene oggi si possano usare 365.24 giorni come approssimazione ragionevole.


0

Puoi usare la libreria awk Velour :

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

O:

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

Risultato:

7
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.