Arithmétique des dates dans les scripts shell Unix

je dois faire l'arithmétique des dates dans les scripts shell Unix que j'utilise pour contrôler l'exécution de programmes tiers.

j'utilise une fonction pour incrémenter un jour et une autre pour décrémenter:

IncrementaDia(){
echo  | awk '
BEGIN {
        diasDelMes[1] = 31
        diasDelMes[2] = 28
        diasDelMes[3] = 31
        diasDelMes[4] = 30
        diasDelMes[5] = 31
        diasDelMes[6] = 30
        diasDelMes[7] = 31
        diasDelMes[8] = 31
        diasDelMes[9] = 30
        diasDelMes[10] = 31
        diasDelMes[11] = 30
        diasDelMes[12] = 31
}
{
        anio=substr(,1,4)
        mes=substr(,5,2)
        dia=substr(,7,2)

        if((anio % 4 == 0 && anio % 100 != 0) || anio % 400 == 0)
        {
                diasDelMes[2] = 29;
        }

        if( dia == diasDelMes[int(mes)] ) {
                if( int(mes) == 12 ) {
                        anio = anio + 1
                        mes = 1
                        dia = 1
                } else {
                        mes = mes + 1
                        dia = 1
                }
        } else {
                dia = dia + 1
        }
}
END {
        printf("%04d%02d%02d", anio, mes, dia)
}
'
}

if [ $# -eq 1 ]; then
        tomorrow=
else
        today=$(date +"%Y%m%d")
        tomorrow=$(IncrementaDia $hoy)
fi

mais maintenant j'ai besoin de faire plus d'arithmétique complexe.

Qu'elle est la meilleure et la plus compatible?

17
demandé sur ggasp 2008-08-09 03:24:02

14 réponses

j'ai écrit un script bash pour convertir les dates exprimées en anglais en conventionnel mm/jj/aaaa dates. Il est appelé ComputeDate.

Voici quelques exemples de son utilisation. Par souci de concision, j'ai placé la sortie de chaque invocation sur la même ligne que l'invocation, séparée par un côlon (:). Les citations ci-dessous sont des nécessaire lors de l'exécution ComputeDate:

$ ComputeDate 'yesterday': 03/19/2010
$ ComputeDate 'yes': 03/19/2010
$ ComputeDate 'today': 03/20/2010
$ ComputeDate 'tod': 03/20/2010
$ ComputeDate 'now': 03/20/2010
$ ComputeDate 'tomorrow': 03/21/2010
$ ComputeDate 'tom': 03/21/2010
$ ComputeDate '10/29/32': 10/29/2032
$ ComputeDate 'October 29': 10/1/2029
$ ComputeDate 'October 29, 2010': 10/29/2010
$ ComputeDate 'this monday': 'this monday' has passed.  Did you mean 'next monday?'
$ ComputeDate 'a week after today': 03/27/2010
$ ComputeDate 'this satu': 03/20/2010
$ ComputeDate 'next monday': 03/22/2010
$ ComputeDate 'next thur': 03/25/2010
$ ComputeDate 'mon in 2 weeks': 03/28/2010
$ ComputeDate 'the last day of the month': 03/31/2010
$ ComputeDate 'the last day of feb': 2/28/2010
$ ComputeDate 'the last day of feb 2000': 2/29/2000
$ ComputeDate '1 week from yesterday': 03/26/2010
$ ComputeDate '1 week from today': 03/27/2010
$ ComputeDate '1 week from tomorrow': 03/28/2010
$ ComputeDate '2 weeks from yesterday': 4/2/2010
$ ComputeDate '2 weeks from today': 4/3/2010
$ ComputeDate '2 weeks from tomorrow': 4/4/2010
$ ComputeDate '1 week after the last day of march': 4/7/2010
$ ComputeDate '1 week after next Thursday': 4/1/2010
$ ComputeDate '2 weeks after the last day of march': 4/14/2010
$ ComputeDate '2 weeks after 1 day after the last day of march': 4/15/2010
$ ComputeDate '1 day after the last day of march': 4/1/2010
$ ComputeDate '1 day after 1 day after 1 day after 1 day after today': 03/24/2010

j'ai inclus ce script comme un réponse à ce problème parce qu'il illustre comment pour faire des opérations arithmétiques via un ensemble de fonctions bash et ces fonctions peuvent s'avérer utiles pour les autres. Il gère les années bissextiles et les siècles bissextiles correctement:

#! /bin/bash
#  ConvertDate -- convert a human-readable date to a MM/DD/YY date
#
#  Date ::= Month/Day/Year
#        |  Month/Day
#        |  DayOfWeek
#        |  [this|next] DayOfWeek
#        |  DayofWeek [of|in] [Number|next] weeks[s]
#        |  Number [day|week][s] from Date
#        |  the last day of the month
#        |  the last day of Month
#
#  Month ::= January | February | March | April | May | ...  | December
#  January  ::= jan | january | 1
#  February  ::= feb | january | 2
#  ...
#  December ::=  dec | december | 12
#  Day   ::= 1 | 2 | ... | 31
#  DayOfWeek ::= today | Sunday | Monday | Tuesday | ...  | Saturday
#  Sunday    ::= sun*
#  ...
#  Saturday  ::= sat*
#
#  Number ::= Day | a
#
#  Author: Larry Morell

if [ $# = 0 ]; then
   printdirections 
   exit
fi



# Request the value of a variable
GetVar () {
   Var=
   echo -n "$Var= [${!Var}]: "
   local X
   read X
   if [ ! -z $X ]; then
      eval $Var="$X"
   fi
}

IsLeapYear () {
   local Year=
   if [ $[20$Year % 4]  -eq  0 ]; then
      echo yes
   else
      echo no
   fi
}

# AddToDate -- compute another date within the same year

DayNames=(mon tue wed thu fri sat sun )  # To correspond with 'date' output

Day2Int () {
   ErrorFlag=
   case  in
      -e )
         ErrorFlag=-e; shift
         ;;
   esac
   local dow=
   n=0
   while  [ $n -lt 7 -a $dow != "${DayNames[n]}" ]; do
      let n++
   done
   if [ -z "$ErrorFlag" -a $n -eq 7 ]; then
      echo Cannot convert $dow to a numeric day of wee
      exit
   fi
   echo $[n+1]

}

Months=(31 28 31 30 31 30 31 31 30 31 30 31)
MonthNames=(jan feb mar apr may jun jul aug sep oct nov dec)
# Returns the month (1-12) from a date, or a month name
Month2Int () {
   ErrorFlag=
   case  in
      -e )
         ErrorFlag=-e; shift
         ;;
   esac
   M=
   Month=${M%%/*}  # Remove /...
   case $Month in
      [a-z]* )
         Month=${Month:0:3}
         M=0
         while [ $M -lt 12 -a ${MonthNames[M]} != $Month ]; do
            let M++
         done
         let M++
   esac
   if [  -z "$ErrorFlag" -a $M -gt 12 ]; then
      echo "'$Month' Is not a valid month."
      exit
   fi
   echo $M
}

# Retrieve month,day,year from a legal date
GetMonth() {
   echo ${1%%/*}
}

GetDay() {
   echo  | col / 2
}

GetYear() {
   echo ${1##*/}
}


