Date arithmetic in Unix shell scripts - unix

I need to do date arithmetic in Unix shell scripts that I use to control the execution of third party programs.
I'm using a function to increment a day and another to decrement:
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
but now I need to do more complex arithmetic.
What it's the best and more compatible way to do this?

Assuming you have GNU date, like so:
date --date='1 days ago' '+%a'
And similar phrases.

Here is an easy way for doing date computations in shell scripting.
meetingDate='12/31/2011' # MM/DD/YYYY Format
reminderDate=`date --date=$meetingDate'-1 day' +'%m/%d/%Y'`
echo $reminderDate
Below are more variations of date computation that can be achieved using date utility.
http://www.cyberciti.biz/tips/linux-unix-get-yesterdays-tomorrows-date.html
http://www.cyberciti.biz/faq/linux-unix-formatting-dates-for-display/
This worked for me on RHEL.

I have written a bash script for converting dates expressed in English into conventional
mm/dd/yyyy dates. It is called ComputeDate.
Here are some examples of its use. For brevity I have placed the output of each invocation
on the same line as the invocation, separarted by a colon (:). The quotes shown below are not necessary when running 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
I have included this script as an answer to this problem because it illustrates how
to do date arithmetic via a set of bash functions and these functions may prove useful
for others. It handles leap years and leap centuries correctly:
#! /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 $*
This script makes extensive use of another script I wrote (called col ... many apologies to those who use the standard col supplied with Linux). This version of
col simplifies extracting columns from the stdin. Thus,
$ echo a b c d e | col 5 3 2
prints
e c b
Here it the 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 "$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}"
It also uses printdirections for printing out directions when the script is invoked improperly:
#!/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.$$
To use this place the three scripts in the files ComputeDate, col, and printdirections, respectively. Place the file in directory named by your PATH, typically, ~/bin. Then make them executable with:
$ chmod a+x ComputeDate col printdirections
Problems? Send me some emaiL: morell AT cs.atu.edu Place ComputeDate in the subject.

For BSD / OS X compatibility, you can also use the date utility with -j and -v to do date math. See the FreeBSD manpage for date. You could combine the previous Linux answers with this answer which might provide you with sufficient compatibility.
On BSD, as Linux, running date will give you the current date:
$ date
Wed 12 Nov 2014 13:36:00 AEDT
Now with BSD's date you can do math with -v, for example listing tomorrow's date (+1d is plus one day):
$ date -v +1d
Thu 13 Nov 2014 13:36:34 AEDT
You can use an existing date as the base, and optionally specify the parse format using strftime, and make sure you use -j so you don't change your system date:
$ 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
And you can use this as the base of date calculations:
$ 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
Note that -v implies -j.
Multiple adjustments can be provided sequentially:
$ date -v +1m -v -1w
Fri 5 Dec 2014 13:40:07 AEDT
See the manpage for more details.

To do arithmetic with dates on UNIX you get the date as the number seconds since the UNIX epoch, do some calculation, then convert back to your printable date format. The date command should be able to both give you the seconds since the epoch and convert from that number back to a printable date. My local date command does this,
% date -n
1219371462
% date 1219371462
Thu Aug 21 22:17:42 EDT 2008
%
See your local date(1) man page.
To increment a day add 86400 seconds.

Why not write your scripts using a language like perl or python instead which more naturally supports complex date processing? Sure you can do it all in bash, but I think you will also get more consistency across platforms using python for example, so long as you can ensure that perl or python is installed.
I should add that it is quite easy to wire in python and perl scripts into a containing shell script.

date --date='1 days ago' '+%a'
It's not a very compatible solution. It will work only in Linux. At least, it didn't worked in Aix and Solaris.
It works in RHEL:
date --date='1 days ago' '+%Y%m%d'
20080807

I have bumped into this a couple of times. My thoughts are:
Date arithmetic is always a pain
It is a bit easier when using EPOCH date format
date on Linux converts to EPOCH, but not on Solaris
For a portable solution, you need to do one of the following:
Install gnu date on solaris (already
mentioned, needs human interaction
to complete)
Use perl for the date part (most unix installs include
perl, so I would generally assume
that this action does not
require additional work).
A sample script (checks for the age of certain user files to see if the account can be deleted):
#!/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);
}

