UltraMega Blog
5Jan/0915

Creating a Calendar in PHP

This tutorial will explain how to create a dynamic calendar from scratch in PHP. There are two functions that will greatly simplify the process: mktime and getdate. The function mktime gets a Unix timestamp from the supplied arguments hour, minute, second, month, day, and year (each defaulting to the current). The function getdate is the opposite, turning a timestamp into an array of date information. See the manual pages (linked) for more information on these functions. Using these functions, it is fairly easy to gather all the information needed to create a dynamic calendar.

The Layout

We will use div tags styled with CSS for the days. The current day will also be highlighted. So let's start with the CSS and XHTML:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
@charset "utf-8";
.calendar {
   width: 600px;
}
.calendar div {
   float: left;
   height: 80px;
   width: 80px;
   border: 1px solid #333333;
}
.calendar .monheader {
   font-weight: bold;
   color: #FFFFFF;
   text-align: center;
   height: 20px;
   width: 572px;
   background-color: #333333;
}
.calendar .dayheader {
   font-weight: bold;
   color: #FFFFFF;
   text-align: center;
   height: 20px;
   background-color: #000000;
}
.calendar .day {
   background-color: #FFFFCC;
}
.calendar .today {
   background-color: #FFCC00;
   border-color: #CC0000;
}
.calendar .inactive {
   background-color: #666666;
}

This will set the overall calendar width to 600px, and style the days, current day, the days before the first and after the last days of the month, and the month and weekday headers. It's a pretty basic design, but it will work.

Here's the start of the XHTML:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Calendar Example</title>
<link href="calendar.css" rel="stylesheet" type="text/css" />
</head>
 
<body>
<div class="calendar">
  <div class="monheader"></div>
  <div class="dayheader">Sun</div>
  <div class="dayheader">Mon</div>
  <div class="dayheader">Tue</div>
  <div class="dayheader">Wed</div>
  <div class="dayheader">Thu</div>
  <div class="dayheader">Fri</div>
  <div class="dayheader">Sat</div>
</div>
</body>
</html>

Here we setup the month header and weekday columns.

The PHP Code

Now we're ready to write the necessary code. The things we need to work out is which day of the week the month starts on and how many days are in the month. This can be figured out from the first and last days of the current month.

We'll add this code to the top of the page:

1
2
3
4
5
6
7
<?php
$today = getdate();
$start = mktime(0,0,0,$today['mon'],1,$today['year']);
$first = getdate($start);
$end = mktime(0,0,0,$first['mon']+1,0,$first['year']);
$last = getdate($end);
?>

These lines (in order):

  1. fetch the array of date information for today (getdate defaults to the current time)
  2. get the timestamp of day 1 of $today's month and year
  3. get the date information from $start
  4. get $end, the timestamp of the last day of the month (day 0 gives the last day of the previous month)
  5. turn $end into an array of date information from that time

So now we have today as well as the first and last days of the month. As you can see, mktime can figure out the correct date from an invalid date, such as January 0, 2009 which becomes December 31, 2008.

We can also enhance this part to accept parameters to display different months:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?php
$today = getdate();
if(isset($_GET['mon'])){
   if(isset($_GET['year'])){
      $start = mktime(0,0,0,$_GET['mon'],1,$_GET['year']);
   }
   else{
      $start = mktime(0,0,0,$_GET['mon'],1,$today['year']);
   }
}
else{
   $start = mktime(0,0,0,$today['mon'],1,$today['year']);
}
$first = getdate($start);
$end = mktime(0,0,0,$first['mon']+1,0,$first['year']);
$last = getdate($end);
?>

Now if GET parameters mon and/or year are set, it will change the dates accordingly.

Now it's time to actually output the calendar. First, we'll add the month and year to the header so it looks like this:

28
  <div class="monheader"><?php echo $first['month'] . ' - ' . $first['year']; ?></div>

Now all that's left is to create the boxes for each day. First, we need to add blank days to pad the beginning (since months don't usually start on Sunday). A simple for loop counting up to the weekday of the first day ($first['wday'] gives us the numeric representation of the weekday, starting at 0 for Sunday).

