Subtracting Dates in Unix - date

is there a way to subtract a month from a date or at least 30 days in unix.
example:
[yyy-mm-dd] - 30 days
2011-07-23 - 30 days
By the way, the date can be any date depending on the user input.
Thanks.

For an arbitrary date,
$ date -d "2011-07-23 - 1 month" "+%F"
2011-06-23
$ date -d "2011-07-23 - 30 days" "+%F"
2011-06-23
$ date -d "2011-08-23 - 1 month" "+%F"
2011-07-23
$ date -d "2011-08-23 - 30 days" "+%F"
2011-07-24
This is GNU date
Without GNU date, you can fall back to perl. The Time::Piece and Time::Seconds module should be available in perl 5.12
perl -MTime::Piece -MTime::Seconds -e '
print "date\t-1 month\t-30 days\n";
while (#ARGV) {
my $t = Time::Piece->strptime(shift, "%Y-%m-%d");
print $t->ymd, "\t";
print $t->add_months(-1)->ymd, "\t";
$t -= 30*ONE_DAY;
print $t->ymd, "\n";
}
' 2011-07-23 2011-08-23
date -1 month -30 days
2011-07-23 2011-06-23 2011-06-23
2011-08-23 2011-07-23 2011-07-24

There is no both straightforward and portable way, all other replies so far are using Gnu specific extensions.
This should work on any Unix:
date "+%Y %-m %-d" |
(
read y m d
m=$(($m - 1))
[ $m = 0 ] && { m=12; y=$(($y - 1)); }
[ $d = 31 -a \( $m = 4 -o $m = 6 -o $m = 9 -o $m = 11 \) ] && d=30
[ $d -gt 28 -a $m = 2 ] && d=28
printf "%04d:%02d:%02d\n" $y $m $d
)

Try this
date -d "30 days ago" "+%Y%m%d00"

#Current Date
date +'%Y:%m:%d'
2013:10:04
#Date a month ago
date --date='-1 month' +'%Y:%m:%d. Last month was %B.'
2013:09:04. Last month was September.
#%B prints out locale's full month name.
Type "info coreutils date invocation" in terminal and learn more.
Section: 28.7 Relative items in date strings. File: coreutils.info
EDIT: Looks OP wants to have the user input of date.
#Nov 1 2012 can be modified to suit user's input.
date --date="$(date -d "Nov 1 2012")-1 month" +'%Y:%m:%d'
2012:10:01

Related

Increment date in shell script on AIX [duplicate]

This question already has answers here:
What is the optimal way to loop between two dates in Perl?
(5 answers)
Closed 6 years ago.
I have to create a shell script file that accepts a string, start date and end date and generates the output file in the below format.
Note : Dates are in MM/DD/YYYY
string,start date
string,start date + 1
...
string,end date
I am not able to increment the date. I tried using date -d option but it is not available on AIX.
Could someone please help with a built in shell script command or perl command to increment the date ?.
Given a constraint of 'uses core':
#!/usr/bin/env perl
use strict;
use warnings;
use Time::Piece;
use Time::Seconds;
my $FORMAT = '%m/%d/%Y';
my $start = '01/22/2016';
my $end = '01/31/2016';
my $start_t = Time::Piece->strptime( $start, $FORMAT );
my $end_t = Time::Piece->strptime( $end, $FORMAT );
while ( $start_t <= $end_t ) {
print $start_t ->strftime($FORMAT), "\n";
$start_t += ONE_DAY;
}
Both Time::Piece and Time::Seconds are core as of perl 5.9.5. The latter is only needed for ONE_DAY - otherwise you can just add 60 * 60 * 24 instead.
I'm sure you can figure out how to print the strings etc. yourself.
(I'd also note - this is a horrible time format, and should be avoided)
You can make a shellscript using awk like this:
str="some string"
startd="12/25/2016"
endd="1/17/2017"
maxloop=10
echo "${maxloop}" |
awk -v string="${str}" -v startdate="${startd}" -v enddate="${endd}" '{
split(startdate,A,"[/]");
T1=mktime(A[3] " " A[1] " " A[2] " 0 0 0");
split(enddate,B,"[/]");
T2=mktime(B[3] " " B[1] " " B[2] " 23 59 59");
linenr=1;
while (T1 < T2) {
printf("%s,%s\n",string,
strftime("%m-%d-%Y",T1));
T1+=3600*24;
if (linenr++ > $1) break;
}
}'
The linenr/maxloop is not adding something to the solution but will help against
mistakes in the input values or maintenance of the script.

