week number 1 - 5

bino

Member
Hi all this may seem like a dumb question but do any of you have a good method to obtain the week number from a date etc.

I know how to get the week number for the year say 38 etc, but I was wondering if their is a method to get the week number for a month from 1 - 5.
 

joey.jeremiah

ProgressTalk Moderator
Staff member
if you're looking to find the week number in a certain year
subtract the date from the beginning of the year date and divide by 7

you can write a function that does that
maybe also start a generic procedure library ( super procedure )
 

bino

Member
Thanks for the reply. I'll give that a shot.

update:
Actually I wasnt thinking. What you provided is what Im already doing with my procedure. I get date and output the (year)week number and the from and thru date for that week.

Code:
/*********************************/
procedure ip-get-week-range:
/*********************************/
def var    v-week-number         as int   no-undo.
def var    v-dStart              as date  no-undo.
def var    v-load-date           as date  no-undo.
def var    v-fm-date             as date  no-undo.
def var    v-year                as int   no-undo.
 
def input  parameter ip-date         as date  no-undo.
def output parameter op-week-num     as int   no-undo.
def output parameter op-wk-from-date as date  no-undo.
def output parameter op-wk-thru-date as date  no-undo.
 
assign v-load-date     = ip-date
       v-dStart        = date(1,1,year(v-load-date)).
 
[COLOR=darkred]the piece mentioned above[/COLOR]
assign v-week-number = INT(TRUNC((v-load-date - v-dStart + 
                       WEEKDAY(v-dStart) - 2) / 7,0)).
 
assign op-wk-from-date = v-dStart + v-week-number * 7 - 5
       op-wk-thru-date = v-dStart + v-week-number * 7 - 1
       op-week-num     = v-week-number.
end procedure. /* ip-get-week-range */

what I am trying to do is get the month week number. It would be a value from 1 - 5, NOT a value from 1 thru 52.
 
It's not at all a dumb question. Here's an algorithm in Delphi which claims to do what you require. It should be a simple exercise to convert it to Progress.

Please post it if you do.

http://www.delphipages.com/threads/thread.cfm?ID=70191&G=70167


Be aware there are locale issues involved, and assumptions about how a week fits in a month, but it should provide you with a starting point.


----

Quote:


As promised here comes my adaption of the ISO8601 week of month algorithm:

function MyWeekOfMonth(date:TDateTime):integer;
var
y,m,d: word;
firstofmonth,
firstthursday,
FirstWeekStart: TDateTime;
h: integer;
begin
decodedate(date,y,m,d);
FirstOfMonth:=encodedate(y,m,1);
h:=dayOfWeek(FirstofMonth);
FirstThursday:=FirstofMonth+((12-h) mod 7);
FirstWeekStart:=FirstThursday-3;
if trunc(date)<FirstWeekStart then
result:=MyWeekofMonth(FirstofMonth-1) (* last day of previous month *)
else
result:=(round(trunc(date)-FirstWeekStart) div 7)+1;
if m=12 then begin
inc(y);
m:=0;
end;
(* check if the day is already in the first week of the next month *)
FirstOfMonth:=encodedate(y,m+1,1);
h:=dayOfWeek(FirstofMonth);
FirstThursday:=FirstofMonth+((12-h) mod 7);
FirstWeekStart:=FirstThursday-3;
if FirstWeekStart<=date then
result:=MyWeekofMonth(FirstThursday);
end;

Notice that the first days of a month can belong to the last week of the previous month, or the last days of a month can belong to first week of the next month. In these cases the above algorithm will return that number. If you just want to count the week in the month by setting the 1st to week 1 the algorithm will be much easier:

function MyWeekOfMonth(date:TDateTime):integer;
var
y,m,d: word;
firstofmonth,
firstthursday,
FirstWeekStart: TDateTime;
h: integer;
begin
decodedate(date,y,m,d);
FirstOfMonth:=encodedate(y,m,1);
h:=dayOfWeek(FirstofMonth);
FirstThursday:=FirstofMonth+((12-h) mod 7);
FirstWeekStart:=FirstThursday-3;
if FirstWeekStart<date then
result:=(round(trunc(date)-FirstWeekStart) div 7)+2
else
result:=1;
end;

------

HTH

Lee
 
To get you started, here's a straight translation - I don't have time at the moment to debug it...


FUNCTION div RETURNS INTEGER
(i1 AS INTEGER,
i2 AS INTEGER):

RETURN INTEGER(TRUNCATE(i1 / i2,0)). <<<EDIT: changed from ROUND

END FUNCTION.


FUNCTION MyWeekOfMonth RETURNS INTEGER
(iDate AS DATE)
:


DEF VAR Y AS INTEGER NO-UNDO.
DEF VAR m AS INTEGER NO-UNDO.
DEF VAR d AS INTEGER NO-UNDO.