36
37
38
39
40
<?php
for($i = 0; $i < $first['wday']; $i++){
   echo '  <div class="inactive"></div>' . "\n";
}
?>

Now we loop through all the days of the month:

40
41
42
43
44
45
46
47
48
49
for($i = 1; $i <= $last['mday']; $i++){
   if($i == $today['mday'] && $first['mon'] == $today['mon'] && $first['year'] == $today['year']){
      $style = 'today';
   }
   else{
      $style = 'day';
   }
   echo '  <div class="' . $style . '">' . $i . '</div>' . "\n";
}
?>

Note that this also checks to see if the day is today and changes the style if it is. At this point, you could also add in events that may take place on each day into the div block.

Now we need to pad the end in case the last day isn't Saturday (just to make it look good). It's pretty much the same idea as the beginning days, substituting $last for $first:

49
50
51
52
53
54
if($last['wday'] < 6){
   for($i = $last['wday']; $i < 6; $i++){
      echo '  <div class="inactive"></div>' . "\n";
   }
}
?>

That's all there is to it.

The Final Product

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
<?php
$today = getdate();
if(isset($_GET['mon'])){
   if(isset($_GET['year'])){
      $start = mktime(0,0,0,$_GET['mon'],1,$_GET['year']);
   }
   else{
      $start = mktime(0,0,0,$_GET['mon'],1,$today['year']);
   }
}
else{
   $start = mktime(0,0,0,$today['mon'],1,$today['year']);
}
$first = getdate($start);
$end = mktime(0,0,0,$first['mon']+1,0,$first['year']);
$last = getdate($end);
?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Calendar Example</title>
<link href="calendar.css" rel="stylesheet" type="text/css" />
</head>
 
<body>
<div class="calendar">
  <div class="monheader"><?php echo $first['month'] . ' - ' . $first['year']; ?></div>
  <div class="dayheader">Sun</div>
  <div class="dayheader">Mon</div>
  <div class="dayheader">Tue</div>
  <div class="dayheader">Wed</div>
  <div class="dayheader">Thu</div>
  <div class="dayheader">Fri</div>
  <div class="dayheader">Sat</div>
<?php
for($i = 0; $i < $first['wday']; $i++){
   echo '  <div class="inactive"></div>' . "\n";
}
for($i = 1; $i <= $last['mday']; $i++){
   if($i == $today['mday'] && $first['mon'] == $today['mon'] && $first['year'] == $today['year']){
      $style = 'today';
   }
   else{
      $style = 'day';
   }
   echo '  <div class="' . $style . '">' . $i . '</div>' . "\n";
}
if($last['wday'] < 6){
   for($i = $last['wday']; $i < 6; $i++){
      echo '  <div class="inactive"></div>' . "\n";
   }
}
?>
</div>
</body>
</html>

See it in action

I hope this comes in handy. Any questions or requests, just ask. Thanks for reading!

Posted by Steve

