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?
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.
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/
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.
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.
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.
je suis tombé là-dessus quelques fois. Mes pensées sont:
- l'arithmétique des dates est toujours pénible
- C'est un peu plus facile lors de l'utilisation d'ÉPOQUE format de la date
- date sur Linux convertit à l'ÉPOQUE, mais pas sur Solaris
- Pour une solution portable, vous devez effectuer l'une des opérations suivantes:
- installer gnu date sur solaris (déjà mentionné, nécessite une interaction humaine pour compléter)
- 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);
}
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
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
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]
}
}
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).
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.
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
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'`