How to get the latest date for a specific day of the week in Bash [duplicate] - bash

This question already has answers here:
How to find out the date of the last Saturday in Linux shell script or python?
(3 answers)
Closed 6 years ago.
Q: How can the latest date for a given_day_of_the_week?
Example: For example if today is the 2016-04-21, I just want to get the date for a given_day_of_the_week for example Monday which would be the 2016-04-18. This date will be still returned if the code was run tomorrow, up until the day before the following given_day_of_the_week.
Explanation: The code should return the latest given_day_of_the_week's (Monday) date until the Sunday 2016-04-24, then the same bit of code running will return the 2016-04-25 next week.

Requires GNU date:
today_dow=$(date +%w)
days=(Sunday Monday Tuesday Wednesday Thursday Friday Saturday)
for ((dow=0; dow<7; dow++)); do
if ((dow < today_dow)); then
date -d "last ${days[dow]}"
else
date -d "${days[dow]}"
fi
done
Sun Apr 17 00:00:00 EDT 2016
Mon Apr 18 00:00:00 EDT 2016
Tue Apr 19 00:00:00 EDT 2016
Wed Apr 20 00:00:00 EDT 2016
Thu Apr 21 00:00:00 EDT 2016
Fri Apr 22 00:00:00 EDT 2016
Sat Apr 23 00:00:00 EDT 2016
So we can do:
given_day_of_the_week() {
local days=(Sunday Monday Tuesday Wednesday Thursday Friday Saturday)
local today_dow=$(date +%w)
local dow datestr
for ((dow=0; dow<7; dow++)); do
if [[ "${days[dow],,}" == "${1,,}" ]]; then
if ((dow < today_dow)); then
datestr="last ${days[dow]}"
else
datestr="${days[dow]}"
fi
date -d "$datestr" "+%F"
fi
done
}
Resulting in:
$ given_day_of_the_week tuesday
2016-04-19
$ given_day_of_the_week friday
2016-04-22
Hardcoding the weekday names like that will give you problems if you're in a different locale
Responding to #ryenus's comment:
$ given_day_of_the_week() {
local -A days=([sunday]=0 [monday]=1 [tuesday]=2 [wednesday]=3 [thursday]=4 [friday]=5 [saturday]=6)
local today_dow=$(date +%w)
local datestr=${1,,}
local dow=${days[$datestr]}
[[ -z "$dow" ]] && { echo "error: unknown day: '$1'" >&2; return 1; }
(( dow < today_dow )) && datestr="last $datestr"
date -d "$datestr" "+%F"
}
$ given_day_of_the_week friday
2016-04-22
$ given_day_of_the_week monday
2016-04-18
$ given_day_of_the_week FOO
error: unknown day: 'FOO'

Inspired by #glenn-jackman's answer, but simpler:
given_day_of_the_week=$1
f='%A %Y-%m-%d'
tomorrow=`date -d 'tomorrow' +"$f"`
today=`date -d 'today' +"$f"`
last5=`seq 1 5 | xargs -I{} date -d '{} day ago' +"$f"`
echo -e "$tomorrow\n$today\n$last5" |
grep -i $given_day_of_the_week |
cut -d' ' -f2
Note: this answer does give tomorrow's date for Monday if used on a Sunday. It also gives tomorrow's date for Tuesday if used on a Monday. Not 100% sure if that's what you had in mind.
If instead the idea is to always go with the current Sunday - Saturday, this will do it:
given_day_of_the_week=$1
f='%A %Y-%m-%d'
[ `date +%A` = 'Sunday' ] && first=`date -d 'today' +"$f"` ||
first=`date -d 'last Sunday' +"$f"`
rest=`seq 1 6 | xargs -I{} date -d "$first +{} day" +"$f"`
echo -e "$first\n$rest" |
grep -i $given_day_of_the_week |
cut -d' ' -f2

# requires bash 4.x and GNU date
last_kday() {
local kday=$1
local -A numbers=([sunday]=0 [monday]=1 [tuesday]=2 [wednesday]=3
[thursday]=4 [friday]=5 [saturday]=6)
if [[ $kday == *day ]]; then
kday=${numbers[${kday,,}]}
elif [[ $kday != [0-6] ]]; then
echo >&2 "Usage: last_kday weekday"
return 1
fi
local today=$(date +%w)
local days_ago=$(( today - kday ))
if (( days_ago < 0 )); then let days_ago+=7; fi
date -d "$days_ago days ago" +%F
}

Related

Equivalent output of PowerShell's get-date, but using bash [duplicate]