Comments (15) Trackbacks (0)
  1. Thanks a lot !!! I don’t know yet if I’ll use it but, nevermind, I’ll keep this article in my favorites.

  2. Too Good, Thank’s a Ton

  3. Looks good! The only thing missing is the ability to view next and previous month!

  4. How would you then add content to the individual dates? I have ‘echo’ed “Test”, but this shows up in all valid dates for the month.

    I would like to show events for each day.

  5. For those that find this and would like to add events, I have figured it out and took it another step further. I use an external XML file to store the events. I then use simpleXML and php to load the XML file, loop through the elements of the XML and return an array for php to use.

    Once I have the array, I simply insert the contents of the XML file to the proper day.
    Here is a sample XML:[code]

    EVENTS 1!

    EVENTS 2!
    //… etc, throught 31 (for 31 days in a month)
    [/code]

    I then, in my php file, under where Steve declared his date variables, create a new variable and foreach loop:
    Open the XML file
    [code]$xml = simplexml_load_file( ‘event.xml’ )
    or die ( “Unable to load the May XML file.” );[/code]
    Loop through the XML “Day’s Events”. Please note, you NEED to have the Event element name attribute set to name=”1″, etc, so that when the loop creates the array, it will match the loop for creating the calendar days!
    [code]foreach( $xml -> event as $a => $b) {
    // Set array from XML
    $event[ (string) $b[ ‘name’ ]] = ( string ) $b[0];
    }[/code]

    Lastly, I just simply insert and echo with the variables, into the that is created for ‘Today’ and ‘Day’:
    [code]echo $event[$i];[/code]

    This works great for a single month, but if you need to schedule events for several, you will need either a better way, or multiple XML files for each month. Then you can create an if/elseif statement for the load fuction to call the right XML file for the right month.
    [code]if( $first[ ‘mon’ ] == 5 ) {
    $xml = simplexml_load_file( ‘may_events.xml’ )
    or die ( “Unable to load the May XML file.” );
    } elseif( $first[ ‘mon’ ] == 6 ) {
    $xml = simplexml_load_file( ‘june_events.xml’ )
    or die ( “Unable to load the June XML file.” );
    } else {
    $xml = simplexml_load_file( ‘july_events.xml’ )
    or die ( “Unable to load the July XML file.” );
    }[/code]

    Then where you call the events into the calendar you could add something like:
    [code]if( $first[ ‘mon’ ] == 5 || $first[ ‘mon’ ] == 6 || $first[ ‘mon’ ] == 7 ) {
    echo $event[$i];
    } else {
    }[/code]

    Hope that helps someone out in the future! Happy Coding!

    Sven

  6. sorry that last post stripped the code out!…. Standard XML format, with a root element of ‘events’, and a child element of ‘event name=”1″‘ You must include that name attribute, to insert properly into your calendar…

  7. Thanks! I hooked this to a mySQL db in order to show events. Works great, but I cannot figure out modifying the css in order to allow the height of a row of days to increase – if there are a bunch of events on that day…
    tried setting overflow to auto, but then I get a scroll bar in the day to scroll the text vertically. Not ideal. Would just like the height to increase as necessary.

    • Hey Dave, I am just starting out in the php. What you did is what I am looking for. I want to connect the calendar to a DB of events. And I am at a wall. I hope you could help me out. Thanks.

  8. i live in belgium and we start always on calendar from monday -> sunday,
    what need i to change?

  9. woww.. thanks a so much..

  10. Awesome script and nice and minimal. Thanks dude, this helped me a crap load…the way you coded it is also really easy to understand customize. Nice one thanks for the contribution. I am modifying it to add events for specific times on selected days getting info from MySQL database using JQuery popup to allow user to select time and add note. Will make code available if anybody is interested let me know. Thanks again.

  11. the “see it in action” link just don’t work. So… how do I know if this is worth it?

  12. Very minimal! Thanks i love it!

    I just it to make a minimal Monthview function. Only 17 lines of code.

    echo Maand(11,2012);

    function Maand($month, $year){
    $maand = array(‘ ‘,’Januari’,’Februari’,’Maart’,’April’,’Mei’,’Juni’,’Juli’,’Augustus’,’September’,’Oktober’,’November’,’December’);
    $dag = array(‘Zondag’,’Maandag’,’Dinsdag’,’Woensdag’,’Donderdag’,’Vrijdag’,’Zaterdag’);
    $today = getdate();
    $first = getdate(mktime(0,0,0,$month,1,$year));
    $last = getdate(mktime(0,0,0,$month+1,0,$year));
    $html.=”{$maand[$month]} – {$year}\n\r”;
    for ($i = 0; $i <= 6; $i++) $html.="{$dag[$i]}\n\r”;
    for($i = 0; $i < $first['wday']; $i++) $html.= " “;
    for($i = 1; $i <= $last['mday']; $i++){
    $class = ($i == $today['mday'] && $first['mon'] == $today['mon'] && $first['year'] == $today['year'])? 'today' : 'day';
    $html.= " {$i}\n\r”;
    } // for
    if($last[‘wday’] < 6) for($i = $last['wday']; $i < 6; $i++) $html.=" \n\r”;
    $html.=””;
    return $html;
    } // function Maand

  13. Hi, do you have any idea how can I make the date continuously? Such that when 1st falls on Friday.. Thurs will show 31th.. And vice versa.

  14. how can i get the all months in this calende ???????
    it is very helpful to me


Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.

No trackbacks yet.