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?
Asked
Active
Viewed 5,775 times
5 Answers
6
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.

Sjoerd
- 74,049
- 16
- 131
- 175
-
`for i in `seq 0 365`; do date -d "2011-1-1 $i day" | grep -q Mon ; if [ $? ]; then date -d "2011-1-1 $i day" +%Y-%m-%d ;fi ; done ` – Hjelm Feb 18 '11 at 09:13
-
Sorry didn't work should be `for i in `seq 0 365`; do date -d "2011-1-1 $i day" | grep -q Mon ; if [ $? = 0 ]; then date -d "2011-1-1 $i day" +%Y-%m-%d ;fi ; done ` – Hjelm Feb 18 '11 at 09:29
-
here's a version that works on mac os x and strips the potentially unwanted day of the week: ``for i in `seq 0 365`; do date -v+${i}d +'%a %m-%d'; done | sed -n "s/^Mon //p"`` – Alec Jacobson Sep 11 '13 at 12:23
-
To do it by incrementing instead of grepping, find offset till next Monday (here 3) then: `for i in \`seq 3 7 365\`` etc. – Jortstek Jan 28 '20 at 06:27
3
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

Pawel Veselov
- 3,996
- 7
- 44
- 62
-
Thanks, The output is exactly what i want. It is rather complex to understand thou. – Hjelm Feb 18 '11 at 08:27
-
-
Why, one liner with no comment what so ever is not a good solution if, whoever ask the question, don't understand anything about what you did. And I already removed the -1. – Anders Feb 18 '11 at 10:06
-
@Anders I got your comment the second I submitted the full version, lol – Pawel Veselov Feb 18 '11 at 10:09
3
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

Dennis Williamson
- 346,391
- 90
- 374
- 439
-
Bug. For some years the last week will begin in the following year. Offset is wrongly assimilated to date in the last section. Change `for ((w = d; w <= $(date -d "$y-12-31" '+%j'); w += 7))` to `for ((w = d; w <= ($(date -d "$y-12-31" '+%j')-1); w += 7))` – Jortstek Jul 17 '20 at 06:41
2
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

Howard
- 21
- 1
1
You can get the current week number using date
. Maybe you can sort on that:
$ date +%W -d '2011-02-18'
07

Sjoerd
- 74,049
- 16
- 131
- 175
-
Why didn't i think of that, i can convert the date in the logfile instead and then bin it into the weeks. – Hjelm Feb 18 '11 at 08:29