Tom's Guide | Tom's Hardware | Tom's Games
![]() |
![]() |
![]() |
I want to calculate a weeknumber out of a given date.
This is easy for the systemdate (date +%V), but how do I do this for a given date?

1) turn the date into the 'day of year' (ie Jan 1 is 1, Jan 31 is 31, Feb 1 is 32, etc)
2) divide 'day of year' by 7 using integer division, then if there is a remainder, add 1
To turn the date into 'day of year', I usually make an array that holds the 'day of year' for the first of each month (non-leap year). For example
int arr = {1,32,60,91....}
Then you figure out what month it is, and look up the value in the array for the month. This gives you the 'day of year' for the first of the month (ie April 1 is 91). Then add the day of month. So April 15 is (91 + 15 - 1).
(you could adjust each array entry down by one (-1) so that you didn't have to subtract one each time)
Then you have to adjust for leap year if necessary.
Be sure to come back and let us know if our suggestions helped!

The problem with dividing the nth day of the year by 7 is that that works only when the first week of the year is a whole week (Jan 1 = Sunday). And yes, it would certainly be nice if we all had GNU date. I don't on HP-UX. So here is one clumsy solution for determining week # for non-GNU date.
I build a file of calendar months, then process the file with awk, which will count the weeks (lines) through the week containing the target day. Partial weeks that begin or end a month are counted as half weeks since together those will count as 1, except for the very first week of the year, which is always counted as 1. If I end up on a half count, bump up to the whole.
MO=$1
DAY=$2
YEAR=$3calfile=calfile$$
rm $calfile 2> /dev/nullmo=1
while [ $mo -le $MO ]
do
cal $mo $YEAR >> $calfile
((mo=mo+1))
doneawk -v MO=$MO -v DAY=$DAY '
{if (match(substr($1,1,1),"[A-Z]"))
{getline # bypass hdrs
mo++
next}
if (NF==7 || mo==1 && $1==1 )
weeks=weeks+1
else
if (NF>0)
weeks=weeks+.5
if (mo==MO && $NF>=DAY)
exit
}
END {
weeks=int(weeks+.5)
print "Week number = " weeks
}' $calfilerm $calfile
./weekcalc.sh 11 23 2004
Week number = 48date +%V
48

Any reason why this wouldn't work?
#!/bin/ksh
function figureWeek
{
today=$(date +%j)
(( theWeek = ((( today - $# ) / 7) + 1 ) ))
print "This is week $theWeek"}
year=$(date +%Y)
counter=0
cal 01 $year | while read line
do
if [[ $counter = 2 ]]
then
figureWeek $line
break
else
(( counter += 1 ))
fi
done

Thank you all for your suggestions and solutions.
I had hoped there was a general solution like the GNU-date command. Unfortunately I do not have the GNU-date function available so "date -d '12/25/1984' '+%V'" doesn't work in my environment.
The solution of Don has the problem that januari 1 should be a Sunday.
Jerry's solution gives an American weeknumber and I need (I forgot to tell you) an ISO-weeknumber.
It looks as if Jim's solution works, but the solution is a little bit long.
To solve the problem I've decided to take a sidestep to Oracle.
WEEK=`printf "$(echo "set echo off feedback off heading off pagesize 0\n select to_char(to_date('${DATUM}','yyyymmdd'),'iw') from dual;" | SQLPLUS -s / )"`
solves the problem for me.

I like Jerry's approach of calling cal only once to see how the year starts. I condensed the code a bit and adjusted the formula. And since the requirement is to compute week# from any given date, I added logic to convert a given date to jday (as Don suggests).
I suspect it is a bit more code than Ron wants, but he could use it when his database is shut for backup. ;-)
mm=$1
dd=$2
yyyy=$3((yyyy%100)) && ((leap=!(yyyy%4))) || ((leap=!(yyyy%400)))
if [ $leap -eq 0 ] ; then
set -A dim 0 0 31 59 90 120 151 181 212 243 273 304 334 365
else
set -A dim 0 0 31 60 91 121 152 182 213 244 274 305 335 366
fi((jday=dim[$mm]+dd))
cal 01 $yyyy |
awk '{getline;getline;print NF;exit}' |
read firstweek
((Week=(jday+13-$firstweek)/7))
print "$mm/$dd/$yyyy: jday=$jday week=$Week"weekcalc.sh 01 03 2004
01/03/2004: jday=3 week=1weekcalc.sh 01 04 2004
01/04/2004: jday=4 week=2weekcalc.sh 11 24 2004
11/24/2004: jday=329 week=48

Jim thank you for your extra solution.
It has a problem however.
In ISO format week 1 off 2004 starts 12/29/03
Your solution give week 53 off 2003.1/2/05 must give week 53 off 2004
1/3/05 is week 1 of 2005I've tried to change your program, I did not find a good resolution (probably because I'm only a novice Unix programmer :-)). To solve the problem you have to find out which day is januari 1st. If it is Thursday or later than week 1 start in december of the previous year. If it is Monday to Wednesday, week 1 starts in the new year and the previous year has a week 53.
So I think with an "if .. then .. else .. fi" in the right place(s) it should be easy but I didn't find the right solution.
Don't spend to much time on this, because I have the Oracle solution implemented already. But if you think it's easy (and you can do it in a couple of minutes) I am curious about the solution.
I won't reply until monday because my weekend starts in 10 minutes.

Ron, thanks for explaining the rules for ISO week. I don't like to leave things undone, so some time this week I will find some time to adjust the code - and I will use Oracle to confirm results.

Calculation of ISO week turns out to be somewhat involved, but still I managed to calculate it with just a single call to cal to establish how January (for the year of the requested date) starts off.
ISO weeks begin on Mondays, and week #1 of each year is the first week having a majority of its days (4+) in the current year. This is also the week that contains Jan 4. The first week of a year can begin as early as Dec 29 of the previous year. Also, as many as 3 days of January can be in the last week of the previous year, which could be either week 52 or 53.
# !/bin/ksh
calcweek() {
if [ $LEAP -eq 0 ] ; then
set -A DIM 0 0 31 59 90 120 151 181 212 243 273 304 334
else
set -A DIM 0 0 31 60 91 121 152 182 213 244 274 305 335
fi
((JDAY=DIM[$MM]+DD))
((WEEK=(JDAY+$1)/7))
echo $YEAR-$WEEK
}MM=$1
DD=$2
YEAR=$3MMDD=$MM$DD
typeset -Z2 WEEK((YEAR%100)) && ((LEAP=!(YEAR%4))) || ((LEAP=!(YEAR%400)))
cal 01 $YEAR |
awk '{getline;getline;print NF;exit}' |
read W1Kcase $W1K$LEAP in
70) echo '0101 52 9999 5' ;;
10) echo '0102 XX 9999 4' ;;
20) echo '0103 53 9999 3' ;;
30) echo '0000 00 9999 9' ;;
40) echo '0000 00 1229 8' ;;
50) echo '0000 00 1230 7' ;;
60) echo '0000 00 1231 6' ;;
71) echo '0101 52 1231 5' ;;
11) echo '0102 52 9999 4' ;;
21) echo '0103 53 9999 3' ;;
31) echo '0000 00 9999 9' ;;
41) echo '0000 00 9999 8' ;;
51) echo '0000 00 1229 7' ;;
61) echo '0000 00 1230 6' ;;
esac | read JCUT PWK DCUT ADJif [ $MMDD -le $JCUT ] ; then
if [ $PWK = 'XX' ] ; then
((PYEAR=YEAR-1))
((PYEAR%100)) && ((PLEAP=!(PYEAR%4))) || ((PLEAP=!
(PYEAR%400)))
((PWK=52+PLEAP))
fi
echo $((YEAR=YEAR-1))-$PWK
else
if [ $MMDD -ge $DCUT ] ; then
echo $((YEAR=YEAR+1))-01
else
calcweek $ADJ
fi
fiexit 0
isoweek.sh 12 01 2004
2004-49

Jim, thank you for taking the time to write this solution.
As far as I've able to test it, it works.I'll keep this solution in mind for future use.

Thanks for the feedback. Quite often, I write code more for the challenge and practice rather than the practicality of it. perl may even have an ISO week function.
But now I certainly know what ISO week is.

![]() |
![]() |
![]() |

This post is quite old and has been locked from receiving new replies. Please create a new posting instead.
| Ads by Google |