DEF VAR firstofmonth AS DATE NO-UNDO.
DEF VAR firstthursday AS DATE NO-UNDO.
DEF VAR FirstWeekStart AS DATE NO-UNDO.

DEF VAR h AS INTEGER NO-UNDO.

DEF VAR iREsult AS INTEGER NO-UNDO.



RUN SplitDate(iDate, OUTPUT y, OUTPUT m, OUTPUT d).


FirstOfMonth = DATE(m,1,Y).

h = WEEKDAY(FirstofMonth).

FirstThursday = FirstofMonth + ((12 - h) mod 7).

FirstWeekStart = FirstThursday - 3.

if iDate < FirstWeekStart then
iResult = MyWeekofMonth(FirstofMonth - 1). /* last day of previous month */
else
iResult = div((idate - FirstWeekStart) , 7) + 1.

if m = 12 then
DO:
Y = Y + 1.
m = 0.
END.

/* check if the day is already in the first week of the next month */
FirstOfMonth = DATE(m + 1, 1, Y).
h = WEEKDAY(FirstofMonth).
FirstThursday = FirstofMonth + ( (12 - h) mod 7).
FirstWeekStart = FirstThursday - 3.

if FirstWeekStart <= iDate then
iResult = MyWeekofMonth(FirstThursday).

RETURN iResult.

END FUNCTION.



PROCEDURE SplitDate:

DEF INPUT PARAMETER myDate AS DATE NO-UNDO.

DEF OUTPUT PARAMETER yy AS INTEGER NO-UNDO.
DEF OUTPUT PARAMETER mm AS INTEGER NO-UNDO.
DEF OUTPUT PARAMETER dd AS INTEGER NO-UNDO.


yy = YEAR(myDate).
mm = MONTH(mydate).
dd = DAY(mydate).



END PROCEDURE.



MESSAGE myweekofmonth(date('28/09/05')) <<<Correct
VIEW-AS ALERT-BOX INFO BUTTONS OK.

MESSAGE myweekofmonth(date('15/10/05')) <<<Incorrect
VIEW-AS ALERT-BOX INFO BUTTONS OK.
 
This rewrite still doesn't quite work, but it's a lot simpler. I think it's too simple to make sense logic wise - I must be missing something obvious (I'm tired) - someone please point it out.

FUNCTION MyWeekOfMonth RETURNS INTEGER
(iDate AS DATE)
:


DEF VAR Y AS INTEGER NO-UNDO.
DEF VAR m AS INTEGER NO-UNDO.
DEF VAR d AS INTEGER NO-UNDO.


DEF VAR firstofmonth AS DATE NO-UNDO.

RUN SplitDate(iDate, OUTPUT y, OUTPUT m, OUTPUT d).


FirstOfMonth = DATE(m,1,Y).


RETURN (div((idate - FirstOfMonth + 1), 7) + 1)
.


END FUNCTION.
 

joey.jeremiah

ProgressTalk Moderator
Staff member
<snippet>

function WeekOfTheMonth returns integer ( pdDate as date ):

/*------------------------------------------------------------
you can calculate it this way -
1. how many weeks have past ( day div 7 )
2. and if day entered the next week also count that week
i.e if day mod 7 ne 0 then + 1

if you take ( day div 7 ) + 1
( day div 7 ) - will give you how many weeks have past
+ 1 - but even if it hasn't entered the next week
i.e. exactly dividable by 7, it will still add 1
--------------------------------------------------------------*/

return int( trunc( day( pdDate ) / 7, 0 ) )

+ ( if day( pdDate ) mod 7 ne 0 then 1 else 0 ).

end function. /* WeekOfTheMonth */



display
WeekOfTheMonth( 10/2/05 ) skip /* israel, mdy */
WeekOfTheMonth( 10/14/05 ) skip
WeekOfTheMonth( 10/15/05 ) skip
WeekOfTheMonth( 10/29/05 ).

</snippet>

please don't take this the wrong way ...

there's no need to modularize things to death, it's progress not pascal
 

bino

Member
My slight rewrite with Lees help I just removed some of the internal functions. Need to test this dont take it verbatim. Joey your rewrite is a a lot cleaner. I was trying to base this of the delphi program, maybe too literally =).

The one thing the code below does do is account for the fact that the last week of the previous month can belong to the first week of the next month. The date 09/04/05 would return 2. With Joeys snippet above the date 09/04/05 would return 1 and consider 09/01/05 - 09/03/05 as 1.

Thanks to both of you for the great help.

Code:
def var v-input-date        as date no-undo.
def var v-year              as int  no-undo.
def var v-month             as int  no-undo.
def var v-day               as int  no-undo.
def var v-date              as int  no-undo.
def var v-firstofmonth      as date no-undo.
def var v-firstthursday     as date no-undo.
def var v-firstweekstart    as date no-undo.
def var h                   as int  no-undo.
def var v-month-weeknum     as int  no-undo.
def var v-month-weeknum2    as int  no-undo.
 
