Hi Daniweb,

I'm trying to create a basic, grid-based (table-layout) calendar using PHP. I'm in the planning stages currently, but need some help coming up with an effective way to display empty boxes (for instance, November starts on Monday, so the left-most box (Sunday) needs to be empty. December starts Wednesday, so Sunday-Monday-Tuesday need to have empty, unmarked boxes (maybe black backgrounds?).

So, here's my current planning info.

What I need to Know
Current date (month + year)
First day value (0-6) [found with date("w", mktime(0,0,0,month,day,year))...]
Total days in month
Number of weeks in a month (can be 4-6)
Counter to loop through each day

---

I have a basic idea of how to create a proper for loop of a for loop, but I can't seem to make the "blanks" appear correctly.

for($rows = 0; $rows < $weeksInMonth; $rows++)
{
   for($daysInWeek = 0; $daysInWeek < 7; $daysInWeek++)
   {
       LOOP THROUGH DAYS, BUT WHAT'S THE LOGIC FOR BLANK BOXES?
   }
}

If anyone could help me out, I'd appreciate it. I don't need a full, detailed answer per se, but a shove the right way would help. :D

Thanks a lot! It's 12:20A.M. and I'll be up all night so I'll check in frequently.

Member Avatar for diafol

you need to created it or can you use a ready-made script?

@ardav,

Thanks for the quick response.

The thing is, I saw MANY ready-made scripts available, but I wanted to work on my own just for "learning sake" and actually managed to get it to work. I have a fully functioning calendar setup and now am in the process of replacing the "empty" boxes with days from the previous month, so that if November starts on Wednesday, Tuesday would be populated with October 31, Monday October 30, and so on and so forth.

:)

I'll mark it as solved.

If you're interested in peering over my code to see if it's efficient (not great with PHP by ANY MEANS) and logical, let me know and I'll be glad to post it.

Member Avatar for diafol

Hmm. I would imagine that you'll need a whole host of functions for a calendar app. To find the first day of a month:

$month = 11; //the current month or whatever 
$year = 2010; 

$firstday = date('w',mktime(0, 0, 0, $month, 1, $year)); //first day of the month (0-6) 

$lastprevious = date('t',mktime(0, 0, 0, $month - 1, 1, $year)); //no. of days in previous month (28-31)

$x = $firstday - 1; //starting point for countdown
$y = $lastprevious; //last last month's day - will countdown 
$chain = "";
while($x >= 0){
 $chain = "<td>$y</td>" . $chain; // prepend day to the calendar
 $y--; //decrement last month's days
 $x--; //decrement the first week's days
}

This is not tested - just an idea. Could use some work.

Hi Ardav,

I've already completed all my goals with getting the calendar to work.

I've PMed you my final "solution" and if you don't mind, I'd love for your review. This isn't some online solution I copied and pasted, this is 8+ hours (sadly) of hard labor. Thankfully, now I know the proper pseudocode to creating a fully functional calendar. Hip hip hooray? :D

Here is one I made last Saturday for a current project of mine.

This one allows for a lot of control over how its displayed.

<?php

class calendar {

	private $time = null;
	private $adjusted_time = null;
	private $select_date = array();
	private $current_date = array();
	private $last_day = null;

	private $weeks = array();
	private $curr_week = 1;

	public function __construct( $date=null ) {
		$this->time = ( is_null( $date ) ? mktime( 0,0,0 ) : strtotime( $date ) );
		if ( $this->time === false ) {
			error::trigger('Invalid date');
		}
		list( $this->select_date['month'],$this->select_date['day'],$this->select_date['year'] ) = explode( '-',date( 'n-j-Y',$this->time ) );
		$time = $this->time;
		if ( uri::is_set('calendar') && ( $ntime = uri::segment('calendar') ) !== false && strpos( $ntime,'-' ) !== false && ( $ntime = strtotime( $ntime ) ) !== false && $ntime !== $time ) {
			$time = $ntime;
			$this->adjusted_time = $time;
		}
		list( $this->current_date['month'],$this->current_date['day'],$this->current_date['year'] ) = explode( '-',date( 'n-j-Y',$time ) );
		$this->last_day = date( 't',$time );
	}

	public function month_start() {
		return mktime( 0,0,0,$this->current_date['month'],1,$this->current_date['year'] );
	}

	public function month_end() {
		return mktime( 23,59,59,$this->current_date['month'],$this->last_day,$this->current_date['year'] );
	}

	public function next_month( $link=false ) {
		$month = ( $this->current_date['month'] + 1 );
		$day   = $this->current_date['day'];
		$year  = $this->current_date['year'];
		if ( $month > 12 ) {
			$month = 1;
			$year += 1;
		}
		return ( $link == false ? "{$year}-{$month}-{$day}" : uri::create(array('calendar'=>"{$year}-{$month}-{$day}"),array('all'=>true)) );
	}

	public function prev_month( $link=false ) {
		$month = ( $this->current_date['month'] - 1 );
		$day   = $this->current_date['day'];
		$year  = $this->current_date['year'];
		if ( $month < 1 ) {
			$month = 12;
			$year -= 1;
		}
		return ( $link == false ? "{$year}-{$month}-{$day}" : uri::create(array('calendar'=>"{$year}-{$month}-{$day}"),array('all'=>true)) );
	}

	private function add_week( $days ) {
		$idx = ( count( $this->weeks ) + 1 );
		$this->weeks[$idx] = $days;
	}

	public function generate() {
		$mstart = $this->month_start();
		$daynum = ( (int) date( 'w',$mstart ) + 1 );
		$cday = ( $daynum > 1 ? date( 'j',( $mstart - ( ( $daynum - 1 ) * (60*60*24) ) ) ) : 1 );
		$cmont = ( $cday > 1 ? false : true );
		$cw = 1;
		$wdays = array();
		for( $i=1,$w=1;$i <= 42;$i++,$w++,$cday++ ) {
			if ( $cw == 1 && $w == $daynum ) {
				$cday = 1;
				$cmont = true;
			}
			elseif ( ( $cw == 5 || $cw == 6 ) && $cday == ( $this->last_day + 1 ) ) {
				$cday = 1;
				$cmont = false;
			}
			$sday = false;
			if ( $this->current_date['month'] == $this->select_date['month'] && $this->current_date['year'] == $this->select_date['year'] && $cday == $this->select_date['day'] ) {
				$sday = true;
			}
			$wdays[$w] = array( 'day'=>$cday,'curr_month'=>$cmont,'curr_day'=>$sday );
			if ( $w == 7 ) {
				$w = 0;
				$this->add_week( $wdays );
				$wdays = array();
				if ( $cw == 1 ) {
					$cmont = true;
				}
				elseif ( $cw == 4 && $cday == $this->last_day ) {
					break;
				}
				elseif ( $cw == 5 && $cmont == false ) {
					break;
				}
				$cw++;
			}
		}
		return $this->weeks;
	}

	public function get_time() {
		return ( $this->is_adjusted() ? $this->adjusted_time : $this->time );
	}

	public function get_weeks() {
		return $this->weeks;
	}

	public function get_week( $idx=null ) {
		$curr_idx = ( !is_null( $idx ) ? $idx : $this->curr_week );
		if ( isset( $this->weeks[$curr_idx] ) ) {
			if ( is_null( $idx ) ) {
				$this->curr_week++;
			}
			return $this->weeks[$curr_idx];
		}
		return false;
	}

	public function get_week_day( $dow ) {
		$days = array();
		foreach( $this->weeks as $w => $week ) {
			foreach( $week as $d => $day ) {
				if ( $d == $dow ) {
					$days[] = $day;
					continue 2;
				}
			}
		}
		return $days;
	}

	public function is_adjusted() {
		return !is_null( $this->adjusted_time );
	}

	public function get_month( $format='F' ) {
		return date( $format,$this->get_time() );
	}

	public function get_year( $format='Y' ) {
		return date( $format,$this->get_time() );
	}

	public function get_day_of_week( $format='l' ) {
		return date( $format,$this->get_time() );
	}

	public function get_last_day() {
		return $this->last_day;
	}

}

?>

To use:

$calendar = new calendar('2010-11-21');
$calendar->generate();

