#1 Le 31/01/2007, à 18:27
- MistaB
[Astuce] shell : Difference dates, calcul sur date
Bonjour à tous,
J'ai galéré suffisamment longtemps pour trouver un moyen de faire une seule boucle permettant de retrouver toutes les dates d'une seule année
On utilise pour cela le jour de l'année (de 1 à 365 ou 366)
Le problème, c'est que le système de date avec le shell bash ne permet pas de retrouver une date à partir du jour de l'année (et de l'année en question), ni de faire des différences entre dates pour retrouver un jour donné (ex : combien y-a-t'il de jours entre le 1er mars 2007 et le 16 avril 2005?)
Plus basique encore
Pour retrouver des fichiers notés selon la date (20070101, pour le premier janvier 2007, par exemple) et les lire avec une boucle, il faut généralement faire :
for MM in 01 02 03 04 05 06 07 08 09 10 11 12
do
mois=$MM
for jj = 01 02 03 ... 31
do
ech $jj
done
done
Et encore, il faut rajouter des test avec "case" ou "if", pour retrouver attribuer le juste nombre de jours dans le mois, et en plus, faire des tests pour voir si l'année est bisextile ou non. Bref, une batterie de test pour faire une simple boucle permettant de retrouver chaque jour de l'année et le transformer en format date.
J'ai donc pris mon courage à deux mains, et j'ai fouiller sur les forums divers et variés, souvent anglophones, pour trouver finalement qu'il fallait la plupart du temps éditer des fonctions complexes...ce que j'ai fait.
Et maintenant que je me suis cassé le derch, j'aimerais que tout le monde puisse en profiter, pour ne pas avoir à galérer comme je l'ai fait! Alors j'espère que cela vous aidera.
Tout d'abord, sur certains shells (pas celui que j'ai sur ma edgy), on peut avoir n'importe quelle date très facilement, pour une année donnée, à partir du seul jour de l'année, en feintant la fonction "date":
an=2000
jour_annee=12
date -d $an-1-$jour "+%d-%m-%Y"
12-01-2000
# ...normal, le 12ème jour de l'année, c'est le 12 janvier!
#...mais là...attention!
jour_annee=366
date -d $an-1-$jour "+%d-%m-%Y"
30-12-2000
#...eh oui! le 366ème jour du mois de janvier 2000 (année bisextile),
# c'est bien le 30 décembre 2000!
Donc, quand ce truc marche, on fait une boucle de 1 à 366 (for jj in `seq 1 366`), et on retrouve n'importe quelle date, avec le format désiré, à partir du seul jour de l'année et de l'année voulue.
Et pour les années bisextiles?
Et bien il suffit de faire un test genre :
Si l'année de la date correspondant au jour 366 est différente de l'année que l'on traite, alors l'année n'est pas bisextile! (bon je sais, c'est je ne suis pô très clair, mais je voudrais aller au bout de ce post...)
ZE PROBLEME, c'est que cette astuce ne marche pas avec la fonction date du shell que j'ai sur ma edgy (je pense que c'est la nouvelle version de la fonction date qui a dû exclure cette possibilité, mais comme je ne m'y connais pas du tout entre bash ksh et cie, je ne m'aventurerai pas à hypothèser des idioties...)
Donc...comment qu'on fait?
Et bien on écrit des fonctions permettant de retrouver le jour julien d'une date, de faire des différences entre les dates, etc...on programme, quoi!
Un petit apparté sur les jours juliens : il s'agit de numéro de jours, des nombres entiers, que l'on attribue à chaque date. Ce n'est pas la même chose que le jour de l'année.
Par exemple, pour le 1er janvier 2007, le jour julien est 2454102.
Par contre, le jour de l'année est 1. Cependant, une fois que l'on a ce jour julien, il suffit de faire la soustraction jour julien de la date voulue avec le jour julien du premier janvier de la même année, et d'ajouter 1, pour obtenir le jour de l'année...
Voici une série de fonctions que j'ai adapté et reprogrammé à partir de celles fournies par les url :
http://aa.usno.navy.mil/faq/docs/JD_Formula.html
http://www.samag.com/documents/s=8284/s … /0307b.htm
et qui permettent de manipuler les dates:
get_astro_JD() :
Calcule et indique le jour julien en fonction du jour dans le mois, du mois et de l'année
get_astro_YEAR() :
Calcule et indique l'année correspondant à un jour julien donné
get_astro_MONTH() :
Calcule et indique le mois correspondant à un jour julien donné
get_astro_DAY() :
Calcule et indique le jour dans le mois (1 à 31 ou 30 ou 28 ou 29 selon le mois) correspondant à un jour julien donné
get_astro_DATE() :
Calcule et indique (au format YYYY-mm-dd , c'est-à-dire année-mois-jour) la date correspondant à un jour julien donné
diff_date() :
Calcule et indique le nombre de jours de différence entre deux dates données (au format YYYY-mm-dd)
get_date_from_DOY_YEAR() :
Calcule et indique une date (au format YYYY-mm-dd) à partir du jour de l'année (1 à 365 ou 366) et de l'année (format YYYY, à quatre chiffres) donnés...correspond à la première astuce ci-dessus
Les voici, donc (NB : les explications sont en anglais, par soucis d'universalité, des fois que des anglophones soient dirigés sur ce poste via un moteur de recherche)
Les fonctions diff_date() et get_date_from_DOY_YEAR() ont été totalement crées "maison"
# This function returns the Astro-Julian Date. The Julian
# date (JD) is a continuous count of days from 1 January 4713 BC.
# The following algorithm is good from years 1801 to 2099.
# See URL:
# http://aa.usno.navy.mil/faq/docs/JD_Formula.html
# for more information
# arguments: $1 = day, $2 = month, $3 = year in format YYYY
get_astro_JD()
{
typeset -i JDD
JDD=$(($1-32075+1461*($3+4800+($2-14)/12)/4+367*($2-2-($2-14)/12*12)/12-3*(($3+4900+($2-14)/12)/100)/4))
echo $JDD
}
# This function gives returns the gregorian YEAR from a Julian date
# The following algorithm is good from years 1801 to 2099.
# See URL:
# http://aa.usno.navy.mil/faq/docs/JD_Formula.html
# for more information
# argument : $1 = julian date (e.g. from get_astro_JD() function)
get_astro_YEAR()
{
typeset -i MONTH
typeset -i YEAR
typeset -i DAY
typeset -i varL
typeset -i varN
varL=$(($1+68569))
varN=$((4*$varL/146097))
varL=$(($varL-(146097*$varN+3)/4))
YEAR=$((4000*($varL+1)/1461001))
varL=$(($varL-1461*$YEAR/4+31))
MONTH=$((80*$varL/2447))
DAY=$(($varL-2447*$MONTH/80))
varL=$(($MONTH/11))
MONTH=$(($MONTH+2-12*$varL))
YEAR=$((100*($varN-49)+$YEAR+$varL))
echo $YEAR
}
# This function gives returns the gregorian MONTH from a Julian date
# The following algorithm is good from years 1801 to 2099.
# See URL:
# http://aa.usno.navy.mil/faq/docs/JD_Formula.html
# for more information
# argument : $1 = julian date (e.g. from get_astro_JD() function)
get_astro_MONTH()
{
typeset -i MONTH
typeset -i YEAR
typeset -i DAY
typeset -i varL
typeset -i varN
varL=$(($1+68569))
varN=$((4*$varL/146097))
varL=$(($varL-(146097*$varN+3)/4))
YEAR=$((4000*($varL+1)/1461001))
varL=$(($varL-1461*$YEAR/4+31))
MONTH=$((80*$varL/2447))
DAY=$(($varL-2447*$MONTH/80))
varL=$(($MONTH/11))
MONTH=$(($MONTH+2-12*$varL))
YEAR=$((100*($varN-49)+$YEAR+$varL))
echo $MONTH
}
# This function gives returns the DAY OF THE MONTH of a gregorian calendar date from a Julian date
# The following algorithm is good from years 1801 to 2099.
# See URL:
# http://aa.usno.navy.mil/faq/docs/JD_Formula.html
# for more information
# argument : $1 = julian date (e.g. from get_astro_JD() function)
get_astro_DAY()
{
typeset -i MONTH
typeset -i YEAR
typeset -i DAY
typeset -i varL
typeset -i varN
varL=$(($1+68569))
varN=$((4*$varL/146097))
varL=$(($varL-(146097*$varN+3)/4))
YEAR=$((4000*($varL+1)/1461001))
varL=$(($varL-1461*$YEAR/4+31))
MONTH=$((80*$varL/2447))
DAY=$(($varL-2447*$MONTH/80))
varL=$(($MONTH/11))
MONTH=$(($MONTH+2-12*$varL))
YEAR=$((100*($varN-49)+$YEAR+$varL))
echo $DAY
}
# This function gives returns the gregorian DATE (format YYYY-mm-dd) from a Julian date
# The following algorithm is good from years 1801 to 2099.
# See URL:
# http://aa.usno.navy.mil/faq/docs/JD_Formula.html
# for more information
# argument : $1 = julian date (e.g. from get_astro_JD() function)
get_astro_DATE()
{
typeset -i MONTH
typeset -i YEAR
typeset -i DAY
typeset -i varL
typeset -i varN
varL=$(($1+68569))
varN=$((4*$varL/146097))
varL=$(($varL-(146097*$varN+3)/4))
YEAR=$((4000*($varL+1)/1461001))
varL=$(($varL-1461*$YEAR/4+31))
MONTH=$((80*$varL/2447))
DAY=$(($varL-2447*$MONTH/80))
varL=$(($MONTH/11))
MONTH=$(($MONTH+2-12*$varL))
YEAR=$((100*($varN-49)+$YEAR+$varL))
echo $YEAR'-'$MONTH'-'$DAY
}
# This function returns the difference in number of day between two dates
# Date format must be "%Y-%m-%d" (day and month with one or two digits, depending on the shell version used)
# arguments : $1 = the latest date, $2 = the earliest date
# example :
# echo $(diff_date 2002-31-12 2002-01-01)
# 365
# #bisextile year
# echo $(diff_date 2000-31-12 2000-01-01)
# 366
diff_date()
{
typeset -i JDD1
typeset -i JDD2
typeset -i JDIFF
typeset -i YEAR
typeset -i MONTH
typeset -i DAY
YEAR=`date -d $1 "+%Y"`
MONTH=`date -d $1 "+%m"`
DAY=`date -d $1 "+%d"`
JDD1=$(($DAY-32075+1461*($YEAR+4800+($MONTH-14)/12)/4+367*($MONTH-2-($MONTH-14)/12*12)/12-3*(($YEAR+4900+($MONTH-14)/12)/100)/4))
YEAR=`date -d $2 "+%Y"`
MONTH=`date -d $2 "+%m"`
DAY=`date -d $2 "+%d"`
JDD2=$(($DAY-32075+1461*($YEAR+4800+($MONTH-14)/12)/4+367*($MONTH-2-($MONTH-14)/12*12)/12-3*(($YEAR+4900+($MONTH-14)/12)/100)/4))
JDIFF=$(($JDD1-$JDD2+1))
echo $JDIFF
}
# This function returns the gregorian date from the day of the year (DOY, 1 to 366)
# and the year number (format YYYY, i.e. with century)
# arguments : $1 = DOY, $2 = year
get_date_from_DOY_YEAR()
{
typeset -i JDD
typeset -i YEAR
typeset -i MONTH
typeset -i DAY
typeset -i varL
typeset -i varN
YEAR=$(($2))
MONTH=$((1))
DAY=$((1))
JDD=$(($DAY-32075+1461*($YEAR+4800+($MONTH-14)/12)/4+367*($MONTH-2-($MONTH-14)/12*12)/12-3*(($YEAR+4900+($MONTH-14)/12)/100)/4))
JDD=$(($JDD+$1-1))
varL=$(($JDD+68569))
varN=$((4*$varL/146097))
varL=$(($varL-(146097*$varN+3)/4))
YEAR=$((4000*($varL+1)/1461001))
varL=$(($varL-1461*$YEAR/4+31))
MONTH=$((80*$varL/2447))
DAY=$(($varL-2447*$MONTH/80))
varL=$(($MONTH/11))
MONTH=$(($MONTH+2-12*$varL))
YEAR=$((100*($varN-49)+$YEAR+$varL))
echo $YEAR'-'$MONTH'-'$DAY
}
Faites tourner!!!
J'espère que cela vous servira...
N'hésitez pas à simplifier les noms (certains sont un peu long)
N'hésitez pas à répondre à ce post si vous trouvez des bugs quelconque dans ces fonctions, ou si vous avez des suggestions.
Seul petit hic pou ma part...je ne m'y connais pas suffisament en linux pour lancer ces fonctions automatiquement au démarrage du shell (je les ai copié dans /etc/profile, mais rien n'y fait...).
Une idée?
Big Up...
...et merci pour l'entraide, merci à la communauté et aux développeurs (chapeau)
MistaB
Dernière modification par MistaB (Le 01/02/2007, à 12:37)
-----------------------
euh....désolé!
Hors ligne
#2 Le 14/01/2011, à 03:34
- nordinatueur
Re : [Astuce] shell : Difference dates, calcul sur date
Quasiment 4 ans après, je suis très heureux du résultat !
Merci... Je vais ranger ma pelle !
Linux User #508094
Pour une meilleure coopération, utilisez des liens relatifs sur le forum !
Hors ligne
#3 Le 07/05/2013, à 18:06
- ze-boulet
Re : [Astuce] shell : Difference dates, calcul sur date
Bonjour.
Voici une solution qui -a priori- ne fait appel qu'à des commandes ksh 'standard'.
Les dates considérées doivent être passées sous la forme aaaammjj.
Les limites du calendrier ne sont pas testées
(Il doit rester des variables qui font double emploi, elles ont été ajoutées pour faciliter la compréhension)
Si 1 seule date est renseignée, la date du jour sera considérée comme étant la seconde.
Le (ou les) paramètres peuvent être donné(s) en réponse à une question.
Il suffit de copier ce qui suit dans un fichier exécutable :
function chkd
{
set -A Mmx 0 31 28 31 30 31 30 31 31 30 31 30 31
(( D +=0 )) 2>/dev/null
[[ $? = 0 ]] || Do=n
[[ ${#D} = 8 ]] || Do=n
if [[ $Do = y ]]
then
typeset -L4 D8=$D
(( D8 +=0 )) 2>/dev/null
[[ ${#D8} = 4 ]] || Do=n
if [[ $Do = y ]]
then
typeset -R4 D4142=$D
typeset -L2 D41=$D4142
(( D41 +=0 )) 2>/dev/null
[[ $? = 0 ]] || Do=n
[[ $D41 -lt 1 || $D41 -ge ${#Mmx[*]} ]] && Do=n
if [[ $Do = y ]]
then
typeset -R2 D42=$D4142
(( D42 +=0 )) 2>/dev/null
[[ $? = 0 ]] || Do=n
if [[ $Do = y ]]
then
D2=0
typeset -R2 yy=$D8
if [[ $yy -eq "00" ]]
then
(( Modulo400=D8%400 ))
(( Modulo400 == 0 )) && D2=1
else
(( Modulo4=D8%4 ))
(( Modulo4 == 0 )) && D2=1
fi
D42l=${Mmx[$D41]}
(( D42l == 28 )) && (( D42l +=D2 ))
[[ $D42 -gt $D42l ]] && Do=n
fi
fi
fi
fi
R0=$D ; R1=$D8 ; R2=$D4142 ; R3=$D41 ; R4=$D42 ; R5=$D2
}
if [[ $# -gt 2 ]]
then
echo ""
echo ""
echo usage : $0 [yyyymmdd [yyyymmdd]]
echo ""
echo ""
exit 1
fi
set -A Adj 0 0 31 59 90 120 151 181 212 243 273 304 334
if [[ $# -gt 0 ]]
then
dollar1=$1
else
echo ""
echo ""
printf "please specify either one or two yyyymmdd date\(s\) : "
read dollar1 dollar2 Dust
fi
if [[ $# = 2 ]]
then
dollar2=$2
fi
[[ "$dollar2" = "" ]] && dollar2=$(date +%Y%m%d)
if [[ $dollar1 -lt $dollar2 ]]
then
Fm=$dollar1 ; To=$dollar2
elif [[ $dollar1 -gt $dollar2 ]]
then
Fm=$dollar2 ; To=$dollar1
else
echo no difference from $dollar1 to $dollar2
exit
fi
Do=y
D=$dollar1
D=$Fm
chkd $D
if [[ $Do = n ]]
then
echo invalid from_date value \($Fm\)
exit 1
fi
Fmy=$R1 ; Fmm=$R3 ; Fmd=$R4 ; Fm2=$D2
(( Fmy +=0 )) ; (( Fmm +=0 )) ; (( Fmd +=0 )) ; (( Fm2 +=0 ))
D=$dollar2
D=$To
chkd $D
if [[ $Do = n ]]
then
echo invalid from_date value \($To\)
exit 1
fi
Toy=$R1 ; Tom=$R3 ; Tod=$R4 ; To2=$D2
Fmq=${Adj[$Fmm]}
(( Fmq += Fmd ))
[[ $Fmm -gt 2 ]] && (( Fmq += Fm2 ))
Toq=${Adj[$Tom]}
(( Toq += Tod ))
[[ $Tom -gt 2 ]] && (( Toq += To2 ))
if [[ $Fmy = $Toy ]]
then
FmTo=$(( Toq - Fmq ))
echo ""
echo the difference from $Fm to $To is $FmTo days
echo ""
exit
else
Lqy=365
Lqy=$(( Lqy += Fm2 ))
FmTo=$(( Lqy -= Fmq ))
(( Fmy += 1 ))
fi
while [[ $Fmy -lt $Toy ]]
do
Fm2=0
typeset -R2 yy=$Fmy
if [[ $yy -eq "00" ]]
then
typeset -L2 SS=$Fmy
(( Modulo4=SS%4 ))
(( Modulo4 == 0 )) && Fm2=1
else
(( Modulo4=Fmy%4 ))
(( Modulo4 == 0 )) && Fm2=1
fi
Lqy=365
FmTo=$(( FmTo += (Lqy + Fm2) ))
(( Fmy += 1 ))
done
FmTo=$(( FmTo += Toq ))
echo ""
echo the difference from $Fm to $To is $FmTo days
echo ""
Hors ligne
#4 Le 19/03/2021, à 10:29
- bef
Re : [Astuce] shell : Difference dates, calcul sur date
Merci MistaB, ton travail m'est très utile...
Hors ligne
#5 Le 19/03/2021, à 12:31
- Nasman
Re : [Astuce] shell : Difference dates, calcul sur date
Tu as prévu les années divisibles par 4 et non bissextiles (comme 1700, 1800, 1900, 2100...) ?
PC fixe sous Bionic 64 bits et portable avec Focal 64 bits
Hors ligne