Tuesday, July 14, 2009

Beginning PHP and Oracle From Novice to Professional by W. Jason Gilmore and Bob Bryla Chapter 12

Time- and date-based information plays a significant role in our lives and, accordingly, program- mers must commonly wrangle with temporal data on a regular basis. When was a tutorial published?
Is the pricing information for a particular product recent? What time did the office assistant log into the accounting system? At what hour of the day does the corporate Web site see the most visitor traffic? These and countless other time-oriented questions come about on a regular basis, making the proper accounting of such matters absolutely crucial to the success of your programming efforts.
This chapter introduces PHP’s powerful date and time manipulation capabilities. After offering some preliminary information regarding how Unix deals with date and time values, in a section called “Date Fu” you’ll learn how to work with time and dates in a number of useful ways. You’ll also create grid calendars using the aptly named PEAR package Calendar. Finally, the vastly improved date and time manipulation functions available as of PHP 5.1 are introduced.

The Unix Timestamp

Fitting the oft-incongruous aspects of our world into the rigorous constraints of a programming envi- ronment can be a tedious affair. Such problems are particularly prominent when dealing with dates and times. For example, suppose you are tasked with calculating the difference in days between two points in time, but the dates are provided in the formats July 4, 2007 3:45pm and 7th of December, 2007
18:17. As you might imagine, figuring out how to do this programmatically would be a daunting affair.
What you need is a standard format, some sort of agreement regarding how all dates and times will be presented. Preferably, the information would be provided in some sort of standardized numerical format—20070704154500 and 20071207181700, for example. In the programming world, date and time values formatted in such a manner are commonly referred to as timestamps.
However, even this improved situation has its problems. For instance, this proposed solution still doesn’t resolve challenges presented by time zones, daylight saving time, or cultural variances to date formatting. You need to standardize according to a single time zone and devise an agnostic format that could easily be converted to any desired format. What about representing temporal values in seconds and basing everything on Coordinated Universal Time (UTC)? In fact, this strategy was embraced by the early Unix development team, using 00:00:00 UTC January 1, 1970, as the base from which all dates are calculated. This date is commonly referred to as the Unix epoch. Therefore, the incongruously formatted dates in the previous example would actually be represented as 1183578300 and 1197069420, respectively.

229

■Caution You may be wondering whether it’s possible to work with dates prior to the Unix epoch (00:00:00 UTC January 1, 1970). Indeed it is, at least if you’re using a Unix-based system. On Windows, due to an integer overflow issue, an error will occur if you attempt to use the timestamp-oriented functions in this chapter in conjunction with dates prior to the epoch definition.

PHP’s Date and Time Library

Even the simplest of PHP applications often involves at least a few of PHP’s date- and time-related functions. Whether validating a date, formatting a timestamp in some particular arrangement, or converting a human-readable date value to its corresponding timestamp, these functions can prove immensely useful in tackling otherwise quite complex tasks.

■Note While your company may be based in Ohio, the corporate Web site could conceivably be hosted anywhere,
be it Texas, California, or even Tokyo. This may present a problem if you’d like date and time representations and calculations to be based on the Eastern Time Zone because by default PHP will rely on the operating system’s time zone settings. You can, however, change your Web site’s time zone through the date.timezone configuration directive, which can be manipulated per usual via the standard routes (see Chapter 2) or by using the date_ default_timezone_set() function. See the PHP manual for more information.

Validating Dates

Although most readers could distinctly recall learning the “Thirty Days Hath September” poem1 back in grade school, it’s unlikely many of us could recite it, present company included. Thankfully, the checkdate() function accomplishes the task of validating dates quite nicely, returning TRUE if the supplied date is valid, and FALSE otherwise. Its prototype follows:

Boolean checkdate(int month, int day, int year)

Let’s consider an example:

echo "April 31, 2007: ".(checkdate(4, 31, 2007) ? 'Valid' : 'Invalid');
// Returns false, because April only has 30 days echo "<br />";
echo "February 29, 2004: ".(checkdate(02, 29, 2004) ? 'Valid' : 'Invalid');
// Returns true, because 2004 is a leap year echo "<br />";
echo "February 29, 2007: ".(checkdate(02, 29, 2007) ? 'Valid' : 'Invalid');
// Returns false, because 2007 is not a leap year

Executing this example produces the following output:

1. Thirty days hath September, April, June, and November; All the rest have thirty-one, Excepting for February alone, Which hath twenty-eight days clear, And twenty-nine in each leap year.

April 31, 2007: Invalid February 29, 2004: Valid February 29, 2007: Invalid

Formatting Dates and Times

The date() function returns a string representation of the current date and/or time formatted according to the instructions specified by a predefined format. Its prototype follows:

string date(string format [, int timestamp])

Table 12-1 highlights the most useful parameters. (Forgive the decision to forgo inclusion of the parameter for Swatch Internet Time.2)
If you pass the optional timestamp, represented in Unix timestamp format, date() will return a corresponding string representation of that date and time. If the timestamp isn’t provided, the current
Unix timestamp will be used in its place.

Table 12-1. The date() Function’s Format Parameters

Parameter Description Example

a Lowercase ante meridiem and post meridiem

A Uppercase ante meridiem and post meridiem

am or pm

AM or PM

d Day of month, with leading zero 01 to 31

D Three-letter text representation of day Mon through Sun

F Complete text representation of month January through December

g 12-hour format, without zeros 1 through 12

G 24-hour format, without zeros 0 through 23

h 12-hour format, with zeros 01 through 12

H 24-hour format, with zeros 00 through 23

i Minutes, with zeros 01 through 60

I Daylight saving time 0 if no, 1 if yes

j Day of month, without zeros 1 through 31

l Text representation of day Monday through Sunday

L Leap year 0 if no, 1 if yes

