Unix Shell scripting - calculating last week date based on given date - shell

Need a help in unix shell script in calculating date.
I will be getting date value (eg: 20150908) as parameter, now inside the script i need to calculate 7 days ago date (20150908 -7).
something like below:
date=20150908
lastweek_date=20150908 - 7 ---> this should output as 20150901
Could someone help me on this.
Thanks

With GNU date, we can subtract one week:
$ date -d "20150908 - 1 week" '+%Y%m%d'
20150901
Alternatively, we could subtract 7 days:
$ date -d "20150908 - 7 days" '+%Y%m%d'
20150901
And, to show that this works over month boundaries:
$ date -d "20150901 - 1 week" '+%Y%m%d'
20150825
This solution is not OSX/BSD compatible.

A week is 604800 seconds long so to get the number of seconds since the epoch in a portable and POSIX compliant fashion and use it to compute the date 1 week ago do as follows:
PRESENT=$( date +%s )
WEEKAGO=$(( PRESENT - 604800 ))
printf "%s\n" "$WEEKAGO"

Related

How can I get a sequence of dates in bash?

In Z shell there is a command called dseq which produces consecutive dates. For example:
$ dseq 5
2022-05-08
2022-05-09
2022-05-10
2022-05-11
2022-05-12
Is there a similar command in bash? I tried the following which gets me close to the desired output.
$ seq -f "2022-05-%g" 5
2022-05-1
2022-05-2
2022-05-3
2022-05-4
2022-05-5
Two issues:
how can I pad the days with 0 so the output contains two-digit days?
how can I start the sequence from today versus the first of the month?
Desired output should match output of $ dseq 5 above.
You can have something like:
$ for i in {1..5}; do date -d "20220507+$i day" +%Y-%m-%d; done
2022-05-08
2022-05-09
2022-05-10
2022-05-11
2022-05-12
If you are on MacOS and don't want to install GNU date (which is what provides the nonstandard -d option which you'll find in most answers to related questions) you will need to perform relative date calculations yourself. Perhaps like this:
base=$(date +"%s")
for((i=0; i<5; ++i)); do
date -r $((i * 24 * 60 * 60 + base)) +%Y-%m-%d
done
This avoids any complex date manipulations; if you need them, they are somewhat unobvious, and less versatile than the GNU date extensions - see, for example, How to convert date string to epoch timestamp with the OS X BSD `date` command?
For what it's worth, GNU date (and generally GNU userspace utilities) are the default on most Linux platforms.
With GNU date you could do date -d #timestamp +format where MacOS/BSD uses date -r timestamp +format.
If you need a portable solution, POSIX is not much help here, but a reasonably de facto portable solution is to use e.g. Perl. For inspiration, perhaps review What is a good way to determine dates in a date range?
perl -le 'use POSIX qw(strftime);
$t = time;
for $_ (1..5) {
print strftime("%Y-%m-%d", localtime($t));
$t += 24 * 60 * 60 }'
Demo (on Linux): https://ideone.com/WkAoib
In case it's not obvious, both these solutions get the current time's epoch (seconds since Jan 1, 1970) and then add increments of 24 hours * 60 minutes * 60 seconds to jump ahead one day at a time.

Sends from epoch, for 1 year ago on MacOS / BSD?

I'm trying to calculate the number of seconds since the Epoch, using date on MacOS BSD.
I can get a one year ago date string:
$ date -v -1y
Tue Apr 21 10:44:47 EST 2020
...but I can't figure out how to convert it into seconds since Epoch. Any suggestions?
Add +%s to tell it to print the datetime as seconds since the epoch:
date -v -1y +%s
The + is a date option to set the output format, and %s is strftime format for "seconds since epoch".
Portability note: while the +%s part is pretty standard and portable (though the %s format is not actually required by POSIX), the -v -1y part is wildly nonportable. With GNU date (e.g. on most Linuxes), you'd use something like --date='1 year ago' instead. On NetBSD, -d '1 year ago' works. Check your local man page to see what your system supports.

Determining how many days ago a timestamp refers to in bash