Perl - date calculation

I'm new in Perl. I want to calculate days (e.g. 5 days) after input.
Steps I need to cover:
1. start_date: 08.12.2015
2. add 5 days
3. end date: ?
My idea is to convert the start date into a timestamp. Then I add 5*84.400 seconds. After that, I want to re-convert the end timestamp.
I read the Time::Local module docs, but I did not understand the logic yet.
Is this the right way to handle date calculations?
You can do it using DateTime and DateTime::Format::Strptime. Like this:
use strict;
use DateTime;
use DateTime::Format::Strptime;
my $strp = DateTime::Format::Strptime->new(
pattern => '%d.%m.%Y'
);
my $date = '08.12.2015';
my $dt = $strp->parse_datetime($date);
printf "%s -> %s\n", $date, $dt->add(days => 5)->strftime("%d.%m.%Y");
Using Time::Piece
#!/usr/bin/env perl
use v5.10;
use Time::Piece;
use Time::Seconds;
my $date_string = '08.12.2015';
my $date_format = '%m.%d.%Y';
my $tp = Time::Piece->strptime( $date_string, $date_format );
$tp += 5 * ONE_DAY;
say $tp->strftime($date_format);
Outputs:
08.17.2015
Or another way, in vanilla perl (no use of packages needed), with help from Unix date:
$ date -d 08/12/2015
Wed, Aug 12, 2015 12:00:00 AM
$ date -d 08/12/2015 +%s
1439352000
$ date -d 08/12/2015 +%s|perl -ne 'print scalar localtime $_ + 5*24*60*60'
Mon Aug 17 00:00:00 2015

How do I capture first tuesday in a month with zero padded in Unix

#Unix
I am trying to capture first tuesday of every month into a variable and trying to pad Zero against it without luck.
Below is the piece of code I was trying:
cal | sed -e 's/ \([1-9]\) /0\1 /g' -e 's/ \([1-9]\)$/0\1/' | awk 'NR>2{Sfields=7-NF; if (Sfields == 0 ) {printf "%d\n",$3;exit}}'
Can someone help me what I am missing here?
This awk should do:
cal | awk 'NR>2 && NF>4 {printf "%02d\n",$(NF-4);exit}'
03
To confirm its working:
for i in {1..12}; do cal -m $i | awk 'NR>2 && NF>4 {printf "%02d\n",$(NF-4);exit}' ; done
06
03
03
07
05
02
07
04
01
06
03
01
Or you can use ncal
ncal | awk '/Tu/ {printf "%02d\n",$2}'
03
If you like a version where you can specify name of week,
and would work if Monday is first day of week, then this gnu awk should do:
cal | awk 'NR==2 {for (i=1;i<=NF;i++) {sub(/ /,"",$i);a[$i]=i}} NR>2 {if ($a["Tu"]~/[0-9]/) {printf "%02d\n",$a["Tu"];exit}}' FIELDWIDTHS="3 3 3 3 3 3 3 3"
03
It uses FIELDWITH to make sure empty columns in start of month does not changes the output.
# for monday calendar
cal -m1 | sed -n '1,2b;/^.\{3\} \{0,1\}\([0-9]\{1,2\}\) .*/ {s//0\1/;s/.*\([0-9]\{2\}\)$/\1/p;q;}'
# for sunday calendar
cal -s1 01 01 2015 | sed -n '1,2b;/^.\{6\} \{0,1\}\([0-9]\{1,2\}\) .*/ {s//0\1/;s/.*\([0-9]\{2\}\)$/\1/p;q;}'
cal option depend on system (tested here on Red Hat 6.6) and mean -m for monday as first day and -sfor sunday (the attached 1 is for 1 month display). Take the line according to your specified output of cal.
don't print line by default
don't care of line 1 and 2
take line with non empty second(/third) group
take second(/third) group (position) of number until next one and replace by a 0, remove trailng char
take the 2 last digit of first group, remove the rest and print it
quit (no other line)
thanks to #Jotne for all remark about first wanted day in second week (4th line and not 3th) and first day of the week
I think I got the answer.
cal | awk 'NR>2{Sfields=7-NF; if (Sfields == 0 ) {printf "%02d\n",$3;exit}}'
Above statement would do."%02d" does it for me
bash and date. May be slower than parsing cal:
y=2015
for m in {1..12}; do
for d in {01..07}; do
if [[ $(date -d "$y-$m-$d" +%w) -eq 2 ]]; then
echo $d
break
fi
done
done
Translating into awk: will be faster as it doesn't have to call date multiple times:
gawk -v y=2015 '
BEGIN {
for (m=1; m<=12; m++) {
for (d=1; d<=7; d++) {
t = mktime( y " " m " " d " 12 0 0" )
if (strftime("%w", t) == 2) {
printf "%02d\n", d
break
}
}
}
}
'

