I was sitting writing a kind of helpdesk call management program in
PHP and found that I needed to work out how much time had elapsed
since somebody had last bothered to contact the client about their
problem. In the past, when I used to use ASP, the solution was simple -
ASP has a command called DateDiff which takes two dates and can tell
you how many seconds has passed between them, how many days, months and
so on. After scouring the PHP manual for a while I decided that PHP has
no such beast. I then wondered how many other people there were out
there like me, and thus this tutorial.
Ok, so what are we going to cover?
- Getting the current date and time - what options do we have ?
- Changing the way the date is displayed - date and time formatting
- Converting existing dates into Unix timestamps
- Back to the future - modifying the date
- Adding time to the current date
- Subtracting time from the current date
- Working out the time between two dates
- Creating a DateAdd function for PHP
-
Creating a DateDiff function for PHP
Getting the current date and time
The way that Unix machines tell the time is by counting the number of seconds
that have elapsed since Midnight 1 January 1970. They call this the Unix Epoch.
So if we had a piece of PHP that looked something like this:
<?php
echo time();
?>
it would return to me a result of
958905820
This just happens to be about 12h43 on Sunday 21 May 2000.
This is all well and good you may say, but how does that help me ?
Well quite a bit actually. A lot of the functions that do date manipulation in
PHP require the timestamp that is returned by the time() function as one of the
inputs. Also, since PHP uses the timestamp in the same way under both Unix and
Windows, it allows you to use code on either operating system without any
problems. A further bonus is that since the time() function is returning an
integer, you can store it as such in a database or text file - no more need for
fancy date/time database fields.
OK, so now that you have the how and why about Unix timestamps, let's get on to
the really important bits and start using it for something useful.
Changing the way the date is displayed - date and time formatting
PHP supplies you with 2 ways of turning the Unix timestamp into something useful.
The first one is a function called date(). This function takes two arguments - a
string that lays out the formatting that you want returned, the second the Unix timestamp.
The formatting string is simply some special characters that display those parts of
the date and time that you want in the format that you want it.
Suppose you wanted the date to be displayed as "18h01 Sunday 21 May"
We will need to use one of the special characters for each of the changing bits in the
string- you can look these up in the PHP manual under function.date.html. There's a fair
number of these characters and they will return things like the day of the week, month
name, year in 2 and 4 digit format, whether it AM or PM and many others.
The ones that we will need for this example are:
| 'H' | which returns the hour of the day in 24-hour format |
| 'i' | which will return the minutes |
| 'l' | which will return the day of the week (long form) |
| 'd' | which returns the day of the month and |
| 'F' | the full month name |
So our string will look something like "Hhi l d F", and our PHP a bit like:
<?php
echo date( 'Hhi l d F', time() );
?>
When we run this though, we find that we actually get the result of
180609 Sunday 21 May
which seems a bit curious until we have a look at the PHP manual and find that the lowercase
h stands for the hour in 12 hour format. So once again, the old saying of "A computer does
what you tell it to do, not what you want it to do" rings true. We have two options here.
The first is to escape the lowercase h and have this:
<?php
echo date( 'H\hi l d F', time() );
?>
which gives us the result:
18h12 Sunday 21 May
This is great, but what happens if we want to include some date and time info into a more
complicated sentence? Do we have to escape every character? No, we use a different
function, one called strftime().
strftime() brings with it 2 advantages. The first which we are not going to cover here is
that if you have used the setlocale() function, you can have strftime bring the month names
up in the correct language. The other advantage is that you can embed your specially
formatted date and time info into free-form strings. This does mean however that if you went
and learned all of the special characters for the date() function, you get to learn a whole
set of different ones now.
The way that strftime() works is exactly the same as date(), except that the special characters
have the percent sign % in front of them. So the previous example in strftime() would look like
<?php
echo strftime( '%Hh %M %A %d %b', time() );
?>
which gives us:
18h24 Sunday 21 May
This may seem a trifle more complicated, but imagine what you actually wanted to say was
"Today is Sunday 21 May 2000. The time is somewhere close to 18h24."
Using the date command in this instance would be a bit annoying I think.
Earlier I mentioned that there were 2 ways of getting something useful out of the Unix
timestamp. We've just seen date() and strftime(). The other is getdate().
This function only needs the Unix timestamp as an argument and it returns an associative
array of the elements in the date.
As an example:
<?php
$date_time_array = getdate( time() );
echo $date_time_array['weekday'];
?>
will return:
Sunday
Besides "weekday", the other parts of the array are:
| "seconds" | seconds |
| "minutes" | minutes |
| "hours" | hours |
| "mday" | day of the month |
| "wday" | day of the week as a number |
| "mon" | month as a number |
| "year" | year |
| "yday" | day of the year as a number |
| "month" | full month name |
So now we can create readable dates and times. But what about going the other way ?
Converting existing dates into Unix timestamps
Often you may have to work with data that is already in some sort of date and time
format. I opened a clients Microsoft Access database now and found that the dates
are all stored in the format YYYY/MM/DD, so inserting the current date would give
me 2000/05/27. The mktime() function can take the values of parts of a date and
turn them into a Unix timestamp.
The format of the function is:
int mktime(int hour, int minute, int second, int month, int day, int year, int [is_dst] );
From the left you provide the hour, minute, second, month, day and year. The last argument
specifies whether you are in daylight savings time or not. It is optional and so to keep
things simple we will leave it out.
The script will look something like this:
<?php
echo mktime( 0,0,0,5,27,2000 );
?>
I've put in zeros for the hour, minute and seconds since we don't know them and you have to
put something in there. Putting in zeros will give us midnight, which is fine. Of course
you're probably pointing out right now that I've cheated and gone and put the numbers in there
myself, which is true, so let's do it again, this time by making the script work out what to put into where:
<?php
$access_date = '2000/05/27';
// The explode() function splits a string by another string. In this case $access_date is
// split on the /
$date_elements = explode("/",$access_date);
// at this point
// $date_elements[0] = 2000
// $date_elements[1] = 5
// $date_elements[2] = 27
echo mktime(0,0,0,$date_elements[1],$date_elements[2],$date_elements[0]);
?>
Right, let's get a bit more complex and imagine that instead of just getting back the date from the
Access database, we got the date and time in this format:
2000/05/27 02:40:21 PM
<?php
// the string we get from Access
$date_time_string = '2000/05/27 02:40:21 PM';
// Split the string into 3 parts
- date, time and AM/PM
$dt_elements = explode(' ',$date_time_string);
// Split the date up
$date_elements = explode('/',$dt_elements[0]);
// Split the time up
$time_elements = explode(':',$dt_elements[1]);
// If we have a PM then we can add 12 hours to the hour to get into 24 hour time.
if ($dt_elements[2]=='PM')
{
$time_elements[0]+=12;
}
// output the result
echo mktime($time_elements[0],
$time_elements[1],$time_elements[2],
$date_elements[1],$date_elements[2],
$date_elements[0]);
?>
Modifying the date
Often we will want to know what the time will be in 6 hours time, what the date was 35 days ago or how
many seconds it has been since you last played Quake3. We've already seen how the mktime() function
can be used to generate a unix timestamp from individual date and time elements. If we wanted to
create a unix timestamp out of the current date and time, how would we go about it ? A bit of a
pointless exercise, but it will help to illustrate what we will be doing later.
As we have seen, mktime() takes the following arguments: hour, minute, second, month, day, year. And
if you recall from way back in section 2, we saw that the getdate() function can get all those bits
and pieces for us.
<?php
// get the current timestamp into an array
$timestamp = time();
echo $timestamp;
echo '<p>';
$date_time_array = getdate($timestamp);
// use mktime to recreate the unix timestamp
$timestamp = mktime(
$date_time_array['hours'],
$date_time_array['minutes'],
$date_time_array['seconds'],
$date_time_array['mon'],
$date_time_array['mday'],
$date_time_array['year']
);
echo $timestamp;
?>
Looks a little bit confusing, so what I'll do is add in a couple of variables so that things make a little bit more sense.
<?php
// get the current timestamp into an array
$timestamp = time();
echo $timestamp;
echo '<p>';
$date_time_array = getdate($timestamp);
$hours = $date_time_array['hours'];
$minutes = $date_time_array['minutes'];
$seconds = $date_time_array['seconds'];
$month = $date_time_array['mon'];
$day = $date_time_array['mday'];
$year = $date_time_array['year'];
// use mktime to recreate the unix timestamp
$timestamp = mktime($hours,$minutes,$seconds,$month,$day,$year);
echo $timestamp;
?>
Now that we've taken the info out of the array that was created by getdate() and into
appropriately named variables, the code is a bit easier to read and understand. The great
thing now is that if we wanted to add 19 hours to the current date, instead of feeding
$hours to mktime(), we can just feed it $hours +19. mktime() will do the automatic
rollover to the next day for us!
<?php
// get the current timestamp into an array
$timestamp = time();
echo strftime('%Hh%M %A %d %b',$timestamp);
echo '<p>';
$date_time_array = getdate($timestamp);
$hours = $date_time_array['hours'];
$minutes = $date_time_array['minutes'];
$seconds = $date_time_array['seconds'];
$month = $date_time_array['mon'];
$day = $date_time_array['mday'];
$year = $date_time_array['year'];
// use mktime to recreate the unix timestamp
// adding 19 hours to $hours
$timestamp = mktime($hours + 19,$minutes,$seconds,$month,$day,$year);
echo strftime('%Hh%M %A %d %b',$timestamp);
echo '<br>~E after adding 19 hours';
?>
Running this now gives me:
14h58 Saturday 03 Jun
09h58 Sunday 04 Jun
~E after adding 19 hours
Subtracting time is done in exactly the same way - just subtract the amount of time you want from the relevant variable.
Working out the difference between two times is also pretty straightforward. All you need to
do is have both converted into unix timestamps and you just subtract the one from the other.
The difference will be the difference between the two times in seconds. Some quick arithmetic
can convert the seconds into days, hours, minutes and seconds.
Creating a DateAdd function for PHP
As I said in the beginning - the reason for this tutorial was because I could find no
function in PHP that was similar to ASP's DateDiff function. So after explaining how
PHP handles date and time I though it would be cool if we ported two commonly used ASP
date functions to PHP. The first one is the DateAdd function.
The DateAdd function, according the VBScript documentation I have, "Returns a date to which a specified time interval has been added."
The syntax is DateAdd (interval,number,date).
The interval is a string expression that defines the interval you want to add. For example minutes or days,
the number is the number of that interval that you wish to add,
and the date is the date.
Interval can be one of:
| yyyy | year |
| q | Quarter |
| m | Month |
| y | Day of year |
| d | Day |
| w | Weekday |
| ww | Week of year |
| h | Hour |
| n | Minute |
| s | Second |
As far as I can tell, w,y and d do the same thing, that is add 1 day to the current date, q adds 3 months and ww adds 7 days. If I've missed some subtlety of VBScript, someone please let me know :)
<?php
function DateAdd($interval, $number, $date) {
$date_time_array = getdate($date);
$hours = $date_time_array['hours'];
$minutes = $date_time_array['minutes'];
$seconds = $date_time_array['seconds'];
$month = $date_time_array['mon'];
$day = $date_time_array['mday'];
$year = $date_time_array['year'];
switch ($interval) {
case 'yyyy':
$year+=$number;
break;
case 'q':
$year+=($number*3);
break;
case 'm':
$month+=$number;
break;
case 'y':
case 'd':
case 'w':
$day+=$number;
break;
case 'ww':
$day+=($number*7);
break;
case 'h':
$hours+=$number;
break
case 'n':
$minutes+=$number;
break;
case 's':
$seconds+=$number;
break;
}
$timestamp= mktime($hours,$minutes,$seconds,$month,$day,$year);
return $timestamp;
}
?>
We can save that code in a file called dateadd.inc and then execute the following code:
<?php
include('dateadd.inc'); $temptime = time();
echo strftime('%Hh%M %A %d %b',$temptime);
$temptime = DateAdd('n',50,$temptime);
echo '<p>';
echo strftime('%Hh%M %A %d %b',$temptime);
?>
which will return to us:
15h41 Saturday 03 Jun
16h31 Saturday 03 Jun
Creating a DateDiff function for PHP
OK, so that's DateAdd out of the way. What about DateDiff now ?
DateDiff, according to the documentation, "Returns the number of intervals between two dates."
The syntax is DateDiff(interval,date1,date2).
The intervals that it uses are the same as those that we saw in the DateAdd function. For the sake of
simplicity we're going to gloss over a number of elements of VBScipt's DateDiff function that would otherwise
complicate matters. In this example the optional arguments to DateDiff (that is whether the week starts on a
Monday or a Sunday) are not used. The intervals that we are going to allow are going to be "w", "d&q, "h", "n" and "s".
Let's see what we can come up with:
<?php
Function DateDiff
($interval,$date1,$date2) {
// get the number of seconds between the two dates
$timedifference = $date2 - $date1;
switch ($interval) {
case 'w':
$retval = bcdiv($timedifference,604800);
break;
case 'd':
$retval = bcdiv($timedifference,86400);
break;
case 'h':
$retval =bcdiv($timedifference,3600);
break
case 'n':
$retval = bcdiv($timedifference,60);
break;
case 's':
$retval = $timedifference;
break;
}
return $retval;
}
?>
Save that code to datediff.inc and you can try the following code:
<?php
include('datediff.inc';
include('dateadd.inc');
$currenttime = time();
echo 'Current time: '.strftime('%Hh%M %A %d %b',$currenttime).'<br>';
$newtime = DateAdd
('n',50,$currenttime);
echo 'Time plus 50 minutes: '.
strftime('%Hh%M %A %d %b',$newtime).'<br>';
$temptime = DateDiff('n',$currenttime,$newtime);
echo 'Interval between two times: '.$temptime;
?>
which, if you've got everything set up properly, should return:
Current time: 16h23 Saturday 03 Jun
Time plus 50 minutes: 17h13 Saturday 03 Jun
Interval between two times: 50
If you are running PHP on a unix system, you will have to compile in support for the bcmath functions.
README.BCMATH in your PHP distribution gives you the full details. PHP4 for Windows can do bcmath calculations without any special tinkering.
That's it then for dates. You should now have all the tools you need to tackle dates and times and their practical application in PHP
--Allan