7

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?

Hjelm
  • 73
  • 1
  • 3

5 Answers5

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
  • Readability is crap also you should consider commenting the code. -1. – Anders Feb 18 '11 at 09:59
  • 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