AddToDate() {

   local Date=
   local days=
   local Month=`GetMonth $Date`
   local Day=`echo $Date | col / 2`   # Day of Date
   local Year=`echo $Date | col / 3`  # Year of Date
   local LeapYear=`IsLeapYear $Year`

   if [ $LeapYear = "yes" ]; then
      let Months[1]++
   fi
   Day=$[Day+days]
   while [ $Day -gt ${Months[$Month-1]} ]; do
       Day=$[Day -  ${Months[$Month-1]}]
       let Month++
   done
   echo "$Month/$Day/$Year"
}

# Convert a date to normal form
NormalizeDate () {
   Date=`echo "$*" | sed 'sX  *X/Xg'`
   local Day=`date +%d`
   local Month=`date +%m`
   local Year=`date +%Y`
   #echo Normalizing Date=$Date > /dev/tty
   case $Date in
      */*/* )
         Month=`echo $Date | col / 1 `
         Month=`Month2Int $Month`
         Day=`echo $Date | col / 2`
         Year=`echo $Date | col / 3`
         ;;
      */* )
         Month=`echo $Date | col / 1 `
         Month=`Month2Int $Month`
         Day=1
         Year=`echo $Date | col / 2 `
         ;;
      [a-z]* ) # Better be a month or day of week
         Exp=${Date:0:3}
         case $Exp in
            jan|feb|mar|apr|may|june|jul|aug|sep|oct|nov|dec )
               Month=$Exp
               Month=`Month2Int $Month`
               Day=1
               #Year stays the same
               ;;
            mon|tue|wed|thu|fri|sat|sun )
               # Compute the next such day
               local DayOfWeek=`date +%u`
               D=`Day2Int $Exp`
               if [ $DayOfWeek -le $D ]; then
                  Date=`AddToDate $Month/$Day/$Year $[D-DayOfWeek]`
               else
                  Date=`AddToDate $Month/$Day/$Year $[7+D-DayOfWeek]`
               fi

               # Reset Month/Day/Year
               Month=`echo $Date | col / 1 `
               Day=`echo $Date | col / 2`
               Year=`echo $Date | col / 3`
               ;;
            * ) echo "$Exp is not a valid month or day"
                exit
               ;;
            esac
         ;;
      * ) echo "$Date is not a valid date"
          exit
         ;;
   esac
   case $Day in
      [0-9]* );;  # Day must be numeric
      * ) echo "$Date is not a valid date"
          exit
         ;;
   esac
      [0-9][0-9][0-9][0-9] );;  # Year must be 4 digits
      [0-9][0-9] )
          Year=20$Year
      ;;
   esac
   Date=$Month/$Day/$Year
   echo $Date
}
# NormalizeDate jan
# NormalizeDate january
# NormalizeDate jan 2009
# NormalizeDate jan 22 1983
# NormalizeDate 1/22
# NormalizeDate 1 22
# NormalizeDate sat
# NormalizeDate sun
# NormalizeDate mon

