Calculating the Nth day of the month

A Visual FoxPro function for determining the Nth occurrence of a given weekday in a given month.

By Mike Lewis

I was recently asked how to use Visual Foxpro to determine the date of Martin Luther King Day. In the US, MLK Day is a holiday that falls on the third Monday in January. I was able to write some simple code to calculate that date. But it occurred to me afterwards that it would be more useful to make the code generic: so that it calculates the Nth occurrence of any day in any month. That sort of calculation would be useful for determining the dates of holidays (such as Thanksgiving, which is on the fourth Thursday of November in the US), as well as invoice due dates, interest payment dates, and other recurring dates.

Here, then, is my NthDayOfMonth function. You pass four parameters:

So, to find Martin Luther King Day in 2015, you would pass 2015 (the year), 1 (January), 2 (Monday), and 3 (because MLK Day is the third Monday).

The function returns a day number within the month. In the case of MLK Day in 2015, it returns 26, indicating that the date in question was the 26th of January.

The function will return 0 if the required date doesn't exist - if you ask for the fifth Friday in a month that only has four Fridays, for example. It also returns 0 if any of the parameters is out of range, such as a month number that's not between 1 and 12. Note that the year must be 1753 or later. That's because the function uses GOMONTH(), and this does not work with dates earlier than that year.

FUNCTION NthDayOfMonth
LPARAMETERS tnYear, tnMonth, tnDayToFind, tnNth

* Finds the Nth weekday in a given month and year (e.g. 3rd Monday of April 2016)

* Parameters: 
* - Year number; must be at least 1753
* - Month number; January = 1, etc.
* - Day to find, Sunday = 1, etc
* - Occurrence to find; e.g. 3 to find the 3rd instance of the specified day.

* Returns the day number within the month; returns 0 if any of the 
* parameters is out of range, or if the date doesn't exist (e.g. if you ask 
* for the 5th Friday in a month that only has four Fridays).

LOCAL lnCounter, lnI, lnDaysInMonth, lnReply

lnReply = 0

* Deal with out-of-range parameters
IF NOT BETWEEN(tnYear, 1753, 9999) OR ;
   NOT BETWEEN(tnMonth, 1, 12) OR ;
   NOT BETWEEN(tnDayToFind, 1, 7) OR ;
   NOT BETWEEN(tnNth, 1, 5)
   
   RETURN lnReply
   
ENDIF 

* How many days in the month?
lnDaysInMonth = DAY(GOMONTH(DATE(tnYear, tnMonth, 1), 1) - 1)

* Loop through the days in the month
lnCounter = 0
FOR lnI = 1 TO lnDaysInMonth
   IF DOW(DATE(tnYear, tnMonth, lnI), 1) = tnDayToFind
     lnCounter = lnCounter + 1 
     IF lnCounter = tnNth
       * This is the one we want
       lnReply = lnI
       EXIT
     ENDIF
   ENDIF
ENDFOR 

RETURN lnReply

Going further, it's possible to use the function to determine the last occurrence of a given day in the month:

FUNCTION LastOfMonth
LPARAMETERS tnYear, tnMonth, tnDayToFind

RETURN MAX(NthDayOfMonth(tnYear, tnMonth, tnDayToFind, 5), ; 
  NthDayOfMonth(tnYear, tnMonth, tnDayToFind, 4))

This takes the same first three parameters as NthDayOfMonth. It simply determines the fourth and fifth occurrences of the required day, and returns whichever of those is the greater (on the basis that, if there are only four occurrences, the fifth will be zero).

June 2015

Please note: The information given on this site has been carefully checked and is believed to be correct, but no legal liability can be accepted for its use. Do not use code, components or techniques unless you are satisfied that they will work correctly with your sites or applications.

If you found this article useful, please tell your friends: