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 its 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 are too many objects, classes and files. It's bloated!
Running the examples on Sourceforge's servers (which are always overloaded), example 3.php renders in under 0.1 seconds (usually half that). The code is highly optimized and every "trick in the book" has been applied to make sure PHP only parses / executes the subset of logic you need for your specific problem. If in doubt, use Cache_Lite to cache the output HTML.
You use Unix timestamps to calculate the Calendar, which limits the range of years it can generate. Can this be changed?
All calculations are handled by a class implementing the Calendar_Engine interface. The default implemention is based on PHP's date() and mktime() functions (so Unix timestamps are required for that engine). A second engine exists which uses PEAR::Date. It's a bit slower but overcomes the limit on the range of Unixstamps. To switch between engines use the constant CALENDAR_ENGINE e.g.
<?php
// The default Unix timestamp engine (this definition is not required)
// define('CALENDAR_ENGINE', 'UnixTs');
// Switch to PEAR::Date engine
define('CALENDAR_ENGINE', 'PearDate');
?>
Note that the PearDate engine is based on PEAR::Date version 1.4 or newer.
This 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.:
<?php
$Day = & new Calendar_Day(2003, 10, 23);
setlocale (LC_TIME, 'de_DE'); // German
echo strftime('%A %d %B %Y', $Day->getTimeStamp());
?>
Note that Calendar_Decorator_Textual provides help in generating month and day of week names in a manner which is independent of the Calendar Engine you are using and can be modified with setlocale().
What are empty days?
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 "empty days". Empty days are generated only by two calendar classes: Calendar_Month_Weekdays and Calendar_Week. For example using Calendar_Month_Weekdays;
<?php
require_once 'Calendar/Month/Weekdays.php';
$Month = & new Calendar_Month_Weekdays(2003, 10);
$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";
}
?>
An empty day can still return values, the date it represents being from the previous or next month in the calendar. You may get empty days for Calendar_Month_Weekdays and Calendar_Week. Using Calendar_Week, you will only build 7 days (use Calendar_Month_Weeks to build 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:
<?php
$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: the date objects you pass to a build() method replace the corresponding built date objects, allowing you to do things like attach your own subclass of Calendar_Decorator to them, then access the decorating functionality inside the loop which renders the calendar. You might display the contents from an "events database table" using this approach.
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, otherwise 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;
<?php
$Month = & new Month(2003, 2, 29); // 29th Feb 2003 (?!?)
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 just isValid() which calls all of the isValidxxx methods).
Can I adjust invalid dates?
If you're allowing end users to navigate your calendar via the URL, were they to modify the URL to something like calendar.php?year=2003&month=13, instead of throwing a validation error at them, you could call the Calendar::adjust() method on the calendar object you create with that URL. You should then end up with January 2004 (in this example). This behaviour is possible thanks to mktime() for the Unix Timestamp engine, while being built into the PearDate engine for you.
After calling build(), I just want to get a single child date object without 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;
<?php
$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?
Weeks are "pseudo" dates. They're useful for formatting the user interface for end users. Weeks are instantiated with a year, a month and a day of the month. You can then have the week tell you its timestamp (which will be the same as the timestamp for the first day in the week), its numeric position within the tabular month (see empty days above), its numeric position within the year (this is an ISO-8601 week number of year, weeks starting on Monday) or an array containing the numeric year, month and the first day of the week (as a number within the month). For example:
<?php
$Week = & new Calendar_Week(2003, 10, 15);
$Week = new Calendar_Week(2003, 10, 15);
echo $Week->thisWeek(); // Displays 2 (week num in month)
echo $Week->thisWeek('n_in_month'); // Display 2 - same as above
echo $Week->thisWeek('n_in_year'); // Displays 41 (week in year)
echo $Week->thisWeek('timestamp'); // Displays unix timestamp or an ISO-8601 datetime
// (YYYY-MM-DD HH:MM:SS), depending on the engine.
print_r $Week->thisWeek('array'); // [year] => 2003 [month] => 10 [day] => 12
?>
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:
<?php
$Year = new Calendar_Year(2003);
$selection = array();
$Year->build($selection, 0); // the second argument is the first day of the week (Sunday)
$MonthWeekdays = new Calendar_Month_Weekdays(2003, 10, 6); // Third argument - Saturday
$MonthWeeks = new Calendar_Month_Weekdays(2003, 10, 2); // Third argument - Tuesday
$Week = new Calendar_Week(2003, 10, 15, 5) // Fourth argument - Friday
?>