How to calculate a date a week ago from today

I wanna calculate the date a week ago from today with a specific format and put it in to a variable. For example, today is Nov 21st. 2014, and I wanna print out: Last week is 2014-11-14.
I know we can use Date::Calc module, but I don't know how.
Check Time::Piece and Time::Seconds core modules,
use Time::Piece;
use Time::Seconds;
my $t = localtime() - ONE_WEEK;
print $t->ymd;
output
2014-11-14
DateTime version
use DateTime;
my $now = DateTime->now(time_zone => 'local')->subtract(weeks => 1);
print $now->ymd, ' ',$now->hms;
Date::Calc version
Instead of one week you can subtract 7 days using Date::Calc module
use Date::Calc qw(Add_Delta_Days);
my #date = Add_Delta_Days( 2014, 11, 21, -7 );
print join('-', #date);
OUTPUT
2014-11-14
This is very simple using Date::Manip
use Date::Manip;
my $today = ParseDate("today");
my $weeksago = DateCalc($today,"-7d");
Why not just subtract X days from the "mday" field of localtime? This example shows subtracting 60 days from the end of august. I'm not sure who corrects the month but I think I'm getting the right answer...
$ date
Wed Aug 30 14:34:14 DFT 2017
$ perl -MPOSIX -e '#t=localtime time; $t[3] -= 60; print strftime( "%Y/%m/%d", #t), "\n";'
2017/07/01

How to mark dates if they are older than 5 days in perl?

I know how to mark dates as YELLOW that are in the past:
perl -MTime::Piece -pe '
BEGIN {$today = localtime->ymd." ".localtime->hms}
#days = $_ =~ /<td>([0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2})<\/td>/g;
for $d (#days) {
$_ =~ s!$d!<font style=BACKGROUND-COLOR:yellow>$d</font>!g if $d lt $today;
}' foo.html > foo-TMP.html
mv foo-TMP.html foo.html
Question: But how can I only mark dates that are older then 5 days?
Example:
It's: 2014.07.20 15:00
So the following example dates would be marked as yellow:
bla-bla random string here<td>2014.05.21 16:32</td>bla-bla random string here
bla-bla random string here<td>2014.07.15 14:59</td>bla-bla random string here
And the following should be left alone:
bla-bla random string here<td>2014.07.15 15:01</td>bla-bla random string here
bla-bla random string here<td>2014.07.18 19:14</td>bla-bla random string here
According to the Time::Piece docs, "using localtime/gmtime in the way documented in perlfunc will still return what you expect", and the perlfunc docs reveal that localtime() takes an argument, a number of seconds since the epoch, the default (if you don't provide one) being the return value of time().
Five days is 432000 seconds. If you change the initial reference point:
BEGIN {
$then = localtime(time() - 432000);
$day = $then->ymd." ".$then->hms
}
And use $day in place of $today, you should get what you want.