I have a date in this format: "27 JUN 2011" and I want to convert it to 20110627
Is it possible to do in bash?
#since this was yesterday
date -dyesterday +%Y%m%d
#more precise, and more recommended
date -d'27 JUN 2011' +%Y%m%d
#assuming this is similar to yesterdays `date` question from you
#http://stackoverflow.com/q/6497525/638649
date -d'last-monday' +%Y%m%d
#going on #seth's comment you could do this
DATE="27 jun 2011"; date -d"$DATE" +%Y%m%d
#or a method to read it from stdin
read -p " Get date >> " DATE; printf " AS YYYYMMDD format >> %s" `date
-d"$DATE" +%Y%m%d`
#which then outputs the following:
#Get date >> 27 june 2011
#AS YYYYMMDD format >> 20110627
#if you really want to use awk
echo "27 june 2011" | awk '{print "date -d\""$1FS$2FS$3"\" +%Y%m%d"}' | bash
#note | bash just redirects awk's output to the shell to be executed
#FS is field separator, in this case you can use $0 to print the line
#But this is useful if you have more than one date on a line
More on Dates
note this only works on GNU date
I have read that:
Solaris version of date, which is unable
to support -d can be resolve with
replacing sunfreeware.com version of
date
On OSX, I'm using -f to specify the input format, -j to not attempt to set any date, and an output format specifier. For example:
$ date -j -f "%m/%d/%y %H:%M:%S %p" "8/22/15 8:15:00 am" +"%m%d%y"
082215
Your example:
$ date -j -f "%d %b %Y" "27 JUN 2011" +%Y%m%d
20110627
date -d "25 JUN 2011" +%Y%m%d
outputs
20110625
If you would like a bash function that works both on Mac OS X and Linux:
#
# Convert one date format to another
#
# Usage: convert_date_format <input_format> <date> <output_format>
#
# Example: convert_date_format '%b %d %T %Y %Z' 'Dec 10 17:30:05 2017 GMT' '%Y-%m-%d'
convert_date_format() {
local INPUT_FORMAT="$1"
local INPUT_DATE="$2"
local OUTPUT_FORMAT="$3"
local UNAME=$(uname)
if [[ "$UNAME" == "Darwin" ]]; then
# Mac OS X
date -j -f "$INPUT_FORMAT" "$INPUT_DATE" +"$OUTPUT_FORMAT"
elif [[ "$UNAME" == "Linux" ]]; then
# Linux
date -d "$INPUT_DATE" +"$OUTPUT_FORMAT"
else
# Unsupported system
echo "Unsupported system"
fi
}
# Example: 'Dec 10 17:30:05 2017 GMT' => '2017-12-10'
convert_date_format '%b %d %T %Y %Z' 'Dec 10 17:30:05 2017 GMT' '%Y-%m-%d'
Just with bash:
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
[[ $2 = ${months[$i]} ]] && break
done
printf "%4d%02d%02d\n" $3 $(( i+1 )) $1
}
And invoke it like this
d=$( convert_date 27 JUN 2011 )
Or if the "old" date string is stored in a variable
d_old="27 JUN 2011"
d=$( convert_date $d_old ) # not quoted
It's enough to do:
data=`date`
datatime=`date -d "${data}" '+%Y%m%d'`
echo $datatime
20190206
If you want to add also the time you can use in that way
data=`date`
datatime=`date -d "${data}" '+%Y%m%d %T'`
echo $data
Wed Feb 6 03:57:15 EST 2019
echo $datatime
20190206 03:57:15
Maybe something changed since 2011 but this worked for me:
$ date +"%Y%m%d"
20150330
No need for the -d to get the same appearing result.

How to convert date in Macbook (Terminal)?

I need to convert the date of the macbook via CLI because I'm running a script:
Date we have to convert 2019-09-17 15:32:27
Final format Jun 17 15:32:27
I tried it with the following script:
date_raw=`grep "^.*,.*,.*,.*,.*,.*,"$i"," ${SNIFFER_BUFFER_USAGE} | head -1 | awk -F ',' '{ print $2}'`
if [ $OSX -eq 1 ];
then
dataHoraMinuto=$(date -j -f "%Y-%m-%d %H:%M:%S" "$date_raw" +"%b %d %H:%M:%S")
else
dataHoraMinuto=`date +"%b %d %H:%M:%S" -d "$date_raw"`
fi
$ date -d"2019-09-17 15:32:27" +"%b %d %T"
Sep 17 15:32:27

How to get the date of the 3. wednesday in the month

