A reader recently asked me how I did the calendar on the right hand side of the blog, and specifically how I highlighted the current day. Then the reader asked if it was PHP. (The horror, the horror!) Building a calendar in ColdFusion is pretty simple, especially with the handy built-in ColdFusion date functions. In fact, the most difficult part of the entire process is laying out the HTML, as I'll explain later.
So let's start off with the simple stuff. We want to build a calendar for the current month. We can get the current date and time by using the Now() function. The next thing we need is the number of days in the month. ColdFusion provides the DaysInMonth() function just for that purpose. It accepts a date object, so we can pass in the result of now(). The last thing we need is the current day of the month. We can get that using using the Day() function. Here is an example showing all three functions.
The current date and time: #now()#<br>
There are #daysInMonth(now())# days in this month.<br>
The current day of the month is #day(now())#.<br>
</cfoutput>
Guess what - that's really all there is to it. The entire rest of this post will just deal with the HTML, which is a bit tricky. Now that your warned, let's get started.
A typical calendar is displayed using a table structure. Each row will start with Sunday and end with Saturday. Let's begin by creating a header for our calendar.
<table border="1" width="100%" height="100%">
<tr>
<cfloop index="x" from="1" to="7">
<th>#dayOfWeekAsString(x)#</th>
</cfloop>
</tr>
</cfoutput>
I start off with a table tag, and use width and height values of a 100%. This will create a calendar that covers the entire page. This is ugly. I know it. But I can't design, so forgive me. What's really cool here is that I'm not entering “Sunday”, “Monday”, etc. I just use the built-in ColdFusion function dayOfWeekAsString function. Not only does this allow me to be lazy, it also will be automatically localized if you run the code on a non-English system. This gives us a header. Now we need to start displaying dates. You might imagine simply looping from 1 to the number of the days in the month, with a new table row starting every 7 days. However, this won't work since the month doesn't always start on Sunday. What we need to do is "pad" the beginning of the calendar (and the end) with some blank days to represent the previous (and next) month.
There are a few ways we can do this, so the solution I'll provide is just one of them. What I'll do is first figure out what day of the week the first of the month is. For this we will use a few more date functions. First we will need to create a date object for the first of the month. Again, there are multiple ways of doing it, but I'll use CreateDate(). This function expects the year, month, and day. To get the current year and month, we use the... wait for it.... can you guess? Yes, the Year() and Month() functions. I really love ColdFusion. So, all together now - to create a date object for the first of the month, all we need is:
In case that was a bit too hard to read, this is the exact same code, broken down a bit:
<cfset thisMonth = month(now())>
<cfset firstOfTheMonth = createDate(thisYear, thisMonth, 1)>
So for so good. Now that we have a date object for the first of the month, we can use the DayOfWeek() function to figure out what day of the week it is.
This gives us a number between 1 and 7. Our pad will be the day of the week minus 1.
Now we need to create the first row of the calendar with empty boxes, if any:
<tr>
</cfoutput>
<cfif pad gt 0>
<cfoutput><td colspan="#pad#"> </td></cfoutput>
</cfif>
You may notice I'm using cfoutput even though I'm not outputting any dynamic values. I tend to always run my code under the setting that suppresses white space, so this is normally something I need to do - but you can remove it if you want.
So – at this point – we are now ready to output our dates. All we need to do is loop from 1 to to the number of days in the month. However, we have two "gotchas". First, we have to start a new table row after every 7th day. Secondly, since we may not be starting on the Sunday, we need to be sure we start a new row after the first Saturday.
This sounds complex, but it really isn't. We have one main loop, again, going from 1 to the number of days in the month. We also have a counter. This counter simply says, "When I get past 7, create a new row, and start counting from 1 again." Let's take a look at the code for this:
<cfset counter = pad + 1>
<cfloop index="x" from="1" to="#days#">
<cfoutput>
<td>#x#</td>
</cfoutput>
<cfset counter = counter + 1>
<cfif counter is 8>
<cfoutput></tr></cfoutput>
<cfif x lt days>
<cfset counter = 1>
<cfoutput>
<tr>
</cfoutput>
</cfif>
</cfif>
</cfloop>
Starting from the top - we begin by creating a variable, days, that represents the number of days in the month. Next, we create our counter variable. This will represent the day of the week. Since we have already covered "pad" days, we need to add one to pad for our initial counter. Now we begin the loop from 1 to days. For each day, we add one to counter. When counter becomes 8, we reset it to 1 and close the table row. Notice that we only reset the counter and start a new table row if we aren't on the last day.
If you run this code now, you will see a pretty calendar... except for where the days end. Just as we needed a pad in the front of the calendar, we need a pad at the end. Luckily, we can reuse the counter variable. If the number is anything but 8, we need to pad again. We can get our pad by just taking 8 and subtracting the counter value:
<cfset endPad = 8 - counter>
<cfoutput>
<td colspan="#endPad#"> </td>
</cfoutput>
</cfif>
Voila! A calendar. Not a very pretty one, but as Scotty used to say, I'm an engineer, not a beret-wearing designer. Oops, I almost forgot. How do we highlight the current day? All we need to do is check to see if x, our loop counter, is equal to the current day of the month. We can then apply any highlight, design, or whatever. The entire code for the calendar display, including the highlight for the current day, may be found below. If folks are interested, later in the week I'll show how to convert this to a custom tag so you can do things like, a) change the month, and b) pass in events.
<table border="1" width="100%" height="100%">
<tr>
<cfloop index="x" from="1" to="7">
<th>#dayOfWeekAsString(x)#</th>
</cfloop>
</tr>
</cfoutput>
<cfset firstOfTheMonth = createDate(year(now()), month(now()), 1)>
<cfset dow = dayofWeek(firstOfTheMonth)>
<cfset pad = dow - 1>
<cfoutput>
<tr>
</cfoutput>
<cfif pad gt 0>
<cfoutput><td colspan="#pad#"> </td></cfoutput>
</cfif>
<cfset days = daysInMonth(now())>
<cfset counter = pad + 1>
<cfloop index="x" from="1" to="#days#">
<cfif x is day(now())>
<cfoutput><td bgcolor="yellow"></cfoutput>
<cfelse>
<cfoutput><td></cfoutput>
</cfif>
<cfoutput>
#x#</td>
</cfoutput>
<cfset counter = counter + 1>
<cfif counter is 8>
<cfoutput></tr></cfoutput>
<cfif x lt days>
<cfset counter = 1>
<cfoutput>
<tr>
</cfoutput>
</cfif>
</cfif>
</cfloop>
<cfif counter is not 8>
<cfset endPad = 8 - counter>
<cfoutput>
<td colspan="#endPad#"> </td>
</cfoutput>
</cfif>
<cfoutput>
</table>
</cfoutput>
Archived Comments
Hi Ray,
Thanks for the calendar. Say you have an event calendar that is linked to a backend database, how do you make an event day a link. Just like you have done for your calendar where you can click on the certain days that you posted.
Ray, don't forget about the new CFCALENDAR tag.
...and if you're really "lazy", the folks over at Interakt have a new, steroid-enhanced, calendar tool for dreamweaver that does all the work for you.
Hey Ray...cool useful post! I've got a snippet around that I wrote that does the same thing with forward/back controls.
I think it's going to butcher the source, but pasting into a page then browsing to it should get the symbols back.
<cfparam name="url.calendardate" default="#now()#">
<cfset firstDOM = createDate(year(calendarDate), month(calendarDate), 1) />
<table border="1" width="100%">
<cfoutput>
<thead>
<tr>
<td><a href="#cgi.script_name#?calendarDate=#urlEncodedFormat(dateAdd("m", -1, calendarDate))#"><</a></td>
<td colspan="5" align="center">#dateFormat(calendarDate, "mmm yyyy")#
<td><a href="#cgi.script_name#?calendarDate=#urlEncodedFormat(dateAdd("m", 1, calendarDate))#">></a></td>
</tr>
<tr>
<cfloop from="1" to="7" index="i">
<td width="14%">#dayOfWeekAsString(i)#</td>
</cfloop>
</tr>
</thead>
</cfoutput>
<tbody>
<tr>
<cfloop from="1" to="#dayOfWeek(firstDOM) - 1#" index="i">
<td> </td>
</cfloop>
<cfoutput>
<cfloop from="1" to="#daysInMonth(firstDOM)#" index="i">
<cfset today = dateAdd("d", i - 1, firstDOM) />
<cfif i gt 1 and dayOfWeek(today) eq 1>
<tr>
</cfif>
<td><cfif i eq day(now())><strong>#i#</strong><cfelse>#i#</cfif></td>
<cfif dayOfWeek(today) eq 7>
</tr>
</cfif>
</cfloop>
<cfif dayOfWeek(today) neq 7>
<td colspan="#7 - dayOfWeek(today)#"> </td></tr>
</cfif>
</cfoutput>
</tr>
</tbody>
</table>
Yeah but if they use cfcalendar or interakt, they don't learn anything! ;)
Great tutorial. I've been at a loss to understand dates in CF, this is a great start. Thanks!
Paakay - when I get time, I'll do a followup that shows both the conversion of the code to a custom tag, and how to pass events to it so they highlight, show labels, links, etc.
yeah cfcalendar would do too but what ray is leaving out is that the calendar knows about iso dates (week starts on monday) and islamic dates (week starts on saturday) as well as it knows when to flop over into BIDI.
Paul - when you say "the calendar" do you mean the cfform calendar? Certainly mine wouldn't. Or would it - since the days of the week are all function based.
no, i meant your blog's calendar. cfcalendar's bound and gagged to the gregorian calendar, so while you could fiddle w/the display you could never get a correct BE or HE date to display (it would always interpert those dates as way in the future or way in the past so the days of the week, etc. would be screwed up). it's also pesky like that w/timezones, always casting to clientside tz, even if you did that already server side.
Ah yes, if folks are interested in a much more advanced calendar, they should check the code in blog.cfc. Good call.
Nice! Thanks!
Thanks a bundle. I am very happy that people like you exist. Please keep up the good work.
anychance for the full snipet of code for the calendar?
Sashi, it should be in the entry.
Hello Ray,
Thank you for your tutorial, it's been very helpful indeed. How can be made the calendar to be editable and add events on certain days?
Giulia, it really is a different topic altogether. You can have a database of events, and you would need to get the events in order to display them in the calendar.
I put together a sample page using Ray's code explaining one of the options you can use to add linkable info from your database with examples and complete code.
http://www.johnramon.com/CF...
I hope this helps anyone else out there trying to put together a calendar system. Thanks again Ray.
Nice job. Can I send you a bill for the license for my code? ;)
Let me add to the many deserved thanks, Ray! This is just the help I needed in getting a calendar off the ground in a pinch.
Please help ;)
I can't get this nice calendar start on monday. as european calendar.
How can I do this.
thanks in aadvance for your help ?
best regards
Gaill
Gaill - don't you know that America is number one and everyone needs to do things our way or the terrorists win? (Sorry, couldn't resist. ;)
So - I think I figured it out and it is a small mod. I first added this line to the very top:
<cfset dowList = "2,3,4,5,6,7,1">
I modified the first block of code like so:
<cfoutput>
<table border="1" width="100%" height="100%">
<tr>
<cfloop index="x" from="1" to="7">
<cfset theDow = listGetAt(dowList, x)>
<th>#dayOfWeekAsString(theDow)#</th>
</cfloop>
</tr>
</cfoutput>
Then I modified the one line that makes pad to this:
<cfset pad = listFind(dowList, dow) - 1>
This seemed to do it. If you need me to send you the entire file, please email me via the Contact address and include your email address.
Thanks for the cheap tutelage! It'll help a lot as a small reference calendar to use as an adjunct to a calendar construct I'm throwing together that will hold and parse values back to a SQLdb as the gui end of a company timesheet app. (basically, I'm a raw amateur--so I wrote out 7 sets of day numbers over cfinputs set to defined variables holding the SQL data--all within a cfif/cfelseif set that uses the DayOfWeek function to choose where the first day of the month falls. It ain't pretty yet, though. Every month shows 31 days. So I have to figure out an approach to that problem that I can wrap my pea-sized brain around.
Thanks again for this--and all the other great stuff!
Thanks so much, not only for the calendar but for the clarity of your directions. i learned a lot more than just how to design a calendar.
Just what I was Looking for - Thanks!
Hi Ray,
The calendar is great. I tried adapting it to be an event calendar for my site and it's working... except for one little detail. On months ending with anything other than 31, i get an error at the end of the calendar, although it does render in all cases and does show my event data.
There's not a great deal of info to go on, just that the error occurs in this line...
<cfset yourDate = createDate(#year_val#, #month_val#, #x#)>
I'm using dropdowns to select month and year values, I'm sure it's not very efficient but I'm pretty new to this. An ideas would be greatly appreciated.
<form name="cal_select" method="get" action="#CGI.script_name#">
<div align="center">Select Month - Select Year</div>
<p align="center">
<select name="get_month" onchange="month()">
<option>Select Month</option>
<cfloop from="1" to="12" step="1" index="x">
<cfoutput>
<option value="#x#">#monthasString(x)#</option>
</cfoutput>
</cfloop>
</select>
<select name="get_year" onchange="year()">
<option>Select Year</option>
<cfloop from="2003" to="2010" step="1" index="x">
<cfoutput>
<option value="#x#">#x#</option>
</cfoutput>
</cfloop>
</select>
<p align="center">
<input type="submit" name="Go" value="GO" />
<cfoutput>
<input name="month_val" value="#month(now())#" />
</cfoutput>
<cfoutput>
<input name="year_val" value="#year(now())#" />
</cfoutput>
<cfoutput>
</p></form>
<form name="caltest1" method="post">
<table border="1" width="100%">
<tr>
<cfloop index="x" from="1" to="7">
<th><span class="style2 style4">#dayOfWeekAsString(x)#</span></th>
</cfloop>
</tr>
</cfoutput>
<cfparam name="year_val" type="integer" default="#DatePart('yyyy', Now())#">
<cfparam name="month_val" type="integer" default="#DatePart('m', Now())#">
<cfset firstOfTheMonth = CreateDate(#year_val#, #month_val#, 1)>
<cfset dow = dayofWeek(firstOfTheMonth)>
<cfset pad = dow - 1>
<cfoutput>
<tr>
</cfoutput>
<cfif pad gt 0>
<cfoutput><td colspan="#pad#"> </td></cfoutput>
</cfif>
<cfset days = daysInMonth(#month_val#)>
<cfset counter = pad + 1>
<cfloop index="x" from="1" to="#days#">
<cfif x is day(now())>
<cfoutput><td bgcolor="yellow"></cfoutput>
<cfelse>
<cfoutput><td></cfoutput>
</cfif>
<cfset yourDate = createDate(#year_val#, #month_val#, #x#)>
<cfquery name="Events" datasource="4NYPElectrical">
SELECT *
FROM Events, Buildings
WHERE Buildings.BuildingID=Events.Site AND Start_Date=#CreateODBCDate(yourDate)#
</cfquery>
<cfoutput>
<div align="center">
<strong>#DateFormat(yourDate, "mmmm d, yyyy")#</strong></cfoutput>
<p> <cfoutput query="Events">
<div align="center">#BuildingName# - #Desc#<BR>
</div><p>
</cfoutput>
</td>
</div>
<cfset counter = counter + 1>
<cfif counter is 8>
<cfoutput></tr>
</cfoutput>
<cfif x lt days>
<cfset counter = 1>
<cfoutput>
<tr>
</cfoutput>
</cfif>
</cfif>
</cfloop>
<cfif counter is not 8>
<cfset endPad = 8 - counter>
<cfoutput>
<td colspan="#endPad#"> </td>
</tr></cfoutput>
</cfif>
<cfoutput>
</table>
</cfoutput>
<cfoutput>
<input name="date" value="#yourDate#" />
</cfoutput>
</form>
In the future, please do not post large code blocks. Thanks.
Change this line
<cfset days = daysInMonth(#month_val#)>
to
<cfset days = daysInMonth(firstOfTheMonth)>
Ray,
Thanks, worked great, sorry about the code!
- Dave
Hi Ray,
Thanx for the code...its nice and very much helpful too....
better to hide this: http://www.johnramon.com/CF...
HI Ray,
Thanks a lot for this post. I am glad that i used this code and this worked simply fine! A small question of mine would be....how can we fill in the last row with the next month's starting dates, that way there are no blank spaces?
Thanks in advance.
Regards,
Sam.
Simple, just increment a counter. So the first space is 1, then 2, etc.
Cool!! That worked.....thanks a lot....
I followed your idea of creating a cfwindow with javascript and ran into a couple of issues.....
1) It doesn't work with IE (i guess the event.pageX and event.pageY)
2) I am unable to pass URL variables to the window to display the events on a particular date....
Any help would be greatly appreciated!
Thanks beforehand!
Sam.
For 1- consult the web. :) I ran into myself but never bothered to fix it for IE, but it should be a simple matter of googling for "IE version of event.pageX"
As for 2, I'd need to know more about how you launch the window, but you can pass arguments to the URL.
HI Ray,
I appreciate your prompt response! some how i managed to solve the 'issue 1'. But still roaming around...for the 'issue2'
I have some records to show in the window(cfwindow) that are populated from Database. And these records are extracted basing on the date....so i need to pass the date as a URL variable to get the records!
Hope this makes sense...
Thanks a million!
Sam.
Well how are you launching the window now? What's that code? I assume it's based on a hyperlink on your event? (About to go dark for a few hours.)
Hi Ray,
I got the window working with URL variables!
Surprisingly it works fine when i press cntrl and hit the link....
I really have no idea what is wrong!
Here is my code..
<cfset event_date=DateFormat(#c1#,"yyyy-mm-dd")>
<cfform method="post">
<a href="" onclick="ColdFusion.Window.create('mywindow4','Results','dater_results_cf8.cfm?today=#event_date#',{x:100,y:100,modal:true,closable:true,draggable:true,resizable:true,center:true})">
<font size="4" color="red">#getalldates.recordcount#</font></a></cfform>
Thanks!
Sam.
I'm confused - you said it works when you ctrl click, but not when you do a regular click?
Hey Ray,
You got out early from the dark :)
And you guessed it correct. When i hold the cntrl key and click the link it works!
Unfortunately....the file size of my code which is done with ColdFusion windows has become massive and my boss wanted me to try it in jquery! Any suggestion in these lines?
Thanks!
Sam
Well jQuery UI has a dialog option that works much like CFWINDOW. You could try that.
Thanks Ray!
I will give it a shot.
Regards,
Sam,
Hi Ray,
I have a ColdFusion event calender populated from database. I started digging into jQuery and created some popup windows when the user clicks on an event on a certain day.
But how do i pass the date as url variable to the target page in jQuery? Any help is greatly appreciated!
Thanks,
Sam.
If ID is a unique variable for the event, you just add it to your link. Unless I'm missing something:
<a href="event.cfm?id=#id#">View</a>
Thanks Ray,
I will try it out...and let you know if there are any concerns.
Thanks for your time!
Sam.
Hi Ray,
I'm stuck with this....hope you can pull me off!
As i said, i have a calender with events populated into the table data from database. All i need is.... to show a popup window (like fading in and fading out) in response to the event clicked.
I tried doing it with a div tag basically hidden and faded in when the event is clicked.
When clicked on the event i need the popup window to show the events on that particular date.
Kindly put me on the right track (if at all this makes sense :))
Thanks,
Sam.
Hey Sam can you post your code in a test folder so we can see what your doing. From what I'm hearing I'm thinking your on click event is not triggering you call.
John Ramon
HI John,
Thanks for your kind response. I think you are right! my click event is not triggering the call to the page. I will try in these lines and post the code if i couldn't draw anything out of it.
Thanks again!
Sam.
HI Ray,
How can i post the form data from the calling page to the called page through url and using jQuery?
I tried using $.post("addtext_dater.cfm",{Data:date},show_popup()); I need the url variable to be the date.(something like addtext_dater.cfm?today=#date#)
I am getting the correct date into my jQuery function though!
Thanks in advance.
Sam.
If you passed the data value to the function, than your post just needs to include it, like you have {Data:date}. If date was the name of your argument, then it will be sent to addtext_dater.cfm as form.date.
HI Ray,
Thanks for the reply. I tried to dump the form variables in addtext_dater.cfm, the form field is empty. Am i going on wrong path in terms of jQuery?
Thanks in advance.
Sam.
Show us the link code and the JS function again.
Hi Ray,
Here is the outline of link and the js file.
<form id="myform"><input type="hidden" value="#date#" id="event_date"/><a href="" name="popup">+</a></form>
When the user clicks on "+" i am getting the href attribute via the name and triggering a .click() function in jQuery.
$.post("addtext_dater.cfm",{Data:date},show_popup());
In the call back function show_popup() i am just fading the div tag in.
The only problem is i guess with the post method where i am trying to append the date value to URL. I am kind of confused in how to pass the date value as a URL parameter to addtext_dater.cfm page.
Forgive me if this doesn't make any sense.
Thanks for your patience.
Sam.
I'm not quite getting it. Can you use Pastebin and put more of the code up there?
Basically your click event is tied to a link. Your ID is in a Form around that link. In theory, you can tell jQuery to get the Parent of the form, then 'find' the event_date id child under that. That would give you the hidden form field.
Hi Ray,
At last ....i figured it out!.....I used capital letter for a jQuery function and i guess that is where i messed up.......
I recoded a part of it and its working now.....
Here is my new doubt....i know its kind of a child stuff for you...but i am trying to get it straight....
I have a component with queries wrapped inside functions. When i create an object for the component in the calling page..does it contain all the functions (something like caching all the functions)?
Please let me know.
Thanks a million for your time and suggestion.
Sam.
@Sam - I don't mind answering this, but if you have more questions, lets switch to the Contact Form so as to keep things here on topic. The short answer is yes - when you create an instance of a component, it contains all the methods inside.
thnks a lots dude! simple basic calendar. awesome :D
Wow 6 years old and it just helped me out in a pinch. Making me look good!
Thanks Ray!
Always happy to know my code still works. ;)
Is there a simple way to show only the current day or the week view?
I'd like to be able to show only one day (today) or the snapshot of the current week.
This help me loads. This is how to get the start of the week:
<!--- show the start of the current week --->
<cfset dowdate = DateFormat(DateAdd("d", "-#DayOfWeek(Now()) - 1#", Now()), "dddd, mmmm d, yyyy")>
<cfset dow = DatePart("d",dowdate)>
Joe Rinehart's code had a small bug. It bolds the day of every month equal to the current day of the month (well, you get it). But I linked the scrolling.
i just recently learned coldfusion ang i need help from anyone who have experience in creating a calendar using hijri format.
Just discovered something crazy. When I put "DaysInMonth" in a DateFormat function for the month of December, one day got subtracted from the day display (i.e. "30" showed for December. But when I left the "DaysInMonth" function on its own, the correct day showed. Using CF11.
#DateFormat(now(), "mm")#/#DateFormat(DaysInMonth(now()), "dd")#/#DateFormat(now(), "yyyy")#
12/30/2014
#daysInMonth(now())#
31
Just FYI. Thanks for the blog Ray!
Woah. Got to be something simple. Looking.
Oh I see it. You are running dateFormat on 31, not the actual date. That's the problem. It is like you did dateFormat("31", "dd"). I bet it treated it like the year AD 31, month 1.