Creating a Calendar Library

Because dates are ubiquitous in Web interfaces, and because working with dates is often comparatively complicated, let's look at creating a class library to automate some of the work that dates can present. Along the way we will revisit some of the techniques we have already covered.


If the concept of classes is completely foreign to you, you can supplement your knowledge by reading through Chapter 14 of the PHP Manual. It is titled "Classes and Objects," and you can find it at

The simple date_pulldown library we will look at was born during the creation of a freelance job listing site. The project necessarily involved the presentation of multiple date pull-downs allowing employers to select both the start and end of contract periods, and for candidates to indicate periods of availability. A date pull-down, in this instance, consists of three separate select elements, one for day of the month, one for month, and one for year.

When a user submits a page, the script will check his input. If there is a problem, we will need to represent the page with the user's input still in place. This is very easy to accomplish with text boxes but is more of a chore with pull-down menus. Pages that display information pulled from a database present a similar problem. Data can be entered straight into the value attributes of text type input elements. Dates will need to be split into month, day, and year values, and then the correct option elements selected.

The date_pulldown class aims to make date pull-downs sticky (so they will remember settings from page to page) and easy to set. In order to create our class, we first need to declare it and create a constructor.


A constructor is a function that exists within a class, and which is automatically called when a new instance of the class is created.

We can also declare some class properties. The following snippet shows the beginning of the class:

  1: class date_pulldown
  2:     var $name;
  3:     var $timestamp = -1;
  4:     var $months = array("Jan", "Feb", "Mar", "Apr", "May", "Jun",
  5:           "Jul", "Aug", "Sep", "Oct", "Nov", "Dec");
  6:     var $yearstart = -1;
  7:     var $yearend = -1;
  9:  function date_pulldown($name) {
 10:       $this->name = $name;
 11:   }

We first declare the $name property. This will be used to name the HTML select elements. The $timestamp property will hold a Unix timestamp. The $months array property contains the strings we will display in our month pull-down. The $yearstart and $yearend properties are both set to ?1 pending initialization. They will eventually hold the first and last years of the range that will be presented in the year pull-down.

The constructor is very simple. It accepts a string, which we assign to the $name property. Now that we have the basis of our class, we need a set of methods by which the client code can set the date. The snippet continues as follows:

 12:      function setDate_global() {
 13:          if (!$this->setDate_array($GLOBALS[$this->name])) {
 14:              return $this->setDate_timestamp(time());
 15:          }
 17:          return true;
 18:     }
 20:     function setDate_timestamp($time) {
 21:          $this->timestamp = $time;
 22:          return true;
 23:     }
 25:     function setDate_array($inputdate) {
 26:         if (is_array($inputdate) &&
 27:             isset($inputdate['mon']) &&
 28:             isset($inputdate['mday']) &&
 29:             isset($inputdate['year'])) {
 31:             $this->timestamp = mktime(11, 59, 59,
 32:                 $inputdate['mon'], $inputdate['mday'], $inputdate['year']);
 34:             return true;
 35:         }
 37:         return false;
 38:    }

Of these methods, setDate_timestamp() is the simplest. It requires a Unix timestamp, which it assigns to the $timestamp property.

The setDate_array() method expects an associative array with at least three keys: 'mon', 'mday', and 'year'. These fields will contain data in the same format as in the array returned by getdate(). This means that setDate_array() will accept a hand-built array such as

array('mday'=> 20, 'mon'=>9, 'year' => 2002);

or the result of a call to getdate():


It is no accident that the pull-downs we will build later will be constructed to produce an array containing 'mon', 'mday', and 'year' fields. The method uses the mktime() function to construct a timestamp, which is then assigned to the $timestamp variable.

The setDate_global() method is called by default. It attempts to find a global variable with the same name as the object's $name property. This is passed to setDate_array(). If this method discovers a global variable of the right structure, it uses that variable to create the $timestamp variable. Otherwise, the current date is used.

The ranges for days and months are fixed, but years are a different matter. We create a few methods to allow the client coder to set her own range of years (although we also provide default behavior):

 39:     function setYearStart($year) {
 40:         $this->yearstart = $year;
 41:     }
 43:     function setYearEnd($year) {
 44:         $this->yearend = $year;
 45:     }
 47:     function getYearStart() {
 48:         if ($this->yearstart < 0) {
 49:             $nowarray = getdate(time());
 50:             $this->yearstart = $nowarray[year]-5;
 51:         }
 53:         return $this->yearstart;
 54:     }
 56:     function getYearEnd() {
 57:         if ($this->yearend < 0) {
 58:             $nowarray = getdate(time());
 59:             $this->yearend = $nowarray[year]+5;
 60:         }
 61:         return $this->yearend;
 62:    }