I need a code sample getting the date of the 3. wednesday of the month.
I simply can't wrap my head around how to do that - the last wednesday is simple:
cal | awk '/^ *[0-9]/ { d=$4 } END { print d }'
Ultimately I need the script to return the "next 3. wednesday" - as in if we have passed the 3. wednesday of this month, return the 3. wednesday of the next month.
Here's a simple approach which would work properly in most regions:
#!/bin/bash
export LC_TIME=C
thismonth=$(date +%m) # this month like "10"
thisyear=$(date +%Y) # this year like "2018"
firstdayofweek=$(date -d "${thisyear}-${thismonth}-1" +%w)
# calculates the day of week of the 1st day of the month
# returns the number between 0 and 6, where 0=Sun, 1=Mon, ...
wed1=$(( (10 - $firstdayofweek) % 7 + 1 ))
# calculates the day of month of the 1st Wednesday
wed3=$(( $wed1 + 14 ))
# the day of the 3rd Wednsday
echo $wed3
which yields "17" as of today.
It would be easy to modify the code above to achieve your next goal.
if you have ncal
$ ncal | awk '/We/{print $4}'
ncal format is transpose of cal and working on rows is easier. 3rd Wednesday is the fourth field.
$ ncal
October 2018
Su 7 14 21 28
Mo 1 8 15 22 29
Tu 2 9 16 23 30
We 3 10 17 24 31
Th 4 11 18 25
Fr 5 12 19 26
Sa 6 13 20 27
for example for November
$ ncal -m 11
November 2018
Su 4 11 18 25
Mo 5 12 19 26
Tu 6 13 20 27
We 7 14 21 28
Th 1 8 15 22 29
Fr 2 9 16 23 30
Sa 3 10 17 24
$ ncal -m 11 | awk '/We/{print $4}'
21
It's not pretty, but on a GNU/Linux system you could loop over each day of the month and count:
#!/bin/bash
month="$1" # e.g. 2018-10
number="$2" # e.g. 3
weekday="$3" # e.g "Wednesday"
export LC_ALL=C TZ=UTC
date -d today > /dev/null 2>&1 || {
echo "You are not using GNU date"
exit 1
}
n="$number"
for day in {1..31}
do
wd=$(date -d "$month-$day" +"%A" 2>&1) || continue
if [[ $wd == "$weekday" ]]
then
(( --n )) || break
fi
done
if ! (( n ))
then
echo "The $weekday number $number in $month is $month-$day"
else
echo "$month does not have a $weekday number $number"
fi
However, note that dates are hard, and this simple approach may not correctly account for various curiosities like Samoa skipping Friday 2011-12-30 due to a time zone shift.
Here are some examples on a GNU system:
$ ./finddate 2018-10 3 Wednesday
The Wednesday number 3 in 2018-10 is 2018-10-17
$ ./finddate 2018-10 5 Thursday
2018-10 does not have a Thursday number 5
Here is an example from a non-GNU system like MacOS:
$ ./finddate 2018-10 3 Wednesday
You are not using GNU date
Here's what I came up with:
#!/bin/bash -xv
CURRENTDAY=$(date +%e)
CURRENTMONTH=$(date +%m)
NEXTMONTH=$(( ${CURRENTMONTH}+1 ))
CURRENTYEAR=$(date +%Y)
WEDNESDAYSFORTHEMONTH=$(cal ${CURRENTMONTH} ${CURRENTYEAR} | awk 'NF <= 7 { print $4 }' | grep -v "^$"| grep -v We)
# I would normally start at 0
COUNTER=1
#Get this months 3rd Wednesday
for WEDNESDAY in ${WEDNESDAYSFORTHEMONTH}
do
if [[ ${COUNTER} -eq 3 ]]
then
echo "This months third wednesday is: ${WEDNESDAY}"
THISMONTHSTHIRDWEDNESDAY=${WEDNESDAY}
fi
COUNTER=$(( ${COUNTER}+1 ))
done
# Get the 3rd Wednesday for the next month
WEDNESDAYSFORTHEMONTH=$(cal ${NEXTMONTH} ${CURRENTYEAR} | awk 'NF <= 7 { print $4 }' | grep -v "^$"| grep -v We)
COUNTER=1
for WEDNESDAY in ${WEDNESDAYSFORTHEMONTH}
do
if [[ ${COUNTER} -eq 3 ]]
then
echo "Next months third wednesday is: ${WEDNESDAY}"
NEXTMONTHSTHIRDWEDNESDAY=${WEDNESDAY}
fi
COUNTER=$(( ${COUNTER}+1 ))
done
#Compare the current date with this months 3rd Wednesday
if [[ ${CURRENTDAY} -le ${THISMONTHSTHIRDWEDNESDAY} ]]
then
echo "Wednesday to use in your script is: ${THISMONTHSTHIRDWEDNESDAY} ${CURRENTMONTH} ${CURRENTYEAR}"
else
echo "Wednesday to use in your script is: ${NEXTMONTHSTHIRDWEDNESDAY} ${NEXTMONTH} ${CURRENTYEAR}"
fi
# Third Wednesday of this month
thirdWednesday=$( cal | cut -c 10-12 | sed '/^\s*$/d' | sed -n '5p' )
# Today
today=$( date +%d )
# If already passed
if [ $today -gt $thirdWednesday ]; then
# Next month
nextMonth=$( date +"%m %Y" -d "next month" )
# Get third Wednesday of next month
thirdWednesday=$( cal $nextMonth | cut -c 10-12 | sed '/^\s*$/d' | sed -n '5p' )
fi
# Result
echo $thirdWednesday

