Calculer rapidement les différences de date

84

Je veux souvent faire quelques calculs rapides de date, tels que:

  • Quelle est la différence entre ces deux dates?
  • Quelle est la date n semaines après cette autre date?

J'ouvre habituellement un calendrier et compte les jours, mais je pense qu'il devrait y avoir un programme / script que je peux utiliser pour faire ce genre de calcul. Aucune suggestion?

daniel kullmann
la source
3
Voir également Outil sous UNIX pour soustraire des dates lorsque la date GNU n'est pas disponible.
Gilles

Réponses:

107

Le "n semaines après une date" est facile avec la date 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

Je ne connais pas de moyen simple de calculer la différence entre deux dates, mais vous pouvez envelopper un peu de logique autour de la date (1) avec une fonction shell.

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

Échangez d1et d2si vous voulez que le calcul de la date soit inversé, ou soyez un peu plus sophistiqué pour le rendre sans importance. De plus, dans le cas où il y aurait une transition non-DST à DST dans l'intervalle, l'un des jours ne durerait que 23 heures; vous pouvez compenser en ajoutant ½ journée à la somme.

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

Pour un ensemble d'outils portables, essayez mes propres outils de données . Vos deux exemples se résumeraient à une ligne:

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

ou en semaines et en jours:

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

et

dadd 2011-11-15 21w
=>
  2012-04-10
hroptatyr
la source
3
+1 vos outils sont géniaux (bien que dateadd -i '%m%d%Y' 01012015 +1dcela ne semble pas fonctionner, il y est suspendu indéfiniment ... cela fonctionne si les spécifications de date sont séparées par un caractère, un caractère quelconque ... aucune idée de ce qui ne va pas?)
don_crissti
2
@don_crissti L'analyseur n'a pas pu faire la distinction entre les dates et les durées uniquement numériques, il est corrigé dans le maître actuel (d0008f98)
hroptatyr
Avez-vous la distribution binaire de dateutils de window?
mosh
@mosh non, et je n'ai pas les moyens d'essayer.
hroptatyr
En retard pour la fête ici, mais dateutils est sur cygwin si vous avez besoin d’utiliser Windows.
gregb212
30

Un exemple en python pour calculer le nombre de jours que j'ai parcourus la planète:

$ python
>>> from datetime import date as D
>>> print (D.today() - D(1980, 6, 14)).days
11476
Daniel Näslund
la source
2
Juste au cas où quelqu'un voudrait que cela se comporte comme une seule commande, au lieu de taper un interprète interactif: 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
cela fonctionne pour moi python3 -c "à partir de la date d'importation de la date / heure en tant que d; print (d.today () - d (2016, 1, 9))" jours à la fin n'est pas nécessaire
Kiran K Telukunta
13

Je préfère généralement avoir l’heure / la date au format unix utime (nombre de secondes depuis l’époque, au début des années 70, UTC). De cette façon, cela revient toujours à une simple soustraction ou à l’addition de secondes.

Le problème est généralement de transformer une date / heure en ce format.

Dans un environnement / script shell, vous pouvez l’obtenir avec. date '+%s' Au moment de la rédaction, l’heure actuelle est 1321358027.

À comparer avec 2011-11-04 (mon anniversaire) date '+%s' -d 2011-11-04,, cédant 1320361200. Soustraire: expr 1321358027 - 1320361200donne les 996827secondes, c'est-à-dire expr 996827 / 86400= il y a 11 jours.

La conversion d'utime (format 1320361200) en une date est très simple, par exemple en C, PHP ou Perl, à l'aide de bibliothèques standard. Avec GNU date, l’ -dargument peut être précédé @de l’indication "Secondes depuis l’époque".

MattBianco
la source
7
Avec la date GNU, date -d @1234567890convertit les secondes écoulées depuis l’époque vers le format de date que vous spécifiez.
Gilles
1
@ Gilles: c'est génial. Impossible de trouver cela dans la page de manuel. Où avez-vous appris ça?
MattBianco
1
@MattBianco, voir info date, en particulier les secondes depuis le nœud Epoch : «Si vous faites précéder un nombre de« @ », il représente un horodatage interne
exprimé en
8

Si un outil graphique vous convient, je le recommande vivement qalculate(une calculatrice mettant l’accent sur les conversions d’unités, elle est fournie avec une interface GTK et KDE, IIRC). Là vous pouvez dire par exemple

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

pour obtenir le nombre de jours (140, puisque 1900 n’était pas une année bissextile) entre les dates, mais vous pouvez bien sûr faire de même pour les heures:

17:12:45 − 08:45:12

donne 8.4591667heures ou, si vous définissez la sortie à la mise en forme du temps, 8:27:33.

quazgar
la source
3
Ce qui est génial, et plus encore parce qalculate - t un CLI. Essayez qalcalors help days.
Sparhawk
qcalcexemple: qalc -t 'days(1900-05-21, 1900-01-01)'-> 140
sierrasdetandil
8