//Get all weeks
foreach( $calendar->get_weeks() as $week ) {}

//Get one week (1-6), returns false if month doesn't have that week
foreach( $calendar->get_week(1) as $days ) {} //can be used without a specific day and it will just loop through all weeks

//Get by week day 1 = Sunday, 7 = Saturday
foreach( $calendar->get_week_day(2) as $day ) {} //gets all days that are on a monday

Its built for my framework, so it won't work if you run it directly, but I figured I would post it so you can see how someone else accomplished the same thing.

(Plus, for the amount of work I put into it, it need to be shared with the world)

Hi kkeith29,

Amazing job! Just looking at all that code gives me a headache and lets me know just how much more I really have to learn about programming!

Here's my very basic PHP Calendar.

<?php

	/* application information */
	$application = "calendar";
	$application_id = 1;
	
	/* there are 7 days in a week */
	$columns_for_week = 7; 

	/*
	 * The following variables are used to initialize the current date
	 * If the month is November 2010, $month = 11 and $year = 2010
	 */
	if(isset($_GET['year']))
	{
		$year = $_GET['year'];
	}
	else
	{
		$year = date("Y");  // 2010
	}
	$max_year = date("Y") + 10;

	if(isset($_GET['month']))
	{
		$month = $_GET['month'];
	}
	else
	{
		$month = date("n"); // 11
	}
	
	/*
   * The following variables are used to print out days for the current month
   *
	 * $days_in_month: returns how many days are in the month
	 * $first_day: returns a numeric representation (0[Sunday]-6[Saturday]) for the day of the week
   * $blank_days: returns how many empty boxes to display before the first of the month based on the $first_day variable
	 * $rows_for_month: returns how many rows the month will require (most require 4-5, some require 6)
	 * $day_counter: represents the first day of the month and is incremented in loop
	 */
	$days_in_month = date("t", mktime(0,0,0,$month,1,$year));
	$first_day = date("w", mktime(0,0,0,$month,1,$year));
	$blank_days = $first_day;
	$rows_for_month = intval(ceil(($days_in_month + $blank_days) / $columns_for_week));
	$day_counter = 1;

	/*
	 * The following variables are used to print the first couple days of the next month if empty boxes remain
	 *
	 * $next_month_day_counter: represents the first day of the next month and is incremented in loop while boxes remain
	 * $total_boxes: returns how many boxes are used for that month (ex: 35 for Novemeber)
	 * $total_boxes_for_month: returns how many boxes are used to display days for current month (ex: 30 for November)
	 * $leftover_boxes: returns how many boxes are used to display days for upcoming month (typically a couple days at end)
	 */
	$next_month_day_counter = 1;
	$total_boxes = $rows_for_month * $columns_for_week;
	$total_boxes_for_month = $blank_days + $days_in_month;
	$leftover_boxes = $total_boxes - $total_boxes_for_month;

	/*
	 * $days_in_previous_month: returns how many days are in the previous month
	 */
	$days_in_previous_month = date("t", mktime(0,0,0,$month-1,1,$year));

	/* URL Structuring */
	$current_page = "new.php";