change time output in bash script

How to change this time output ?
date --date="#$(echo $(TZ=UTC date +%s) - $(date +%s)'%(5*60)-(5*60)' | bc)"
Output: za apr 12 00:25:00 CEST 2014
Should output in this layout: %Y%m%d%H%M
How to implement this in the string ?
Thanks!!
i think this should do what you want:
d="#$(echo $(TZ=UTC date +%s) - $(date +%s)'%(5*60)-(5*60)' | bc)"&&echo `date --date="$d"` `date --date="$d" +%Y%m%d%H%M`
d="#$(echo $(TZ=UTC date +%s) - $(date +%s)'%(5*60)-(5*60)' | bc)"&&echo `date --date="$d" --utc` `date --date="$d" +%Y%m%d%H%M --utc`
Second one is in UTC.

how to expand date by week and by month in shell?

In short, I want something works like this:
When I input a date like 2012-12-27 and want to expand the date by
week(start with Monday), it
outputs:2012-12-24,2012-12-25,2012-12-26,2012-12-27,2012-12-28,2012-12-29,2012-12-30
When I input a date like 2012-12-27 and want to expand the date by month, it outputs:2012-12-01,2012-12-02 ... 2012-12-31
or, how can I group a bunch of dates by week? e.g. when I input2012-12-01,2012-12-02 ... 2012-12-31. It outputs:2012-12-01,2012-12-02|2012-12-03 ... 2012-12-09|2012-12-10 ... 2012-12-16|...|2012-12-31
I have no idea how to complete this, any clue may be helpful!
DAYSECS=86400 # seconds in a day
WEEKSECS=604800
echo "Expand on week:"
g_epoch=$(date +"%s" -d $1) # given date as seconds from epoch
g_dayno=$(date +"%u" -d $1) # given date as day of week
g_month=$(date +"%m" -d $1) # given month
g_year=$(date +"%Y" -d $1) # given year
s_epoch=$(($g_epoch - $DAYSECS * ($g_dayno - 1)))
e_epoch=$(($s_epoch + $WEEKSECS))
for etime in $(seq $s_epoch $DAYSECS $e_epoch); do
date +"%Y-%m-%d" -d "#$etime"
done
echo "Expand on month:"
s_epoch=$(date +"%s" -d "$g_year-$g_month-01")
e_epoch=$(($s_epoch + 4 * $WEEKSECS))
for etime in $(seq $s_epoch $DAYSECS $e_epoch); do
if [ $(date +"%m" -d "#$etime") -ne "$g_month" ]; then
break;
fi
date +"%Y-%m-%d" -d "#$etime"
done
The script below work out from #perreal, I leave it here because:
It shows the power of GNU date.
It makes #perreal 's idea more clear and more universal.
Thank you, perreal!
Here it is
dd="2012-12-27" # test date
DAYSECS=86400 # seconds in a day
echo "expand by week:"
s_epoch=$(date +%s -d "$dd -$(($(date +%u -d $dd) - 1)) day") # start date of the week
e_epoch=$(date +%s -d "1970-01-01 00:00:00 +0000 +${s_epoch} seconds +6 days") # end date of the week
for etime in $(seq $s_epoch $DAYSECS $e_epoch); do
date +"%Y-%m-%d" -d "#$etime";
done
echo "expand by month:"
s_epoch=$(date +%s -d "$dd -$(($(date +%d -d $dd) - 1)) day") # start date of the month
e_epoch=$(date +%s -d "1970-01-01 00:00:00 +0000 +${s_epoch} seconds +1 month -1 day") # end date of the month
for etime in $(seq $s_epoch $DAYSECS $e_epoch); do
date +"%Y-%m-%d" -d "#$etime";
done

Resources