The setYearStart() and setYearEnd() methods are straightforward. A year is directly assigned to the appropriate property. The getYearStart() method tests whether or not the $yearstart property has been set. If it has not been set, getYearStart() assigns a $yearstart value five years before the current year. The getYearEnd() method performs a similar operation. We're now ready to create the business end of the class:

 63:     function output() {
 64:         if ($this->timestamp < 0) {
 65:             $this->setDate_global();
 66:         }
 67:         $datearray = getdate($this->timestamp);
 68:         $out = $this->day_select($this->name, $datearray);
 69:         $out .= $this->month_select($this->name, $datearray);
 70:         $out .= $this->year_select($this->name, $datearray);
 71:         return $out;
 72:    }
 74:    function day_select($fieldname, $datearray) {
 75:        $out = "<select name=\"$fieldname"."[mday]\">\n";
 76:        for ($x=1; $x<=31; $x++) {
 77:            $out .= "<option value=\"$x\"".($datearray['mday']==($x)
 78:                ?" SELECTED":"").">".sprintf("%02d", $x) ."\n";
 79:        }
 80:        $out .= "</select>\n";
 81:        return $out;
 82:    }
 84:    function month_select($fieldname, $datearray) {
 85:        $out = "<select name=\"$fieldname"."[mon]\">\n";
 86:        for ($x = 1; $x <= 12; $x++) {
 87:            $out .= "<option value=\"".($x)."\"".($datearray['mon']==($x)
 88:                ?" SELECTED":"")."> ".$this->months[$x-1]."\n";
 89:        }
 90:        $out .= "</select>\n";
 91:        return $out;
 92:    }
 94:    function year_select($fieldname, $datearray) {
 95:        $out = "<select name=\"$fieldname"."[year]\">";
 96:        $start = $this->getYearStart();
 97:        $end = $this->getYearEnd();
 98:        for ($x= $start; $x < $end; $x++) {
 99:            $out .= "<option value=\"$x\"".($datearray['year']==($x)
100:                ?" SELECTED":"").">$x\n";
101:        }
102:        $out .= "</select>\n";
103:        return $out;
104:    }
105: }

The output() method orchestrates most of this code. It first checks the $timestamp property. Unless the client coder has called one of the setDate methods, $timestamp will be set to ?1 and setDate_global() will be called by default. The timestamp is passed to the getdate() function to construct a date array, and a method is called for each pull-down to be produced.

The day_select() method simply constructs an HTML select element with an option element for each of the 31 possible days in a month. The object's current date is stored in the $datearray argument variable, which is used during the construction of the element to set the selected attribute of the relevant option element. The sprintf() function formats the day number, adding a leading zero to days 1?9. The month_select() and year_select() methods use similar logic to construct the month and year pull-downs.

Why did we break down the output code into four methods, rather than simply creating one block of code? When we build a class, we have two kinds of user in mind: the client coder who will want to instantiate a date_pulldown object, and the client coder who will want to subclass the date_pulldown class to refine its functionality. For the former, we want to provide a simple and clear interface to the class's functionality. The coder can instantiate an object, set its date, and call the output() method. For the latter, we want to make it easy to change discrete elements of the class's functionality. By putting all the output code into one method, we would force a child class that needed to tweak output to reproduce a lot of code that is perfectly usable. By breaking this code into discrete methods, we allow for subclasses that can change limited aspects of functionality without disturbing the whole. If a child class needs to represent the year pull-down as two radio buttons, for example, the coder can simply override the year_select() method.

Listing 12.4 contains some code that calls the library class. Before you try to execute this code, take all the class snippets we've covered and put them into a file called date_pulldown.class.php, and place this file in the document root of your Web server. You'll be calling it in a moment, so it had better be there!

Listing 12.4 Using the date_pulldown Class
  1: <html>
  2: <head>
  3: <title>Listing 12.4 Using the date_pulldown Class</title>
  4: </head>
  5: <?php
  6: include("date_pulldown.class.php");
  7: $date1 = new date_pulldown("fromdate");
  8: $date2 = new date_pulldown("todate");
  9: $date3 = new date_pulldown("foundingdate");
 10: $date3->setYearStart(1972);
 11: if (empty($foundingdate))
 12:     $date3->setDate_array(array('mday'=>26, 'mon'=>4, 'year'=>1984));
 13: ?>
 14: <body>
 16: <form>
 17: From:<br>
 18: <?php print $date1->output(); ?><p>
 20: To:<br>
 21: <?php print $date2->output(); ?><p>
 23: Company founded:<br>
 24: <?php print $date3->output(); ?><p>
 26: <input type="submit">
 27: </form>
 29: </body>
 30: </html>

We include the date_pulldown.class.php on line 6 (see Figure 12.3). Once we have included the class file, we can use all of its methods. We use the class's default behavior for all the pull-downs apart from "foundingdate". For this object, we override the default year start, setting it to 1972 on line 10. On line 12, we assign this pull-down an arbitrary date that will be displayed until the form is submitted. Note that this is only the front end of a form, with no action or method; you need to supply your own action or method in order for this to actually do something!

Figure 12.3. The pull-downs generated by the date_pulldown class.


    Part III: Getting Involved with the Code