Cela s'est produit lors de l'utilisation date -d "$death_date - $y years - $m months - $d days"d'une date de naissance (pour la généalogie). Cette commande est FAUX. Les mois ne sont pas tous de la même longueur, donc (date + offset) - offset != date. Les âges, en année / mois / jour, sont des mesures allant de la date de naissance.

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

Date donne la sortie correcte dans les deux cas, mais dans le second cas, vous posiez la mauvaise question. Quel que soit 11 mois de l’année la couverture +/- 11, avant d’additionner / soustraire des jours. Par exemple:

$ 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

Pour que la soustraction soit l’opération inverse d’ajout, il faudrait inverser l’ordre des opérations. L'ajout ajoute des années, PUIS des mois, PUIS des jours. Si la soustraction utilisait l'ordre inverse, vous reviendriez à votre point de départ. Ce n'est pas le cas, donc vous ne le faites pas, si le décalage des jours dépasse une limite de mois dans un mois de longueur différente.

Si vous devez travailler en arrière à partir d'une date et d'un âge de fin, vous pouvez le faire avec plusieurs invocations de date. Commencez par soustraire les jours, puis les mois, puis les années. (Je ne pense pas qu'il soit prudent de combiner les années et les mois en une seule dateinvocation, car les années bissextiles modifient la durée de février.)

Peter Cordes
la source
4

Une autre façon de calculer la différence entre deux dates de la même année civile est d'utiliser ceci:

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 ligne 1 indique au shell l’interpréteur à utiliser.
  • La ligne 2 attribue la valeur de out dateà la variable DATEfirstnum. Le -ddrapeau affiche la chaîne au format heure dans ce cas, le 14 mai 2014, et +"%j"indique datede formater la sortie uniquement au jour de l'année (1-365).
  • La ligne 3 est identique à la ligne 2 mais avec une date et un format différents pour la chaîne, le 31 décembre 2014.
  • La ligne 4 attribue la valeur DAYSdifà la différence des deux jours.
  • La ligne 5 affiche la valeur de DAYSdif.

Cela fonctionne avec la version GNU de date, mais pas avec la version PC-BSD / FreeBSD. J'ai installé à coreutilspartir de l'arborescence des ports et utilisé la commande à la /usr/local/bin/gdateplace.

Justin Holcomb
la source
1
Ce script ne fonctionnera pas. Il y a une faute de frappe sur la dernière ligne et des espaces autour des assignations de variable, aussi bash tente-t-il d'exécuter un programme appelé DATEfirst name avec deux arguments. Essayez ceci: DATEfirstnum=$(date -d "$1" +%s) DATElastnum=$(date -d "$2" +%s) De plus, ce script ne pourra pas calculer la différence entre deux années différentes. +%jfait référence au jour de l'année (001..366) donc ./date_difference.sh 12/31/2001 12/30/2014output -1. Comme d'autres réponses l'ont noté, vous devez convertir les deux dates en secondes depuis le 1970-01-01 00:00:00 UTC.
Six
Vous n'avez pas besoin d'une $expression arithmétique interne: $((DATElastnum - DATEfirstnum))cela fonctionnera également.
Ruslan
3

J'utilise fréquemment SQL pour les calculs de date. Par exemple, 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

D'autres fois, je me sens d'humeur pour JavaScript. Par exemple, 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

(Faites attention lorsque vous passez le mois au Dateconstructeur de l'objet JavaScript . Commence par 0.)

homme au travail
la source
3

À l'aide de solutions Dannas, cela peut être fait en une ligne avec le code suivant:

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

(Fonctionne à la fois dans Python 2.x et Python 3.)

Kiran K Telukunta
la source
1
Vous pouvez éditer votre message au lieu de commenter
Stephen Rauch
3

dateet bash peuvent faire des différences de date (options OS X affichées). Placez cette dernière date en premier.

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

Il y a aussi les calculs de temps de l'unité GNU combinés avec la date 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 est des unités sous Linux, gdate est la date)

Larry
la source
1

datiff.sh sur 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
utilisateur178063
la source
1

La réponse de camh s’occupe de la plus grande partie, mais nous pouvons améliorer la gestion des arrondis, des fuseaux horaires, etc.

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"))
}

-dindique dateque nous fournissons une date-heure pour convertir. +%s.%Nmodifie la date-heure en secondess.nanoseconds depuis le 1970-01-01 00:00:00 UTC. bccalcule la différence entre les deux nombres et le facteur de division nous donne les différentes unités. printfajoute un 0 avant la décimale si nécessaire ( bcne le fait pas) et veille à ce que l’arrondi soit le plus proche ( bctronque). Vous pouvez également utiliser awk.

Maintenant, exécutons ceci avec un cas de test génial,

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

Notez que comme la durée d'un mois ou d'une année n'est pas toujours la même, il n'y a pas une seule réponse "correcte", bien que l'on puisse utiliser 365,24 jours comme une approximation raisonnable de nos jours.

TTT
la source
0

Vous pouvez utiliser la bibliothèque awk Velour :

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

Résultat:

7
Steven Penny
la source