Skip to main content
Ben Nadel at RIA Unleashed (Nov. 2009) with: Andy Powell
Ben Nadel at RIA Unleashed (Nov. 2009) with: Andy Powell ( @umAndy )

A Small Calendar Utility For Reference

By on

I created a rather small calendar utility for use during project management meetings. We usually brain storm a project and use the Windows date/time calendar (in the icon tray) to think out our time estimates. The problem is that calendar sucks and only has one month at a time (and moving from month to month is a pain). My small calendar utility, written in ColdFusion with some Javascript, displays a year's worth of calendars and allows the user to click and highlight a single time span.

Feel free to use it if you like:

http://www.bennadel.com/util/calendar

If anyone is interested in the code that goes behind this, it's fairly small.

Action Script

<cfscript>

	// Get the current month.
	REQUEST.ThisMonth = CreateDate(
		Year( REQUEST.Environment.DateTime.Now ),
		Month( REQUEST.Environment.DateTime.Now ),
		1
		);


	// Create an array of months.
	REQUEST.Months = ArrayNew( 1 );

	// Add a date for each month to the array. Right now, we are going
	// to be using one month previous to this one, plus 11 months going
	// forward. This should give us just over a year going forward.
	for (intI = -1 ; intI LTE 12 ; intI = (intI + 1)){

		ArrayAppend(
			REQUEST.Months,
			DateAdd( "m", intI, REQUEST.ThisMonth )
			);

	}


</cfscript>

Java Script Code

<script type="text/javascript">

	// These are the global variables neede to keep track of
	// the calendar and to provide faster lookup via indexing.
	var objStartDay = null;
	var objEndDay = null;
	var arrDays = new Array();
	var objToday = null;


	// This loads the global properties based on the calendar.
	function InitCalendars(){
		var objHolder = document.getElementById( "calendars" );
		var arrTBody = objHolder.getElementsByTagName( "tbody" );
		var arrTD = null;
		var intI, intJ;

		// Loop over tbodys and get their tds.
		for (intI = 0 ; intI < arrTBody.length ; intI++){

			// Get all the TDs.
			arrTD = arrTBody[ intI ].getElementsByTagName( "td" );

			// Loop over all the TDs to init.
			for (intJ = 0 ; intJ < arrTD.length ; intJ ++){

				InitTD( arrTD[ intJ ] );

			}

		}

	}


	// This initialized each TD within the calendar days.
	function InitTD( objTD ){
		// Set the TD collection index.
		objTD.dayIndex = arrDays.length;

		// Add this TD to the collection.
		arrDays[ arrDays.length ] = objTD;

		// Store the original class name.
		objTD.baseClassName = objTD.className;

		// Check to see if this is today.
		if (objTD.className == "today"){
			objToday = objTD;
		}

		// Set onclick handler.
		objTD.onclick = function(){
			SetDay( this );
		}
	}


	// This is the onClick handler for each calendar day. It is
	// used to set the start and end days of the selected day span,
	// then it renders the calendar.
	function SetDay( objTD ){

		// We can only set a day that is NOT an other month. If this
		// an other day, just return out.
		if (objTD.className == "othermonth"){
			return;
		}

		// Check to see if we if this is the day one.
		if (objTD == objStartDay){

			// Set the start day to be the end day.
			objStartDay = objEndDay;

			// Turn off end day.
			objEndDay = null;

		} else if (objTD == objEndDay){

			// Turn off the end day.
			objEndDay = null;

		} else {

			// We are turning ON a day. Check to see if there is an
			// existing start and end day.
			if (objStartDay && objEndDay){

				// Both days already exist. We need to alter the
				// way the selection exists. Check to see the
				// indexes.
				if (objTD.dayIndex < objStartDay.dayIndex){

					// Expanding back.
					objStartDay = objTD;

				} else {

					// We are going beyong the end day OR we are
					// going somewhere in the middle of the
					// existing range. Either way, when we do that,
					// we are always going to change the END day,
					// not the Start day.
					objEndDay = objTD;

				}

			} else if (objStartDay){

				// Check to see if the new TD is less than or
				// greater than the current start day.
				if (objStartDay.dayIndex < objTD.dayIndex){

					objEndDay = objTD;

				} else {

					objEndDay = objStartDay;
					objStartDay = objTD;
				}

			} else {

				// We will never have JUST an end day. Set this to
				// the start day.
				objStartDay = objTD;

			}

		}


		// Render the calendar with the new time span.
		RenderCalendar();

	}


	// This renders the calendar and turns the days on or off
	// depending on where they fall in any selected time span.
	function RenderCalendar(){
		var intDay = null;

		// Loop over all the days in the collection.
		for (intDay = 0 ; intDay < arrDays.length ; intDay++){

			// We cannot do anything to other month days. If this
			// is an other month day, just continue the loop.
			if (arrDays[ intDay ].className == "othermonth"){
				continue;
			}

			// Check to see if we have both start and end day.
			if (
				objStartDay &&
				objEndDay &&
				(objStartDay.dayIndex <= arrDays[ intDay ].dayIndex) &&
				(objEndDay.dayIndex >= arrDays[ intDay ].dayIndex)
				){

				// This is a selected day.
				arrDays[ intDay ].className = (
					( (arrDays[ intDay ] == objToday) ? "today" : "" ) +
					"selected"
					);

			} else if (
				(arrDays[ intDay ] == objStartDay) ||
				(arrDays[ intDay ] == objEndDay)
				){

				// This is a selected day.
				arrDays[ intDay ].className = (
					( (arrDays[ intDay ] == objToday) ? "today" : "" ) +
					"selected"
					);

			} else {

				// This is NOT a selected day. Turn off.
				arrDays[ intDay ].className = arrDays[ intDay ].baseClassName;

			}

		}

	}