I'm trying to determine the age of AWS keys in days. I'm using Bash.
I get the creation date of the AWS key using this command:
user_key1_date=$(aws iam list-access-keys --user-name "$aws_user_name" --profile "$aws_key" --output text --query 'AccessKeyMetadata[*].[AccessKeyId,CreateDate]' | awk 'NR==1 { print $2 }')
And I get a result like this:
2018-01-04T20:59:01Z
I am then trying to get the age of the keys in days and assign it to a variable with this line:
key1Age=$(date -d "$user_key1_date" +%j)
I then try to report the age of the key with this line:
The AWS access key: $user_access_key1 for user name: $aws_user_name was created on $date1. This key is $key1Age days old and needs to be replaced.
But the age of the key that gets reported is innacurate:
Key AKIAIPSNLEFKBLH2CLOQ is 004 days old. Time to change your key!
What am I doing wrong?
%j gives you the number of days into a year on which a date takes place. January 4th of 2018 is thus 4, but so is January 4th of 1998; it's not useful when you need to do comparisons that can cross year boundaries.
Consider something more like:
now=$(date +%s) # slow but more reliable way
key1Date=2018-01-04T20:59:01Z
key1TimeSeconds=$(date -d "$key1Date" +%s)
key1AgeSeconds=$(( now - key1TimeSeconds ))
key1AgeDays=$(( key1AgeSeconds / (60 * 60 * 24) ))
echo "Key created on $key1Date. This key is $key1AgeDays days old and needs to be replaced."
...which, when run (as of today), emits:
Key created on 2018-01-04T20:59:01Z. This key is 188 days old and needs to be replaced.
If you're only using new versions of bash, printf -v now '%(%s)T' -1 is a more efficient alternative to now=$(date +%s).
The date command may not be giving the answer you want, but it is giving the correct answer to the question you asked. :) The %j gives Julian days, or the number of days from the beginning of the year. Jan 4 is the 4th day of the year.
There are probably several ways of doing what you want. I'll give you one way to get you in the right direction.
echo "($(date +%s) - $(date -d "2018-01-04T20:59:01Z" +%s)) / 3600 / 24"|bc
The %s converts a date into the the number of seconds from Jan 1, 1970. Here I took the number of seconds now and subtract the number of seconds from your key date. Dividing by 3600 gets me the number of hours and then by 24 gets me the number of days. bc converts the math expression to a value. I believe this will be truncated, but you might want to experiment with this.
The %j format in my date's man page refers to day of year (001..366), so for Jan 4th, 004 would make sense.
You need to do some math to get the difference and there are plenty of answers out there for that, e.g How to find the difference in days between two dates?

How to get 10 days back date by giving one date as parameter to the shell script

If i give the date "20130828"(not a current date) in YYYYMMDD format, how can i get 10 days back date using shell script i.e. 20130818
Thanks in advance!
Try
date +%Y%m%d --date="20130818 -10 day"
or even
date +%Y%m%d --date="20130818 10 days ago"
+%Y%m%d is the format of your date (YYYYmmdd), and through --date you can provide a string (in a very human readable format) to specify when you want this date.
The following:
date --date="20130828 - 10 days" +"%Y%m%d"
Outputs
20130818
if you have gnu date, you can just give 10 days ago to date's -d option:
kent$ date -d'10 days ago 20130828' +%Y%m%d
20130818

date command in a bash shell script

At work all the days config files are generated fresh and appended with a
session number. The company went public on Feb 16, and the 86400 is seconds
in one day. The session number is generated by subtracting the company start
day from seconds_since_last_day and adding a few zero's
That is the key to interacting with the days config files. I get this - However I do not
understand the
date -ud "$distance days ago 00:00:00".
Is it the number of seconds since 1970?
if $session; then
# return the session of the last day
seconds_since_day_one=`date -ud "Feb 16 2002" +"%s"`
seconds_since_last_day=`date -ud "$distance days ago 00:00:00" +"%s"`
days_between=`printf "%010d" $(( (seconds_since_last_day - seconds_since_day_one) / 86400 ))`
# Truncate on the left to 9 bytes to leave room
# to append the engine suffix for your environment
echo $days_between | awk '{l=length($1); print substr( $1, (l-8), l )}'
date -ud "$distance days ago 00:00:00" in itself just prints the date a certain amount of days ago in a quite readable format, but when you add the FORMAT string to control the output +"%s" does indeed mean the number in so called Unix Time (number of seconds since 1970-01-01 00:00:00 UTC).
If the variable $distance is set to a number it shows the date that number of days ago, if its set to 0 it means today, 1 it means yesterday, 2 the day before yesterday and so on. To better understand these formats and relative keywords there are rather good documentations in (amongst other places) the GNU coreutils package.
Check these URLs:
http://www.gnu.org/software/coreutils/manual/html_node/Relative-items-in-date-strings.html#Relative-items-in-date-strings
http://www.gnu.org/software/coreutils/manual/html_node/Date-input-formats.html
http://www.gnu.org/software/coreutils/manual/html_node/date-invocation.html#date-invocation
Wikipedia explanation of Unix Time:
http://en.wikipedia.org/wiki/Unix_time
The option -d to date provides a generic string to obtain the date.
So, for example, date -d yesterday will print yesterday's date, and date -d 'yesterday 12:00 AM' will print yesterday's date with the time set to 12:00 AM.
So, date -d 6 days ago 00:00:00 will print the date from 6 days ago, with the time set to 00:00:00. I hope it answers your question.
The format +"%s" tells date to print the number of seconds from 1970, instead the date.
mktime and strftime in awk can be used to get the date of the time.
http://www.gnu.org/software/gawk/manual/html_node/Time-Functions.html
For instance, strftime("%A",mktime("YYYY MM DD 00 00 00"))
should give you the day.

Resources