How can I get a sequence of dates in bash? - 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.

Related

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?

date: illegal option -- d, Find difference between two dates

I am trying to convert timestamps read from a file from string to date format so that I can find the difference of 2 dates/timestamps. most of the threads/discussions on web show usage of date argument '-d' to convert the string to epoch or to find the difference of two timestamps Find difference between two dates in bash
But it looks like my environment/OS doesn't support -d date argument. Below are the details of my env:
bash --version
GNU bash, version 3.2.52(1)-release (i386-pc-solaris2.10)
Copyright (C) 2007 Free Software Foundation, Inc.
uname -a
SunOS s01***** 5.10 Generic_147148-26 i86pc i386 i86pc
Sample dates read from file:
START_TIME="09/03/16 - 01:04:56"
END_TIME="09/03/16 - 05:10:44"
Code that I have tried
I have tried to mimic the below code from Find difference between two dates in bash
!# /usr/bin/sh
date1="Sat Dec 28 03:22:19 2013"
date2="Sun Dec 29 02:22:19 2013"
date -d #$(( $(date -d "$date2" +%s) - $(date -d "$date1" +%s) )) -u +'%H:%M:%S'
bash test.sh
date: illegal option -- d
usage: date [-u] mmddHHMM[[cc]yy][.SS]
date [-u] [+format]
date -a [-]sss[.fff]
date: illegal option -- d
usage: date [-u] mmddHHMM[[cc]yy][.SS]
date [-u] [+format]
date -a [-]sss[.fff]
test.sh: line 5: - : syntax error: operand expected (error token is " ")
I don't think syntax error on line 5 is the main culprit cause I didnot find option -d in my date's man page.
In response to comments:
>>> date --version
date: illegal option -- version
usage: date [-u] mmddHHMM[[cc]yy][.SS]
date [-u] [+format]
date -a [-]sss[.fff]
>>> date --help
date: illegal option -- help
usage: date [-u] mmddHHMM[[cc]yy][.SS]
date [-u] [+format]
date -a [-]sss[.fff]
>>> echo $0
bash
Even these arguments are not supported. Apologies if I am committing any silly mistake.
Could someone please give me the equivalent of -d for the env details shared above or a way to find the difference between two dates without using -d.
Thanks in advance
awk mktime has a decent chance of existing on your system:
#!/bin/bash
START_TIME="09/03/16 - 01:04:56"
END_TIME="09/03/16 - 05:10:44"
echo -e "$START_TIME\n$END_TIME" |
tr '/:-' ' ' |
awk '{print "20"$3" "$2" "$1" "$4" "$5" "$6}' |
awk '{printf "%s ", mktime($0)}' |
awk '{print $2 - $1}'
explanation:
echo both time strings
tr converts 09/03/16 - 01:04:56 to 09 03 16 01:04:56
first awk changes 09 03 16 01 04 56 to 2016 03 09 01 04 56
second awk converts 2016 03 09 01 04 56 to epoch time: 1457514296 and prints both on one line: 1457514296 1457529044
third awk subtracts first from second, giving difference in seconds: 14748
the awks could also easily be merged, but here i kept each separate for clarity.
According to the POSIX standard, date does not do date and time math for you. It gets or sets the system date and time, possibly with timezone adjustments. See date(1) and note the lack of a -d option (or indeed, any interesting options!).
The question becomes "How do we do date and time math without date?" The timestamps you provided do not have time zone information, so the usual behavior is to assume local time. Local time is bad. Seriously. Some time zones have crazy discontinuities or didn't meaningfully exist hundreds of years ago (e.g. most American time zones). So while you might be able to hack together a custom solution that works in your particular part of the world during the recent past, it simply will not be robust.
If you can get your timestamps into Unix time, you can just subtract them, and that will give you a mostly but not entirely correct answer. Unfortunately, to the best of my knowledge, that can't be done at the command line. Unix provides strptime(3) to do this from C (and from there you go on to mktime(3), as shown in this answer), but I don't believe there is any fully standard utility which provides a command-line interface for this. You may have to write and compile your own.
Correcting for leap seconds is difficult in the extreme because, to the best of my knowledge, POSIX has never provided a standard interface for finding out when leap seconds have happened in the past. Such an interface would require internet connectivity in order to remain up-to-date, which is likely a non-starter for a number of implementations. Without knowing more about your system and its capabilities, I simply cannot begin to guess at what will or will not work for your use case.

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

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"

