Friday, December 20, 2013

A Method For Determining A (Very) Specific Day Of The Month:


A recent discussion on the U2UG.org mail list concerned hypothetical questions to be asked of someone interviewing for a programming job. One proposed question was "How do you determine the last Thursday in a month?". This reminded me of one of the first functions I ever learned in college (COC3110 I think (UF 1983)). The premise of the function is that certain months have 30 days and the rest have 31 (except for February, which usually has 28 and sometimes 29). Further expanding the rules we know that, except for February, the odd months from January through July have 31 days while the even months have 30 days. The rest of the year is reversed. Even months have 31 days and odd months have 30 days. Finally we know that every four years is a leap year (unless it falls on a century year).

With these rules and constraints the function looks like this:

Note: All programming displayed on this site is for illustrative purposes only. Use at your own risk.



function days.in.month(date)
if date = "" then date = oconv(date(),"D4/")
month = date[1,2]
year = date[7,4]
lastDOM = ""
if month < 8 then
   * January through July
   if month = 2 then
      * February
      if mod(year,4)=0 and mod(year,100)<>0 then
         lastDOM = 29          ;* Feb leap year
      end else lastDOM = 28    ;* Feb non-leap year
   end else
      * Not February
      if mod(month,2)=0 then
         lastDOM = 30          ;* even month Jan to Jul
      end else lastDOM = 31    ;* odd  month Jan to Jul
   end
end else
   * August through December
   if mod(month,2)=0 then
      lastDOM = 31             ;* even month Aug to Dec
   end else lastDOM = 30       ;* odd  month Aug to Dec
end

return (lastDOM)
end




Note: There is no consideration in this example for handling a malformed date. The date format I used is mm/dd/yyyy. The goal of this exercise is to highlight the simple math required to apply to consistent rules, that results in a bullet-proof algorithm.

Programmers used to have to be concerned with optimization to reduce CPU cycles. Except for very few examples (loops requiring millions of iterations, financial batch operations, etc.) programming today can solve similar types of problems with case statement blocks or even many nested if-then-else blocks.

Here is an example:



function days.in.month(date)
if date = "" then date = oconv(date(),"D4/")
month = date[1,2]
year = date[7,4]
lastDOM = ""

begin case
   case month=1; lastDOM=31
   case month=2 and mod(year,4)=0 and mod(year,100)<>0
      lastDOM=29            ;* Leap year
   case month= 2; lastDOM=28 ;* Non-leap year
   case month= 3; lastDOM=31
   case month= 4; lastDOM=30
   case month= 5; lastDOM=31
   case month= 6; lastDOM=30
   case month= 7; lastDOM=31
   case month= 8; lastDOM=31
   case month= 9; lastDOM=30
   case month=10; lastDOM=31
   case month=11; lastDOM=30
   case month=12; lastDOM=31
end case

return (lastDOM)
end



Or better still only check for February and the 30 day months:


function days.in.month(date)
if date = "" then date = oconv(date(),"D4/")
month = date[1,2]
year = date[7,4]

lastDOM=31
begin case
   case month= 2 and mod(year,4)=0 and mod(year,100)<>0
      lastDOM=29             ;* Leap year
   case month= 2; lastDOM=28 ;* Non-leap year
   case month= 4; lastDOM=30
   case month= 6; lastDOM=30
   case month= 9; lastDOM=30
   case month=11; lastDOM=30
end case

return (lastDOM)
end



Note: There is no consideration in this example for handling a malformed date. The date format I used is mm/dd/yyyy.

This second example is the cleanest (in my humble opinion) and easier to understand. Running VLIST on the object code implies the first example has "tighter" opcode, with fewer potential cycles Update: As of v11.3.1 the first example has MORE instructions/branches. The second and third examples result in fewer instructions and smaller object code.

Programmers in today's development environment who solve problems using complex methods like the first example above are, hopefully, the exception to the norm.

Having shown two different ways (out of doubtless many more) to determine the last day of the month, I will complete the exercise by showing how I would then determine the last Thursday in any given month:



subroutine get.last.thursday(date,last.thursday.in.month)
deffun DAYS.IN.MONTH(ax)
month = date[1,2]
year = date[7,4]
num.days.in.month = DAYS.IN.MONTH(date)
y = 0
loop

   test.date = month:"/":num.days.in.month:"/":year
   until mod(iconv(test.date,"D4/")-y,7)=4
   y=y+1   ;* only increment the day counter if not Thursday
repeat
last.thursday.in.month =
month:"/":num.days.in.month-y:"/":year
return
end



Note: As before, there is no consideration in this example for handling a malformed date. The date format I used is mm/dd/yyyy.  The bit of math that is needed to determine the day of the week is mod(date,7). The date must be in internal format (number of days since December 31, 1967). Using this formula we know:
Sunday = 0
Monday = 1
Tuesday = 2
Wednesday = 3
Thursday = 4
Friday = 5
Saturday = 6

Now we can determine the date of the month for the last occurrence of  the seven days of the week.

To answer the job interview question, it would probably be sufficient to explain how you would determine the number of days in a month and how you would determine if a date is a Thursday. Then a brief description of a program to calculate the number of days in the month, and working backwards until the date is a Thursday. I think this would take no more than one or two minutes to explain at the most.

If you want to know if the person has been programming in MV DBMS systems for 20 years or more ask them "What is day ten thousand?"

No comments:

Post a Comment