Generate specific date in bash - bash

Hi I have written down some bash to generate the date in format YYYYDDMM.
I know that is not perfect but the final thing will be:
Generate a range of 2 dates that are 31 days apart from each other and start with a minimum of today + one day and the older ones end at the end of this year. In format YYYYMMDD
month="$(awk -v min=1 -v max=12 'BEGIN{srand(); print int(min+rand()*(max-min+1))}')"
day="$(awk -v min=1 -v max=31 'BEGIN{srand(); print int(min+rand()*(max-min+1))}')"
year="$(date +%Y)"
if (( "${month}" < 10 )); then
month_proper="$(echo 0"${month}")"
else
month_proper="$(echo "${month}")"
fi
if (( "${day}" < 10 )); then
day_proper="$(echo 0"${day}")"
else
day_proper="$(echo "${day}")"
fi
echo month "${month}"
echo month with 0 if smaller than 10 : "${month_proper}"
echo day "${day}"
echo day with 0 smaller than 10 : "${day_proper}"
ok="$(date -d ""$year""${month_proper}""${day_proper}"" +"%Y%m%d")"
echo date with proper format "${ok}"
date -d "$year""${month_proper}""${day_proper}"
In which direction would I have to expand this script to get the final result? I already have the date generation, but there is no checking if there is one day ahead of today.

Requirements specify range nearly exactly (it is Dec-31 till Jan-31 next year), so you can write
a_fmt=`date -d "tomorrow" +%Y1231`
a_num=`date -d "$a_fmt" +%s`
b_num=$(( a_num + 31 * 24 * 3600 ))
b_fmt=`date -d #$b_num +%Y%m%d`
echo "Range is '$a_fmt'..'$b_fmt'"

Related

if condition older with epoch date

I'm creating a condition that checks the actual date in epoch format and compares another string in epoch format; if the strings are more than 10 days old, do something...
i tried something like this:
#!/bin/bash
timeago='10 days ago'
actual_date=$(date --date "now" +'%s')
last_seen_filter=$(date --date "$timeago" +'%s')
echo "INFO: actual_date=$actual_date, last_seen_filter=$last_seen_filter" >&2
if [ "$actual_date" -lt "$last_seen_filter" ]; then
echo "something"
else
echo "do something"
fi
or
#!/bin/bash
cutoff=$(date -d '10 days ago' +%s)
key="1624684050 1624688000"
while read -r "$key"
do
age=$(date -d "now" +%s)
if (($age < $cutoff))
then
printf "Warning! key %s is older than 10 days\n" "$key" >&2
fi
done < input
That's not enough for what I need, I have epoch dates in a file called converted_data, i need to include this strings on if comparision.
1624684050
1634015250
1614661650
1622005650
It's not clear what you're trying to do but maybe this with GNU awk will get you started:
$ awk '
BEGIN { today=strftime("%F") }
{
secs = $0
days = 0
date = strftime("%F",secs)
while ( strftime("%F",secs+=(24*60*60)) < today ) {
++days
}
print $0":", date, "->", today, "=", days
}
' file
1624684050: 2021-06-26 -> 2021-06-13 = 0
1634015250: 2021-10-12 -> 2021-06-13 = 0
1614661650: 2021-03-01 -> 2021-06-13 = 102
1622005650: 2021-05-26 -> 2021-06-13 = 17
The 0s for future dates are because you only asked about 10-days past dates so I don't care to adapt for both past and future deltas. I also didn't put much thought into it so check the logic and the math!

Randomly loop over days in bash-script

At the moment, I have a while-loop that takes a starting date, runs a python script with the day as the input, then takes the day + 1 until a certain due date is reached.
day_start=2016-01-01
while [ "$day_start"!=2018-01-01 ] ;
do
day_end=$(date +"%Y-%m-%d" -d "$day_start + 1 day")
python script.py --start="$day_start" --end="$day_end";
day_start=$(date +"%Y-%m-%d" -d "$day_start + 1 day")
done
I would like to do the same thing, but now to pick a random day between 2016-01-01 and 2018-01-01 and repeat until all days have been used once. I think it should be a for-loop instead of this while loop, but I have trouble to specify the for-loop over this date-range in bash. Does anyone have an idea how to formulate this?
It can take quite a long time if you randomly choose the dates because of the Birthday Problem. (You'll hit most of the dates over and over again but the last date can take quite some time).
The best idea I can give you is this:
Create all dates as before in a while loop (only the day_start-line)
Output all dates into a temporary file
Use sort -R on this file ("shuffles" the contents and prints the result)
Loop over the output from sort -R and you'll have dates randomly picked until all were reached.
Here's an example script which incorporates my suggestions:
#!/bin/bash
day_start=2016-01-01
TMPFILE="$(mktemp)"
while [ "$day_start" != "2018-01-01" ] ;
do
day_start=$(date +"%Y-%m-%d" -d "$day_start + 1 day")
echo "${day_start}"
done > "${TMPFILE}"
sort -R "${TMPFILE}" | while read -r day_start
do
day_end=$(date +"%Y-%m-%d" -d "$day_start + 1 day")
python script.py --start="$day_start" --end="$day_end";
done
rm "${TMPFILE}"
By the way, without the spaces in the while [ "$day_start" != "2018-01-01" ];, bash won't stop your script.
Fortunately, from 16 to 18 there was no leap year (or was it, and it just works because of that)?
Magic number: 2*365 = 730
The i % 100, just to have less output.
for i in {0..730}; do nd=$(date -d "2016/01/01"+${i}days +%D); if (( i % 100 == 0 || i == 730 )); then echo $nd ; fi; done
01/01/16
04/10/16
07/19/16
10/27/16
02/04/17
05/15/17
08/23/17
12/01/17
12/31/17
With the format instruction (here +%D), you might transform the output to your needs, date --help helps.
In a better readable format, and with +%F:
for i in {0..730}
do
nd=$(date -d "2016/01/01"+${i}days +%F)
echo $nd
done
2016-01-01
2016-04-10
2016-07-19
...
For a random distribution, use shuf (here, for bevity, with 7 days):
for i in {0..6}; do nd=$(date -d "2016/01/01"+${i}days +%D); echo $nd ;done | shuf
01/04/16
01/07/16
01/05/16
01/01/16
01/03/16
01/06/16
01/02/16

How could I use bash to work out how many tuesdays there are in a month? [duplicate]

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

How can I find the first monday of current month using cygwin bash date utilty?

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.

How to decode Seagate's hard drive date code in a Bash script

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!

Resources