Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations gkittelson on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

Find the Nth weekday in a given month and year 2

Status
Not open for further replies.

Mike Lewis

Programmer
Jan 10, 2003
17,516
Scotland
I was interested in Griff's offering in thread184-1817870. I'm constantly finding a need for this sort of date-manipulation function. With that in mind, I decided to resurrect one that I wrote several years ago, which determines the Nth occurrence of a given weekday (e.g. the 1st Monday or the 3rd Thursday) in a given month and year. It will also find the last occurrence, such as the last Sunday.

The parameters should be self-explanatory, but if you are in doubt, read the comments at the top of the code. I've tried to make it as straightforward as possible.

Code:
FUNCTION OrdinalDay
LPARAMETERS tnOrdinal, tnDay, tnMonth, tnYear
* Determines the date corresponding to the Nth weekday
* (for example, the 1st Monday or the 3rd Wednesday) of a
* given month and year; can also determine the last
* of a given weekday in the month and year.

* Parameters:
*  - The ordinal (1 = 1st, 2 = 2nd, etc) or -1 for the last.
*  - The weekday (1 = Monday, 2 = Tuesday, etc)
*			&& note: for this purpose, weeks start on Monday
*  - The month (1 - 12); defaults to current month
*  - The year; defaults to current year (must be later than 1752 because
*    of a quirk in VFP's GOMONTH() function)

* Examples:
*  - OrdinalDay(2, 4, 6, 2024) to find the 2nd Thursday in June 2024
*  - OrdinalDay(-1, 1, 12, 1900) to find the last Monday in December 1900
*  - OrdinalDay(1, 7) to find the first Sunday in the current month and year

* Returns:
*    The speficied date; returns an empty date if the specified date does 
*    not exist(which will occur if the ordinal is 5 and the specified month
*    and year does not have five of the specified days).

LOCAL llParamsOK, lnFirstOfMonth, lnDaysInMonth, lnDayOffset, ;
  lnWeekOffset, ldResult

* Deal with default parameters
tnMonth = EVL(tnMonth, MONTH(DATE()))
tnYear = EVL(tnYear, YEAR(DATE()))

* Validate parameters
llParamsOK = ;
  BETWEEN(tnMonth, 1, 12) AND ;
  BETWEEN(tnYear, 1753, 9999) AND ;
  (BETWEEN(tnOrdinal, 1, 5) OR tnOrdinal = -1)  AND ;
  BETWEEN(tnDay, 1, 7)
IF NOT llParamsOK
  ERROR "Invalid parameters(s)"
ENDIF

*Day of week on which first of the month falls
lnFirstOfMonth = DOW(DATE(tnYear, tnMonth, 1), 2)

* No. of days in the month
lnDaysInMonth = DAY(GOMONTH(DATE(tnYear, tnMonth, 1),1) - 1)

lnDayOffset = tnDay - lnFirstOfMonth + 1
IF lnDayOffset < 1
  lnDayOffset = lnDayOffset + 7
ENDIF

lnWeekOffset = IIF(tnOrdinal = -1, ;
  IIF(lnDayOffset + 28 > lnDaysInMonth, 21, 28), ;
  (tnOrdinal - 1) * 7)

* Calculate the required date, or make it blank if it does not exist
ldResult = ;
  IIF (lnDayOffset + lnWeekOffset > lnDaysInMonth, ;
  {}, ;
  DATE(tnYear, tnMonth, lnDayOffset + lnWeekOffset))

RETURN ldResult

I'd be grateful for your comments, corrections and bug reports.

Mike

__________________________________
Mike Lewis (Edinburgh, Scotland)

Visual FoxPro articles, tips and downloads
 
I searched the forum and found this thread184-1751319. With another one from you. Seems you improved it.

There's a link to thread184-1141890, too.



Chriss
 
Mike, I have somewhat similar code written in (classic) VB back in (eek) 2002 ...
 
Good heavens, Chris. I had completely forgotten I had already posted this. Surprising, as it was "only" seven years ago.

I can't claim any credit for my first chunk of code in that thread. It was done by a client of mine back in 1999, specifically to find the dates of MLK Day. If I remember right, my more generic version (in the same thread) was converted to VFP from a PHP version which I wrote for a website I was working on.

I prefer my latest version, posted above, as it uses an offset calculation rather than a loop (as you remarked yourself, Chris, in 2015).

Mike

__________________________________
Mike Lewis (Edinburgh, Scotland)

Visual FoxPro articles, tips and downloads
 
It seems quite an elegant solution to me B-)

Regards

Griff
Keep [Smile]ing

There are 10 kinds of people in the world, those who understand binary and those who don't.

I'm trying to cut down on the use of shrieks (exclamation marks), I'm told they are !good for you.

There is no place like G28 X0 Y0 Z0
 
An additional pointer to anyone interested or in need of performing date calculations, the iCal4VFP repository integrates a Recurrence Rule calculator that adheres to the RFC 5545/iCalendar specification.

Some example rules, as per above:
[ul]
[li][tt]FREQ=YEARLY;BYMONTH=6;BYDAY=2FR[/tt] (second Friday of June)[/li]
[li][tt]FREQ=YEARLY;BYMONTH=12;BYDAY=-1MO[/tt] (last Monday in December)[/li]
[/ul]

Of course, if just simple patterns such as these are required, more precise and oriented functions could (or should) be chosen over an iCalendar library.

The examples folder contains a demo that runs through all RFC 5545 examples. For those interested, I suggest running and studying it to get a grasp of what the specification and its VFP implementation are capable of.
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top