Léa-Linux & amis :   LinuxFR   GCU-Squad   GNU
Shell : les fonctions de date, difference de date, etc.
Envoyé par: MistaB

Bonjour,

Un message que j'ai posté sur le forum d'ubuntu, mais j'aimerais en faire profiter l'ensemble des utilsateurs de linux qui ont eu comme moi bien des soucis avec les dates en programmation shell...
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 :
[aa.usno.navy.mil]
[www.samag.com] … /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: 
# [aa.usno.navy.mil] 
# 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: 
# [aa.usno.navy.mil] 
# 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: 
# [aa.usno.navy.mil] 
# 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: 
# [aa.usno.navy.mil] 
# 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: 
# [aa.usno.navy.mil] 
# 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 :hat:
big_smile

MistaB

Poste le Thursday 1 February 2007 12:45:53
Répondre     Citer    
Re: Shell : les fonctions de date, difference de date, etc.

j'ai l'impression que tu te complique beaucoup la vie pour ton diff_date


(( nb_jour = ( $( date --date '29 Feb 2000 00:00 ' +%s ) - $( date --date '01 Jan 1972 00:00 ' +%s ) ) /(3600*24) ))


--
Brugmans Frédéric

[www.brugmans.net]
[triathlon.sport-challenge.be]

Poste le Friday 2 February 2007 12:02:22
Répondre     Citer    

Veuillez vous authentifier auparavant pour commenter.

 

Ce forum !
Shell : les fonctions de date, difference de date, etc.
Un problème avec une commande du shell ? Comment utiliser la crontab ? Vous avez des soucis pour la gestion réseau sous Linux ? Pour vous la gestion des utilisateurs/groupes est du chinois ? Etc... Posez donc vos questions ici.

Sauf mention contraire, les documentations publiées sont sous licence Creative-Commons