PEAR::Calendar

Documentation for PEAR::Calendar 0.4+ is now hosted in the PEAR manual at http://pear.php.net/manual/en/package.datetime.php. If it's not there yet, it's because there's some remaining docbook issues to fix, but it's written. - 24th Nov 2003

Version: $Id: index.html,v 1.5 2003/10/29 08:36:52 harryf Exp $

API Docs : Examples : Tutorials : Download

Contents

- Introduction
- Features
- FAQ

Introduction

PEAR::Calendar is an package for generating Calendars as data structures (it does not render content - that's your job!) allowing you to easily connect up your user interface with an underlying data store (such as MySQL).

It provides a simple API to make generating a calendar much like querying a database - just pick a date you want then render the output with a loop. For example;

require_once('Calendar/Month.php');

$Month = & new Calendar_Month();
$Month->build();

while ( $Day = & $Month->fetch() ) {
    echo ( $Day->thisDay() ."\n" );
}

Features

FAQ

- Why doesn't it generate HTML?

What if you want WML, SOAP, PDF, GIF, command line, etc. etc.? PEAR::Calendar can be used to generate any output format you like (see the examples for SOAP and WML). Tying it to a particular output content type will limit it's use (a problem that every public domain PHP Calendar library I've looked at suffers from). A PEAR::HTML_Calendar is likely to be developed using PEAR::Calendar.

- There's too many objects, classes and files. It's bloated!

Leave your attitude towards OOP behind - PHP makes an excellent OO language. On Sourceforge's high overworked servers, it's generating calendars in under 0.1 seconds (see example 3). The code is well optimized and designed only to include the classes needed for the current operation.

- You use Unix timestamps to calculate the Calendar, which limits the years it can generate. Can this be changed?

All calculations are handled by a class implementing the Calendar_Engine interface. Currently the only implemention is based on PHP's date() and mktime() functions (so Unix timestamps are required for that engine).

Because all calculations are externalized, it should be possible to implement different engines (in theory - not tested yet) for example PEAR::Date should be able to be used as the basis for an engine (if not - it needs fixing). Hopefully that also means you can implement something like a Chinese calendar. See the Calendar/Engine/Interface.php to see what an engine has to provide (note that this file is not used anywhere but simply there to make the engine interface clear to developers). One the engine is written, modification will need to be made to Calendar/Engine/Factory.php to allow it to be used.

- Your examples use the English language for days and months. Can this be changed?

PEAR::Calendar only uses base 10 numbers for calculations - the names of months and days of the week and generated as the calendar is being rendered (by you). You should only need to change PHP's locale with setlocale() and use the strftime() function e.g.;

$Day = & new Calendar_Day(2003,10,23);

setlocale (LC_TIME, 'de_DE'); // German

echo ( strftime('%A %d %B %Y',$Day->getTimeStamp()));
(note setlocale usage varies depends on OS).

- What are EmptyDays?

PEAR::Calendar makes it easy to render calendars in tabular format (like humans are used to) such as;
October 2003
M	T	W	T	F	S	S
		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
Notice the top left and botton right of this example - these are "EmptyDays". EmptyDays are generated only by two calendar classes: Calendar_Month_Weekdays and Calendar_Week. For example using Calendar_Month_Weekdays;
require_once('Calendar/Month/Weekdays.php');

$Month = & new Calendar_Month_Weekdays();
$Month->build();

while ( $Day = & $Month->fetch() ) {
    if ( $Day->isFirst() ) // Check for the start of a week
        echo ( "\n" );

    if ( $Day->isEmpty() ) // Check to see if day is empty
        echo ( "\t" );

    else
        echo ( $Day->thisDay()."\t" );

    if ( $Day->isLast() ) // Check for the end of a week
        echo ( "\n" );
}
Using Calendar_Week, you will only get 7 days (use Calendar_Month_Weeks to get Calendar_Week objects), so the isFirst() and isLast() methods are not applicable.

- How do I select some dates?

All calendar objects (except Calendar_Second, which has no "children") have the method build() to build the "children" of that object. For example Calendar_Year::build() builds Calendar_Month objects while Calendar_Hour::build() builds Calendar_Minute objects.
You have the option of passing this method an indexed array of Calendar objects which will be used to "select" the matching built children. For example;

$Month = & new Calendar_Month(2003,10); // Oct 2003

$SelectedDay1 = & new Calendar_Day(2003,10,5); Oct 5th 2003
$SelectedDay2 = & new Calendar_Day(2003,10,21); Oct 21st 2003