?>
<style type="text/css">
#mini-calendar { width:200px; background:#000000; font-family:Arial; font-size:12px; }
#mini-calendar th { background:#613D46; color:#FFFFFF; text-align:center; padding:4px; }
#mini-calendar td { background:#FFFFFF; text-align:center; padding:4px; }
#mini-calendar td.previous-month { background:#AAAAAA; color:#FFFFFF; }
#mini-calendar td.next-month { background:#AAAAAA; color:#FFFFFF; }
#mini-calendar-navigation { width:200px; background:#000000; }
</style>
<table class="mini-calendar" id="mini-calendar" border="0" cellpadding="0" cellspacing="1">
	<thead>
		<tr>
			<th>S</th>
			<th>M</th>
			<th>T</th>
			<th>W</th>
			<th>T</th>
			<th>F</th>
			<th>S</th>
		</tr> 
	</thead>
	<tbody>
		<?php
			// loops through rows creating table-rows (<tr>)
			for($r = 1; $r <= $rows_for_month; $r++)
			{
				echo "<tr>";
				// loops through columns creating table-data (<td>)
				for($c = 1; $c <= $columns_for_week; $c++)
				{
					/* 
				   * if the first day of the month doesn't start on Sunday (numeric value 0),
					 * "blank" boxes exist that are replaced with a couple days from the previous month.
				   * For example, November 2010 starts on Monday (numeric value 1), so October 31 is displayed in the box(es).
					 */
					if($blank_days > 0)
					{
						$blank_days--;
						echo "<td class=\"previous-month\">" . ($days_in_previous_month - $blank_days) . "</td>";
					}
					/*
					 * print out table-data with dates for month
					 */
					else if($day_counter <= $days_in_month)
					{
			?>
			<?php
				$date_url = $current_page . "?app=" . $application . "&id=" . $application_id . "&y=" . $year . "&m=" . $month . "&d=". $day_counter;
			?>
					<td class="current-month"><a href="<?php echo $date_url; ?> "><?php echo $day_counter ?></a></td>
			<?php
						//echo "<td>" . $day_counter . "</td>";
						$day_counter++;
					}
					/* 
				   * if the last day of the month doesn't end on Saturday (numeric value 6),
					 * "blank" boxes exist that are replaced with a couple days from the upcoming month.
				   * For example, November 2010 ends on Tuesday (numeric value 2), so December 1-4 are displayed in the box(es).
					 */
					else if( $leftover_boxes > 0)
					{
						echo "<td class=\"next-month\">" . $next_month_day_counter . "</td>";
						$next_month_day_counter++;
						$left--;
					}
				}
				echo "</tr>";
			}		
		?>
	</tbody>
</table>
<div class="mini-calendar-navigation" id="mini-calendar-navigation">
	<form action="new.php" method="get" onchange>
		<select name="month" class="month-selection" id="month-selection">
			<?php
				for($m = 1; $m <= 12; $m++)
				{
					if($m == $month)
					{
						echo "<option value=\"${m}\" selected=\"selected\"s>" . date("F", mktime(0,0,0,$m,1,$year)) . "</option>";
					}
					else
					{
						echo "<option value=\"${m}\">" . date("F", mktime(0,0,0,$m,1,$year)) . "</option>";
					}
				}
			?>
		</select>
		<select name="year" class="year-selection" id="year-selection">
			<?php
				for($y = ($year - 10); $y <= $max_year; $y++)
				{					
					if($y == $year)
					{
						echo "<option value=\"${y}\" selected=\"selected\"s>" . $y . "</option>";
					}
					else
					{
						echo "<option value=\"${y}\">" . $y . "</option>";
					}
				}
			?>
		</select>
		<input type="submit" value="go" />
	</form>
</div>

</table>
Member Avatar for diafol

@fD - I was about to say don't post anything to me via PM, but I'm glad to see you've posted it to the forum. KK is such a show off isn't he? :)

@fD - I was about to say don't post anything to me via PM, but I'm glad to see you've posted it to the forum. KK is such a show off isn't he? :)

Hey ardav,

Do you prefer not to receive PM's or is it that you wanted me to post the code for everyone to see (since it was finished)?

If it's because you prefer not to receive PM's, I'll make a mental note of that. If it's because you wanted me to share finished code, I'll try to do so more often.

Member Avatar for diafol

Both. Try to avoid sending questions via PM, it tends to annoy a lot of the furniture! POst it in the forum so that everybody can see it and make valid contributions - and profit from the discussion that ensues.

KK's solution is rather neat don't you think? He's using OOP, so it may look a little weird.

@ardav

LOL...I think you think I don't know it's OOP. My admiration came from how few lines of code he managed to squeeze a full calendar with mod-ability into.

I am trying to replicate the Google calendar layout using PHP but I just cannot get my head around the table structure used by Google. The problem for me arises when I want to add a multi day event. Also recurring events are blowing my mind. Can anybody point in the right direction - examples are always useful.

Member Avatar for diafol

You may find using the iCalendar format: http://en.wikipedia.org/wiki/ICalendar useful. I made a calendar application some time ago which stored data using this format. Almost killed me! But the beauty of this is that you can export it to many different programs. However, there are other formats out there and it's been a while since I checked them out.

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.