How to date as a user input in shell scripting?

I am new to shell scripting..
I want a script to get any date as a input from user and print date of 3 days back?
example:
If user enters date as 2013-01-01
then output should be
2012-12-29.
If you have GNU date, then this will work:
user_date=2013-01-01
date +%Y-%m-%d -d "$user_date - 3 days"
With BSD date, you'd have to do like this:
user_date=2013-01-01
date -j -v -3d +%Y-%m-%d -d "${user_date//-}0000"
because BSD date needs date to be in the format YYYYmmddHHMM.
I don't have a Solaris now to test there. If you're in Solaris then hopefully there is gdate, and you can use the first option, just replace the date command with gdate.
Whichever OS you are in, there are two important points:
In what format can you pass dates to the date command. I tested that GNU date can accept YYYY-mm-dd format (and probably many others), while BSD needs YYYYmmddHHMM.
In what format can you ask for a difference. With GNU date simply DATE - 3 days works, with BSD date it's trickier with -j -v -3d flags.
man date of your system should help you get through these hurdles. In the worst case, you could do all the date operations you need in perl or similar.
You can just do:
date --date="3 days ago"
to get get date of 3 days back.

getting a previous date in bash/unix

I am looking to get previous date in unix / shell script .
I am using the following code
date -d ’1 day ago’ +’%Y/%m/%d’
But I am getting the following error.
date: illegal option -- d
As far as I've read on the inetrnet , it basically means I am using a older version of GNU. Can anyone please help with this.
Further Info
unix> uname -a
SunOS Server 5.10 Generic_147440-19 sun4v sparc SUNW,Sun-Fire-T200
Also The below command gives an error.
unix> date --version
date: illegal option -- version
usage: date [-u] mmddHHMM[[cc]yy][.SS]
date [-u] [+format]
date -a [-]sss[.fff]
try this:
date --date="yesterday" +%Y/%m/%d
dtd="2015-06-19"
yesterday=$( date -d "${dtd} -1 days" +'%Y_%m_%d' )
echo $yesterday;
you can use
date -d "30 days ago" +"%d/%m/%Y"
to get the date from 30 days ago, similarly you can replace 30 with x amount of days
In order to get 1 day back date using date command:
date -v -1d It will give (current date -1) means 1 day before .
date -v +1d
This will give (current date +1) means 1 day after.
Similarly below written code can be used in place of d to find out year,month etc
y-Year,
m-Month
w-Week
d-Day
H-Hour
M-Minute
S-Second
Several solutions suggested here assume GNU coreutils being present on the system. The following should work on Solaris:
TZ=GMT+24 date +’%Y/%m/%d’
SunOS ships with legacy BSD userland tools which often lack the expected modern options. See if you can get the XPG add-on (it's something like /usr/xpg4/bin/date) or install the GNU coreutils package if you can.
In the meantime, you might need to write your own simple date handling script. There are many examples on the net e.g. in Perl. E.g. this one:
vnix$ perl -MPOSIX=strftime -le 'print strftime("%Y%m", localtime(time-86400))'
201304
(Slightly adapted, if you compare to the one behind the link.)
the following script prints the previous date to the targetDate (specified Date or given date)
targetDate=2014-06-30
count=1
startDate=$( echo `date -d "${targetDate} -${count} days" +"%Y-%m-%d"`)
echo $startDate
I have used the following workaround to get to the required solution .
timeA=$(date +%Y%m)
sysD=$(date +%d)
print "Initial sysD $sysD">>LogPrint.log
sysD=$((sysD-1))
print "Final sysD $sysD">>LogPrint.log
finalTime=$timeA$sysD
I hope this is useful for people who are facing the same issue as me.
$ date '+%m/%d/%Y' --- current date
$ TZ=Etc/GMT+24 date '+%m/%d/%Y' -- one dayprevious date
Use time zone appropriately
Problem
You are using backticks, rather than single quotes, for your arguments. You may also not be using GNU date, or a version of date that supports the flag you are using.
Solution
Quote your arguments properly. For example:
$ date -d '1 day ago' +'%Y/%m/%d'
2013/04/14
Try This:
gdate -d "1 day ago" +"%Y/%m/%d"
date -d "yesterday" '+%Y-%m-%d'

Resources