// Place in an array...
$selection = array($SelectedDay1,$SelectedDay2);

$Month->build($selection);

while ($Day = & $Month->fetch()) {
    if ( $Day->isSelected() )
        echo ( $Day->thisYear()." ".$Day->thisMonth()." ".$Day->thisDay()." is selected\n"
}

Note: date objects you pass to a build() method replace existing date objects, allowing you to do things like apply a Calendar_Decorator to them.

- Why do I have to call build() explicitly. Why can't children be built automatically?

First and foremost, for performance. Building the children has a performance cost and you won't always need to have the children, so it should be called explicitly, other wise you might have $Year->build(), expecting to get just months but behind the scenes, months built days, which built hours, which build minutes etc.
Also calling build() yourself give you a chance to "select" some of the children.

- How do I validate a date?

Validity is determined by the Calendar_Engine being used as well as the time the date object you're working with represents (e.g. $Month = & new Month(2003,2,29); is invalid, because Feb 2003 was not a leap year).
For quick validation, you can call the method isValid() on any date object, which will return FALSE if there's a problem. For more information of more detailed validation, you can call the method getValidator() on any date object, which returns an instance of the class Calendar_Validator. For example;

$Month = & new Month(2003,2,29);
if (! $Month->isValid() ) {
    $Validator = & $Month->getValidator();
    while ( $Error = $Validator->fetch() ) {
        echo ( $Error->toString() );
    }
}

You can also begin validation by calling getValidator() then either isValidYear(), isValidMonth(), isValidDay(), isValidHour(), isValidMinute() and isValidSecond() (or isValid() which calls all of the preceding methods).

Note: the method isValidWeek() also exists but needs to be explicitly called and expects you to have got your validator from a Calendar_Week object.

- After calling build(), I just want to get a single child date object with looping through the lot. How?

The method fetchAll() can be called on any date object to get an indexed array of all the children which have been built, allowing you to reference them directly. Be careful with the first index of this array - in some cases it will be [1] not [0], depending on the type of date object built. For example;

$Month = & new Calendar_Month(2003,10);
$Month->build();

$days = & $Month->fetchAll(); // Now all in array
echo ( $days[1]->thisDay() ); // The first day has index 1

$Hour = & new Calendar_Hour(2003,10,25,15); // Oct 25th 2003, 3pm
$Hour->build();

$hours = & $Hour->fetchAll(); // Now all in array
echo ( $hours[0]->thisHour() ); // The first hour has index 0
The following classes are always built to have the first index as 1: Calendar_Month, Calendar_Month_Weekdays, Calendar_Month_Weeks, Calendar_Week and Calendar_Day
The following classes are always built to have the first index as 0: Calendar_Hour, Calendar_Minute and Calendar_Second

Note also the method size() can be called on any date object, after build() has been called, to get the number of children.

- What does a week actually represent in PEAR::Calendar?

A week in PEAR::Calendar is a tabular week - i.e. a week is a single row when displaying a month in calendar in tabular form (see the example above). Any given month will either have 4, 5 or 6 week, depending on the number of days in the month and the day of the week that the 1st of the month falls on.

- Why do Calendar_Week::nextWeek() and Calendar_Week::lastWeek() sometimes return nothing?

If it's the first week of the month, lastWeek() returns nothing and if it's the last week of the month, nextWeek() returns nothing. This may change in future but for the time being, it's easier that way.

- How do I get a Calendar_Year to build Calendar_Month_Weekdays or Calendar_Month_Weeks, instead of the default Calendar_Month objects?

When working with a Calendar_Year, the constants CALENDAR_MONTH_STATE controls what type of month object is built. You can define CALENDAR_MONTH_STATE to CALENDAR_USE_MONTH_WEEKDAYS or CALENDAR_USE_MONTH_WEEKS for the Calendar_Month_Weekdays and Calendar_Month_Week classes, respectively.

- You use Monday as the start of the week. Can I change that?

Yes. For the classes which are concerned with the notion of a "week", you can can pass a value which defines the first day of the week. For the default timestamp based Calendar engine, this is a number from 0 to 6, 0 being Sunday through to 6 being Saturday.

This value can be passed to the following:
Calendar_Year::build($selection,$firstday) - the second argument is the first day of the week.
Calendar_Month_Weekdays::Calendar_Month_Weekdays($y,$m,$firstday) - the third argument to the constructor
Calendar_Month_Weeks::Calendar_Month_Weeks($y,$m,$firstday) - the third argument to the constructor
Calendar_Week::Calendar_Week($y,$m,$w,$firstday) - the fourth argument to the constructor