function MyWeekOfMonth returns int ( ipDate as date ):
assign v-year           = YEAR(ipDate)
       v-month          = MONTH(ipDate)
       v-day            = DAY(ipDate)
       v-firstofmonth   = date(v-month,1,v-year)
       h                = WEEKDAY(v-firstofmonth)
       v-firstthursday  = v-firstofmonth + ((12 - h) mod 7)
       v-firstweekstart = v-firstofmonth - 3.
 
if ipDate < v-firstweekstart then
assign v-month-weeknum = MyWeekofMonth(v-firstofmonth - 1). /* last day of prev month */
else
assign v-month-weeknum = ((ipDate - v-firstweekstart) / 7) + 1.
 
if v-month = 12 then 
do:
   v-year = v-year + 1.
   v-month = 0.
end.
/* is day in first week of month */
assign v-firstofmonth   = DATE(v-month + 1, 1, v-year)
       h                = WEEKDAY(v-firstofmonth)
       v-firstthursday  = v-firstofmonth + ( (12 - h) mod 7)
       v-firstweekstart = v-firstthursday - 3.
if v-firstweekstart <= ipDate then
v-month-weeknum = MyWeekofMonth(v-firstthursday).
return v-month-weeknum.
end function.
display MyWeekOfMonth( 09/21/05 ).
 

joey.jeremiah

ProgressTalk Moderator
Staff member
this is a sesame street type problem all you need is 2 lines of code ( actually one expression ), you've turned this into a "project".

think what you're trying to do before writing ...
why did we even go to delphi ???

<snippet>

int( trunc( day( pdDate ) / 7, 0 ) )

+ ( if day( pdDate ) mod 7 ne 0 then 1 else 0 ).

</snippet>
 

bino

Member
Your code is two lines but it returns the wrong value for my purpose. I need to count each week in the month including any partial first or last week.

Your code would return 1 for 09/04/05 and 4 for 09/25/05. counting 4 weeks.

The example that Lee and I were working on accounts for this, although it could be simplified. It counts 5 weeks. I know this seems blatantly simple, but even Progress Knowledge base doesnt have an entry example.

Everything is about calculating the week of the year or the day of a week. Nothing for the week of the month.
 

joey.jeremiah

ProgressTalk Moderator
Staff member
the 5th week is from day 29 and upwards. ( scratch head ) maybe you're thinking 31 minus 7 ???

and the posted function would ofcourse return that. it returns the first ... all the way to the 5th and last week of the month

there's no reason this sort of remedial stuff should be in the knowledge base. for the same reason the multiplication table doesn't need to be there


the most complicated, literal way that i can think of doing this
and which returns the same results as the function i posted -

define var iday as int no-undo.
iday = day( dateparam ).

if iday >= 1 and iday <= 7 then return 1.
else if iday >= 8 and iday <= 14 then return 2.
else if iday >= 15 and iday <= 21 then return 3.
else if iday >= 22 and iday <= 28 then return 4.
else /* if iday >= 29 and iday <= 31 then */ return 5.
 
"no need to modularise this to death", "remedial", etc.

I wanted to recognise the Delphi original had taken into account 'special considerations' for calculating the week no.

The modularisation occurred because there were two Delphi routines, both using functions not available in Progress (DIV, decodedate). It was intended as a straight translation for Bino to use as a basis for his procedure, not as a definitive 4GL solution. I didn't have time to think about it in Progress terms (and it seemed sensible to use a pre-existing algorithm).

To clarify - I didn't actually analyse what the Delphi routine was doing, and neither did I spend much time trying to invent my own algorithm (I had got the impression from a quick search that it wan't quite as simple as you imply) - I just translated when I had a few minutes. My solution is over complicated as a result - but it was intended as a stepping stone, which Bino seems to have used successfully.

I was just trying to help - however, I should have waited till I had a final solution, rather than posting Work In Progress. I was aware though that my work commitments wouldn't have enabled me to post an answer at a convenient pace for Bino.

Anyway Bino, I'm glad it helped a little.

Lee
 

bino

Member
Lee your Delphi example etc was exactly what I was looking for because it counts for the fact that the first or last week of a month can follow over into the previous or next month. Thank you.
 

ddavis

New Member
Well, it's a little obfuscated, but it will work... basically add to the day of date, the day of the week of the first month so you're counting from the first day of the first week of the month even though it's in the previous month.

I subtract 2 because (1) the weekday is not zero based and (2) to back up one extra day to the week number flips on sunday instead of Sat.

Then div by 7 and add 1 (because the first week is week 0 ... ).

FUNCTION weekOfMonth RETURNS INTEGER
( INPUT i-date AS DATE ).

RETURN INTE(TRUNC((DAY(i-date) + WEEKDAY(date(MONTH(i-date),1,YEAR(i-date))) - 2) / 7, 0)) + 1.

END FUNCTION.


Hope you have fun with that.

DD
 
Top