2008-08-08 28 views
17

Necesito hacer una aritmética de fecha en las secuencias de comandos de shell de Unix que utilizo para controlar la ejecución de programas de terceros.Aritmética de fecha en scripts de shell Unix

estoy usando una función para incrementar un día y otro para disminuir:

IncrementaDia(){ 
echo $1 | 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,1,4) 
     mes=substr($1,5,2) 
     dia=substr($1,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=$1 
else 
     today=$(date +"%Y%m%d") 
     tomorrow=$(IncrementaDia $hoy) 
fi 

pero ahora tengo que hacer operaciones aritméticas más complejas.

¿Cuál es la mejor y más compatible manera de hacer esto?

Respuesta

15

He escrito un script bash para convertir las fechas expresadas en inglés a las fechas convencionales mm/dd/aaaa. Se llama ComputeDate.

Aquí hay algunos ejemplos de su uso. Para abreviar, he colocado la salida de cada invocación en la misma línea que la invocación, separada por dos puntos (:). Las cotizaciones se muestran a continuación son no necesaria cuando se ejecuta 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 

He incluido este script como una respuesta a este problema, ya que ilustra cómo a hacer aritmética de fechas a través de un conjunto de funciones de bash y éstos las funciones pueden resultar útiles para otros. Se ocupa de los años bisiestos y siglos bisiestos correctamente:

#! /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 $0 
    exit 
fi 



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

IsLeapYear() { 
    local Year=$1 
    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 $1 in 
     -e) 
     ErrorFlag=-e; shift 
     ;; 
    esac 
    local dow=$1 
    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 $1 in 
     -e) 
     ErrorFlag=-e; shift 
     ;; 
    esac 
    M=$1 
    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 $1 | col/2 
} 

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


AddToDate() { 

    local Date=$1 
    local days=$2 
    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=$1; 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=$1 
    eval "unset $Var[0]" 
    eval "$Var=(\${$Var[*]})" 
} 

ComputeDate() { 
    local Date=`NormalizeDate $1`; 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 $* 

Este script hace un amplio uso de otro guión que escribí (llamado col ... muchas disculpas a aquellos que utilizan el estándar col suministrado con Linux).Esta versión de col simplifica la extracción de columnas del stdin. Por lo tanto,

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

impresiones

e c b 

Aquí la col guión:

#!/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 "$1" in 
-r) Reverse=true; shift; 
;; 
[0-9]*) 
;; 
*)Separator="$1"; shift; 
;; 
esac 

case "$1" in 
-r) Reverse=true; shift; 
;; 
[0-9]*) 
;; 
*)Separator="$1"; 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}" 

También utiliza printdirections para imprimir las direcciones cuando el script se invoca indebidamente:

#!/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 $0 
    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/#//' $1 > /tmp/printdirections.$$ 

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

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


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

Para utilizar este lugar los tres guiones en los archivos ComputeDate, col, y printdirections, respectivamente. Coloque el archivo en el directorio nombrado por su RUTA, generalmente, ~/bin. A continuación, hágalos ejecutables con:

$ chmod a+x ComputeDate col printdirections 

¿Problemas? Envíeme un correo electrónico: morell AT cs.atu.edu Lugar ComputeDate en el asunto.

+0

Vea mi respuesta para la solución más simple –

64

Asumiendo que tiene GNU date, así:

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

Y similar phrases.

+2

querían dar un doble arriba !! :-) desperdicia tanto tiempo en esto! :-(gracias a ti! :-) –

+1

¿En qué Unixes (Unices?) funciona esto? – Johnsyweb

+2

Parece que funciona en Linux. No me sorprendería si fuera una extensión de GNU. Incluso funciona para compensar cantidades arbitrarias de fechas arbitrarias, por ejemplo, "fecha - fecha = '2012-01-27 - 30 días'" –

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

No es una solución muy compatible. Funcionará solo en Linux. Al menos, no funcionó en Aix y Solaris.

Funciona en RHEL:

date --date='1 days ago' '+%Y%m%d' 
20080807 
4

Por qué no escribir secuencias de comandos utilizando un lenguaje como Perl o Python en lugar de los que más naturalmente compatible con el procesamiento fecha complejo? Claro que puede hacerlo todo en bash, pero creo que también obtendrá una mayor coherencia en todas las plataformas que usen python, por ejemplo, siempre que pueda asegurarse de que Perl o Python estén instalados.

Debo añadir que es bastante fácil de conectar en scripts de Python y Perl en un script de shell contenedor.

+0

Estoy de acuerdo con Justin y recomiendo usar una herramienta/lenguaje mejor para su secuencia de comandos. Probablemente vaya con Perl, ya que está instalado en la mayoría de los sistemas Unix listos para usar. Python, aunque es más moderno, requiere que lo instales primero, lo que puede ser doloroso o incluso políticamente imposible. –

3