Looking into it further, I think you can simply use date.
I've tried the following on OpenBSD: I took the date of Feb. 29th 2008 and a random hour (in the form of 080229301535) and added +1 to the day part, like so:
$ date -j 0802301535
Sat Mar 1 15:35:00 EST 2008
As you can see, date formatted the time correctly...
HTH

If you want to continue with awk, then the mktime and strftime functions are useful:
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]
}
}

The book "Shell Script Recipes: A Problem Solution Approach" (ISBN: 978-1-59059-471-1) by Chris F.A. Johnson has a date functions library that might be helpful. The source code is available at http://apress.com/book/downloadfile/2146 (the date functions are in Chapter08/data-funcs-sh within the tar file).

If the GNU version of date works for you, why don't you grab the source and compile it on AIX and Solaris?
http://www.gnu.org/software/coreutils/
In any case, the source ought to help you get the date arithmetic correct if you are going to write you own code.
As an aside, comments like "that solution is good but surely you can note it's not as good as can be. It seems nobody thought of tinkering with dates when constructing Unix." don't really get us anywhere. I found each one of the suggestions so far to be very useful and on target.

Here are my two pennies worth - a script wrapper making use of date and grep.
Example Usage
> 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 \"$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

This works for me:
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'`

Related

File pulling with yesterday's date in UNIX based on file name [duplicate]