2. You can actually use date() to format Swatch Internet Time. Created in the midst of the dot-com insanity, the watchmaker Swatch (http://www.swatch.com/) came up with the concept of “Internet time,” which intended to do away with the stodgy old concept of time zones, instead setting time according to “Swatch Beats.” Not surprisingly, the universal reference for maintaining Swatch Internet Time was established via a meridian residing at the Swatch corporate office.

Table 12-1. The date() Function’s Format Parameters (Continued)

Parameter Description Example

m Numeric representation of month, with zeros

01 through 12

M Three-letter text representation of month Jan through Dec

n Numeric representation of month, without zeros

O Difference to Greenwich Mean
Time (GMT)

1 through 12

–0500

r Date formatted according to RFC 2822 Tue, 19 Apr 2007 22:37:00 –0500

s Seconds, with zeros 00 through 59

S Ordinal suffix of day st, nd, rd, th

t Total number of days in month 28 through 31

T Time zone PST, MST, CST, EST, etc.

U Seconds since Unix epoch (timestamp) 1172347916

w Numeric representation of weekday 0 for Sunday through 6 for
Saturday

W ISO 8601 week number of year 1 through 52 or 1 through 53, depending on the day in which the week ends. See ISO 8601 standard for more information.

Y Four-digit representation of year 1901 through 2038 (Unix); 1970 through 2038 (Windows)

z Day of year 0 through 364

Z Time zone offset in seconds –43200 through 50400

Despite having regularly used PHP for years, many PHP programmers still need to visit the documentation to refresh their memory about the list of parameters provided in Table 12-1. Therefore, although you won’t necessarily be able to remember how to use this function simply by reviewing a few examples, let’s look at the examples just to give you a clearer understanding of what exactly date() is capable of accomplishing.
The first example demonstrates one of the most commonplace uses for date(), which is simply to output a standard date to the browser:
echo "Today is ".date("F d, Y");
// Today is August 22, 2007

The next example demonstrates how to output the weekday:

echo "Today is ".date("l");
// Today is Wednesday

Let’s try a more verbose presentation of the present date:

$weekday = date("l");
$daynumber = date("dS");
$monthyear = date("F Y");

printf("Today is %s the %s day of %s", $weekday, $daynumber, $monthyear);

This returns the following:

Today is Wednesday the 22nd day of August 2007

You might be tempted to insert the nonparameter-related strings directly into the date() function, like this:

echo date("Today is l the ds day of F Y");

Indeed, this does work in some cases; however, the results can be quite unpredictable. For instance, executing the preceding code produces the following:

EST200724pm07 3842 Saturday 2803America/New_York 2442 24pm07 2007f February 2007

However, because punctuation doesn’t conflict with any of the parameters, feel free to insert it as necessary. For example, to format a date as mm-dd-yyyy, use the following:

echo date("m-d-Y");
// 04-26-2007

Working with Time

The date() function can also produce time-related values. Let’s run through a few examples, starting with simply outputting the present time:
echo "The time is ".date("h:i:s");
// The time is 07:44:53

But is it morning or evening? Just add the a parameter:

echo "The time is ".date("h:i:sa");
// The time is 07:44:53pm

Learning More About the Current Time

The gettimeofday() function returns an associative array consisting of elements regarding the current time. Its prototype follows:

mixed gettimeofday([boolean return_float])

For those running PHP 5.1.0 and newer, the optional parameter return_float causes
gettimeofday() to return the current time as a float value. In total, four elements are returned:

• dsttime: The daylight saving time algorithm used, which varies according to geographic location.
There are 11 possible values: 0 (no daylight saving time enforced), 1 (United States), 2 (Australia),
3 (Western Europe), 4 (Middle Europe), 5 (Eastern Europe), 6 (Canada), 7 (Great Britain and
Ireland), 8 (Romania), 9 (Turkey), and 10 (the Australian 1986 variation).

• minuteswest: The number of minutes west of Greenwich Mean Time (GMT).

• sec: The number of seconds since the Unix epoch.

• usec: The number of microseconds should the time fractionally supercede a whole second value.

Executing gettimeofday() from a test server on February 24, 2007 16:18:04 produces the following output:

Array (
[sec] => 1172351884 [usec] => 321924 [minuteswest] => 300 [dsttime] => 1
)

Of course, it’s possible to assign the output to an array and then reference each element as necessary:

$time = gettimeofday();
$GMToffset = $time['minuteswest'] / 60;
printf("Server location is %d hours west of GMT.", $GMToffset);

This returns the following:

Server location is 5 hours west of GMT.

Converting a Timestamp to User-Friendly Values

The getdate() function accepts a timestamp and returns an associative array consisting of its compo- nents. The returned components are based on the present date and time unless a Unix-format timestamp is provided. Its prototype follows:

array getdate([int timestamp])

In total, 11 array elements are returned, including the following:

hours: Numeric representation of the hours. The range is 0 through 23.

mday: Numeric representation of the day of the month. The range is 1 through 31.

minutes: Numeric representation of the minutes. The range is 0 through 59.

mon: Numeric representation of the month. The range is 1 through 12.

month: Complete text representation of the month, e.g., July.

seconds: Numeric representation of the seconds. The range is 0 through 59. wday: Numeric representation of the day of the week, e.g., 0 for Sunday. weekday: Complete text representation of the day of the week, e.g., Friday.

yday: Numeric offset of the day of the year. The range is 0 through 364.

year: Four-digit numeric representation of the year, e.g., 2007.

0: Number of seconds since the Unix epoch (timestamp). While the range is system-dependent, on Unix-based systems it’s generally –2147483648 through 2147483647, and on Windows the
range is 0 through 2147483648.

■Caution The Windows operating system doesn’t support negative timestamp values, so the earliest date you
could parse with this function on Windows is midnight, January 1, 1970.

Consider the timestamp 1172350253 (February 24, 2007 15:50:53 EST). Let’s pass it to getdate()
and review the array elements:

Array (
[seconds] => 53 [minutes] => 50 [hours] => 15 [mday] => 24 [wday] => 6 [mon] => 2 [year] => 2007 [yday] => 54
[weekday] => Saturday [month] => February [0] => 1172350253
)

Working with Timestamps

PHP offers two functions for working with timestamps: time() and mktime(). The former is useful for retrieving the current timestamp, whereas the latter is useful for retrieving a timestamp corresponding to a specific date and time. Both functions are introduced in this section.

Determining the Current Timestamp

The time() function is useful for retrieving the present Unix timestamp. Its prototype follows:

int time()

The following example was executed at 15:25:00 EDT on August 27, 2007:

echo time();

This produces a corresponding timestamp:

1187897100

Using the previously introduced date() function, this timestamp can later be converted back to a human-readable date:

echo date("F d, Y h:i:s", 1187897100);

This returns the following:

August 7, 2007 03:25:00

Creating a Timestamp Based on a Specific Date and Time

The mktime() function is useful for producing a timestamp based on a given date and time. If no date and time is provided, the timestamp for the current date and time is returned. Its prototype follows:
int mktime([int hour [, int minute [, int second [, int month
[, int day [, int year [, int is_dst]]]]]]])

The purpose of each optional parameter should be obvious, save for perhaps is_dst, which should be set to 1 if daylight saving time is in effect, 0 if not, or –1 (default) if you’re not sure. The default value prompts PHP to try to determine whether daylight saving time is in effect. For example, if you want to know the timestamp for February 24, 2007, 4:24 p.m., all you have to do is plug in the appropriate values:

echo mktime(16,24,00,2,24,2007);

This returns the following:

1172352240

This is particularly useful for calculating the difference between two points in time. For instance, how many hours are there between now (June 4, 2007) and midnight April 15, 2008?

$now = mktime();
$taxday = mktime(0,0,0,4,15,2008);

// Difference in seconds
$difference = $taxday - $now;

// Calculate total hours
$hours = round($difference / 60 / 60);

echo "Only $hours hours until tax day!";

This returns the following:

Only 7568 hours until tax day!

Date Fu

This section demonstrates several of the most commonly requested date-related tasks, some of which involve just one function and others that involve some combination of several functions.

Displaying the Localized Date and Time

Throughout this chapter, and indeed this book, the Americanized temporal and monetary formats have been commonly used, such as 04-12-07 and $2,600.93. However, other parts of the world use different date and time formats, currencies, and even character sets. Given the Internet’s global reach, you may have to create an application that’s capable of adhering to foreign, or localized, formats. In fact, neglecting to do so can cause considerable confusion. For instance, suppose you are going to create a Web site that books reservations for a hotel in Orlando, Florida. This particular hotel is popular among citizens of various countries, so you decide to create several localized versions of the site. How should you deal with the fact that most countries use their own currency and date formats, not to mention different languages? While you could go to the trouble of creating a tedious method of managing such matters, it likely would be error-prone and take some time to deploy. Thankfully, PHP offers a built-in set of features for localizing this type of data.
PHP not only can facilitate proper formatting of dates, times, currencies, and such, but also can translate the month name accordingly. In this section, you’ll learn how to take advantage of this feature to format dates according to any locality you please. Doing so essentially requires two functions: setlocale() and strftime(). Both are introduced next, followed by a few examples.

Setting the Default Locale

The setlocale() function changes PHP’s localization default by assigning a new value. Its prototype follows:

string setlocale(mixed category, string locale [, string locale...])
string setlocale(mixed category, array locale)

Localization strings officially follow this structure:

language_COUNTRY.characterset

For example, if you want to use Italian localization, the locale string should be set to it_IT. Israeli localization would be set to he_IL, British localization to en_GB, and United States localization to en_US. The characterset component would come into play when potentially several character sets are available for a given locale. For example, the locale string zh_CN.gb18030 is used for handling Mongolian, Tibetan, Uigur, and Yi characters, whereas zh_CN.gb3212 is for Simplified Chinese.
You’ll see that the locale parameter can be passed as either several different strings or an array
of locale values. But why pass more than one locale? This feature is in place (as of PHP version 4.2.0) to counter the discrepancies between locale codes across different operating systems. Given that the vast majority of PHP-driven applications target a specific platform, this should rarely be an issue; however, the feature is there should you need it.
Finally, if you’re running PHP on Windows, keep in mind that, apparently in the interests of keeping us on our toes, Microsoft has devised its own set of localization strings. You can retrieve a list of the language and country codes at http://msdn.microsoft.com.

■Tip On some Unix-based systems, you can determine which locales are supported by running the command
locale -a.

It’s possible to specify a locale for a particular classification of data. Six different categories are supported:

LC_ALL: This sets localization rules for all of the following five categories.

LC_COLLATE: String comparison. This is useful for languages using characters such as â and é.

LC_CTYPE: Character classification and conversion. For example, setting this category allows PHP to properly convert â to its corresponding uppercase representation of  using the strtolower() function.

LC_MONETARY: Monetary representation. For example, Americans represent dollars in this format:
$50.00; Europeans represent euros in this format: 50,00.

LC_NUMERIC: Numeric representation. For example, Americans represent large numbers in this format: 1,412.00; Europeans represent large numbers in this format: 1.412,00.

LC_TIME: Date and time representation. For example, Americans represent dates with the month followed by the day, and finally the year. February 12, 2005, would be represented as 02-12-2005. However, Europeans (and much of the rest of the world) represent this date as 12-02-2005. Once set, you can use the strftime() function to produce the localized format.

Suppose you are working with monetary values and want to ensure that the sums are formatted according to the Italian locale:

setlocale(LC_MONETARY, "it_IT");
echo money_format("%i", 478.54);

This returns the following:

EUR 478,54

To localize dates and times, you need to use setlocale() in conjunction with strftime(), intro- duced next.

Localizing Dates and Times

The strftime() function formats a date and time according to the localization setting as specified by
setlocale(). Its prototype follows:

string strftime(string format [, int timestamp])

strftime()’s behavior is quite similar to the date()function, accepting conversion parameters that determine the layout of the requested date and time. However, the parameters are different from those used by date(), necessitating reproduction of all available parameters, shown in Table 12-2 for your reference. Keep in mind that all parameters will produce the output according to the set locale. Also note that some of these parameters aren’t supported on Windows.

Table 12-2. The strftime() Function’s Format Parameters

Parameter Description Examples or Range
%a Abbreviated weekly name Mon, Tue
%A Complete weekday name Monday, Tuesday
%b Abbreviated month name Jan, Feb
%B Complete month name January, February
%c Standard date and time 04/26/07 21:40:46
%C Century number 21

Table 12-2. The strftime() Function’s Format Parameters

Parameter Description Examples or Range

%d Numerical day of month, with leading zero 01, 15, 26

%D Equivalent to %m/%d/%y 04/26/07

%e Numerical day of month, no leading zero 26

%g Same output as %G, but without the century 05

%G Numerical year, behaving according to rules set by %V 2007

%h Same output as %b Jan, Feb

%H Numerical hour (24-hour clock), with leading zero 00 through 23

%I Numerical hour (12-hour clock), with leading zero 01 through 12

%j Numerical day of year 001 through 366

%m Numerical month, with leading zero 01 through 12

%M Numerical minute, with leading zero 00 through 59

%n Newline character \n

%p Ante meridiem and post meridiem AM, PM

%r Ante meridiem and post meridiem, with periods A.M., P.M.

%R 24-hour time notation 00:01:00 through
23:59:59

%S Numerical seconds, with leading zero 00 through 59

%t Tab character \t

%T Equivalent to %H:%M:%S 22:14:54

%u Numerical weekday, where 1 = Monday 1 through 7

%U Numerical week number, where the first Sunday of the year is the first day of the first week of the year

%V Numerical week number, where week 1 = first week with >= 4 days

%W Numerical week number, where the first Monday is the first day of the first week

17

01 through 53

08

%w Numerical weekday, where 0 = Sunday 0 through 6

%x Standard date 04/26/07

%X Standard time 22:07:54

%y Numerical year, without century 05

%Y Numerical year, with century 2007

%Z or %z Time zone Eastern Daylight
Time

%% The percentage character %

By using strftime() in conjunction with setlocale(), it’s possible to format dates according to your user’s local language, standards, and customs. For example, it would be simple to provide a travel Web site user with a localized itinerary with dates and ticket cost:
Benvenuto abordo, Sr. Sanzi<br />
<?php
setlocale(LC_ALL, "it_IT");
$tickets = 2;
$departure_time = 1118837700;
$return_time = 1119457800;
$cost = 1350.99;
?>
Numero di biglietti: <?php echo $tickets; ?><br />
Orario di partenza: <?php echo strftime("%d %B, %Y", $departure_time); ?><br /> Orario di ritorno: <?php echo strftime("%d %B, %Y", $return_time); ?><br /> Prezzo IVA incluso: <?php echo money_format('%i', $cost); ?><br />
This example returns the following:

Benvenuto abordo, Sr. Sanzi
Numero di biglietti: 2
Orario di partenza: 15 giugno, 2007
Orario di ritorno: 22 giugno, 2007
Prezzo IVA incluso: EUR 1.350,99

Displaying the Web Page’s Most Recent Modification Date

Barely a decade old, the Web is already starting to look like a packrat’s office. Documents are strewn everywhere, many of which are old, outdated, and often downright irrelevant. One of the common- place strategies for helping the visitor determine the document’s validity involves adding a timestamp to the page. Of course, doing so manually will only invite errors, as the page administrator will eventually forget to update the timestamp. However, it’s possible to automate this process using date() and getlastmod(). The getlastmod() function returns the value of the page’s Last Modified header, or FALSE in the case of an error. Its prototype follows:

int getlastmod()

If you use it in conjunction with date(), providing information regarding the page’s last modi- fication time and date is trivial:

$lastmod = date("F d, Y h:i:sa", getlastmod());
echo "Page last modified on $lastmod";

This returns output similar to the following:

Page last modified on February 26, 2007 07:59:34pm

Determining the Number of Days in the Current Month

To determine the number of days in the current month, use the date() function’s t parameter. Consider the following code:

printf("There are %d days in %s.", date("t"), date("F"));

If this is executed in April, the following result will be output:

There are 30 days in April.

Determining the Number of Days in Any Given Month

Sometimes you might want to determine the number of days in some month other than the present month. The date() function alone won’t work because it requires a timestamp, and you might only have a month and year available. However, the mktime() function can be used in conjunction with date() to produce the desired result. Suppose you want to determine the number of days found in February 2007:
$lastday = mktime(0, 0, 0, 3, 0, 2007);
printf("There are %d days in February 2007.", date("t",$lastday));

Executing this snippet produces the following output:

There are 28 days in February 2007.

Calculating the Date X Days from the Present Date

It’s often useful to determine the precise date of some specific number of days into the future or past. Using the strtotime() function and GNU date syntax, such requests are trivial. Suppose you want to know what the date will be 45 days into the future, based on today’s date of February 25, 2007:
$futuredate = strtotime("45 days");
echo date("F d, Y", $futuredate);

This returns the following:

April 12, 2007

By prepending a negative sign, you can determine the date 45 days into the past (today being
February 25, 2007):

$pastdate = strtotime("-45 days");
echo date("F d, Y", $pastdate);

This returns the following:

January 11, 2007

What about ten weeks and two days from today (February 25, 2007)?

$futuredate = strtotime("10 weeks 2 days");
echo date("F d, Y", $futuredate);

This returns the following:

May 9, 2007

Taking Advantage of PEAR: Creating a Calendar

The Calendar PEAR package consists of a number of classes capable of automating numerous chro- nological tasks such as the following:

• Rendering a calendar of any scope in a format of your choice (hourly, daily, weekly, monthly, and yearly being the most common).
• Navigating calendars in a manner reminiscent of that used by the Gnome Calendar and
Windows Date & Time Properties interface.

• Validating any date. For example, you can use Calendar to determine whether April 1, 2019, falls on a Monday (it does).
• Extending Calendar’s capabilities to tackle a variety of other tasks—date analysis for instance.

Before you can begin taking advantage of this powerful package, you need to install it. You learned about the PEAR package installation process in Chapter 11 but for those of you not yet entirely familiar with it, the necessary steps are reproduced next.

Installing Calendar

To capitalize upon all of Calendar’s features, you also need to install the Date package. Let’s take care of both during the Calendar installation process, which follows:

%>pear install -a -f Date

WARNING: failed to download pear.php.net/Calendar within preferred state
"stable", will instead download version 0.5.3, stability "beta"
downloading Calendar-0.5.3.tgz ...
Starting to download Calendar-0.5.3.tgz (63,274 bytes)
................done: 63,274 bytes downloading Date-1.4.7.tgz ...
Starting to download Date-1.4.7.tgz (55,754 bytes)
...done: 55,754 bytes
install ok: channel://pear.php.net/Date-1.4.7 install ok: channel://pear.php.net/Calendar-0.5.3

The –f flag is included when installing Calendar here because, at the time of this writing, Calendar was still a beta release. By the time of publication, Calendar could be officially stable, meaning you won’t need to include this flag. See Chapter 11 for a complete introduction to PEAR and the install command.

Working with Calendar

In addition to the Calendar base class, the Calendar package consists of several public classes broken down into four distinct groups:

Date classes: Used to manage the six date components: years, months, days, hours, minutes, and seconds. A separate class exists for each component: Calendar_Year, Calendar_Month, Calendar_Day, Calendar_Hour, Calendar_Minute, and Calendar_Second.

Tabular date classes: Used to build monthly and weekly grid-based calendars. Three classes are available: Calendar_Month_Weekdays, Calendar_Month_Weeks, and Calendar_Week. These classes are useful for building monthly tabular calendars in daily and weekly formats, and weekly tabular calendars in a seven-day format, respectively.

Validation classes: Used to validate dates. The two classes are Calendar_Validator, which is used to validate any component of a date and can be called by any subclass, and Calendar_ Validation_Error, which offers an additional level of reporting if something is wrong with a date and provides several methods for dissecting the date value.

Decorator classes: Used to extend the capabilities of the other subclasses without having to actually extend them. For instance, suppose you want to extend Calendar’s functionality with a few features for analyzing the number of Saturdays falling on the 17th of any given month. A decorator would be an ideal way to make that feature available. Several decorators are offered for reference and use, including Calendar_Decorator, Calendar_Decorator_Uri, Calendar_Decorator_ Textual, and Calendar_Decorator_Wrapper. In the interest of covering only the most commonly used features, Calendar’s decorator internals aren’t discussed here; consider examining the decorators installed with Calendar for ideas regarding how to go about creating your own.

All four classes are subclasses of Calendar, meaning all of the Calendar class’s methods are available to each subclass. For a complete summary of the methods for this superclass and the four subclasses, see http://pear.php.net/package/Calendar.

Creating a Monthly Calendar

These days, grid-based monthly calendars seem to be one of the most commonly desired Web site features, particularly given the popularity of time-based content such as blogs. Yet creating one from scratch can be deceivingly difficult. Thankfully, Calendar handles all of the tedium for you, offering the ability to create a grid calendar with just a few lines of code. For example, suppose you want to create a calendar as shown in Figure 12-1.
The code for creating this calendar is surprisingly simple and is presented in Listing 12-1. An explanation of key lines follows the code, referring to their line numbers for convenience.

Figure 12-1. A grid calendar

Listing 12-1. Creating a Monthly Calendar

01 <?php
02 require_once 'Calendar/Month/Weekdays.php';
03
04 $month = new Calendar_Month_Weekdays(2006, 4, 0);
05
06 $month->build();
07
08 echo "<table class='calendar'>\n";
09 echo "<tr><th>April, 2006</th></tr>";
10 echo "<tr><td>Su</td><td>Mo</td><td>Tu</td><td>We</td>
11 <td>Th</td><td>Fr</td><td>Sa</td></tr>";

12 while ($day = $month->fetch()) {
13 if ($day->isFirst()) {
14 echo "<tr>";
15 }
16
17 if ($day->isEmpty()) {
18 echo "<td>&nbsp;</td>";
19 } else {
20 echo '<td>'.$day->thisDay()."</td>";
21 }
22
23 if ($day->isLast()) {
24 echo "</tr>";
25 }
26 }
27
28 echo "</table>";
29 ?>

Line 02: Because you want to build a grid calendar representing a month, the
Calendar_Month_Weekdays class is required. Line 02 makes this class available to the script.

Line 04: The Calendar_Month_Weekdays class is instantiated, and the date is set to April, 2006. The calendar should be laid out from Sunday to Saturday, so the third parameter is set to 0, which is representative of the Sunday numerical offset (1 for Monday, 2 for Tuesday, etc.).

Line 06: The build() method generates an array consisting of all dates found in the month.

Line 12: A while loop begins, responsible for cycling through each day of the month.

Lines 13–15: If $Day is the first day of the week, output a <tr> tag.

Lines 17–21: If $Day is empty, output an empty cell. Otherwise, output the day number.

Lines 23–25: If $Day is the last day of the week, output a </tr> tag.

Pretty simple isn’t it? Creating weekly and daily calendars operates on a very similar premise. Just choose the appropriate class and adjust the format as you see fit.

Validating Dates and Times

While PHP’s checkdate() function is useful for validating a date, it requires that all three date compo- nents (month, day, and year) are provided. But what if you want to validate just one date component, the month, for instance? Or perhaps you’d like to make sure a time value (hours:minutes:seconds), or some particular part of it, is legitimate before inserting it into a database. The Calendar package offers several methods for confirming both dates and times, or any part thereof. This list introduces these methods:

isValid(): Executes all the other time and date validator methods, validating a date and time

isValidDay(): Ensures that a day falls between 1 and 31 isValidHour(): Ensures that the value falls between 0 and 23 isValidMinute(): Ensures that the value falls between 0 and 59 isValidMonth(): Ensures that the value falls between 1 and 12

isValidSecond(): Ensures that the value falls between 0 and 59

isValidYear(): Ensures that the value falls between 1902 and 2037 on Unix, or 1970 and 2037 on Windows

Date and Time Enhancements for PHP 5.1+ Users Enhanced support for PHP’s date and time support was added in PHP 5.1. Not only was an object- oriented interface added, but so was the ability to manage your dates and times in respect to various time zones. This section touches solely upon the object-oriented interface.

Introducing the DateTime Constructor

Before you can use the Date features, you need to instantiate a date object via its class constructor. This constructor’s prototype follows:

object DateTime([string $time [, DateTimeZone $timezone]])

The Date() method is the class constructor. You can set the date either at the time of instantia- tion or later by using a variety of mutators (setters). To create an empty date object (which will set the object to the current date), just call DateTime() like so:

$date = new DateTime();

To create an object and set the date to February 27, 2007, execute the following:

$date = new Date("27 February 2007");

You can set the time as well, for instance to 9:55 p.m., like so:

$date = new Date("27 February 2007 21:55");

Or you can just set the time like so:

$date = new Date("21:55");

In fact, you can use any of the formats supported by PHP’s strtotime() function, introduced earlier in this chapter. Refer to the PHP manual for additional examples of supported formats.
The optional $timezone parameter refers to one of PHP’s supported time zone settings. Remember that by default PHP is going to use the time as specified by your server, which could conceivably be located anywhere on the planet. If you want the dates and times to correspond to a set time zone, you can use this parameter. Consult the PHP manual for more information about its time zone support.

Formatting Dates

To format the date and time for output, or easily retrieve a single component, you can use the format() method. This method accepts the same parameters as the date() function. For example, to output the date and time using the format 2007-02-27 09:55:00pm you would call format() like so:

echo $date->format("Y-m-d h:i:sa");

Setting the Date After Instantiation

Once the DateTime object is instantiated, you can set its date with the setDate() method. The setDate() method sets the date object’s day, month, and year, returning TRUE on success, and FALSE otherwise. Its prototype follows:

Boolean setDate(integer year, integer month, integer day)

Let’s set the date to February 27, 2007:

$date = new DateTime();
$date->setDate(2007,2,27);
echo $date->format("F j, Y");

This returns the following:

February 27, 2007

Setting the Time After Instantiation

Just as you can set the date after DateTime instantiation, you can set the time using the setTime() method. The setTime() method sets the object’s hour, minute, and optionally the second, returning TRUE on success and FALSE otherwise. Its prototype follows:

Boolean setTime(integer hour, integer minute [, integer second])

Let’s set the time to 8:55 p.m.:

$date = new DateTime();
$date->setTime(20,55);
echo $date->format("h:i:s");

This returns the following:

08:55:00

Modifying Dates and Times

You can modify a DateTime object using the modify() method. This method accepts the same user- friendly syntax as that used within the constructor. For example, suppose you create a DateTime object having the value February 28, 2007 00:33:00. Now you want to adjust the date forward by seven hours, changing it to February 28, 2007 7:33:00:
$date = new DateTime("February 28, 2007 00:33");
$date->modify("+7 hours");
echo $date->format("Y-m-d h:i:s");

This returns the following:

2007-02-28 07:33:00

Summary

This chapter covered quite a bit of material, beginning with an overview of several date and time functions that appear almost daily in typical PHP programming tasks. Next up was a journey into the ancient art of Date Fu, where you learned how to combine the capabilities of these functions to carry out useful chronological tasks. You also read about the useful Calendar PEAR package, where you learned how to create grid-based calendars and validation and navigation mechanisms. Finally, an introduction to PHP 5.1’s object-oriented date-manipulation features was provided.
The next chapter focuses on the topic that is likely responsible for piquing your interest in learning more about PHP: user interactivity. We’ll jump into data processing via forms, demonstrating both basic features and advanced topics such as how to work with multivalued form components and automated form generation. You’ll also learn how to facilitate user navigation by creating breadcrumb navigation trails and custom 404 messages.

0 comments: