I need to create three variables, each for Year, Month, and Day for Today's date, minus X number of days. For this question I'll choose a random amount of days: 222.
So if:
TodayYear=`date +%Y`
TodayMonth=`date +%m`
TodayDay=`date +%d`
What I want is 222 days before this.
222days_before_TodayYear=???
222days_before_TodayMonth=???
222days_before_TodayDay=???
Edit: Need 222 working days instead 222 regular days.
For GNU date:
date_222days_before_TodayYear=$(date --date="222 days ago" +"%Y")
date_222days_before_TodayMonth=$(date --date="222 days ago" +"%m")
date_222days_before_TodayDay=$(date --date="222 days ago" +"%d")
For BSD date::
If you are using OS X or FreeBSD, use the following instead because BSD date is different from GNU date:
date_222days_before_TodayYear=$(date -j -v-222d +"%Y")
date_222days_before_TodayMonth=$(date -j -v-222d +"%m")
date_222days_before_TodayDay=$(date -j -v-222d +"%d")
Source: BSD date manual page
Note:
In bash and many other languages, you cannot start a variable name with a numerical character, so I prefixed them with date_ for you.
Second Update: New requirement - Using 222 Working days instead of 222 Regular days:
(Assumption: Not considering statutory holidays, because that just gets far beyond the scope of what I can help you with in a shell script:)
Consider 222 working days:
5 working days per week, that is floor(222/5) == 44 weeks
44 weeks * 7 days per week == 308 days
Extra days leftover: 222 % 5 == 2
Therefore 222 working days == 310 regular days
But, there is a catch! If the number of regular days is 308 or some multiple of 7, then we would have been fine, because any multiple of 7-days ago from a working day is still a working day. So we need to consider whether today is a Monday or a Tuesday:
If today is a Monday, we'd get Saturday where we wanted Thursday
If today is a Tuesday, we'd get Sunday where we wanted Friday
So you see we need an additional offset of 2 more days if today is either Monday or Tuesday; so let's find that out first before we proceed:
#!/bin/bash
# Use 310 days as offset instead of 222
offset=310
# Find locale's abbreviated weekday name (e.g., Sun)
today=$(date -j +"%a")
# Check for Mon/Tue
if [[ "$today" == "Mon" ]] || [[ "$today" == "Tue" ]]; then
offset=$((offset+2))
fi
date_222_working_days_before_TodayYear=$(date -j -v-${offset}d +"%Y")
date_222_working_days_before_TodayMonth=$(date -j -v-${offset}d +"%m")
date_222_working_days_before_TodayDay=$(date -j -v-${offset}d +"%d")
And that should do it =)
date '+%Y' --date='222 days ago'
You can get exact past date from the following in bash
Number=222
current_date=$(date +%Y%m%d)
past_date=$(date -d "$current_date - $Number days" +%Y%m%d)
echo "$current_date\t$past_date"
Hope this helps !
epoch=$(( `date '+%s'` - ( 24 * 60 * 60 * 222 ) ))
year=`date -d "#$epoch" '+%Y'`
month=`date -d "#$epoch" '+%m'`
day=`date -d "#$epoch" '+%d'`
Should do the trick.
I would say easier solution would be
222days_before_TodayYear = $(date -v -222d +%Y)
222days_before_TodayMonth = $(date -v -222d +%m)
222days_before_TodayDay = $(date -v -222d +%d)
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 need to sort data on a weekly base and all i have are dates in a logfile.
Therefore to sort out data per week i would like to create a list with the dates of all mondays for a given year. I have tried to work something out and the only idea i currently have is to use ncal with year and month as argument looping over all months and extracting all mondays. Isn't there a more efficient way?
To get all mondays, by getting all dates and filtering by Mondays:
for i in `seq 0 365`
do date -d "+$i day"
done | grep Mon
Of course, you could also take a monday and keep incrementing by 7 days.
hope that's what you mean. Below can be changed to vary the output formats of the dates.
date command can be used for that, dunno if ncal is any more/less efficient.
I know you went for "binning" now, but here is a more readable v.
$ cat /tmp/1.sh
#!/bin/bash
test -z "$year" && {
echo "I expect you to set \$year environment variable"
echo "In return I will display you the Mondays of this year"
exit 1
}
# change me if you would like the date format to be different
# man date would tell you all the combinations you can use here
DATE_FORMAT="+%Y-%m-%d"
# change me if you change the date format above. I need to be
# able to extract the year from the date I'm shoing you
GET_YEAR="s/-.*//"
# this value is a week, in milliseconds. Changing it would change
# what I'm doing.
WEEK_INC=604800
# Use another 3-digit week day name here, to see dates for other week days
DAY_OF_WEEK=Mon
# stage 1, let's find us the first day of the week in this year
d=1
# is it DAY_OF_WEEK yet?
while test "$(date -d ${year}-1-${d} +%a)" != "$DAY_OF_WEEK"; do
# no, so let's look at the next day
d=$((d+1));
done;
# let's ask for the milliseconds for that DAY_OF_WEEK that I found above
umon=$(date -d ${year}-1-${d} +%s)
# let's loop until we break from inside
while true; do
# ndate is the date that we testing right now
ndate=$(date -d #$umon "$DATE_FORMAT");
# let's extract year
ny=$(echo $ndate|sed "$GET_YEAR");
# did we go over this year? If yes, then break out
test $ny -ne $year && { break; }
# move on to next week
umon=$((umon+WEEK_INC))
# display the date so far
echo "$ndate"
done
No need to iterate over all 365 or 366 days in the year. The following executes date at most 71 times.
#!/bin/bash
y=2011
for d in {0..6}
do
if (( $(date -d "$y-1-1 + $d day" '+%u') == 1)) # +%w: Mon == 1 also
then
break
fi
done
for ((w = d; w <= $(date -d "$y-12-31" '+%j') - 1; w += 7))
do
date -d "$y-1-1 + $w day" '+%Y-%m-%d'
done
Output:
2011-01-03
2011-01-10
2011-01-17
2011-01-24
2011-01-31
2011-02-07
2011-02-14
2011-02-21
2011-02-28
2011-03-07
. . .
2011-11-28
2011-12-05
2011-12-12
2011-12-19
2011-12-26
Another option that I've come up based on the above answers. The start and end date can now be specified.
#!/bin/bash
datestart=20110101
dateend=20111231
for tmpd in {0..6}
do
date -d "$datestart $tmpd day" | grep -q Mon
if [ $? = 0 ];
then
break
fi
done
for ((tmpw = $tmpd; $(date -d "$datestart $tmpw day" +%s) <= $(date -d "$dateend" +%s); tmpw += 7))
do
echo `date -d "$datestart $tmpw day" +%d-%b-%Y`
done
You can get the current week number using date. Maybe you can sort on that:
$ date +%W -d '2011-02-18'
07
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!
Using the GNU date command line utility, I know:
how to substract 3 days from any given date:
date -d "20110405 -3 days" "+%Y%m%d"
20110402
how to get the last Friday from today:
date -d "last friday" "+%Y%m%d"
20110408
But I don't know how to get the last Friday from any given date:
date -d "20110405 last friday" "+%Y%m%d"
Simply returns the given date:
20110405
Any ideas on how to do this? If a one-liner is not possible a few lines of script would also be helpful.
Ugly, but one line:
date -d "20110405 -2 days -$(date -d '20110405' '+%w') days" "+%Y%m%d"
EDIT: See comments.
date -d "20110405 -$(date -d "20110405 +2 days" +%u) days" "+%Y%m%d"
Explanation:
%w returns day of the week. Friday = 5 so take off 2 more days to get the right offset.
Works out as "20110405 -x days", where x is the number of days back to last Friday.
I don't like that it repeats the date string, but hopefully it goes some way to helping.
Script example (based on the accepted answer)
DT="20170601"
# get the Friday before $DT
# expected value is 20170526
date -d "$DT -`date -d "$DT +2 days" +%u` days" "+%Y%m%d"
Further examples, using undocumented features of GNU date (from unix.com)
# assign a value to the variable DT for the examples below
DT="2006-10-01 06:55:55"
echo $DT
# add 2 days, one hour and 5 sec to any date
date --date "$DT 2 days 1 hour 5 sec"
# subtract from any date
date --date "$DT 3 days 5 hours 10 sec ago"
date --date "$DT -3 days -5 hours -10 sec"
# or any mix of +/-. What will be the date in 3 months less 5 days?
date --date "now +3 months -5 days"
In a bash script, if I have a number that represents a time, in the form hhmmss (or hmmss), what is the best way of subtracting 10 minutes?
ie, 90000 -> 85000
This is a bit tricky. Date can do general manipulations, i.e. you can do:
date --date '-10 min'
Specifying hour-min-seconds (using UTC because otherwise it seems to assume PM):
date --date '11:45:30 UTC -10 min'
To split your date string, the only way I can think of is substring expansion:
a=114530
date --date "${a:0:2}:${a:2:2}:${a:4:2} UTC -10 min"
And if you want to just get back hhmmss:
date +%H%M%S --date "${a:0:2}:${a:2:2}:${a:4:2} UTC -10 min"
why not just use epoch time and then take 600 off of it?
$ echo "`date +%s` - 600"| bc; date
1284050588
Thu Sep 9 11:53:08 CDT 2010
$ date -d '1970-01-01 UTC 1284050588 seconds' +"%Y-%m-%d %T %z"
2010-09-09 11:43:08 -0500
Since you have a 5 or 6 digit number, you have to pad it before doing string manipulation:
$ t=90100
$ while [ ${#t} -lt 6 ]; do t=0$t; done
$ echo $t
090100
$ date +%H%M%S --utc -d"today ${t:0:2}:${t:2:2}:${t:4:2} UTC - 10 minutes"
085100
Note both --utc and UTC are required to make sure the system's timezone doesn't affect the results.
For math within bash (i.e. $(( and ((), leading zeros will cause the number to be interpreted as octal. However, your data is more string-like (with a special format) than number-like, anyway. I've used a while loop above because it sounds like you're treating it as a number and thus might get 100 for 12:01 am.
My version of bash doesn't support -d or --date as used above. However, assuming a correctly 0-padded input, this does work
$ input_time=130503 # meaning "1:05:03 PM"
# next line calculates epoch seconds for today's date at stated time
$ epoch_seconds=$(date -jf '%H%M%S' $input_time '+%s')
# the 600 matches the OP's "subtract 10 minutes" spec. Note: Still relative to "today"
$ calculated_seconds=$(( epoch_seconds - 600 )) # bc would work here but $((...)) is builtin
# +%H%M%S formats the result same as input, but you can do what you like here
$ echo $(date -r $calculated_seconds '+%H%M%S')
# output is 125503: Note that the hour rolled back as expected.
For MacOS users you can do the following:
$(date -v -10M +"%H:%M:%S")
Date time without a specific format:
$(date -v -10M)
For non-macOS users:
Date time without a specific format:
date --date '-10 min'