Al analizarlo más a fondo, creo que simplemente puede usar la fecha. He intentado lo siguiente en OpenBSD: Tomé la fecha de Feb. de 2008 y 29na una hora al azar (en forma de 080229301535) y se añade 1 a la parte del día, así:

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

Como se puede ver, fecha formateada la hora correctamente ...

HTH

0

Si la versión GNU de fecha funciona para usted, ¿por qué no agarrar la fuente y compilarlo en AIX y Solaris?

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

En cualquier caso, la fuente debe ayudarle a obtener la aritmética fecha correcta si se va a escribir código es el propietario.

Como comentario adicional, "esa solución es buena, pero seguramente se nota que no es tan buena como se puede. Parece que a nadie se le ocurrió retocar las fechas cuando se construyó Unix". no nos lleves a ningún lado. Encontré que cada una de las sugerencias hasta ahora es muy útil y objetivo.

6

Para hacer operaciones aritméticas con fechas en UNIX, obtienes la fecha como el número de segundos desde la época de UNIX, haz algunos cálculos y luego vuelve a convertir a tu formato de fecha imprimible. El comando de fecha debería poder darle los segundos desde la época y convertir desde ese número a una fecha imprimible.Mi fecha de mando local hace esto,

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

Ver su página local date(1) hombre. Para incrementar un día, agregue 86400 segundos.

4

Me he topado con esto un par de veces. Mis pensamientos son:

  1. Fecha aritmética es siempre un dolor
  2. Es un poco más fácil cuando se utiliza el formato de fecha EPOCH
  3. fecha en Linux se convierte en EPOCH, pero no en Solaris
  4. Para una solución portátil , que tiene que hacer uno de los siguientes:
    1. fecha de instalación de GNU en Solaris (ya mencionado, necesita la interacción del para completar)
    2. uso de Perl para la parte de fecha (más UNIX instala incluir Perl, por lo general, yo asumiría que esta acción no requieren trabajo adicional).

Un script de ejemplo (comprueba la edad de ciertos archivos de usuario para ver si la cuenta se puede eliminar):

#!/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); 
} 
3

Si desea continuar con awk, entonces el mktime y strftime funciones son útiles:


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] 
    } 
} 
+0

pero, como acabo de descubrir, mktime es boquiabierto, y no está allí en solaris awk/nawk :( –

3

El libro "shell script de Recetas: un enfoque de solución de problemas" (ISBN: 978-1-59059-471-1) por Chris Johnson tiene una FA funciones de fecha biblioteca que podría ser útil. El código fuente está disponible en http://apress.com/book/downloadfile/2146 (las funciones de fecha están en el Capítulo08/data-funcs-sh dentro del archivo tar).

16

Aquí hay una manera fácil de hacer cómputos de fecha en scripts de shell.

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

A continuación se presentan más variaciones de fecha de computación que se pueden lograr utilizando date utilidad. http://www.cyberciti.biz/tips/linux-unix-get-yesterdays-tomorrows-date.html http://www.cyberciti.biz/faq/linux-unix-formatting-dates-for-display/

Esto funcionó para mí en RHEL.

+0

+1 Esto es lo que necesitaba para hacer aritmética. Muy fácil y sencillo. Corregí el orden m/d/Y. –

0

Estos son mis dos centavos que vale la pena: una envoltura de script que hace uso de date y grep.

Ejemplo de Uso

> 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 :-(

Guión

#!/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 \"$1\" +%s"` 
if [ -z $date ]; then 
echo "Invalid date :-(" 
exit; fi 

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

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

s=`echo $3 | grep -oe '[0-9]*s' | grep -m 1 -oe '[0-9]*'` 
m=`echo $3 | grep -oe '[0-9]*m' | grep -m 1 -oe '[0-9]*'` 
h=`echo $3 | grep -oe '[0-9]*h' | grep -m 1 -oe '[0-9]*'` 
d=`echo $3 | 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 $1 $2 $d"d"$h"h"$m"m"$s"s" 
echo $formattedout 
5

Para compatibilidad BSD/OS X, también puede utilizar la utilidad de la fecha con -j y -v que ver la fecha de matemáticas. Vea el FreeBSD manpage for date. Podría combinar las respuestas de Linux anteriores con esta respuesta que podría proporcionarle compatibilidad suficiente.

en BSD, como Linux, corriendo date le dará la fecha actual:

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

Ahora, con fecha de BSD que puede hacer matemáticas con -v, por ejemplo lista la fecha de mañana (+1d es más un día):

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

usted puede utilizar una fecha existente como la base, y opcionalmente especificar el formato de análisis mediante strftime, y asegúrese de usar -j para que no cambie la fecha del sistema:

$ 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 

y se puede utilizar esto como la base de cálculos de fechas:

$ 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 

Tenga en cuenta que -v implica -j.

ajustes múltiples se pueden proporcionar de forma secuencial:

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

Consulte la página de manual para más detalles.

+0

¡Gracias por la magia! – Yoga

-1

Esto funciona para mí:

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'` 
Cuestiones relacionadas