ColdFusion Muse

Ask-a-Muse: Getting a Specific Occurence of a Day

Mark Kruger February 26, 2009 7:02 PM ColdFusion Comments (2)

Muse reader James asks:
If a person picks a date on a calendar, say the first Tuesday in a month, how do you calculate date of the first Tuesday in the next month and each succeeding month there after?

This is an immensely complicated issue that will take you days and weeks to sort out. What you need is to know the day number you are looking for (1=Sunday, 2=Monday, etc), The month number (where 1 = January, 2 = February etc.), and the year (where 2009 = 2009, 20010 = 20010 etc.). Then you need to download this handy function from cflib.org called FirstXDayOfMonth, written by a Mr. Troy Pullis. Pass in your values and it will give you back the first occurrence of that day in the month. Add 7 to it get the second occurrence, 14 to get the third etc. Here is an example:

The Third Thursday

For a lark we'll take the date "2/19/2009" - the third Thursday in February and see if we can't figure out the third Thursday in march and April. First, we need to get our 3 variables - month, day of the week and year.

<cfscript>
    //The date to mirror.
    dt = createDate(2009,2,19);
    //next month
    month = month(dateadd('m',1,dt));
    year = year(dateadd('m',1,dt));
    dayOfWk = dayofweek(dt);
</cfscript>
Ok.. that's given us the variables dayOfWk=5 (Thursday), year=2009, month=3 (March). Now to call our function:
<cfscript>
    //The date to mirror.
    dt = createDate(2009,2,19);
    //next month
    month = month(dateadd('m',1,dt));
    year = year(dateadd('m',1,dt));
    dayOfWk = dayofweek(dt);
    // first thursday in march
    firstDt = FirstXDayOfMonth(
    //write it out for convenience
    writeoutput(firstDt);
</cfscript>
Our call returns 3/5/2009. A quick gander at the calendar shows us that yes indeed, march 5th is the first Thursday in march. All we have left to do now is add 14 days to this date:
<cfscript>
    thirdThur = dateadd('d',14,firstDt);
</cfscript>
This gives us the third Thursday of the month of March (or 3/19/2009). Of course you would need to wrap some logic around it if you are looking for a 5th occurrence - which is a possibility in every month except February.

Remember CFlib.org

One more note. Any time you have a question like this and you say to yourself "Self, surely you aren't the only one who's ever had this problem," then go take a peek at cflib.org or riaforge. Chances are you are right and someone has already solved your problem. In case you missed it, here is the code to the "firstXDayOfMonth" function.

<cfscript>
/**
* Returns a date object of the first occurrence of a specified day in the given month and year.
*
* @param day_number An integer in the range 1 - 7. 1=Sun, 2=Mon, 3=Tue, 4=Wed, 5=Thu, 6=Fri, 7=Sat. (Required)
* @param month_number Month value. (Required)
* @param year Year value. (Required)
* @return Returns a date object.
* @author Troy Pullis (tpullis@yahoo.com)
* @version 1, March 23, 2005
*/

function FirstXDayOfMonth(day_number,month_number,year) {
// date object of first day for given month/year
var start_of_month = CreateDate(year,month_number,1);
var daydiff = DayOfWeek(start_of_month) - day_number;
var return_date = "";
switch(daydiff) {
case "-1": return_date = DateAdd("d",1,start_of_month); break;
case "6": return_date = DateAdd("d",1,start_of_month); break;
case "-2": return_date = DateAdd("d",2,start_of_month); break;
case "5": return_date = DateAdd("d",2,start_of_month); break;
case "-3": return_date = DateAdd("d",3,start_of_month); break;
case "4": return_date = DateAdd("d",3,start_of_month); break;
case "-4": return_date = DateAdd("d",4,start_of_month); break;
case "3": return_date = DateAdd("d",4,start_of_month); break;
case "-5": return_date = DateAdd("d",5,start_of_month); break;
case "2": return_date = DateAdd("d",5,start_of_month); break;
case "-6": return_date = DateAdd("d",6,start_of_month); break;
case "1": return_date = DateAdd("d",6,start_of_month); break;
default: return_date = start_of_month; break;
// daydiff=0, default to first day in current month
} //end switch
return return_date;
}
</cfscript>

  • Share:

2 Comments

  • James Conlon's Gravatar
    Posted By
    James Conlon | 2/27/09 6:45 AM
    Thanks for your prompt response. I will add cflib.org to my list of Cold Fusion references.
  • James Conlon's Gravatar
    Posted By
    James Conlon | 4/9/09 1:20 PM
    It was suggested that I post how I used the code that Mark Provided

    Here is what I did. I took the out put of function FirstXDayOfMonth and calculated the correct day and week I needed. I hope that it will be useful to some one.

    <!--- Collect the variables to calculate the dates --->
    <cfset Conflicts = ''>
    <!--- get the day number for the day of the week --->
       <cfset daynumber= dayofweek(StartDate)>
       <!--- get the year --->
       <cfset year= datepart('yyyy',StartDate)>
       <!--- get the week that the start date occures in --->
       <cfset startweek= int((datepart('d',StartDate)/7))>
       <!--- set the startmonth for the date calculator loop --->
       <cfset startmonth=datepart('m',StartDate)>
       <!--- set the end month for the date calculator loop --->
       <cfset endmonth=((startmonth + months)-1)>
    <!--- loop thru and calculate each date to check --->

       <!--- date calculator and conflict checking loop --->
    <cfloop index="Month" from="#startmonth#" to="#endmonth#">


           <!--- check and see if the year needs to be incremented
           note this only works for one year--->
           <cfif month gt 12>
           <cfset year = year + 1>
    <cfset month = month - 12>
           </cfif>

           <!--- calculate the date to be checked--->
    <cfset Middate = FirstXDayOfMonth(#daynumber#,#Month#,#Year#)>


    <cfif startweek neq 0>
    <cfset checkdate = dateadd('d',startweek * 7,middate)>
    <cfelse>
    <cfset checkdate = middate>
    </cfif>

        <!---perform the conflict check--->
    <cfquery name="MonthCheck" datasource=#db_name# username=#db_uid# password=#db_pwd#>
    select id
    from sch_reservation_req as a
    where status = 'APPROVED'
    and bl_id = '#Reservation.bl_id#'
    and fl_id = '#Reservation.fl_id#'
    and rm_id = '#Reservation.rm_id#'
    and eq_id is null
    and exists (select id
    from sch_reservations as b
    where b.reservation_id = a.id
    and date_res = '#DateFormat(CheckDate, 'yyyy-mm-dd')#'
    and b.status <> 'CANCELLED'
    and (not((b.time_end <= '#StartTime#')
    or (b.time_start >= '#EndTime#'))))
    </cfquery>


    <!---update the conflict list--->
    <cfif MonthCheck.RecordCount is not 0>
    <cfset Conflicts = ListAppend(Conflicts, MonthCheck.ID)>
    </cfif>

    </cfloop>