ComputeExtension () {

   local Date=; shift
   local Month=`GetMonth $Date`
   local Day=`echo $Date | col / 2`
   local Year=`echo $Date | col / 3`
   local ExtensionExp="$*"
   case $ExtensionExp in
      *w*d* )  # like 5 weeks 3 days or even 5w2d
            ExtensionExp=`echo $ExtensionExp | sed 's/[a-z]/ /g'`
            weeks=`echo $ExtensionExp | col  1`
            days=`echo $ExtensionExp | col 2`
            days=$[7*weeks+days]
            Due=`AddToDate $Month/$Day/$Year $days`
      ;;
      *d )    # Like 5 days or 5d
            ExtensionExp=`echo $ExtensionExp | sed 's/[a-z]/ /g'`
            days=$ExtensionExp
            Due=`AddToDate $Month/$Day/$Year $days`
      ;;
      * )
            Due=$ExtensionExp
      ;;
   esac
   echo $Due

}


# Pop -- remove the first element from an array and shift left
Pop () {
   Var=
   eval "unset $Var[0]"
   eval "$Var=(${$Var[*]})"
}

ComputeDate () {
   local Date=`NormalizeDate `; shift
   local Expression=`echo $* | sed 's/^ *a /1 /;s/,/ /' | tr A-Z a-z `
   local Exp=(`echo $Expression `)
   local Token=$Exp  # first one
   local Ans=
   #echo "Computing date for ${Exp[*]}" > /dev/tty
   case $Token in
      */* ) # Regular date
         M=`GetMonth $Token`
         D=`GetDay $Token`
         Y=`GetYear $Token`
         if [ -z "$Y" ]; then
            Y=$Year
         elif [ ${#Y} -eq 2 ]; then
            Y=20$Y
         fi
         Ans="$M/$D/$Y"
         ;;
      yes* )
         Ans=`AddToDate $Date -1`
         ;;
      tod*|now )
         Ans=$Date
         ;;
      tom* )
         Ans=`AddToDate $Date 1`
         ;;
      the )
         case $Expression in
            *day*after* )  #the day after Date
               Pop Exp;   # Skip the
               Pop Exp;   # Skip day
               Pop Exp;   # Skip after
               #echo Calling ComputeDate $Date ${Exp[*]} > /dev/tty
               Date=`ComputeDate $Date ${Exp[*]}` #Recursive call
               #echo "New date is " $Date > /dev/tty
               Ans=`AddToDate $Date 1`
               ;;
            *last*day*of*th*month|*end*of*th*month )
               M=`date +%m`
               Day=${Months[M-1]}
               if [ $M -eq 2 -a `IsLeapYear $Year` = yes ]; then
                  let Day++
               fi
               Ans=$Month/$Day/$Year
               ;;
            *last*day*of* )
               D=${Expression##*of }
               D=`NormalizeDate $D`
               M=`GetMonth $D`
               Y=`GetYear $D`
               # echo M is $M > /dev/tty
               Day=${Months[M-1]}
               if [ $M -eq 2 -a `IsLeapYear $Y` = yes ]; then
                  let Day++
               fi
               Ans=$[M]/$Day/$Y
               ;;
            * )
               echo "Unknown expression: " $Expression
               exit
               ;;
         esac
         ;;
      next* ) # next DayOfWeek
         Pop Exp
         dow=`Day2Int $DayOfWeek` # First 3 chars
         tdow=`Day2Int ${Exp:0:3}` # First 3 chars
         n=$[7-dow+tdow]
         Ans=`AddToDate $Date $n`
         ;;
      this* )
         Pop Exp
         dow=`Day2Int $DayOfWeek`
         tdow=`Day2Int ${Exp:0:3}` # First 3 chars
         if [ $dow -gt $tdow ]; then
            echo "'this $Exp' has passed.  Did you mean 'next $Exp?'"
            exit
         fi
         n=$[tdow-dow]
         Ans=`AddToDate $Date $n`
         ;;
      [a-z]* ) # DayOfWeek ...

         M=${Exp:0:3}
         case $M in
            jan|feb|mar|apr|may|june|jul|aug|sep|oct|nov|dec )
               ND=`NormalizeDate ${Exp[*]}`
               Ans=$ND
               ;;
            mon|tue|wed|thu|fri|sat|sun )
               dow=`Day2Int $DayOfWeek`
               Ans=`NormalizeDate $Exp`

               if [ ${#Exp[*]} -gt 1 ]; then # Just a DayOfWeek
                  #tdow=`GetDay $Exp` # First 3 chars
                  #if [ $dow -gt $tdow ]; then
                     #echo "'this $Exp' has passed.  Did you mean 'next $Exp'?"
                     #exit
                  #fi
                  #n=$[tdow-dow]
               #else  # DayOfWeek in a future week
                  Pop Exp  # toss monday
                  Pop Exp  # toss in/off
                  if [ $Exp = next ]; then
                     Exp=2
                  fi
                  n=$[7*(Exp-1)]   # number of weeks
                  n=$[n+7-dow+tdow]
                  Ans=`AddToDate $Date $n`
               fi
               ;;
         esac
         ;;
      [0-9]* ) # Number  weeks [from|after] Date
         n=$Exp
         Pop Exp;
         case $Exp in
            w* ) let n=7*n;;
         esac

         Pop Exp; Pop Exp
         #echo Calling ComputeDate $Date ${Exp[*]} > /dev/tty
         Date=`ComputeDate $Date ${Exp[*]}` #Recursive call
         #echo "New date is " $Date > /dev/tty
         Ans=`AddToDate $Date $n`
         ;;
   esac
   echo $Ans
}

Year=`date +%Y`
Month=`date +%m`
Day=`date +%d`
DayOfWeek=`date +%a |tr A-Z a-z`

Date="$Month/$Day/$Year"
ComputeDate $Date $*

ce script utilise largement un autre script que j'ai écrit (appelé col ... de nombreuses excuses à ceux qui utilisent la norme col fourni avec Linux). Cette version de col simplifie l'extraction des colonnes du stdin. Ainsi,

$ echo a b c d e | col 5 3 2

imprime

e c b

Ici col script:

#!/bin/sh
# col -- extract columns from a file
# Usage:
#    col [-r] [c] col-1 col-2 ...
#   where [c] if supplied defines the field separator
#   where each col-i represents a column interpreted according to  the presence of -r as follows:
#        -r present : counting starts from the right end of the line
#        -r absent  : counting starts from the left side of the line
Separator=" "
Reverse=false
case "" in
 -r )  Reverse=true; shift;
 ;;
 [0-9]* )
 ;;
 * )Separator=""; shift;
 ;;
esac

case "" in
 -r )  Reverse=true; shift;
 ;;
 [0-9]* )
 ;;
 * )Separator=""; shift;
 ;;
esac

#  Replace each col-i with $i
Cols=""
for  f in $*
do
  if [ $Reverse = true ]; then
     Cols="$Cols $(NF-$f+1),"
  else
     Cols="$Cols $$f,"
  fi

done

Cols=`echo "$Cols" | sed 's/,$//'`
#echo "Using column specifications of $Cols"
awk -F "$Separator"  "{print $Cols}"

Il utilise aussi printdirections pour imprimer les directions quand le script est appelé mal:

#!/bin/sh
#
#  printdirections -- print header lines of a shell script
#
#  Usage:
#      printdirections path
#  where
#      path is a *full* path to the shell script in question
#      beginning with '/'
#
#  To use printdirections, you must include (as comments at the top
#  of your shell script) documentation for running the shell script.

if [ $# -eq 0 -o "$*" = "-h" ]; then
   printdirections 
   exit
fi
#  Delete the command invocation at the top of the file, if any
#  Delete from the place where printdirections occurs to the end of the file
#  Remove the # comments
#  There is a bizarre oddity here.
   sed '/#!/d;/.*printdirections/,$d;/ *#/!d;s/# //;s/#//'  > /tmp/printdirections.$$

#  Count the number of lines
numlines=`wc -l /tmp/printdirections.$$ | awk '{print }'`

#  Remove the last   line
numlines=`expr $numlines - 1`


head -n $numlines /tmp/printdirections.$$
rm /tmp/printdirections.$$

Pour utiliser ce lieu les trois scripts dans les fichiers ComputeDate, col et printdirections, respectivement. Placez le fichier dans le répertoire nommé par votre chemin, généralement ~/bin. Rendre exécutable avec:

$ chmod a+x ComputeDate col printdirections

Problèmes? Envoyez-moi un e-mail: morell AT cs.atu.edu Place ComputeDate dans le sujet.

15
répondu Larry Morell 2010-06-26 19:04:41

en Supposant que vous avez GNU date, comme ceci:

date --date='1 days ago' '+%a'

Et expressions similaires.

66
répondu abyx 2015-02-11 22:14:15

voici un moyen facile pour faire des calculs de date dans les scripts shell.

meetingDate='12/31/2011' # MM/DD/YYYY Format
reminderDate=`date --date=$meetingDate'-1 day' +'%m/%d/%Y'`
echo $reminderDate

vous trouverez ci-dessous d'autres variations du calcul de la date qui peuvent être obtenues en utilisant date utilitaire. http://www.cyberciti.biz/tips/linux-unix-get-yesterdays-tomorrows-date.html http://www.cyberciti.biz/faq/linux-unix-formatting-dates-for-display/

16
répondu Harun Prasad 2012-05-31 13:36:22

pour la compatibilité BSD / OS X, vous pouvez également utiliser l'utilitaire date avec -j et -v pour la date de mathématiques. Voir le page de Manuel FreeBSD pour la date. Vous pouvez combiner les réponses Linux précédentes avec cette réponse qui pourrait vous fournir une compatibilité suffisante.

Sur BSD, Linux, running date vous donnera la date actuelle:

$ date
Wed 12 Nov 2014 13:36:00 AEDT

maintenant avec la date de BSD Vous pouvez faire des maths avec -v, par exemple d'inscription la date du lendemain (+1d plus un jour):

$ date -v +1d
Thu 13 Nov 2014 13:36:34 AEDT

vous pouvez utiliser une date existante comme base, et éventuellement spécifier le format parse en utilisant strftime, et assurez-vous d'utiliser -j si vous ne changez pas votre date de système:

$ date -j -f "%a %b %d %H:%M:%S %Y %z" "Sat Aug 09 13:37:14 2014 +1100"
Sat  9 Aug 2014 12:37:14 AEST

Et vous pouvez l'utiliser comme la base de calculs de date:

$ date -v +1d -f "%a %b %d %H:%M:%S %Y %z" "Sat Aug 09 13:37:14 2014 +1100"
Sun 10 Aug 2014 12:37:14 AEST

Notez que -v implique -j.

plusieurs réglages peuvent être fournis de façon séquentielle:

$ date -v +1m -v -1w
Fri  5 Dec 2014 13:40:07 AEDT

voir la page de manuel pour plus de détails.

7
répondu sj26 2014-11-12 02:45:14

pour faire de l'arithmétique avec des dates sur UNIX vous obtenez la date comme le nombre de secondes depuis L'époque UNIX, faire quelques calculs, puis convertir de nouveau à votre format de date imprimable. La commande date devrait être capable à la fois de vous donner les secondes depuis l'époque et de convertir de ce nombre de nouveau à une date imprimable. Ma commande de date locale fait ceci,

% date -n
1219371462
% date 1219371462
Thu Aug 21 22:17:42 EDT 2008
% 

voir votre local date(1) page de man. Pour augmenter un jour ajoute 86400 secondes.

6
répondu caerwyn 2009-11-23 23:48:07

pourquoi ne pas écrire vos scripts en utilisant un langage comme perl ou python qui supporte plus naturellement le traitement de date complexe? Assurez-vous de faites tout en bash, mais je pense que vous obtiendrez aussi plus de cohérence entre les plates-formes utilisant python par exemple, tant que vous pouvez vous assurer que perl ou python est installé.

je dois ajouter qu'il est assez facile de transférer les scripts Python et perl dans un script shell contenant.

4
répondu Justin Standard 2008-08-08 23:48:23

je suis tombé là-dessus quelques fois. Mes pensées sont:

  1. l'arithmétique des dates est toujours pénible
  2. C'est un peu plus facile lors de l'utilisation d'ÉPOQUE format de la date
  3. date sur Linux convertit à l'ÉPOQUE, mais pas sur Solaris
  4. Pour une solution portable, vous devez effectuer l'une des opérations suivantes:
    1. installer gnu date sur solaris (déjà mentionné, nécessite une interaction humaine pour compléter)
    2. utiliser perl pour la partie date (plus unix installe include perl, donc je suppose généralement que cette action fait nécessitent un travail supplémentaire).

un exemple de script (vérifie l'âge de certains fichiers utilisateurs pour voir si le compte peut être supprimé):

#!/usr/local/bin/perl

$today = time();

$user = $ARGV[0];

$command="awk -F: '/$user/ {print $6}' /etc/passwd";

chomp ($user_dir = `$command`);

if ( -f "$user_dir/.sh_history" ) {
    @file_dates   = stat("$user_dir/.sh_history");
    $sh_file_date = $file_dates[8];
} else {
    $sh_file_date = 0;
}
if ( -f "$user_dir/.bash_history" ) {
    @file_dates     = stat("$user_dir/.bash_history");
    $bash_file_date = $file_dates[8];
} else {
    $bash_file_date = 0;
}
if ( $sh_file_date > $bash_file_date ) {
    $file_date = $sh_file_date;
} else {
    $file_date = $bash_file_date;
}
$difference = $today - $file_date;

if ( $difference >= 3888000 ) {
    print "User needs to be disabled, 45 days old or older!\n";
    exit (1);
} else {
    print "OK\n";
    exit (0);
}
4
répondu Jonathan Bourke 2008-09-16 09:34:20
date --date='1 days ago' '+%a'

ce n'est pas une solution très compatible. Il ne fonctionne que sous Linux. Au moins, ça n'a pas marché à Aix et Solaris.

Cela fonctionne dans RHEL:

date --date='1 days ago' '+%Y%m%d'
20080807
4
répondu ggasp 2014-09-05 04:10:47

en y regardant de plus près, je pense que vous pouvez simplement utiliser la date. J'ai essayé ce qui suit sur OpenBSD: j'ai pris la date de février. 29 2008 et aléatoire d'une heure (sous la forme de 080229301535) et ajouté +1 pour la partie jour, comme suit:

$ date -j 0802301535
Sat Mar  1 15:35:00 EST 2008

comme vous pouvez le voir, date formaté l'heure correctement...

HTH

3
répondu abyx 2008-08-09 12:37:19

si vous voulez continuer avec awk, alors les fonctions mktime et strftime sont utiles:


BEGIN { dateinit }
      { newdate=daysadd(OldDate,DaysToAdd)}

 # daynum: convert DD-MON-YYYY to day count
 #-----------------------------------------
function daynum(date,  d,m,y,i,n)
{
     y=substr(date,8,4)
     m=gmonths[toupper(substr(date,4,3))]
     d=substr(date,1,2)
     return mktime(y" "m" "d" 12 00 00")
}

 #numday: convert day count to DD-MON-YYYY
 #-------------------------------------------
function numday(n,  y,m,d)
{
    m=toupper(substr(strftime("%B",n),1,3))
    return strftime("%d-"m"-%Y",n)
}

 # daysadd: add (or subtract) days from date (DD-MON-YYYY), return new date (DD-MON-YYYY)
 #------------------------------------------
function daysadd(date, days)
{
    return numday(daynum(date)+(days*86400))
}

 #init variables for date calcs
 #-----------------------------------------
function dateinit(   x,y,z)
{
     # Stuff for date calcs
     split("JAN:1,FEB:2,MAR:3,APR:4,MAY:5,JUN:6,JUL:7,AUG:8,SEP:9,OCT:10,NOV:11,DEC:12", z)
     for (x in z)
     {
        split(z[x],y,":")
        gmonths[y[1]]=y[2]
     }
}
3
répondu Joe Watkins 2008-09-16 12:43:15

Le livre "Script Shell Recettes: Une Solution du Problème de l'Approche" (ISBN: 978-1-59059-471-1) par Chris F. A. Johnson a une date de bibliothèque de fonctions qui pourraient être utiles. Le code source est disponible à http://apress.com/book/downloadfile/2146 (les fonctions de date sont dans le Chapitre08/data-funcs-sh dans le fichier tar).

3
répondu joast 2008-09-16 20:17:13

si la version GNU de date fonctionne pour vous, pourquoi ne pas saisir la source et la compiler sur AIX et Solaris?

http://www.gnu.org/software/coreutils/

dans tous les cas, la source devrait vous aider à obtenir la date arithmétique correcte Si vous allez écrire votre propre code.

à part, des commentaires comme "cette solution est bonne, mais vous pouvez sûrement noter qu'elle n'est pas aussi bonne que possible. Il semble que personne n'a pensé à bricoler avec des dates la construction d'Unix."ne pas vraiment nous. J'ai trouvé chacune des suggestions jusqu'à présent très utiles et conformes aux objectifs.

0
répondu Jon Ericson 2008-08-15 22:13:52

Voici mes deux centimes de valeur - un script faisant usage de date et grep.

Exemple D'Utilisation

> sh ./datecalc.sh "2012-08-04 19:43:00" + 1s
2012-08-04 19:43:00 + 0d0h0m1s
2012-08-04 19:43:01

> sh ./datecalc.sh "2012-08-04 19:43:00" - 1s1m1h1d
2012-08-04 19:43:00 - 1d1h1m1s
2012-08-03 18:41:59

> sh ./datecalc.sh "2012-08-04 19:43:00" - 1d2d1h2h1m2m1s2sblahblah
2012-08-04 19:43:00 - 1d1h1m1s
2012-08-03 18:41:59

> sh ./datecalc.sh "2012-08-04 19:43:00" x 1d
Bad operator :-(

> sh ./datecalc.sh "2012-08-04 19:43:00"
Missing arguments :-(

> sh ./datecalc.sh gibberish + 1h
date: invalid date `gibberish'
Invalid date :-(

Script

#!/bin/sh

# Usage:
#
# datecalc "<date>" <operator> <period>
#
# <date> ::= see "man date", section "DATE STRING"
# <operator> ::= + | -
# <period> ::= INTEGER<unit> | INTEGER<unit><period>
# <unit> ::= s | m | h | d

if [ $# -lt 3 ]; then
echo "Missing arguments :-("
exit; fi

date=`eval "date -d \"\" +%s"`
if [ -z $date ]; then
echo "Invalid date :-("
exit; fi

if ! ([  == "-" ] || [  == "+" ]); then
echo "Bad operator :-("
exit; fi
op=

minute=$[60]
hour=$[$minute*$minute]
day=$[24*$hour]

s=`echo  | grep -oe '[0-9]*s' | grep -m 1 -oe '[0-9]*'`
m=`echo  | grep -oe '[0-9]*m' | grep -m 1 -oe '[0-9]*'`
h=`echo  | grep -oe '[0-9]*h' | grep -m 1 -oe '[0-9]*'`
d=`echo  | grep -oe '[0-9]*d' | grep -m 1 -oe '[0-9]*'`
if [ -z $s ]; then s=0; fi
if [ -z $m ]; then m=0; fi
if [ -z $h ]; then h=0; fi
if [ -z $d ]; then d=0; fi

ms=$[$m*$minute]
hs=$[$h*$hour]
ds=$[$d*$day]

sum=$[$s+$ms+$hs+$ds]
out=$[$date$op$sum]
formattedout=`eval "date -d @$out +\"%Y-%m-%d %H:%M:%S\""`

echo   $d"d"$h"h"$m"m"$s"s"
echo $formattedout
0
répondu Johnny Baloney 2012-05-03 14:56:26

Cela fonctionne pour moi:

TZ=GMT+6;
export TZ
mes=`date --date='2 days ago' '+%m'`
dia=`date --date='2 days ago' '+%d'`
anio=`date --date='2 days ago' '+%Y'`
hora=`date --date='2 days ago' '+%H'`
-1
répondu Tulio Galdamez 2016-07-13 22:22:28