I've got a shell script which does the following to store the current day's date in a variable 'dt':
date "+%a %d/%m/%Y" | read dt
echo ${dt}
How would i go about getting yesterdays date into a variable?
Basically what i'm trying to achieve is to use grep to pull all of yesterday's lines from a log file, since each line in the log contains the date in "Mon 01/02/2010" format.
Thanks a lot
dt=$(date --date yesterday "+%a %d/%m/%Y")
echo $dt
On Linux, you can use
date -d "-1 days" +"%a %d/%m/%Y"
You can use GNU date command as shown below
Getting Date In the Past
To get yesterday and earlier day in the past use string day ago:
date --date='yesterday'
date --date='1 day ago'
date --date='10 day ago'
date --date='10 week ago'
date --date='10 month ago'
date --date='10 year ago'
Getting Date In the Future
To get tomorrow and day after tomorrow (tomorrow+N) use day word to get date in the future as follows:
date --date='tomorrow'
date --date='1 day'
date --date='10 day'
date --date='10 week'
date --date='10 month'
date --date='10 year'
If you have Perl available (and your date doesn't have nice features like yesterday), you can use:
pax> date
Thu Aug 18 19:29:49 XYZ 2010
pax> dt=$(perl -e 'use POSIX;print strftime "%d/%m/%Y%",localtime time-86400;')
pax> echo $dt
17/08/2010
If you are on a Mac or BSD or something else without the --date option, you can use:
date -r `expr \`date +%s\` - 86400` '+%a %d/%m/%Y'
Update: or perhaps...
date -r $((`date +%s` - 86400)) '+%a %d/%m/%Y'
I have shell script in Linux and following code worked for me:
#!/bin/bash
yesterday=`TZ=EST+24 date +%Y%m%d` # Yesterday is a variable
mkdir $yesterday # creates a directory with YYYYMMDD format
You have atleast 2 options
Use perl:
perl -e '#T=localtime(time-86400);printf("%02d/%02d/%02d",$T[4]+1,$T[3],$T[5]+1900)'
Install GNU date (it's in the sh_utils package if I remember correctly)
date --date yesterday "+%a %d/%m/%Y" | read dt
echo ${dt}
Not sure if this works, but you might be able to use a negative timezone. If you use a timezone that's 24 hours before your current timezone than you can simply use date.
Try the following method:
dt=`case "$OSTYPE" in darwin*) date -v-1d "+%s"; ;; *) date -d "1 days ago" "+%s"; esac`
echo $dt
It works on both Linux and OSX.
Here is a ksh script to calculate the previous date of the first argument, tested on Solaris 10.
#!/bin/ksh
sep=""
today=$(date '+%Y%m%d')
today=${1:-today}
ty=`echo $today|cut -b1-4` # today year
tm=`echo $today|cut -b5-6` # today month
td=`echo $today|cut -b7-8` # today day
yy=0 # yesterday year
ym=0 # yesterday month
yd=0 # yesterday day
if [ td -gt 1 ];
then
# today is not first of month
let yy=ty # same year
let ym=tm # same month
let yd=td-1 # previous day
else
# today is first of month
if [ tm -gt 1 ];
then
# today is not first of year
let yy=ty # same year
let ym=tm-1 # previous month
if [ ym -eq 1 -o ym -eq 3 -o ym -eq 5 -o ym -eq 7 -o ym -eq 8 -o ym - eq 10 -o ym -eq 12 ];
then
let yd=31
fi
if [ ym -eq 4 -o ym -eq 6 -o ym -eq 9 -o ym -eq 11 ];
then
let yd=30
fi
if [ ym -eq 2 ];
then
# shit... :)
if [ ty%4 -eq 0 ];
then
if [ ty%100 -eq 0 ];
then
if [ ty%400 -eq 0 ];
then
#echo divisible by 4, by 100, by 400
leap=1
else
#echo divisible by 4, by 100, not by 400
leap=0
fi
else
#echo divisible by 4, not by 100
leap=1
fi
else
#echo not divisible by 4
leap=0 # not divisible by four
fi
let yd=28+leap
fi
else
# today is first of year
# yesterday was 31-12-yy
let yy=ty-1 # previous year
let ym=12
let yd=31
fi
fi
printf "%4d${sep}%02d${sep}%02d\n" $yy $ym $yd
Tests
bin$ for date in 20110902 20110901 20110812 20110801 20110301 20100301 20080301 21000301 20000301 20000101 ; do yesterday $date; done
20110901
20110831
20110811
20110731
20110228
20100228
20080229
21000228
20000229
19991231
Thanks for the help everyone, but since i'm on HP-UX (after all: the more you pay, the less features you get...) i've had to resort to perl:
perl -e '#T=localtime(time-86400);printf("%02d/%02d/%04d",$T[3],$T[4]+1,$T[5]+1900)' | read dt
If your HP-UX installation has Tcl installed, you might find it's date arithmetic very readable (unfortunately the Tcl shell does not have a nice "-e" option like perl):
dt=$(echo 'puts [clock format [clock scan yesterday] -format "%a %d/%m/%Y"]' | tclsh)
echo "yesterday was $dt"
This will handle all the daylight savings bother.
If you don't have a version of date that supports --yesterday and you don't want to use perl, you can use this handy ksh script of mine. By default, it returns yesterday's date, but you can feed it a number and it tells you the date that many days in the past. It starts to slow down a bit if you're looking far in the past. 100,000 days ago it was 1/30/1738, though my system took 28 seconds to figure that out.
#! /bin/ksh -p
t=`date +%j`
ago=$1
ago=${ago:=1} # in days
y=`date +%Y`
function build_year {
set -A j X $( for m in 01 02 03 04 05 06 07 08 09 10 11 12
{
cal $m $y | sed -e '1,2d' -e 's/^/ /' -e "s/ \([0-9]\)/ $m\/\1/g"
} )
yeardays=$(( ${#j[*]} - 1 ))
}
build_year
until [ $ago -lt $t ]
do
(( y=y-1 ))
build_year
(( ago = ago - t ))
t=$yeardays
done
print ${j[$(( t - ago ))]}/$y
ksh93:
dt=${ printf "%(%a %d/%m/%Y)T" yesterday; }
or:
dt=$(printf "%(%a %d/%m/%Y)T" yesterday)
The first one runs in the same process, the second one in a subshell.
For Hp-UX only below command worked for me:
TZ=aaa24 date +%Y%m%d
you can use it as :
ydate=`TZ=aaa24 date +%Y%m%d`
echo $ydate
If you have access to python, this is a helper that will get the yyyy-mm-dd date value for any arbitrary n days ago:
function get_n_days_ago {
local days=$1
python -c "import datetime; print (datetime.date.today() - datetime.timedelta(${days})).isoformat()"
}
# today is 2014-08-24
$ get_n_days_ago 1
2014-08-23
$ get_n_days_ago 2
2014-08-22
$var=$TZ;
TZ=$TZ+24;
date;
TZ=$var;
Will get you yesterday in AIX and set back the TZ variable back to original
Though all good answers, unfortunately none of them worked for me. So I had to write something old school. ( I was on a bare minimal Linux OS )
$ date -d #$( echo $(( $(date +%s)-$((60*60*24)) )) )
You can combine this with date's usual formatting. Eg.
$ date -d #$( echo $(( $(date +%s)-$((60*60*24)) )) ) +%Y-%m-%d
Explanation :
Take date input in terms of epoc seconds ( the -d option ), from which you would have subtracted one day equivalent seconds. This will give the date precisely one day back.

Yesterdays and Next date from given date using ksh on AIX

I'm trying to write a script in that need to compute date-1 and date+1 after
reading the Date from variable
echo 2017-09-30 | read YYYY MM DD
how to print next date as 2017-10-01 trying on AIX OS .
This is the piece of code from it
date ()
{
DAY=$D1
TODAY=$D1
DAY1=$D1
echo $DAY1| read YYYY MM DD
let DD=DD+1
DD=`printf %02d $DD`
if [[ $DD -eq 0 ]] then
let MM=MM-1
MM=`printf %02d $MM`
if [[ $MM -eq 0 ]] then
let YYYY=YYYY-1
let MM=12
let DD=31
fi
DD=`cal $MM $YYYY|grep . |fmt -1|tail -1`
fi
NEXTDAY=`echo $YYYY-$MM-$DD`
echo $NEXTDAY
}
Tomorrow:
date --date='tomorrow'
date --date='1 day'
Yesterday:
date --date='yesterday'
date --date='1 day ago'
How to save time/date format to the shell variable?
$ NOW=$(date +"%m-%d-%Y")
To display a variable use echo command:
$ echo $NOW
I answered yesterday at https://stackoverflow.com/a/21283578/3220113
You can have yesterday and tomorrow with the same method, keeping in mind the Daylight Saving Time:
# Tomorrow
echo "$(TZ=GMT-30 date +%Y-%m-%d)
$(TZ=GMT-20 date +%Y-%m-%d)" | grep -v $(date +%Y-%m-%d) | head -1
# Yesterday
echo "$(TZ=GMT+30 date +%Y-%m-%d)
$(TZ=GMT+20 date +%Y-%m-%d)" | grep -v $(date +%Y-%m-%d) | tail -1

How to get the data from logfile if the input and logdata are in different format?

My log file data is
[10/04/16 02:07:20 BST] Data 1
[11/04/16 02:07:20 BST] Data 1
[10/05/16 04:11:09 BST] Data 2
[12/05/16 04:11:09 BST] Data 2
[11/06/16 06:22:35 BST] Data 3
My input format is
./filename Apr 11 16 00:00:00 Jul 10 16 00:00:00
I am converting the input format to logfile format with the following function,
convert_date () {
local months=( Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec )
local i
for (( i=0; i<11; i++ )); do
[[ $1 = ${months[$i]} ]] && break
done
printf "\[%2d\/%02d\/%02d $4 BST\]\n" $2 $(( i+1 )) $3
for (( i=0; i<11; i++ )); do
[[ $5 = ${months[$i]} ]] && break
done
}
And also I am storing the result in variable and using it
Start=$( convert_date $1 $2 $3 $4 )
End=$( convert_date $5 $6 $7 $8 )
But the codes gives me result only if the Stattime and endtime are present in the log file. How can I get the data between the two times even if the start and endtimes are not present in the logfile. What awk script can I use?
Your Bash (assumed) function seems to output date in following format:
$ bash test.sh "Apr 11 16 00:00:00"
\[11\/04\/16 00:00:00 BST\]
Working with that, test.awk:
BEGIN {
FS="[[/: ]+"; # set field separator to all delimiters in datetime format in the date
split(start,arr,"[\\\\[/ :]") # split the start variable in pieces for reorganize
start=arr[4]" "arr[3]" "arr[2]" "arr[5]" "arr[6]" "arr[7] # reorganize
}
start <= $4" "$3" "$2" "$5" "$6" "$7 # compare reorganized data to date in start variable
$ awk -v start="\[11\/04\/16 00:00:00 BST\]" -f test.awk test.in
[11/04/16 02:07:20 BST] Data 1
[10/05/16 04:11:09 BST] Data 2
[12/05/16 04:11:09 BST] Data 2
[11/06/16 06:22:35 BST] Data 3
It complains a bit, though:
awk: warning: escape sequence '\[' treated as plain '['
The input format is in "Input.Format" file and the log file is in "Log.File". This shell script 'cat' both files and pipes to 'awk'. The awk script changes the month format to number an compares to log file to turn on or off a print switch.
#!/bin/sh
cat Input.Format Log.File | awk 'BEGIN {
Month = " JanFebMarAprMayJunJulAugSepOctNovDec"
} {
if (NR == 1) {
startm = index(Month, $2) / 3
if (length(startm) == 1) { startm = "0" startm }
startm = $4 startm $3
endm = index(Month, $6) / 3
if (length(endm) == 1) { endm = "0" endm }
endm = $4 endm $3
# print startm " " endm
}
else {
logdate = substr($1,8,2) substr($1,5,2) substr($1,2,2)
# print logdate
if (logdate >= startm ) { prtsw = 1 }
if (logdate > endm ) { prtsw = 0 }
if (prtsw == 1 ) { print $0 }
}
}'

unix ftp script to get latest file from server

I have a unix script to get files via ftp looks something like this:
#!/bin/sh
HOST='1.1.1.1'
USER='user'
PASSWD='pass'
FILE='1234'
ftp -n $HOST <<END_SCRIPT
quote USER $USER
quote PASS $PASSWD
cd .LogbookPlus
get $FILE
quit
END_SCRIPT
exit 0
Instead of getting a specific file, I want to get the last modified file in a folder, or all files created in the last 24 hours. Is this possible via ftp?
This is really pushing the FTP client further than it should be pushed, but it is possible.
Note that the LS_FILE_OFFSET might be different on your system and this won't work at all if the offset is wrong.
#!/bin/sh
HOST='1.1.1.1'
USER='user'
PASSWD='pass'
DIRECTORY='.LogbookPlus'
FILES_TO_GET=1
LS_FILE_OFFSET=57 # Check directory_listing to see where filename begins
rm -f directory_listing
# get listing from directory sorted by modification date
ftp -n $HOST > directory_listing <<fin
quote USER $USER
quote PASS $PASSWD
cd $DIRECTORY
ls -t
quit
fin
# parse the filenames from the directory listing
files_to_get=`cut -c $LS_FILE_OFFSET- < directory_listing | head -$FILES_TO_GET`
# make a set of get commands from the filename(s)
cmd=""
for f in $files_to_get; do
cmd="${cmd}get $f
"
done
# go back and get the file(s)
ftp -n $HOST <<fin
quote USER $USER
quote PASS $PASSWD
cd $DIRECTORY
$cmd
quit
fin
exit 0
You should have definitely given some more information about the systems you are using, e.g. not every ftp server supports ls -t that #JesseParker uses. I used the opportunity and put some ideas that I have used myself for some time into a script that uses awk to to the dirty deeds. As you can see, knowing what flavor of unix your client uses would be beneficial. I have tested this script to run under Debian Wheezy GNU/Linux and FreeBSD 9.2.
#!/bin/sh
# usage: <this_script> <num_files> <date...> [ <...of...> <...max....> <...age...> ... ]
#
# Fetches files from preconfigured ftp server to current directory.
# Maximum number of files is <num_files>
# Only files that have a newer modification time than given date are considered.
# This date is given according to the local 'date' command, which is very different
# on BSD and GNU systems, e.g.:
#
# GNU:
# yesterday
# last year
# Jan 01 1970
#
# BSD:
# -v-1d # yesterday (now minus 1 day)
# -v-1y # last year (now minus 1 year)
# -f %b %e %C%y Jan 01 1970 # format: month day century year
#
# Script tries to autodetect date system, YMMV.
#
# BUGS:
# Does not like quotation marks (") in file names, maybe much more.
#
# Should not have credentials inside this file, but maybe have them
# in '.netrc' and not use 'ftp -n'.
#
# Plenty more.
#
HOST='1.1.1.1'
USER='user'
PASSWD='pass'
DIR='.LogbookPlus'
# Date format for numerical comparison. Can be simply +%s if supported.
DATE_FMT=+%C%y%m%d%H%M%S
# The server's locale for date strings.
LC_SRV_DATE=C
# The 'date' command from BSD systems and that from the GNU coreutils
# are completely different. Test for the appropriate system here:
if LC_ALL=C date -j -f "%b %e %C%y" "Jan 01 1970" $DATE_FMT > /dev/null 2>&1 ; then
SYS_TYPE=BSDish
elif LC_ALL=C date -d "Jan 01 1970" $DATE_FMT > /dev/null 2>&1 ; then
SYS_TYPE=GNUish
else
echo "sh: don't know how to date ;-) sorry!"
exit 1;
fi
# Max. number of files to get (newest files first)
MAX_NUM=$(( ${1:-1} + 0 )) # ensure argv[1] is treated as a number
shift
# Max. age of files. Only files newer that this will be considered.
if [ GNUish = "$SYS_TYPE" ] ; then
MAX_AGE=$( date "$DATE_FMT" -d "${*:-yesterday}" )
elif [ BSDish = "$SYS_TYPE" ] ; then
MAX_AGE=$( date -j "${*:--v-1d}" "$DATE_FMT" )
fi
# create temporary file
TMP_FILE=$(mktemp)
trap 'rm -f "$TMP_FILE"' EXIT INT TERM HUP
ftp -i -n $HOST <<END_FTP_SCRIPT | \
awk -v max_age="$MAX_AGE" \
-v max_num="$MAX_NUM" \
-v date_fmt="$DATE_FMT" \
-v date_loc="$LC_SRV_DATE" \
-v sys_type="$SYS_TYPE" \
-v tmp_file="$TMP_FILE" '
BEGIN {
# columns in the 'dir' output from the ftp server:
# drwx------ 1 user group 4096 Apr 8 2009 Mail
# -rw------- 1 user group 13052 Nov 20 02:07 .bash_history
perm=1; links=2; user=3; group=4; size=5; month=6; day=7; yeartime=8; # name=$9..$NF
if ( "BSDish" == sys_type ) {
date_cmd="LC_ALL=" date_loc " date -j -f"
} else if ( "GNUish" == sys_type ) {
date_cmd="LC_ALL=" date_loc " date -d"
} else {
print "awk: don'\''t know how to date ;-) sorry!" > "/dev/stderr"
exit 1;
}
files[""] = ""
file_cnt = 0
out_cmd = "sort -rn | head -n " max_num " > " tmp_file
}
$perm ~ /^[^-]/ { # skip non-regular files
next
}
{
if ( "BSDish" == sys_type ) {
if ( $yeartime ~ /[0-9][0-9][0-9][0-9]/ ) {
ts_fmt = "\"%b %e %C%y\""
} else if ( $yeartime ~ /[0-9][0-9:[0-9][0-9]/ ) {
ts_fmt = "\"%b %e %H:%M\""
} else {
print "has neither year nor time: " $8
exit 1
}
} else { # tested in BEGIN: must be "GNUish"
ts_fmt = ""
}
cmd = date_cmd " " ts_fmt " \"" $month " " $day " " $yeartime "\" " date_fmt
cmd | getline timestamp
close( cmd )
if ( timestamp > max_age ) {
# clear everything but the file name
$perm=$links=$user=$group=$size=$month=$day=$yeartime=""
files[ file_cnt,"name" ] = $0
files[ file_cnt,"time" ] = timestamp
++file_cnt
}
}
END {
for( i=0; i<file_cnt; ++i ) {
print files[ i,"time" ] "\t" files[ i,"name" ] \
| out_cmd
}
close( out_cmd )
print "quote USER '$USER'\nquote PASS '$PASSWD'\ncd \"'$DIR'\""
i = 0
while( (getline < tmp_file) > 0 ) {
$1 = "" # drop timestamp
gsub( /^ /,"" ) # strip leading space
print "get \"" $0 "\""
}
print "quit"
}
' \
| ftp -v -i -n $HOST
quote USER $USER
quote PASS $PASSWD
cd "$DIR"
dir .
quit
END_FTP_SCRIPT

In a unix shell, how to get yesterday's date into a variable?

I've got a shell script which does the following to store the current day's date in a variable 'dt':
date "+%a %d/%m/%Y" | read dt
echo ${dt}
How would i go about getting yesterdays date into a variable?
Basically what i'm trying to achieve is to use grep to pull all of yesterday's lines from a log file, since each line in the log contains the date in "Mon 01/02/2010" format.
Thanks a lot
dt=$(date --date yesterday "+%a %d/%m/%Y")
echo $dt
On Linux, you can use
date -d "-1 days" +"%a %d/%m/%Y"
You can use GNU date command as shown below
Getting Date In the Past
To get yesterday and earlier day in the past use string day ago:
date --date='yesterday'
date --date='1 day ago'
date --date='10 day ago'
date --date='10 week ago'
date --date='10 month ago'
date --date='10 year ago'
Getting Date In the Future
To get tomorrow and day after tomorrow (tomorrow+N) use day word to get date in the future as follows:
date --date='tomorrow'
date --date='1 day'
date --date='10 day'
date --date='10 week'
date --date='10 month'
date --date='10 year'
If you have Perl available (and your date doesn't have nice features like yesterday), you can use:
pax> date
Thu Aug 18 19:29:49 XYZ 2010
pax> dt=$(perl -e 'use POSIX;print strftime "%d/%m/%Y%",localtime time-86400;')
pax> echo $dt
17/08/2010
If you are on a Mac or BSD or something else without the --date option, you can use:
date -r `expr \`date +%s\` - 86400` '+%a %d/%m/%Y'
Update: or perhaps...
date -r $((`date +%s` - 86400)) '+%a %d/%m/%Y'
I have shell script in Linux and following code worked for me:
#!/bin/bash
yesterday=`TZ=EST+24 date +%Y%m%d` # Yesterday is a variable
mkdir $yesterday # creates a directory with YYYYMMDD format
You have atleast 2 options
Use perl:
perl -e '#T=localtime(time-86400);printf("%02d/%02d/%02d",$T[4]+1,$T[3],$T[5]+1900)'
Install GNU date (it's in the sh_utils package if I remember correctly)
date --date yesterday "+%a %d/%m/%Y" | read dt
echo ${dt}
Not sure if this works, but you might be able to use a negative timezone. If you use a timezone that's 24 hours before your current timezone than you can simply use date.
Try the following method:
dt=`case "$OSTYPE" in darwin*) date -v-1d "+%s"; ;; *) date -d "1 days ago" "+%s"; esac`
echo $dt
It works on both Linux and OSX.
Here is a ksh script to calculate the previous date of the first argument, tested on Solaris 10.
#!/bin/ksh
sep=""
today=$(date '+%Y%m%d')
today=${1:-today}
ty=`echo $today|cut -b1-4` # today year
tm=`echo $today|cut -b5-6` # today month
td=`echo $today|cut -b7-8` # today day
yy=0 # yesterday year
ym=0 # yesterday month
yd=0 # yesterday day
if [ td -gt 1 ];
then
# today is not first of month
let yy=ty # same year
let ym=tm # same month
let yd=td-1 # previous day
else
# today is first of month
if [ tm -gt 1 ];
then
# today is not first of year
let yy=ty # same year
let ym=tm-1 # previous month
if [ ym -eq 1 -o ym -eq 3 -o ym -eq 5 -o ym -eq 7 -o ym -eq 8 -o ym - eq 10 -o ym -eq 12 ];
then
let yd=31
fi
if [ ym -eq 4 -o ym -eq 6 -o ym -eq 9 -o ym -eq 11 ];
then
let yd=30
fi
if [ ym -eq 2 ];
then
# shit... :)
if [ ty%4 -eq 0 ];
then
if [ ty%100 -eq 0 ];
then
if [ ty%400 -eq 0 ];
then
#echo divisible by 4, by 100, by 400
leap=1
else
#echo divisible by 4, by 100, not by 400
leap=0
fi
else
#echo divisible by 4, not by 100
leap=1
fi
else
#echo not divisible by 4
leap=0 # not divisible by four
fi
let yd=28+leap
fi
else
# today is first of year
# yesterday was 31-12-yy
let yy=ty-1 # previous year
let ym=12
let yd=31
fi
fi
printf "%4d${sep}%02d${sep}%02d\n" $yy $ym $yd
Tests
bin$ for date in 20110902 20110901 20110812 20110801 20110301 20100301 20080301 21000301 20000301 20000101 ; do yesterday $date; done
20110901
20110831
20110811
20110731
20110228
20100228
20080229
21000228
20000229
19991231
Thanks for the help everyone, but since i'm on HP-UX (after all: the more you pay, the less features you get...) i've had to resort to perl:
perl -e '#T=localtime(time-86400);printf("%02d/%02d/%04d",$T[3],$T[4]+1,$T[5]+1900)' | read dt
If your HP-UX installation has Tcl installed, you might find it's date arithmetic very readable (unfortunately the Tcl shell does not have a nice "-e" option like perl):
dt=$(echo 'puts [clock format [clock scan yesterday] -format "%a %d/%m/%Y"]' | tclsh)
echo "yesterday was $dt"
This will handle all the daylight savings bother.
If you don't have a version of date that supports --yesterday and you don't want to use perl, you can use this handy ksh script of mine. By default, it returns yesterday's date, but you can feed it a number and it tells you the date that many days in the past. It starts to slow down a bit if you're looking far in the past. 100,000 days ago it was 1/30/1738, though my system took 28 seconds to figure that out.
#! /bin/ksh -p
t=`date +%j`
ago=$1
ago=${ago:=1} # in days
y=`date +%Y`
function build_year {
set -A j X $( for m in 01 02 03 04 05 06 07 08 09 10 11 12
{
cal $m $y | sed -e '1,2d' -e 's/^/ /' -e "s/ \([0-9]\)/ $m\/\1/g"
} )
yeardays=$(( ${#j[*]} - 1 ))
}
build_year
until [ $ago -lt $t ]
do
(( y=y-1 ))
build_year
(( ago = ago - t ))
t=$yeardays
done
print ${j[$(( t - ago ))]}/$y
ksh93:
dt=${ printf "%(%a %d/%m/%Y)T" yesterday; }
or:
dt=$(printf "%(%a %d/%m/%Y)T" yesterday)
The first one runs in the same process, the second one in a subshell.
For Hp-UX only below command worked for me:
TZ=aaa24 date +%Y%m%d
you can use it as :
ydate=`TZ=aaa24 date +%Y%m%d`
echo $ydate
If you have access to python, this is a helper that will get the yyyy-mm-dd date value for any arbitrary n days ago:
function get_n_days_ago {
local days=$1
python -c "import datetime; print (datetime.date.today() - datetime.timedelta(${days})).isoformat()"
}
# today is 2014-08-24
$ get_n_days_ago 1
2014-08-23
$ get_n_days_ago 2
2014-08-22
$var=$TZ;
TZ=$TZ+24;
date;
TZ=$var;
Will get you yesterday in AIX and set back the TZ variable back to original
Though all good answers, unfortunately none of them worked for me. So I had to write something old school. ( I was on a bare minimal Linux OS )
$ date -d #$( echo $(( $(date +%s)-$((60*60*24)) )) )
You can combine this with date's usual formatting. Eg.
$ date -d #$( echo $(( $(date +%s)-$((60*60*24)) )) ) +%Y-%m-%d
Explanation :
Take date input in terms of epoc seconds ( the -d option ), from which you would have subtracted one day equivalent seconds. This will give the date precisely one day back.

Resources