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:
- The year
- The month number (January = 1, etc.)
- The day of the week to find (Sunday = 1, etc.)
- The occurrence (1 to first the first occurrence, etc.)
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).
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.