BASIC User Group > Code Challenges

Calculate week number

(1/4) > >>

jalih:
Your challenge is to write a procedure that returns the week number for a given date. If week number belongs to a week numbering of the previous year then that week number should be returned instead of the zero week.

Here is my PL/I version using bit strings to turn calculations into simple string search:

--- Code: ---
*PROCESS MARGINS(1, 140);

test: proc options (main);
dcl 1 date_type based,
2 day   pic'99',
2 month pic'99',
2 year  pic'9999';

dcl d like date_type;

/* fill date structure */
d.day   = '06';
d.month = '11';
d.year =  '2018';

/* test our proggy */
put skip list (trim(week(d)));

/****************************************************************************************************************/
week: proc (day) returns (fixed bin (31));
dcl day like date_type;

dcl (from, to) like date_type;
dcl (days_from, days_to) fixed bin (31);
dcl total_days fixed bin (31);
dcl week_index fixed bin (31);
dcl weeknro fixed bin (31);
dcl result bit (*) aligned controlled;
dcl weeks (7) bit (7) aligned nonasgn
init ('0100000'b, '0010000'b, '0001000'b, '0000100'b, '0000100'b, '0000010'b, '0000001'b);
dcl masks (7) bit (7) aligned nonasgn
init ('0111100'b, '1111000'b, '1110001'b, '1100011'b, '1000111'b, '0001111'b, '0011110'b);
dcl bool bit (1);

dcl (days, weekday, tally, substr, lbound, hbound, copy, omitted) builtin;

/* we start from first of january, naturally */
from.day   = '01';
from.month = '01';
from.year  = day.year;

to = day;

days_from = days(string(from), 'DDMMYYYY');
days_to   = days(string(to), 'DDMMYYYY');

/* calculate required length for the bit string */
total_days = days_to - days_from + 1;

/* allocate bit string, holding date range */
allocate result bit (total_days);

/* fill bit string with correct rotation of the week pattern */
week_index = weekday(days_from);
result = copy(weeks(week_index), total_days / 7) || substr(weeks(week_index), 1, mod(total_days, 7));

/* find the number of weeks starting on monday */
weeknro = tally(result, '1'b);

/* free allocated storage */
free result;

/* first week can be partial and if it includes thursday, then add it to the result */
if bool then weeknro += 1;

/* maybe first days of the year belong to week numbering of the previous year? */
if weeknro = 0 then
do;
from.year -= 1;
to = from;
to.day   = '31';
to.month = '12';

days_from = days(string(from), 'DDMMYYYY');
days_to   = days(string(to), 'DDMMYYYY');

total_days = days_to - days_from + 1;
allocate result bit (total_days);

week_index = weekday(days_from);

result = copy(weeks(week_index), total_days / 7) || substr(weeks(week_index), 1, mod(total_days, 7));
weeknro = tally(result, '1'b);
free result;
end;
return (weeknro);
end week;
/****************************************************************************************************************/
end test;

--- End code ---

John:
Perl

--- Code: Perl ---use DateTime; print DateTime->now->week_number,"\n";

\$ perl weeknow.pl
45
\$

jalih:

--- Quote from: John on November 06, 2018, 11:27:09 am ---Perl

--- Code: Perl ---use DateTime; print DateTime->now->week_number,"\n";
--- End quote ---

Okay, that is certainly shorter than my PL/I version.  ???

John:
PHP

--- Code: PHP ---<?phpecho date('W')."\n";?>

\$ php weeknow.php
45
\$

John:
Python

--- Code: Python ---from datetime import date print date.today().isocalendar()[1]

\$ python weeknow.py
45
\$