Suppose that today is Thursday, June 30, 2016.
Then date +%Y%m%d is 20160630, date -d'last-thursday' +%Y%m%d is 20160623, and date -d'next-thursday' +%Y%m%d is 20160707.
However, date -d'last-wednesday' +%Y%m%d is 20160629, and date -d'next-wednesday' +%Y%m%d is 20160706.
Is there an inclusive version of {next,last}-*day which would return today's date if today is day?
Inclusive next weekday is built in, just use -d"Thursday", (no prefix), and date will return today if today is Thursday, or next Thursday if not.
But date has no inclusive last, so here's an inclusive last weekday kludge function:
ilast() { a=$(date -d'today' "+%A") ; b=$(date -d"$1" "+%A") ; \
[ $a != $b ] && echo -n "last " ; echo $b ; }
Test, (which also shows ilast's usage):
for d in Sun. Mon. Tue. Wed. Thu. Fri. Sat. ; do \
echo -n $(ilast $d)" " ; \
date -d"$(ilast $d)" "+%Y%m%d" ; \
done | column -t
Output:
last Sunday 20160626
last Monday 20160627
last Tuesday 20160628
last Wednesday 20160629
Thursday 20160630
last Friday 20160624
last Saturday 20160625
The function sets $a to today's weekday, and $b to $1's weekday, which would be a different weekday, unless $1 is today's weekday. So if the weekdays $a and $b are not equal, (six days out of seven they'd differ), we prepend "last " to the weekday's long name. If the weekdays $a and $b are equal, don't print "last ", since, (one day out of seven), the default inclusive next already does the desired thing.
Related
My try:
we=$(LC_TIME=C date +%A)
dm=$(date +%d)
wday="Friday"
if [ "$we" = "$wday" ] && [ "$dm" -lt 8 ]
then do some stuff
I means on first friday he make sume stuff. How can I store this in a variable, that if the next time is the first Friday in the month for an echo output.
Now the script executes me only on the first Friday in the month something.
It must be evaluate if the next friday is in the next month or not if yes do some. (Debian 11)
As your OS is debian, I assume GNU date command which supports -d option
is available. Then would you please try the following:
dayofweek="$(date -d "tomorrow" +%u)" # day of week (1..7); 1 is Monday
dayofmonth="$(date -d "tomorrow" +%d)" # day of month (e.g., 01)
if (( dayofweek == 5 && 10#$dayofmonth < 8 )); then
# do some stuff
fi
As the format %u prints the day of week between 1 and 7, you can compare
it with 5 to detect Friday.
The day of month includes the leading zero such as 01 and you need to
convert it to a decimal digit number by prefixing 10#. Otherwise
the number with a leading zero is interpreted as octal then 08 and 09
will cause an error.
I want to find some information between two dates (Last Friday of Month and it's successive Sunday).
For now, I'm adding all the dates(formatted as mm/dd/yy) in a file dates_file.in like this:
05/29/20,05/31/20
06/26/20,06/28/20
07/31/20,08/02/20
and running a for loop like this
for dates in `cat dates_file.in`
do
date1=`echo $dates | cut -d',' -f1`
date2=`echo $dates | cut -d',' -f2`
xyz_cmd -start="$date1" -end="$date2" -report_parms
done
Is there a way I can use calendar to fetch the dates or export the dates to dates_file.in
file?
OS is RHEL 6.5
Using bash and GNU date:
# a function to return a positive number for "x % y"
# e.g. `floorMod -1 7` outputs: 6
floorMod() { local num=$1 div=$2; echo $(( ((num % div) + div) % div )); }
formatDate() { local y=$1 m=$2 d=$3 delta=$4; date -d "$y-$m-$d $delta" "+%m/%d/%y"; }
year=2020
for month in {1..12}; do
# get the last day of this month, and its day of the week
read -r day dow < <(date -d "$year-$month-01 + 1 month - 1 day" "+%d %w")
# decrement until we find Friday
until ((dow == 5)); do
((day--))
dow=$(floorMod $((dow - 1)) 7)
done
start=$(formatDate $year $month $day)
end=$(formatDate $year $month $day "+2 days")
echo "$start $end"
done
01/31/20 02/02/20
02/28/20 03/01/20
03/27/20 03/29/20
04/24/20 04/26/20
05/29/20 05/31/20
06/26/20 06/28/20
07/31/20 08/02/20
08/28/20 08/30/20
09/25/20 09/27/20
10/30/20 11/01/20
11/27/20 11/29/20
12/25/20 12/27/20
Could you please try following, using cal here.
cat script.ksh
month="$1"
year="$2"
####To get current month's Friday.
cal "$month" "$year" 2>/dev/null |
tac |
awk '
NF>=6{
print "current month last Friday is:"$(NF-1)" and Sunday is:"$NF
exit
}
'
I need to know the first monday of the current month using Cygwin bash.
One Liner:
d=$(date -d "today 1300" '+%Y%m01'); w=$(date -d $d '+%w'); i=$(( (8 - $w) % 7)); answer=$(( $d + $i ));
The result is stored in $answer. It uses working variables $d, $w, and $i.
Proof (assuming you just ran the one liner above):
echo $answer; echo $(date -d $answer '+%w')
Expected Result: Monday of current month in YYYYMMDD. On the next line, a 1 for the day of the week.
Expanded Proof (checks the next 100 month's Mondays):
for x in {1..100}; do d=$(date -d "+$x months 1300" '+%Y%m01'); w=$(date -d $d '+%w'); i=$(( (8 - $w) % 7)); answer=$(( $d + $i )); echo $answer; echo $(date -d $answer '+%w'); done | egrep -B1 '^[^1]$'
Expected Result: NOTHING
(If there are results, something is broken)
Breaking it down
The first statement gets the first day of the current month, and stores that in $d, formatted as YYYYMMDD.
The second statement gets the day of the week number of the date $d, and stores that in $w.
The third statement computes the increment of days to add and stores it in $i. Zero is perfectly valid here, because...
The last statement computes the sum of the date $d (as an integer) and the increment $i (as an integer). This works because the domain of the $i is 0 to 6, and we will always start at the first day of the month. This can quickly be converted back to a date variable (see Proof for example of this).
This has been tested on BASH v4.1 (CentOS 6), v4.4 (Ubuntu), and v5 (Archlinux)
A one-liner--I hope it's correct
d=$(date -d date +%Y%m"01" +%u);date -d date +%Y%m"0"$(((9-$d)%7))
the variable d contains the day of week (1..7) where 1 is Monday
then I print the current year and month changing the day with $((9-$d))
This should do it, but I have no Cygwin here to test:
#!/bin/bash
# get current year and month:
year=$( date +"%Y" )
month=$( date +"%m" )
# for the first 7 days in current month :
for i in {1..7}
do
# get day of week (dow) for that date:
dow=$( date -d "${year}-${month}-${i}" +"%u" )" "
# if dow is 1 (Monday):
if [ "$dow" -eq 1 ]
then
# print date of that Monday in default formatting:
date -d "${year}-${month}-${i}"
break
fi
done
See manpage date(1) for more information.
Seagate hard drives display a code instead of the manufacturing date. The code is described here and an online decoder is available here.
In short, it's a 4 or 5 digit number of the form YYWWD or YYWD, where:
YY is the year, 00 is year 1999
W or WW is the week number beginning 1
D is day of week beginning 1
Week 1 begins on the first saturday of July in the stated year
Examples
06212 means Sunday 20 November 2005
0051 means Saturday 31 July 1999
How can this be decoded in a bash script ?
This is what I did, it should work:
#!/bin/bash
DATE=$1
REGEX="^(..)(..?)(.)$"
[[ $DATE =~ $REGEX ]]
YEAR=$(( ${BASH_REMATCH[1]} + 1999 ))
WEEK=$(( ${BASH_REMATCH[2]} - 1))
DAYOFWEEK=$(( ${BASH_REMATCH[3]} - 1))
OFFSET=$(( 6 - $(date -d "$YEAR-07-01" +%u) ))
DATEOFFIRSTSATURDAY=$(date -d "$YEAR-7-01 $OFFSET days" +%d)
FINALDATE=`date -d "$YEAR-07-$DATEOFFIRSTSATURDAY $WEEK weeks $DAYOFWEEK days"`
echo $FINALDATE
It worked for the two dates given above...
If you want to customize the date output, add a format string at the end of the FINALDATe assignment.
Here is a short script, it takes two arguments: $1 is the code to convert and $2 is an optional format (see man date), otherwise defaulted (see code).
It uses the last Saturday in June instead of the first one in July because I found it easer to locate and it allowed me to just add the relevant number of weeks and days to it.
#!/bin/bash
date_format=${2:-%A %B %-d %Y}
code=$1
[[ ${#code} =~ ^[4-5]$ ]] || { echo "bad code"; exit 1; }
let year=1999+${code:0:2}
[[ ${#code} == 4 ]] && week=${code:2:1} || week=${code:2:2}
day=${code: -1}
june_last_saturday=$(cal 06 ${year} | awk '{ $6 && X=$6 } END { print X }')
date -d "${year}-06-${june_last_saturday} + ${week} weeks + $((${day}-1)) days" "+${date_format}"
Examples:
$ seadate 06212
Sunday November 20 2005
$ seadate 0051
Saturday July 31 1999
I created a Seagate Date Code Calculator that actually works with pretty good accuracy. I've posted it here on this forum for anyone to use: https://www.data-medics.com/forum/seagate-date-code-conversion-translation-tool-t1035.html#p3261
It's far more accurate than the other ones online which often point to the entirely wrong year. I know it's not a bash script, but will still get the job done for anyone else who's searching how to do this.
Enjoy!
Given a start date, I set the date to the end of the month of the previous year, and then try to loop 12m using a bash while loop.
The following loop never exits, and the date eventually skips from the end of the month to the beginning of the next one.
startdate="2014-06-22"
cod=${startdate:0:7}-01
echo $cod
cod=$( date --date "$cod -1 year +1 month -1 day " +"%Y-%m-%d" )
echo "$cod , $startdate "
while [ "$cod" < "$startdate" ];
do
echo $cod
cod=$( date --date "$cod +1 month" +"%Y-%m-%d" )
echo $cod
done
What I'm expecting is
2013-06-30
2013-07-31
2013-08-31
2013-09-30
2013-10-31
2013-11-30
2013-12-31
2014-01-31
2014-02-28
2014-03-31
2014-04-30
2014-05-31
So what you want is the last day of each month, but you are trying to get it by taking the first "last day of the month" and adding one month each time. That doesn't work because you get January 30th and then March 2nd rather than February 28th, because February is weird and "+1 month" is weird :-)
$ date -d '2014-01-30'
Thu Jan 30 00:00:00 GMT 2014
$ date -d '2014-01-30 +1 month'
Sun Mar 2 00:00:00 GMT 2014
To get the last day of each month you need to repeat the trick you use on the first date - find the first day of the following month and then -1 day from it.
#!/bin/bash
startdate="2014-06-22"
cod=${startdate:0:7}-01
cod=$( date --date "$cod -1 year +1 month -1 day " +"%Y-%m-%d" )
for i in $(seq 1 12); do
echo $cod
cod=$( date --date "$(date --date "$cod +32 day" +"%Y-%m-01") -1 day" +"%Y-%m-%d" )
done
Which outputs:
$ ./twelve_months.sh
2013-06-30
2013-07-31
2013-08-31
2013-09-30
2013-10-31
2013-11-30
2013-12-31
2014-01-31
2014-02-28
2014-03-31
2014-04-30
2014-05-31
Note that I also changed the loop to a straight 1 .. 12 instead of a while loop with a logic check. The while loop seemed needlessly complicated when you know you always want twelve dates.
< is getting executed by the shell. You need to escape it or use the [[ keyword for lexicographic comparison.
while [ "$cod" \< "$startdate" ];
or
while [[ "$cod" < "$startdate" ]];
I don't think you can compare date strings, but I am sure you can convert date strings to epoch and compare them as integer
Epoch is time in seconds since 1/1/1970 00:00:00+000
example:
startdate="2014-06-22"
epoch_start=$(date -d "$startdate" +%s)
cod=$startdate
cod=$( date --date "$cod -1 year +1 month -1 day " +%s )
while [ "$cod" -lt "$epoch_start" ];
do
date=$(date -d "#${cod}" +%Y-%m-%d)
date -d "#${cod}" +%Y-%m-%d
cod=$(date --date "${date} +1 month" +%s)
done
date -d "#${cod}" +%Y-%m-%d
Output:
bash test4.sh
2013-07-21
2013-08-21
2013-09-21
2013-10-21
2013-11-21
2013-12-21
2014-01-21
2014-02-21
2014-03-21
2014-04-21
2014-05-21
2014-06-21
2014-07-21
I hope that helps you.