|
9.4. Date HandlingPHP has a range of functions that handle date and time. Some of these functions work with a so-called UNIX timestamp, which is the number of seconds since January 1, 1970 at 00:00:00 GMT, the beginning of the UNIX epoch. Because PHP only handles unsigned 32-bit integers and most operating systems don't support negative timestamps, the range in which most of the PHP date functions operate is January 1, 1970 to January 19, 2038. The PEAR::Date package handles dates outside this range and also in a platform-independent way. 9.4.1. Retrieving Date and Time InformationThe easiest way of obtaining the current time is with the time() function. It accepts no parameters and simply returns the current timestamp: <?php echo time(); // Outputs something similar to "1077913162" ?> The resolution is 1 second. If you want some more accuracy, you have two options: microtime() and gettimeofday(). The microtime() function has one annoying peculiarity: The return value is a floating-point number containing the decimal part of the timestamp and the number of seconds since the epoch, concatenated with a space. This makes it, of course, a bit hard to use for a timestamp with sub-second resolution: <?php // Outputs something similar to "0.87395100 1078006447" echo microtime(); $time = preg_replace('@^(.*)\s+(.*)$@e', '\\2 + \\1', microtime()); echo $time; // Outputs 1078006447.8741 ?> In putting the two parts back together, you lose some of the precision. The gettimeofday() function has a nicer interface. It returns an array with elements representing the timestamp and additional microseconds. Two more elements are included in this array, but you cannot really rely on them because the underlying system functionalityat least in Linuxis not working correctly: <?php print_r(gettimeofday()); ?> returns Array ( [sec] => 1078006910 [usec] => 339699 [minuteswest] => -60 [dsttime] => 0 ) localtime() and getdate() both return an array. The elements contain information belonging to the (optional) timestamp passed to the function. The returned arrays are not exactly the same. Table 9.5 shows what the elements in the arrays mean.
The tm_isdst element of localtime() is especially interesting. It's the only way in PHP to see whether the server is in DST. Also, note that the month number in the return array of localtime() starts with 0, not with 1, which makes December month 11. The first parameter for both functions is a time stamp, allowing the functions to return date information based on the time you pass them, rather than just on the current time. localtime() normally returns an array with numerical indices, rather than the indices as described in the previous table. To signal the function to return an associative array, you need to pass TRue as the second parameter. If you want to return this associative array with information about the current time, you need to pass the time() function as first parameter: <?php print_r(localtime(time(), true)); ?> Two more date functions are available: gmmktime() and mktime(). Both functions create a timestamp based on parameters passed when the function is called. The difference between the two functions is that gmmktime() TReats the date/time parameters passed as a Greenwich Mean Time (GMT), while parameters passed to mktime() are treated as local time. The order of parameters is not very user friendly, as you can see in the prototype of the following function: timestamp mktime ( [$hour [, $minute [, $second [, $month [, $day [, $year [, $is_dst]]]]]]]) Note the particularly weird order of the parameters. All parameters are optional. If any parameter is not included, the "current" value is used, depending on the current date and time. The last parameter, is_dst, controls whether the date and time parameters that are passed to the function are DST-enabled or not. The default value for the parameter is -1, which signals PHP to determine for itself whether the date falls into the range when DST is observed. Here is an example: <?php /* mktime with a date outside the DST range */ echo date("Ymd H:i:s", mktime(15, 16, 17, 1, 17, 2004)). "\n"; echo date("Ymd H:i:s", mktime(15, 16, 17, 1, 17, 2004, 0)). "\n"; echo date("Ymd H:i:s", mktime(15, 16, 17, 1, 17, 2004, 1)). "\n"; /* mktime with a date inside the DST range */ echo date("Ymd H:i:s", mktime(15, 16, 17, 6, 17, 2004)). "\n"; echo date("Ymd H:i:s", mktime(15, 16, 17, 6, 17, 2004, 0)). "\n"; echo date("Ymd H:i:s", mktime(15, 16, 17, 6, 17, 2004, 1)). "\n\n"; ?> The first three calls "make" a timestamp for January 17, in which no DST is observed. Therefore, setting the $is_dst parameter to 0 has no effect on the returned timestamp. If it's set to 1, though, the timestamp will be one hour earlier, as the mktime() function converts the DST time (which is always one hour ahead of non-DST). For the second set of mktime() calls, we use June 17 in which DST is observed. Setting the $is_dst parameter to 0 now makes the function convert the time from non-DST to DST and, thus, the returned time-stamp will be one hour ahead of the result of the first and third calls. The output is 20040217 15:16:17 20040217 15:16:17 20040217 14:16:17 20040617 15:16:17 20040617 16:16:17 20040617 15:16:17 It's best not to touch the $is_dst parameter, because PHP usually interprets the date and time correctly. If we replace all calls to mktime() by gmmktime(), the parameters passed to the function are treated as GMT time, with no time zones taken into account. With mktime(), the time zone that the server has configured is taken into account. For instance, if you are on Central European Time (CET), passing the same parameters as shown previously to gmmktime output times that are one hour "later." Because the date function does take into account time zones, the generated GMT timestamp is treated as a CET time zone, resulting in times that are one hour for non-DST times and two hours for DST times (CEST is CET+1). 9.4.2. Formatting Date and TimeMaking a GMT date with gmmktime() and then showing it in the current time zone with the date() function doesn't make much sense. Thus, we also have two functions for formatting date/time: date() to format a local date/time, and gmdate() to format a GMT date/time. Both functions accept exactly the same parameters. The first parameter is a format string (more about that in a bit), and the second is an optional timestamp. If the timestamp parameter is not included, the current time is used in formatting the output. gmdate() and date() always format the date in English, not in the current "locale" that is set on your system. Two functions are provided to format local time/date according to locale settings: strftime() for local time and gmstrftime() for GMT times. Table 9.6 describes formatting string characters for both functions. Note that the (gm)strftime() prefix to the formatting string options with a %.
9.4.2.1 Example 1: ISO 8601 Week NumbersThis example shows that the ISO 8601 year format option (%V) might differ from the normal year format option (%Y) if a year has less than four days: <?php for ($i = 27; $i <= 31; $i++) { echo gmstrftime( "%Y-%m-%d (%V %G, %A)\n", gmmktime(0, 0, 0, 12, $i, 2004) ); } for ($i = 1; $i <= 6; $i++) { echo gmstrftime( "%Y-%m-%d (%V %G, %A)\n", gmmktime(0, 0, 0, 1, $i, 2005) ); } ?> The script outputs 2004-12-27 (53 2004, Monday) 2004-12-28 (53 2004, Tuesday) 2004-12-29 (53 2004, Wednesday) 2004-12-30 (53 2004, Thursday) 2004-12-31 (53 2004, Friday) 2005-01-01 (53 2004, Saturday) 2005-01-02 (53 2004, Sunday) 2005-01-03 (01 2005, Monday) 2005-01-04 (01 2005, Tuesday) 2005-01-05 (01 2005, Wednesday) 2005-01-06 (01 2005, Thursday) As you can see, the ISO year is different for January 1 and 2, 2005, because the first week (Monday to Sunday) only has two days. 9.4.2.2 Example 2: DST IssuesEvery year around October, at least 1025 bugs are reported when a day is listed twice in somebody's overview. Actually, the day listed twice is the date on which DST ends, as you can see in this example: <?php /* Start date for the loop is October 31th, 2004 */ $ts = mktime(0, 0, 0, 10, 31, 2004); /* We loop for 4 days */ for ($i = 0; $i < 4; $i++) { echo date ("Y-m-d (H:i:s)\n", $ts); $ts += (24 * 60 * 60); /* 24 hours */ } ?> When this script is run, you see the following output: 2004-10-31 (00:00:00) 2004-10-31 (23:00:00) 2004-11-01 (23:00:00) 2004-11-02 (23:00:00) The 31st is listed twice because there are actually 25 hours between midnight, October 31 and November 1, not the 24 hours that were added in our loop. You can solve the problem in one of two ways. If you pick a different time of day, such as noon, the script will always have the correct date: <?php /* Start date for the loop is October 29th, 2004 */ $ts = mktime(12, 0, 0, 10, 29, 2004); /* We loop for 4 days */ for ($i = 0; $i < 4; $i++) { echo date ("Y-m-d (H:i:s)\n", $ts); $ts += (24 * 60 * 60); } ?> Its output is 2004-10-29 (12:00:00) 2004-10-30 (12:00:00) 2004-10-31 (11:00:00) 2004-11-01 (11:00:00) However, there is still a difference in the time. A better solution is to abuse the mktime() function a little: <?php /* We loop for 6 days */ for ($i = 0; $i < 6; $i++) { $ts = mktime(0, 0, 0, 10, 30 + $i, 2004); echo date ("Y-m-d (H:i:s) T\n", $ts); } ?> Its output is 2004-10-30 (00:00:00) CEST 2004-10-31 (00:00:00) CEST 2004-11-01 (00:00:00) CET 2004-11-02 (00:00:00) CET 2004-11-03 (00:00:00) CET 2004-11-04 (00:00:00) CET We add the day offset to the mktime() parameter that describes the day of month. mktime() then correctly wraps into the next months and years and takes care of the DST hours, as you can see in the previous output. 9.4.2.3 Example 3: Showing the Local Time in Other Time ZonesSometimes, you want to show a formatted time in the current time zone and in other time zones as well. The following script shows a full textual date representation for the U.S., Norway, the Netherlands, and Israel: <?php echo strftime("%c\n"); echo "\nEST in en_US:\n"; setlocale(LC_ALL, "en_US"); putenv("TZ=EST"); echo strftime("%c\n"); echo "\nMET in nl_NL:\n"; setlocale(LC_ALL, "nl_NL"); putenv("TZ=MET"); echo strftime("%c\n"); echo "\nMET in no_NO:\n"; setlocale(LC_ALL, "no_NO"); putenv("TZ=MET"); echo strftime("%c\n"); echo "\nIST in iw_IL:\n"; setlocale(LC_ALL, "iw_IL"); putenv("TZ=IST"); echo strftime("%c\n"); ?> Figure 9.4 shows its output. Figure 9.4. March 1 in different locales.Note You need to have the locales and time-zone settings installed on your system before this will work. It is a system-dependent setting and not everything is always available on your system. If you're a Mac OS X user, have a look at http://www.macmax.org/locales/index_en.html to install locales. 9.4.3. Parsing Date FormatsThe opposite of formatting text is parsing a textual description of a date into a timestamp. The strtotime() function handles a many different formats. In addition to the formats listed at http://www.gnu.org/software/tar/manual/html_chapter/tar_7.html, PHP also supports some extra ISO 8601 formats (http://www.w3.org/TR/NOTE-datetime). Table 9.7 contains a list of the most useful formats.
Using the strtotime() function is easy. It accepts two parameters: the string to parse to a timestamp and an optional timestamp. If the timestamp is included, the time is converted relative to the timestamp; if it's not included, the current time is used. The relative calculations are only written with yesterday, tomorrow, and the 1 year 2 days (ago) format strings. strtotime() parsing is always done with the current time zone, unless a different time zone is specified in the string that is parsed: <?php echo date("H:i T\n", strtotime("09:22")); // shows 09:22 CET echo date("H:i T\n\n", strtotime("09:22 GMT")); // shows 10:22 CET echo gmdate("H:i T\n", strtotime("09:22")); // shows 08:22 GMT echo gmdate("H:i T\n", strtotime("09:22 GMT")); // shows 09:22 GMT ?> For more information on time zones, times, and calendars, see the excellent web site at http://www.timeanddate.com/. |
|