</script>

Calendar Display Code

<div id="calendars">

	<!--- Loop over the months in the array. --->
	<cfloop
		index="intI"
		from="1"
		to="#ArrayLen( REQUEST.Months )#"
		step="1">

		<cfsilent>

			<!--- Get the pointer to the current date. --->
			<cfset dtThisMonth = REQUEST.Months[ intI ] />

			<!--- Get the starting day. --->
			<cfset dtStartDay = DateAdd(
				"d",
				-(DayOfWeek( dtThisMonth ) - 1),
				dtThisMonth
				) />

			<!--- Get the ending day. --->
			<cfset dtEndDay = (DateAdd( "m", 1, dtThisMonth ) - 1) />

			<!--- Stretch it to the end of the week. --->
			<cfset dtEndDay = DateAdd(
				"d",
				(7 - DayOfWeek( dtEndDay )),
				dtEndDay
				) />


			<!--- Get today's date. --->
			<cfset dtToday = Fix( REQUEST.Environment.DateTime.Now ) />

		</cfsilent>

		<table width="100%" border="0" cellspacing="2" cellpadding="0" class="calendarmonth">
		<thead>
			<tr>
				<td colspan="7">
					#MonthAsString( Month( dtThisMonth ) )# #Year( dtThisMonth )#
				</td>
			</tr>
		</thead>
		<tbody>
			<cfsilent>
				<cfsavecontent variable="strMonthCode">

					<tr>

						<!--- Loop over the days of the month. --->
						<cfloop index="dtDay" from="#dtStartDay#" to="#dtEndDay#" step="1">

							<td class="<cfif (dtDay EQ dtToday)>today<cfelseif (Month( dtDay ) EQ Month( dtThisMonth ))>thismonth<cfelse>othermonth</cfif>">
								#Day( dtDay )#
							</td>

							<cfif (
								(DayOfWeek( dtDay ) EQ 7) AND
								(dtDay NEQ dtEndDay)
								)>
								</tr>
								<tr>
							</cfif>

						</cfloop>

					</tr>

				</cfsavecontent>
			</cfsilent>

			<!--- Output code with as little white space as possible. --->
			#strMonthCode.ReplaceAll( ">\s+", ">" ).ReplaceAll( "\s+<", "<" )#
		</tbody>
		</table>

		<!--- Flush for user feedback. --->
		<cfflush />

	</cfloop>

</div>

<!--- Initialize the Calendar javascript. --->
<script type="text/javascript">
	InitCalendars();
</script>

CSS Code

table.calendarmonth {
	margin-bottom: 20px ;
	}

table.calendarmonth td {
	border: 1px solid #999999 ;
	cursor: default ;
	padding: 5px 10px 5px 10px ;
	}

table.calendarmonth thead td {
	background-color: #F0F0F0 ;
	border-bottom-width: 2px ;
	font-size: 12px ;
	font-weight: bold ;
	text-align: center ;
	}

table.calendarmonth td.today {
	background-color: #FEEAB7 ;
	border-color: #333333 ; /* #E4A705 ; */
	font-weight: bold ;
	}

table.calendarmonth td.thismonth {}

table.calendarmonth td.othermonth {
	background-color: #EAEAEA ;
	border-color: #999999 ;
	color: #AAAAAA ;
	}

table.calendarmonth td.selected {
	background-color: #FFE8E1 ;
	border-color: #FF3333 ;
	/* color: #FA3E0A ; */
	}

table.calendarmonth td.todayselected {
	background-color: #FFB8A4 ;
	border-color: #FF3333 ;
	font-weight: bold ;
	}

Want to use code from this post? Check out the license.

Reader Comments

15,377 Comments

Awesome :) Glad you like it. Please feel free to suggest any changes you want to it. Someone already suggested dragging for creating time span.

I believe in love. I believe in compassion. I believe in human rights. I believe that we can afford to give more of these gifts to the world around us because it costs us nothing to be decent and kind and understanding. And, I want you to know that when you land on this site, you are accepted for who you are, no matter how you identify, what truths you live, or whatever kind of goofy shit makes you feel alive! Rock on with your bad self!
Ben Nadel