Core PHP

Calendar with Events

This PHP program creates a calendar that calculates various holidays and displays them, along with randomly-generated events.

Calendar.php

<!DOCTYPE html>
<html>
	<head>
		<style>
.cArrow{
	font-size:20px;
	font-weight: bolder;
	margin: 0px 20px;
	background-color: gray;
	color:white;
	padding-top:2px;
	padding-bottom:2px;
	border-radius: 10px;
	border:5px outset black;
}
.cArrow:hover {
	background-color: lightgray;
}
.cCenter {
	width: fit-content;
	margin-left: auto;
	margin-right: auto;
}
body {
	font-family: arial, sans-serif;
}
h1 {
	line-height: 120%;
	padding: 15px 0px 0px 0px;
	margin: 5px 0px 0px 0px;
	border-width: 5px 0px 0px 0px;
}
th {
	padding:2px;
	border: none;
}
td {
	border: 1px solid black;
	padding:2px 5px;
	text-align:center;
}
.cMonthTable {
	float: left;
	margin: 5px 10px;
	border-collapse: collapse;
}
.cMonthRow {
	width: fit-content;
	margin-left: auto;
	margin-right: auto;
}
.cAppointment {
	background-color: #FF000060;
}
.cReview {
	background-color: #00FF0060;
}
.cMeeting {
	background-color: #0000FF60;
}
.cAppointment.cReview {
	background: linear-gradient(90deg, #FF000060, #FF000060 50%,#00FF0060 50%,#00FF0060);
}
.cAppointment.cMeeting {
	background: linear-gradient(90deg, #FF000060, #FF000060 50%,#0000FF60 50%,#0000FF60);
}
.cReview.cMeeting {
	background: linear-gradient(90deg, #00FF0060, #00FF0060 50%,#0000FF60 50%,#0000FF60);
}
.cAppointment.cReview.cMeeting {
	background: linear-gradient(90deg, #FF000060, #FF000060 33%,#00FF0060 33%,#00FF0060 66%,#0000FF60 66%,#0000FF60);
}
.cHoliday {
	background: repeating-conic-gradient(lightgray 0 25%, white 0 50%);
	background-position: 0 0, 32px 32px;
	background-size: 8px 8px;
}
		</style>
		<script>
var gqEvents = null;
var giYearIndex = 1;

function DecrementYear() {
	if (giYearIndex > 0) {
		--giYearIndex;
		CreateSchedule();
	}
}

function IncrementYear() {
	if (giYearIndex < gqEvents.miaYears.length - 1) {
		++giYearIndex;
		CreateSchedule();
	}
}

function GetRouteScheduleViaAjax(sRoute) {
	// The event arrays must be create to be the corrent size for each month
	gqEvents = {miaYears:[2024,2025,2026],
		msaEventNames:["Appointment", "Review", "Meeting"],
		muiaaaEvents:[[[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
			[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
			[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
			[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
			[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
			[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
			[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
			[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
			[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
			[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
			[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
			[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]],
				[[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
			[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
			[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
			[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
			[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
			[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
			[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
			[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
			[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
			[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
			[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
			[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]],
				[[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
			[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
			[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
			[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
			[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
			[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
			[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
			[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
			[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
			[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
			[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
			[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]]]};
	// Randomly change some of the events
	for (var i = 0; i < 40; ++i) {
		// Get a random year
		var iYear = Math.floor(Math.random()*gqEvents.miaYears.length);
		// Get a random month
		var iMonth = Math.floor(Math.random()*12);
		// Get a random date
		const kiDatesInMonth = gqEvents.muiaaaEvents[iYear][iMonth].length;
		var iDate = Math.floor(Math.random()*kiDatesInMonth);
		// Get a random event number 0 - 7
		var iEvent = Math.floor(Math.random()*8);
		gqEvents.muiaaaEvents[iYear][iMonth][iDate] = iEvent;
	}
	CreateSchedule();
}

function CreateSchedule() {
	// Reset the year index, if necessary.
	if (giYearIndex > gqEvents.miaYears.length - 1) {
		giYearIndex = gqEvents.miaYears.length - 1;
	}

	// Hide the unnecessary year alteration buttons.
	// If the year index is zero hide the left button. If it is length -1, hide the right button.
	// Get the arrow button array of two buttons
	var qaArrowButtons = document.getElementsByClassName("cArrow");
	if (giYearIndex <= 0) {
		qaArrowButtons[0].style.visibility = "hidden";
	} else {
		qaArrowButtons[0].style.visibility = "visible";
	}
	if (giYearIndex >= gqEvents.miaYears.length - 1) {
		qaArrowButtons[1].style.visibility = "hidden";
	} else {
		qaArrowButtons[1].style.visibility = "visible";
	}

	// Set the year for display
	var iYear = gqEvents.miaYears[giYearIndex];
	document.getElementById("idYear").innerHTML = "Calendar " + iYear;

	// Get the holiday array
	var qaHolidays = GetOrderedHolidays(iYear);
	var iCurrentHolidayIndex = 0;

	var uiaaCurrentYear = gqEvents.muiaaaEvents[giYearIndex];
	for (var iMonth = 0; iMonth < 12; ++iMonth) {
		// Get the month table
		var qMonthTable = document.getElementById("idMonth"+iMonth).children[0];
		var qFirstDayOfTheMonth = new Date(iYear, iMonth, 1);
		var iFirstDayOfMonthIndex = qFirstDayOfTheMonth.getDay();
		var qEndOfMonth = new Date(iYear, iMonth + 1, 0);
		var iEndOfMonthDate = qEndOfMonth.getDate();
		var iCurrentDate = -iFirstDayOfMonthIndex;
		// Start at the third row
		for (var iDateRow = 2; iDateRow < 8; ++iDateRow) {
			var qDateRow = qMonthTable.children[iDateRow];
			// Reset the date row class; it may get set by the holiday check later
			qDateRow.className = "";
			for (var iDayOfWeek = 0; iDayOfWeek < 7; ++iDayOfWeek) {
				var qDateElement = qDateRow.children[iDayOfWeek];
				qDateElement.className = "";
				if (iCurrentDate >= 0 && iCurrentDate < iEndOfMonthDate) {
					qDateElement.innerHTML = iCurrentDate + 1;
					for (var i = 1; i < 8; i <<= 1) {
						if ((uiaaCurrentYear[iMonth][iCurrentDate] & i) == i) {
							switch (i) {
								case 1:
								qDateElement.className += "cAppointment ";
								break;
								case 2:
								qDateElement.className += "cReview ";
								break;
								case 4:
								qDateElement.className += "cMeeting ";
								break;
							}
						}
					}
				} else {
					qDateElement.innerHTML = "&nbsp;&nbsp;";
				}
				++iCurrentDate;
				// Check for a holiday
				if (iCurrentHolidayIndex < qaHolidays.length) {
					var qNextHoliday = qaHolidays[iCurrentHolidayIndex];
					// If today is a holiday, color it.
					if (qNextHoliday.getMonth() == iMonth && qNextHoliday.getDate() == iCurrentDate) {
						qDateElement.className += "cHoliday";
						++iCurrentHolidayIndex;
					}
				}
			}
		}
	}
}

function GetOrderedHolidays(iYear) {
	// Remember that months are zero-based and dates are not.
	var qaHolidays = new Array();
	// Add New Year's Day
	qaHolidays.push(new Date(iYear, 0, 1));
	// Add in Easter day
	qaHolidays.push(CalculateEasterDay(iYear));
	// Add Memorial day (last Monday of May)
	var qEndOfMonth = new Date(iYear, 5, 0);
	var iLastDateInMay = qEndOfMonth.getDate();
	var iLastDayInMay = qEndOfMonth.getDay(); // Monday is the 2nd day or 1
	var iDateOfMemorialDay = iLastDateInMay - (((iLastDayInMay - 1) + 7) % 7);
	qaHolidays.push(new Date(iYear, 4, iDateOfMemorialDay));
	// Add the 4th of July
	qaHolidays.push(new Date(iYear, 6, 4));
	// Add in Labor Day (first Monday of September)
	var qSept1 = new Date(iYear, 8, 1);
	var qLaborDate = ((1 - qSept1.getDay() + 7) % 7) + 1;
	qaHolidays.push(new Date(iYear, 8, qLaborDate));
	// Add Thanksgiving (fourth sunday of November) - days of the week are zero based
	var qNov1 = new Date(iYear, 10, 1);
	var iThanksgivingDate = ((4 - qNov1.getDay() + 7) % 7) + 22;
	qaHolidays.push(new Date(iYear, 10, iThanksgivingDate));
	// Add Christmas
	qaHolidays.push(new Date(iYear, 11, 25));
	return qaHolidays;
}

// This is the calculation for Easter. All division is integer-based.
// y = year, m = month, d = day of the month
// Reference: https://aa.usno.navy.mil/faq/easter
//----------------------------------------------------------------
// c = y / 100
// n = y - 19 * ( y / 19 )
// k = ( c - 17 ) / 25
// i = c - c / 4 - ( c - k ) / 3 + 19* n + 15
// i = i - 30 * ( i / 30 )
// i = i - ( i / 28 ) * ( 1 - ( i / 28 ) * ( 29 / ( i + 1 ) ) * ( ( 21 - n ) / 11 ) )
// j = y + y / 4 + i + 2 - c + c / 4
// j = j - 7 * ( j / 7 )
// l = i - j
// m = 3 + ( l + 40 ) / 44
// d = l + 28 - 31 * ( m / 4 )
function CalculateEasterDay(iYear) {
	const kiC = Math.floor(iYear/100);
	const kiN = iYear - (19*Math.floor(iYear/19));
	const kiK = Math.floor((kiC - 17)/25);
	var iI = kiC - Math.floor(kiC/4) - Math.floor((kiC - kiK)/3) + 19*kiN + 15;
	iI = iI - 30*Math.floor(iI/30);
	iI = iI - Math.floor(iI/28)*(1 - Math.floor(iI/28)*Math.floor(29/(iI+1))*Math.floor((21 - kiN)/11));
	var iJ = iYear + Math.floor(iYear/4) + iI + 2 - kiC + Math.floor(kiC/4);
	iJ = iJ - 7 * Math.floor(iJ/7);
	const kiL = iI - iJ;
	const kiM = 3 + Math.floor((kiL + 40)/44);
	const kiD = kiL + 28 - 31*Math.floor(kiM/4);
	// Subtract 1 because the months are zero-based.
	return new Date(iYear, kiM - 1, kiD);
}
		</script>
	</head>
	<body onload="GetRouteScheduleViaAjax('A')">

	<div class="cCenter">
		<button class="cArrow" type="button" onclick="DecrementYear()">&#8678;</button>
		<h1 style="display:inline;" id="idYear"></h1>
		<button class="cArrow" type="button" onclick="IncrementYear()">&#8680;</button>
	</div>
	<div id="idCalendar" class="text-center">
<?php
	// Add in the month tables.
	$saMonths = array();
	array_push($saMonths, "January");
	array_push($saMonths, "February");
	array_push($saMonths, "March");
	array_push($saMonths, "April");
	array_push($saMonths, "May");
	array_push($saMonths, "June");
	array_push($saMonths, "July");
	array_push($saMonths, "August");
	array_push($saMonths, "September");
	array_push($saMonths, "October");
	array_push($saMonths, "November");
	array_push($saMonths, "December");
	
	for ($iRow = 0; $iRow < 3; ++$iRow) {
		echo '<div class="cMonthRow">';
		for ($iCol = 0; $iCol < 4; ++$iCol) {
			$iMonthIndex = 4*$iRow + $iCol;
			$sMonth = $saMonths[$iMonthIndex];
			$sMonthId = "idMonth".$iMonthIndex;
			// Start with the months as empty tables of dates
			echo '<table cellspacing="0" cellpadding="0" border="1" id="'.$sMonthId.'" class="cMonthTable">';
			echo '<tr><th colspan="7">'.$sMonth.'</th></tr>';
			echo '<tr><th>S</th><th>M</th><th>T</th><th>W</th><th>T</th><th>F</th><th>S</th></tr>';
			// Create the 6 rows of potential weeks
			for ($iWeek = 0; $iWeek < 6; ++$iWeek) {
				echo '<tr>';
				for ($iWeekday = 0; $iWeekday < 7; ++$iWeekday) {
					echo '<td>&nbsp;&nbsp;</td>';
				}
				echo '</tr>';
			}
			echo '</table>';
		}
		echo '</div>';
	}
?>

<div style="clear:both;"></div>
<div class="cCenter">
	<table>
		<tr>
			<td style="border:none;"><div class="cAppointment" style="width:20px;height:20px;border:1px solid black;"></div></td>
			<td style="border:none;text-align:left;">Appointment Date</td>
			<td style="border:none;"><div class="cReview" style="width:20px;height:20px;border:1px solid black;"></div></td>
			<td style="border:none;text-align:left;">Review Date</td>
			<td style="border:none;"><div class="cMeeting" style="width:20px;height:20px;border:1px solid black;"></div></td>
			<td style="border:none;text-align:left;">Meeting Date</td>
		</tr>
		<tr>
			<td style="border:none;"><div class="cAppointment cReview" style="width:20px;height:20px;border:1px solid black;"></div></td>
			<td style="border:none;text-align:left;">Appointment and Review Date</td>
			<td style="border:none;"><div class="cAppointment cMeeting" style="width:20px;height:20px;border:1px solid black;"></div></td>
			<td style="border:none;text-align:left;">Appointment and Meeting Date</td>
			<td style="border:none;"><div class="cReview cMeeting" style="width:20px;height:20px;border:1px solid black;"></div></td>
			<td style="border:none;text-align:left;">Review and Meeting Date</td>
		</tr>
		<tr>
			<td style="border:none;"><div class="cAppointment cReview cMeeting" style="width:20px;height:20px;border:1px solid black;"></div></td>
			<td style="border:none;text-align:left;">Appointment, Review, and Meeting Date</td>
			<td style="border:none;"><div class="cHoliday" style="width:20px;height:20px;border:1px solid black;"></div></td>
			<td style="border:none;text-align:left;">Holiday</td>
		</tr>
	</table>
</div>
<div style="clear:both;"></div>
	</body>
</html>
 

Output

 
 

© 2007–2025 XoaX.net LLC. All rights reserved.