<

Go Premium for a chance to win a PS4. Enter to Win

x

Handling Date and Time in PHP and MySQL - Procedural Version

Published on
230,302 Points
30,802 Views
50 Endorsements
Last Modified:
Awarded

Introduction

This article is very old, and while most of its principles are sound, PHP programming has changed a lot in the last decade.  For that reason, I've updated this information in another article that shows the object-oriented ways of handling date/time issues.  You can find the 2015 version of this information here:

http://www.experts-exchange.com/articles/20920/Handling-Date-and-Time-in-PHP-and-MySQL-OOP-Version.html


The DATE/TIME Tower of Babel

Human beings can read dates and times in a variety of ways.  We readily understand such things as September 5th, 2010, and we know that it comes before November 11th, 2010,and after May 9th, 2010.  We have shorthand conventions that let us write things like 9/5/2010 or the military correspondence format 5 Sep 2010.  But when we try to process dates and times in a computer program, this variety of formats becomes a jumble of confusion.  In response to the confusion, the International Organization for Standards was moved to publish a standard in 1988.  The standard has been revised and extended somewhat since the original publication.  The only thing that mystifies students of history is why it took so long to promulgate a standard.


Toward a Universal DATETIME Notation

Date formats for computer processing are prescribed by the ISO-8601 standard.  The ISO-8601 standard places all the information in fixed-width data strings with leading zeros where needed.  When you read ISO-8601 standard information you notice immediately that everything lines up in columns.  The larger values are to the left and progressively smaller values are to the right, starting with Year, then Month, then Day, then Hour, etc.  Date / time information formatted according to the standard is very useful because it is both easy to read and easy to understand in comparisons and computations.  For example, the date '2010-09-05' is a valid ISO-8601 string meaning September 5th, 2010.  Imagine how difficult it would be to write programming that works with dates in the text formats, or dates that are formatted like this: 05.09.2010.  Does that mean May 9th, 2010 or September 5th, 2010?  Fortunately the ISO-8601 standard removes the ambiguity.


The ISO-8601 standard removes the ambiguity about the time of day, as well.  All of the hours are represented on a 24-hour clock, so there is no question about what "two o'clock" might mean.  The string 0200 or 02:00:00 refers to 2:00 a.m.  If you're thinking of 2:00 p.m. your ISO-8601 standard will use 14:00:00.


This link gives a good discussion and examples of permissible variations on the ISO-8601 format.

See http://en.wikipedia.org/wiki/ISO_8601


The Value of PHP time() is Always the Same

No matter where in the world you stand, the value of time() is always the same.  The PHP function time() returns the number of seconds since the Unix Epoch.  While local times may differ, time() ticks on second-by-second.  Run this script to see the effect.  Knowing this, we can do arithmetic with seconds, and then return human-readable date and time values that are sensible for different locations around the world.

// TIMEZONES AROUND THE GLOBE
$locations
= array
( 'Pacific/Auckland'
, 'Australia/Sydney'
, 'Australia/Perth'
, 'Asia/Tokyo'
, 'Asia/Calcutta'
, 'Asia/Tel_Aviv'
, 'Africa/Cairo'
, 'Europe/Rome'
, 'Europe/London'
, 'Atlantic/Bermuda'
, 'America/Chicago'
, 'America/Anchorage'
, 'Pacific/Honolulu'
, 'UTC'
)
;
// ITERATE OVER THE TIMEZONES
foreach ($locations as $location)
{
    // SET OUR ZONE
    date_default_timezone_set($location);

    // SHOW THE LOCATION AND THE CURRENT DATE / TIME
    echo PHP_EOL . str_pad($location, 24, ' ');
    echo date('r');

    // SHOW THE NUMBER OF SECONDS SINCE THE EPOCH, RECOMPUTED IN EACH TIMEZONE
    echo ' ' . number_format( time() );
}


Handling External Input

Whenever you accept a date / time string from an external data source, what is the first thing you should do?  Turn it (immediately) into the ISO-8601 representation with two PHP instructions.  The PHP function strtotime() can turn almost any English language human-readable date / time text into a UNIX timestamp.  See http://php.net/manual/en/function.strtotime.php for a description of this function.  See http://php.net/manual/en/datetime.formats.php for examples of the kinds of strings that work with strtotime().  The PHP function date() can turn any UNIX timestamp into a formatted date.  See http://php.net/manual/en/function.date.php for the details.  Taken together, these two functions work in ways that are almost magical:


$unix_timestamp = strtotime($external_datetime_string);
$iso_datetime = date('c', $unix_timestamp);

Once you have done that internal date format conversion your programming will handle internal date / time computations easily.


You might be tempted to skip the second step, and just keep your internal dates in the form of UNIX timestamps.  Technically speaking you would be on firm ground in doing that, but for reasons related to the human factors of programming I recommend against it.  Eventually you will find yourself debugging a piece of date-related code, and you will want to print out your intermediate data.  If it comes out in a easily readable form, instead of a large integer, you will save yourself considerable debugging time.


Handling External Input that is Invalid

The PHP function strtotime() can turn virtually any human-readable date / time text into a UNIX timestamp - but it won't work on everything.  When it fails to make the conversion, it returns FALSE.  You can and should test for this.

$unix_timestamp = strtotime($external_datetime_string);
if ($unix_timestamp === FALSE)
{
    echo "ERROR: I DO NOT UNDERSTAND $external_datetime_string";
}
else
{
    $iso_datetime = date('c', $unix_timestamp);
    echo "SUCCESS: $external_datetime_string EQUALS ISO-8601 $iso_datetime";
}


Interesting Forms of External Input

All of these external inputs work correctly with strtotime() and this gives you and your clients powerful ways of talking about dates and computing with dates.

- 3 hours

tomorrow

tomorrow midnight

tomorrow 1:35pm

March 15, 1986

yesterday

yesterday + 1 week

next year

now

now + 627 hours 15 minutes

last Tuesday

third Wednesday

3 minutes, 15 seconds


Producing "Pretty" Dates From ISO-8601 Dates

When you are ready to present the dates to people and you want them to see nicely formatted dates, you can use the PHP strtotime() and date() functions to do the reformatting.  Let's say you have this timestamp value: 1,284,593,400 (which equals the ISO-8601 date: '2010-09-15T18:30:00-0500'. )  Maybe you got it out of your data base or computed it in your script.  It's meaningful, but not what you expect when you open the envelope.  "Please join us for cocktails on Unix Timestamp 1,284,593,400."  How can we get something that would be appropriate on an invitation?  Here is how you might do the conversion from the timestamp or ISO-8601 date to the pretty date.

$unix_timestamp = strtotime($iso_datetime);
$pretty_date = date('l, F jS, Y \a\t g:ia', $unix_timestamp);
echo "Please join us for cocktails on " . $pretty_date;

Outputs "Please join us for cocktails on Tuesday, September 15th, 2010 at 6:30pm" -- very civilized!


Breaking down the date formatting, we have the following (all documented on PHP.net)...

$pattern
= 'l, '    // LOWERCASE L - text name of the day of the week
. 'F '     // UPPERCASE F - text name of the month
. 'j'      // LOWERCASE J - day of the month
. 'S, '    // UPPERCASE S - ordinal like the letters in 1st, 2nd, etc 
. 'Y '     // UPPERCASE Y - 4-digit year
. '\a\t '  // ESCAPED LETTERS... More to follow
. 'g:'     // LOWERCASE G - hours on a 12-hour clock
. 'i'      // LOWERCASE I - minutes 
. 'a'      // LOWERCASE A - designator of "am" or "pm"
;


You might envision several patterns that you need in your application.  Most PHP sites and services are written with a central "common.php" file that gets included into all the specialized scripts.  You could prepare the date() patterns in the common script and refer to them by a variable name, or define the patterns as constants.

define('MILITARY_CORRESPONDENCE', 'j M y');
define('SHORT_8601', 'Y-m-d');
define('COCKTAILS', 'l, F jS, Y \a\t g:ia');


What about the escaped letters?  The date() function uses letter patterns as signals to describe the conversion of the timestamp into human-readable character strings, so some letters have special relevance to date() and some do not.  Any character that is not used as a signal to date() is returned from the function unaltered.  Punctuation is one example - commas and hyphens go right through.  But if we want to get one of the signal letters back, we must tell date() to ignore the signal and just return the letter.  We do that with the back-slash character, as shown here.  Try running these two lines of code to see how the escape can be useful:

echo PHP_EOL . date('The year is: Y');
echo PHP_EOL . date('\T\h\e \y\e\a\r\ \i\s\: Y');


Setting Your Own Clock Values

Since the values returned by strtotime() are relative to the time on your server, you want to have control over the way PHP interprets the server time.  You control this time with the PHP function, date_default_timezone_set().  In PHP 5.1+ the use of date_default_timezone_set(), or an equivalent initialization setting is required.  In PHP 5.3+ failure to use date_default_timezone_set() may result in a warning message when you call strtotime() or date().  See http://php.net/manual/en/function.date-default-timezone-set.php for more information.  Like frequently used date() patterns, your date_default_timezone_set() would fit nicely into the common script.  Here is the one that I use, because my server is located in the midwest at ChiHost.com.

date_default_timezone_set('America/Chicago');


Special Meanings for NOW and TODAY

We know how to convert external date / time strings to timestamps with strtotime(), and we know how to return them to human-preferred formats with date().  Now we can return to some of the special uses of the strtotime() function.  These two statements are particularly useful.

$today = strtotime('TODAY');
$now   = strtotime('NOW');

The principal difference between NOW and TODAY is the time of day.  TODAY is always today's local date at midnight, with hour, minute and second equal to zero.  NOW is today's date including the current time of day.  So in our September example, NOW and TODAY have UNIX timestamp values equal to 1,284,593,400 and 1,284,526,800 respectively.  The difference is 66,600.  This is the number of seconds from midnight to 6:30pm (18 hours plus 30 minutes).  You might reasonably think you could format the current time with a statement like this:


$time = date('H:i:sa', strtotime('NOW') - strtotime('TODAY'));

But you would probably be wrong because PHP date / time functions are location-aware, and you're probably not in Greenwich.  In PHP, timestamps are based on the UNIX epoch which began at January 1, 1970 at midnight UTC.  UTC (also known as Greenwich Mean Time) is the timezone where the current local time is also the current time of the UNIX epoch.  In other timezones the clocks are set differently.


When you use a timezone setting other than UTC, most values returned by strtotime() will be adjusted for consistency with your timezone (with the notable exception of strtotime('NOW') which returns the same value as the time() function, the UNIX timestamp).  That makes sense - the term "today" defines local midnight, and this is not the same absolute time for clients in New York and Los Angeles.  Your programming defines its own personal epoch, using date_default_timezone_set().  Run this snippet to see the effect of different timezones on identical calculations.

date_default_timezone_set('UTC');
$timestamp_now   = strtotime('NOW');
$timestamp_today = strtotime('TODAY');
$elapsed_seconds = $timestamp_now - $timestamp_today;
$printable_time  = date('H:i:sa', $elapsed_seconds);
echo PHP_EOL . date_default_timezone_get();
echo PHP_EOL . 'NOW   ' . number_format($timestamp_now);
echo PHP_EOL . 'TODAY ' . number_format($timestamp_today);
echo PHP_EOL . 'DIFF  ' . number_format($elapsed_seconds);
echo PHP_EOL . $printable_time;  // CORRECT IN UTC

date_default_timezone_set('America/Chicago');
$timestamp_now   = strtotime('NOW');
$timestamp_today = strtotime('TODAY');
$elapsed_seconds = $timestamp_now - $timestamp_today;
$printable_time  = date('H:i:sa', $elapsed_seconds);
echo PHP_EOL . date_default_timezone_get();
echo PHP_EOL . 'NOW   ' . number_format($timestamp_now);
echo PHP_EOL . 'TODAY ' . number_format($timestamp_today);
echo PHP_EOL . 'DIFF  ' . number_format($elapsed_seconds);
echo PHP_EOL . $printable_time;  // INCORRECT IN CHICAGO


As you can see, it is important to be consistent about your timezone.  You can deal with this phenomenon in one of two ways.  You can set your timezone to UTC.  Or you can set your timezone to a reasonable setting for the location of your server.  I prefer the latter.  See http://php.net/manual/en/timezones.php and http://php.net/manual/en/timezones.others.php for more.


Either way, reliable coding practices should prevail.  If you want to get the UNIX timestamp of the current time, you use strtotime('NOW').  If you want to get the UNIX timestamp of the current date, you use strtotime('TODAY').  Compare these two statements.  The latter prints 00:00:00am.


echo date('H:i:sa', strtotime('NOW'));
echo date('H:i:sa', strtotime('TODAY'));


Time Without Date

Since TODAY will give us a value that is relative to our server time and with hour, minute and second equal to zero, we can make a special use of it.  Let's say we want to know the elapsed time between two events, and we want to express it in notation like HH:MM:SS.  Consider this code snippet.  Line 7 will almost always print the wrong answer.  Line 9 will print the right answer, no matter what date_default_timezome_set() you used.

$alpha = "2:30:47pm";
$omega = "3:43:16pm";
$ts_alpha = strtotime($alpha);
$ts_omega = strtotime($omega);
$elapsed  = $ts_omega - $ts_alpha;
echo "<br/>TWIXT $alpha AND $omega THERE ARE " . number_format($elapsed) . " SECONDS";
echo "<br/>WRONG ELAPSED TIME IS " . date('H:i:s', $elapsed);
$better   = strtotime('TODAY') + $elapsed;
echo "<br/>RIGHT ELAPSED TIME IS " . date('H:i:s', $better);


Leap Year and Daylight Savings Time

These are amazingly easy in PHP.

if (date('L')) echo "IT IS A LEAP YEAR";
if (date('I')) echo "IT IS DAYLIGHT SAVINGS TIME";
$a = date('c', strtotime('November 7, 2010 1:57am'));
$z = date('c', strtotime('November 7, 2010 2:02am'));
while ($a < $z)
{
    $a = date('c', strtotime($a . '+ 1 minute'));
    echo "<br/>$a ";
    if (date('I', strtotime($a))) echo "IS DAYLIGHT SAVINGS TIME";
}


Using mktime() in DATETIME Computations

The PHP command mktime() returns a Unix timestamp according to an argument list.  See http://us3.php.net/manual/en/function.mktime.php for details.  The arguments in order are hour, minute, second, month, day, year.  Because mktime() will work with out-of-range arguments, it is useful for date calculations.  And it is especially useful when coupled with the date() function.


// WORKS NO MATTER WHAT MONTH IT IS
$last_month = mktime(0, 0, 0, date("m")-1, date("d"), date("Y")); 
// HIGH NOON THIRTY DAYS FROM NOW
$new_date_string = date('g:ia T \o\n D, M j, Y', mktime(12, 0, 0, date("m"), date("d")+30, date("Y")));


Ambiguity of the term "Month"

PHP is very permissive in its language requirements for strtotime() and this may lead to some confusion when it comes to months.  Consider the meaning of strtotime('next month') when used on January 5.  Obviously the answer is February 5.  Now consider what happens when it is January 30.  What should the correct answer be?  This code snippet shows how I handle this question.  Different business rules may apply for your work.  There may be ambiguity in leap year, too.  Consider "Feb 29, 2008 + 1 year."

<?php 
error_reporting(E_ALL);
echo "<pre>";

// REQUIRED FOR PHP 5.1+
date_default_timezone_set('America/New_York');

// TEST DATES INCLUDE SOME THAT MIGHT CAUSE AMBIGUITY WHEN YOU SAY "next month"
$my_dates
= array
( 'January 28, 2011'
, 'January 29, 2011'
, 'January 30, 2011'
, 'January 31, 2011'
, 'February 1, 2011'
, 'February 2, 2011'
, 'February 28, 2011'
, 'March 30, 2011'
, 'March 31, 2011'
)
;

// TEST EACH OF THE DATES
foreach ($my_dates as $my_date)
{
    list
    ( $safe_date
    , $requested_day
    , $actual_day
    ) = nextMonth($my_date, 'r');
    echo PHP_EOL . "MY_DATE $my_date";
    echo PHP_EOL . "NEXTMONTH $safe_date";
    echo PHP_EOL . "REQUESTED DAY $requested_day";
    echo PHP_EOL . "ACTUAL DAY $actual_day";
    echo PHP_EOL;
}


function nextMonth($date, $format='c')
{
    $timestamp  = strtotime($date);
    $start_Y    = date('Y', $timestamp);
    $start_m    = date('m', $timestamp);
    $start_d    = date('d', $timestamp);

    // MAKE A TIMESTAMP FOR THE FIRST, LAST AND REQUESTED DAY OF NEXT MONTH
    $timestamp_first = mktime(0,0,0, $start_m+1,  1, $start_Y);
    $timestamp_last  = mktime(0,0,0, $start_m+1, date('t', $timestamp_first), $start_Y);
    $timestamp_try   = mktime(0,0,0, $start_m+1, $start_d, $start_Y);

    // USE THE LESSER OF THE REQUESTED DAY AND THE LAST OF THE MONTH
    if ($timestamp_try > $timestamp_last) $timestamp_try = $timestamp_last;
    $good_date = date($format, $timestamp_try);

    return array
    ( $good_date
    , $start_d
    , date('d', $timestamp_try)
    )
    ;
}


Storing Date and Time in Your Database

In MySQL, date columns are usually carried in the format DATE and DATETIME.  These are very "relaxed" fields.  You can insert invalid dates (such as February 31) and MySQL will accept these.  More on point, you can store and recall ISO-8601 datetime strings.  MySQL has a large complement of internal date and time functions.  You may sometimes find it practical to perform date / time calculations in your query strings.  See http://dev.mysql.com/doc/refman/5.5/en/datetime.html and http://dev.mysql.com/doc/refman/5.5/en/date-and-time-functions.html for references and examples.


PHP and MySQL Have Different Time Zone Settings

Note that the time zone values for PHP and for MySQL are completely independent of each other.  You may want to adjust the time zones for one or both of these systems.  Use date_default_timezone_set() to adjust the value in PHP.  Use a query that says something like SET time_zone = '+00:00' to adjust the value in MySQL.  See http://dev.mysql.com/doc/refman/5.5/en/time-zone-support.html for more.  The code snippet illustrates this independence.


<?php // RAY_mysqli_set_time_zone.php
ini_set('display_errors', TRUE);
error_reporting(E_ALL);
echo '<pre>';


// DEMONSTRATE THAT PHP AND MYSQL TIME ZONE VALUES ARE INDEPENDENT
// MAN PAGE http://php.net/manual/en/function.date-default-timezone-set.php
// MAN PAGE http://dev.mysql.com/doc/refman/5.5/en/time-zone-support.html


// DATABASE CONNECTION AND SELECTION VARIABLES - GET THESE FROM YOUR HOSTING COMPANY
$db_host = "localhost"; // PROBABLY THIS IS OK
$db_name = "??";
$db_user = "??";
$db_word = "??";

// OPEN A CONNECTION TO THE DATA BASE SERVER AND SELECT THE DB
$mysqli = new mysqli($db_host, $db_user, $db_word, $db_name);

// DID THE CONNECT/SELECT WORK OR FAIL?
if ($mysqli->connect_errno)
{
    trigger_error("CONNECT FAIL: $mysqli->connect_errno $mysqli->connect_error", E_USER_ERROR);
}


// SET THE PHP TIMEZONE
date_default_timezone_set('GMT');
print_r( 'IN PHP, THE GMT TIME IS: ' . date('c') );
echo PHP_EOL;


// GET THE DEFAULT MYSQL TIME SETTINGS
$res = $mysqli->query("SELECT CURRENT_TIMESTAMP AS t, NOW() AS n");
$row = $res->fetch_object();
echo 'IN MySQL, THE CURRENT TIMES ARE: ';
print_r($row);
echo PHP_EOL;


// SET THE MYSQL TIMEZONE TO GMT
$sql = "SET time_zone = '+00:00' ";
$res = $mysqli->query($sql);

echo "<b>$sql</b>" . PHP_EOL;

// GET THE NEW MYSQL VALUES
$res = $mysqli->query("SELECT CURRENT_TIMESTAMP AS t, NOW() AS n");
$row = $res->fetch_object();
echo 'IN MySQL, THE CURRENT TIMES HAVE BEEN CHANGED TO: ';
print_r($row);
echo PHP_EOL;


Practical Application #1

How old are you today? Given your birthday, this function can return your age.

// A FUNCTION TO RETURN THE AGE ON A GIVEN DATE
function your_age($birth_day, $test_date='Today')
{
    // IF THE DATE STRINGS ARE NOT USABLE
    if (!$b_timestamp = strtotime($birth_day)) return FALSE;
    if (!$t_timestamp = strtotime($test_date)) return FALSE;

    // GET THE YEARS AND SUBTRACT ONE
    $years_elapsed = date('Y', $t_timestamp) - date('Y', $b_timestamp) - 1;

    // IF THE BIRTHDAY IS BEFORE CUTOFF ADD ONE YEAR
    if ( date('m-d', $b_timestamp) <= date('m-d', $t_timestamp) ) $years_elapsed++;

    return $years_elapsed;
}


Practical Application #2

How many days between two dates?  The built-in PHP calendar functions can make this easy.  See http://php.net/manual/en/ref.calendar.php for more.  Warning: If the dates are the same, this function will return a zero which may be perceived to be the same as FALSE.  To test for failure of the function, use === FALSE instead of == FALSE.


function days_between($alpha=TODAY, $omega=TODAY)
{
    // RETURN FALSE IF THE DATES ARE BOGUS
    if (!$a = strtotime($alpha)) return FALSE;
    if (!$z = strtotime($omega)) return FALSE;
    
    // MAN PAGE http://php.net/manual/en/function.gregoriantojd.php
    $a_jd = GregorianToJD( date('m', $a), date('d', $a), date('Y', $a) );
    $z_jd = GregorianToJD( date('m', $z), date('d', $z), date('Y', $z) );
    return $z_jd - $a_jd;
}


Practical Application #3

You want to know how many weeks, days, hours, minutes, and/or seconds will elapse before an upcoming event.  Given a DATETIME string this function can return a text string or array with the answers.

function elapsed($then, $wdhms='WDHMS', $return_array=FALSE)
{
    // TIME CONSTANT VALUES (MONTHS AND YEARS ARE OMITTED - NOT FIXED VALUES)
    $seconds_per["W"] = 60*60*24*7;
    $seconds_per["D"] = 60*60*24;
    $seconds_per["H"] = 60*60;
    $seconds_per["M"] = 60;
    $seconds_per["S"] = 1;

    // TIME DESCRIPTIVE TERMS - YOU CAN USE SOME OR ALL OF THESE
    $terms["W"] = 'week';
    $terms["D"] = 'day';
    $terms["H"] = 'hour';
    $terms["M"] = 'minute';
    $terms["S"] = 'second';

    // SET THE FLAGS FOR OUTPUT
    if (empty($wdhms)) $wdhms = 'WDHMS';
    $wdhms = strtoupper($wdhms);
    $wdhms = str_split($wdhms);
    $wdhms = array_combine($wdhms, $wdhms);

    // SET THE FUTURE/PAST TIMESTAMP OR FAIL ON ERROR
    if (!$other_timestamp = strtotime($then)) return FALSE;

    // SET THE CURRENT TIMESTAMP
    $current_timestamp = time();

    // COMPUTE THE DIFFERENCE IN SECONDS
    $lapsed_seconds    = $other_timestamp - $current_timestamp;
    if ($lapsed_seconds == 0) return 'RIGHT NOW!';

    // DETERMINE FUTURE OR PAST
    $since_until = "until";
    if ($lapsed_seconds < 0)
    {
        $since_until = "since";
        $lapsed_seconds = abs($lapsed_seconds);
    }

    // COMPUTE THE INCREMENTS
    foreach ($seconds_per as $key => $secs)
    {
        if (array_key_exists($key, $wdhms))
        {
            $lapsed_time    = floor($lapsed_seconds / $secs);
            $lapsed_seconds = $lapsed_seconds - ($lapsed_time * $secs);
            $wdhms[$key]    = (int)$lapsed_time;
        }
    }

    // RETURN AN ARRAY
    if ($return_array) return $wdhms;

    // RETURN A RESPONSE STRING
    $s = NULL;
    foreach ($terms as $key => $str)
    {
        // RETURN ONLY THE UNITS THAT WERE REQUESTED IN $wdhms
        if (!array_key_exists($key, $wdhms)) continue;
        $s
        .= ' '
        . $wdhms[$key]
        . ' '
        . $str
        ;
        if ($wdhms[$key] != 1) $s .= 's';
        $s .= ',';
    }
    
    // TIDY UP THE RETURN STRING AND SHOW UNTIL OR SINCE
    $s = rtrim($s, ',');
    $s .= " $since_until $then";
    $s = trim($s);
    return $s;
}

// USE CASES
$then = '+ 1 days';
echo PHP_EOL . elapsed($then, 'h');
echo PHP_EOL . elapsed($then, 'm');
echo PHP_EOL . elapsed($then, 's');
$then = '+1304 hours +2917 seconds';
echo PHP_EOL . elapsed($then);
echo PHP_EOL . elapsed($then, 'WD');
echo PHP_EOL . elapsed($then, 'DH');
echo PHP_EOL . elapsed($then, 'HMS');
echo PHP_EOL;
$arr = elapsed($then,NULL,TRUE);
var_dump($arr);

Practical Application #3a

You want to know if a given date is yesterday, today or tomorrow.  This function can tell you.

function ytt($date)
{
    // NORMALIZE THE DATES TO ISO-8601 STANDARDS
    $then      = date('Y-m-d', strtotime($date));
    $yesterday = date('Y-m-d', strtotime('Yesterday'));
    $today     = date('Y-m-d');
    $tomorrow  = date('Y-m-d', strtotime('Tomorrow'));

    // CHOOSE A RETURN
    if ($then == $yesterday) return 'Yesterday';
    if ($then == $today)     return 'Today';
    if ($then == $tomorrow)  return 'Tomorrow';
    return NULL;
}


Practical Application #4

You decide you want to have a party on the fifth Friday of every month that has five Fridays.  When will you be celebrating?

function find_fifth_friday($date='Today')
{
    $timestamp  = strtotime(date('Y-1-1',   strtotime($date)));
    $endstamp   = strtotime(date('Y-12-31', strtotime($date)));
    $my_fridays = array();
    while ($timestamp < $endstamp)
    {
        $month = date('m',       strtotime("first Friday", $timestamp));
        $day_n = date('Y-m-d D', strtotime("fifth Friday", $timestamp));
        if (substr($day_n,5,2) == $month) $my_fridays[] = $day_n;
        $timestamp = mktime(0,0,0, date('m', $timestamp)+1, 1, date('Y', $timestamp));
    }
    return $my_fridays;
}

echo "<pre>WE PARTY ON: ";
print_r(find_fifth_friday());


Practical Application #5

You wonder if there could ever be a fifth Friday in that short month February.  The answer is "yes" and here is proof.

function find_weekdays($date='Today', $day='Sunday')
{
    $weekdays    = array();
    $timestamp   = strtotime(date('Y-m-0', strtotime($date)));
    $weekdays[0] = date('Y-m-d D', strtotime("first  $day", $timestamp));
    $weekdays[1] = date('Y-m-d D', strtotime("second $day", $timestamp));
    $weekdays[2] = date('Y-m-d D', strtotime("third  $day", $timestamp));
    $weekdays[3] = date('Y-m-d D', strtotime("fourth $day", $timestamp));
    $weekdays[4] = date('Y-m-d D', strtotime("fifth  $day", $timestamp));
    if (substr($weekdays[4],5,2) != date('m', strtotime("first $day", $timestamp))) { unset($weekdays[4]); }
    return $weekdays;
}

function last_weekday($date='Today', $day='Sunday')
{
    $weekdays   = find_weekdays($date, $day);
    $n_weekdays = count($weekdays) - 1;
    return $weekdays[$n_weekdays];
}

echo "<pre>";
var_dump(find_weekdays('February 2008', 'Friday'));
var_dump(last_weekday('Feb 2008', 'Friday'));


Practical Application #6

It would be nice to be able to print out a little calendar table... And if you wanted to send calendar information between applications, you might be interested in the hCalendar microformat.


function little_calendar_table($date='Today')
{
    $timestamp         = strtotime(date('Y-m-01', strtotime($date)));
    $caption           = date("F Y", $timestamp);
    $first_day_number  = date("w", $timestamp);
    $last_day_of_month = date("t", $timestamp);
    $day_counter       = 0;
    $html              = NULL;

    $html .= '<table>' . PHP_EOL;
    $html .= '<caption style="text-align:left;">' . $caption . '</caption>';
    $html .= PHP_EOL;

    $html .= '<tr>';
    $html .= '<th abbr="Sunday"    width="14%" >S</th>';
    $html .= '<th abbr="Monday"    width="14%" >M</th>';
    $html .= '<th abbr="Tuesday"   width="14%" >T</th>';
    $html .= '<th abbr="Wednesday" width="14%" >W</th>';
    $html .= '<th abbr="Thursday"  width="14%" >T</th>';
    $html .= '<th abbr="Friday"    width="14%" >F</th>';
    $html .= '<th abbr="Saturday"  width="14%" >S</th>';
    $html .= '</tr>';
    $html .= PHP_EOL;


    // THE FIRST ROW MAY HAVE DAYS THAT ARE NOT PART OF THIS MONTH
    $html .= '<tr>';
    while ($day_counter < $first_day_number)
    {
        $html .= '<td>&nbsp;</td>';
        $day_counter++;
    }

    // THE DAYS OF THE MONTH
    $mday = 1;
    while ($mday <= $last_day_of_month)
    {
        // THE DAYS OF THE WEEK
        while ($day_counter < 7)
        {
            $html .= '<td style="text-align:right;">' . " $mday</td>";
            $day_counter++;
            $mday++;
            if ($mday > $last_day_of_month) break;
        }

        $html .= '</tr>' . PHP_EOL;
        $html .= '<tr>';
        $day_counter = 0;
    }

    // THE LAST ROW MAY HAVE DAYS THAT ARE NOT PART OF THIS MONTH
    while ($day_counter < 7)
    {
        $html .= '<td>&nbsp;</td>';
        $day_counter++;
    }

    $html .= '</tr>' . PHP_EOL;
    $html .= '</table>' . PHP_EOL;

    return $html;
}

echo little_calendar_table('February');


Practical Application #6a

The author of this question wanted a weekly calendar with "previous" and "next" links.  This function returns an array of nine elements giving previous, weekdays, and next.


function weekly_calendar($date='TODAY')
{
    // USE THE FUNCTION ARGUMENT, OR USE TODAY
    $ts = !empty($date) ? strtotime($date) : FALSE;
    if (!$ts) $ts = strtotime('TODAY');

    // NORMALIZE TO THE ISO-8601 DATE WITHOUT TIME
    $ymd = date('Y-m-d', $ts);
    $day = date('D', $ts);

    // THE WEEKLY CALENDAR WILL START ON SUNDAY AND END ON SATURDAY
    if ($day == 'Sun')
    {
        $alpha = $ymd;
    }
    else
    {
        $alpha = date('Y-m-d', strtotime($ymd . ' LAST SUNDAY'));
    }

    $omega = date('Y-m-d', strtotime($alpha . ' SATURDAY'));

    // THE PREV AND NEXT WEEKS WILL BE COMPUTED FROM THE CURRENT DAY
    $prev  = date('Y-m-d', strtotime("$ymd - 1 WEEK"));
    $next  = date('Y-m-d', strtotime("$ymd + 1 WEEK"));

    // MAKE THE URLS
    $prev_uri = $_SERVER['PHP_SELF'] . "?d=$prev";
    $next_uri = $_SERVER['PHP_SELF'] . "?d=$next";

    // MAKE THE LINK TEXTS
    $prev_lnk = '<a href="' . $prev_uri . '">Prev</a>';
    $next_lnk = '<a href="' . $next_uri . '">Next</a>';

    // MAKE THE REPRESENTATION OF THE WEEK
    $week['prev'] = $prev_lnk;
    while ($alpha <= $omega)
    {
        // A TEXT VERSION OF THE WEEKDAY
        $day = date('D', strtotime($alpha));

        // URL AND LINK TEXT
        $curr_uri = $_SERVER['PHP_SELF'] . "?d=$alpha";
        $curr_lnk = '<a href="' . $curr_uri . '">' . $day . '</a>';

        // HIGHLIGHT THE CURRENT DAY
        if ($alpha == $ymd)
        {
            $week[$day] = "<b>$curr_lnk</b>";
        }
        else $week[$day] = $curr_lnk;

        // ON TO THE NEXT DAY
        $alpha = date('Y-m-d', strtotime("$alpha + 1 DAY"));
    }
    $week['next'] = $next_lnk;

    return $week;
}

// SHOW THE REPRESENTATION OF THE WEEK
$thing = weekly_calendar();
print_r($thing);


Practical Application #7

When you are working with human input to a computer program, it's good to know what works with strtotime().  The script in this code snippet will tell you.

<?php
error_reporting(E_ALL);

// PHP 5.1+  SEE http://us3.php.net/manual/en/function.date-default-timezone-set.php
date_default_timezone_set('America/Chicago');

// IF WE HAVE INPUT FROM THE URL QUERY STRING
if (!empty($_GET["s"]))
{
    // USE strtotime() FUNCTION TO MAKE A TIMESTAMP
    // MAN PAGE: http://us.php.net/manual/en/function.strtotime.php
    $unix_timestamp = strtotime($_GET["s"]);

    // TEST FOR SUCCESS OR FAILURE
    if ($unix_timestamp === FALSE)
    {
        echo "<strong>HONK!</strong><br /> <u>{$_GET["s"]}</u> NOT USEFUL WITH strtotime() <br/><br/><br/><br/>";
    }

    // ON SUCCESS, PRINT THE RESULTS
    else
    {
        echo "<strong>BINGO</strong><br /> <strong><u>{$_GET["s"]}</u></strong> WORKS WITH strtotime() <br/>";
        echo "THE INTEGER TIMESTAMP VALUE IS ";
        echo number_format($unix_timestamp) . "<br />";

        // FORMAT ISO AND HUMAN-READABLE DATES
        // MAN PAGE: http://us.php.net/manual/en/function.date.php
        $y = date('c', $unix_timestamp);
        echo "THE ISO8601 DATETIME STRING IS $y<br />";
        $z = date('l dS \o\f F Y g:i:s A', $unix_timestamp);
        echo "THE TEXTUAL DATE IS $z<br />";
    }

    echo PHP_EOL;
} // END OF PROCESSING INPUT
// PUT UP THE FORM
?>
<html>
<head>
<title>TEST THE PHP FUNCTION strtotime()</title>
</head>
<body onload="document.f.s.focus()">
<form name="f" method="get">
<br />TO TEST A STRING FOR A VALID DATE/TIME, TYPE IT HERE:<input name="s" />
<input type="submit" value="GO" />
</form>


Practical Application #8

What if you wanted to send an email message to your client at 7:00am every day?  But your clients may be all over the world!  You need a way to coordinate your server time with the time at the client's location.  This script shows how to work that out.

<?php // RAY_easy_client_time.php
error_reporting(E_ALL);


// USE JAVASCRIPT TO GET THE CLIENT TIME AND COMPUTE THE OFFSET FROM THE SERVER TIME


// LOCATION OF THE SERVER - COULD BE ANYWHERE
date_default_timezone_set('America/Denver');

// DIFFERENCE OF SERVER TIME FROM UTC
$server_offset_seconds = date('Z');

// WHEN THE FORM IS SUBMITTED
if (!empty($_POST))
{
    // JAVASCRIPT TELLS US THE CLIENT TIME OFFSET FROM GMT / UTC
    $client_offset_minutes = $_POST["date_O"];
    $client_offset_seconds = $client_offset_minutes * 60;

    // THE TIME WE WANT AT THE CLIENT LOCATION
    $client_timestring = 'TODAY 7:00AM';

    // MAKE THE COMPUTATIONS, INCORPORATING THE OFFSET FROM GMT
    $client_timestamp  = strtotime($client_timestring) + $client_offset_seconds;
    $server_timestamp  = $client_timestamp + $server_offset_seconds;
    $server_timestring = date('l, F j, Y \a\t g:i a', $server_timestamp);

    echo "<br/>ACCORDING TO THE VALUE FROM PHP date Z";
    echo "<br/>SERVER IS LOCATED $server_offset_seconds SECONDS FROM UTC";
    echo "<br/>";

    echo "<br/>ACCORDING TO THE VALUE FROM JS dateObject.getTimezoneOffset()";
    echo "<br/>CLIENT IS LOCATED $client_offset_minutes MINUTES FROM UTC";
    echo "<br/>";

    echo "<br/>WHEN IT IS '$client_timestring' AT THE CLIENT, IT IS '$server_timestring' IN " . date_default_timezone_get();
}

// END OF PHP - USE HTML AND JS TO CREATE THE FORM
echo PHP_EOL; ?>

<form method="post">
<input name="date_O" id="dateTime_O" type="hidden" />
<input type="submit" value="CHECK CLIENT DATETIME" />
</form>

<!-- NOTE THIS WILL GIVE YOU THE VALUES AT PAGE-LOAD TIME, NOT AT SUBMIT TIME -->
<!-- MAN PAGE REF: http://www.w3schools.com/jsref/jsref_obj_date.asp -->
<script type="text/javascript">
var dateObject = new Date();
document.getElementById("dateTime_O").value = dateObject.getTimezoneOffset();
</script>


Practical Application #9

We promise to ship our products within "n" business days.  This function can tell you the required shipment date.  Orders are deemed to have arrived at the close of business on the actual date of arrival.  Example: If you promise to ship within one business day and an order arrives at any time on Tuesday, you have all day Tuesday and until close of business on Wednesday before the shipment is required.  If the order arrives on Saturday or Sunday, it is deemed to have arrived on the following business day, probably Monday, thus shipment is not required until the close of business Tuesday.  This is probably good enough for commercial applications, but for Government work you might want to add a greater awareness of holidays into the function.  


A technical note: While we usually choose the ISO-8601 format for an internal representation of a date, we make an exception here and choose the RFC2822 format, created by date('r').  It has an advantage in this case because we want to eliminate the weekend days from the computation and these are the only days that start with S.  RFC2822 dates look something like this, with the abbreviation of the weekday at the left: Wed, 10 Aug 2011 00:00:00 -0400.  The PHP substr() function makes it easy to find the days that start with S.


// A FUNCTION TO ADD BUSINESS DAYS TO A GIVEN DATE
function add_business_days($days=0, $date="TODAY", $format="c")
{
    // CREATE YOUR ARRAY OF HOLIDAYS
    $holidays = array();
    $november = strtotime(date('Y') . '-11-0');
    $january  = strtotime(date('Y') . '-01-0');
    $nextyear = mktime(0,0,0, 1, 1, date('Y') + 1);
    $holidays['Dr_M_L_King']  = date('r', strtotime('Third Monday', $january));
    $holidays['Independence'] = date('r', strtotime(date('Y') . '-07-04'));
    $holidays['Thanksgiving'] = date('r', strtotime('Fourth Thursday', $november));
    $holidays['Christmas']    = date('r', strtotime(date('Y') . '-12-25'));
    $holidays['NewYear']      = date('r', $nextyear);

    // ACTIVATE THIS TO SEE THE HOLIDAYS
    // print_r($holidays);

    // INTERPRET THE INPUTS INTO TIMESTAMPS
    $days = round($days);
    if ($days < 0) return FALSE;
    if (!$current   = strtotime($date)) return FALSE;
    if (!$timestamp = strtotime("$date $days DAYS")) return FALSE;

    // PAD THE FUTURE TO ALLOW ROOM FOR THE WEEKENDS AND TRADE DAYS
    $weeks     = $days * 2 + 15;
    $timestamp = strtotime("$date $weeks DAYS");

    // MAKE AN ARRAY OF FUTURE TIMESTAMPS AND RFC2822 DATES
    $arr = range($current, $timestamp, 86400);
    $arr = array_flip($arr);
    foreach ($arr as $timestamp_key => $nothing)
    {
        // ASSIGN RFC2822 DATE STRINGS TO EACH TIMESTAMP
        $arr[$timestamp_key] = date('r', $timestamp_key);

        // REMOVE THE DAY FROM THE ARRAY IF IT IS A HOLIDAY OR WEEKEND DAY
        if (in_array($arr[$timestamp_key], $holidays)) $arr[$timestamp_key] = 'S';
        if (substr($arr[$timestamp_key],0,1) == 'S') unset($arr[$timestamp_key]);
    }

    // RECAST THE ARRAY KEYS INTO OFFSETS FROM THE STARTING DATE
    $arr = array_values($arr);

    // RETURN THE FUTURE DATE ACCORDING TO THE REQUESTED FORMAT
    return date($format, strtotime($arr[$days]));
}


Practical Application #10

We need an array of 7 weekdays, beginning on a specified date, as expressed in this question:

http://www.experts-exchange.com/Web_Development/Web_Languages-Standards/PHP/Q_27242589.html


function weekdays($t)
{
    // $t TRANSFORMATION IS OPTIONAL IF YOU WANT TO LET THE INPUT TO BE ANY DATETIME STRING
    $t = date('D', strtotime($t));
    $d = array('Sun','Mon','Tue','Wed','Thu','Fri','Sat','Sun','Mon','Tue','Wed','Thu','Fri','Sat');
    return array_slice($d, array_search($t, $d), 7);
}

After reviewing that question and answer, we might want a more generalized solution.  Perhaps we do not only want those abbreviations; we might want the 7 weekdays returned in a different format.  This function preserves the functionality of the original answer and generalizes the solution to allow for different return formats.  It will return FALSE if the input date is unusable.  The increment of 86,400 on line 11 just happens to be the number of seconds in a day ;-)


function weekdays($t, $format='D')
{
    $d = FALSE;
    $a = strtotime($t);
    $z = strtotime($t . ' + 6 DAYS');
    if ($a)
    {
        while ($a < $z)
        {
            $d[] = date($format, $a);
            $a += 86400;
        }
    }
    return $d;
}


Practical Application #11

Given the beginning date of a Fiscal Year, what are the beginning dates of the Fiscal Quarters?  Since each quarter is three months, we can easily get the dates for the quarters.

// SOME TEST DATES FOR THE FISCAL YEARS
$fys = array
( 'CALENDAR' => 'January 1'
, 'ACADEMIC' => 'July 1'
, 'FEDERAL'  => 'October 1'
)
;

// COMPUTE THE QUARTERS
foreach ($fys as $key => $start)
{
    $res["q1"] = date('c', strtotime($start));
    $res["q2"] = date('c', strtotime($start . ' + 3 Months'));
    $res["q3"] = date('c', strtotime($start . ' + 6 Months'));
    $res["q4"] = date('c', strtotime($start . ' + 9 Months'));

    // SAVE THESE ELEMENTS
    $new[$key] = $res;
}


Practical Application #12

What are the essential elements of a resource scheduling calendar?  Unlike an appointment calendar which assumes the general availability of all calendar dates and times, a resource calendar must enumerate the resources and keep track of when they are available and when they are scheduled, and therefore unavailable.  The important moving parts are the resource name and the period of unavailability as given by the beginning DATETIME and the ending DATETIME.  Armed with this information we can write SQL queries that will allow us to schedule the resources and detect potential conflicts.  A starting point for the resource scheduling calendar might be something like this.


<?php // RAY_mysqli_resource_calendar.php
ini_set('display_errors', TRUE);
error_reporting(E_ALL);
echo '<pre>';


// DEMONSTRATE A RESOURCE-SCHEDULING CALENDAR


// DATABASE CONNECTION AND SELECTION VARIABLES - GET THESE FROM YOUR HOSTING COMPANY
$db_host = "localhost"; // PROBABLY THIS IS OK
$db_name = "??";
$db_user = "??";
$db_word = "??";

// OPEN A CONNECTION TO THE DATA BASE SERVER AND SELECT THE DB
$mysqli = new mysqli($db_host, $db_user, $db_word, $db_name);

// DID THE CONNECT/SELECT WORK OR FAIL?
if ($mysqli->connect_errno)
{
    trigger_error("CONNECT FAIL: $mysqli->connect_errno $mysqli->connect_error", E_USER_ERROR);
}


// CREATING A TABLE FOR OUR TEST DATA
$sql
=
"
CREATE TEMPORARY TABLE my_calendar
( id    INT         NOT NULL AUTO_INCREMENT PRIMARY KEY
, rname VARCHAR(24) NOT NULL DEFAULT ''                    # RESOURCE NAME
, alpha DATETIME    NOT NULL DEFAULT '0000-00-00 00:00:00' # BEGINNING OF COMMITMENT
, omega DATETIME    NOT NULL DEFAULT '0000-00-00 00:00:00' # END OF COMMITMENT
)
"
;

// CREATE THE TABLE OR LOG AND SHOW THE ERROR
if (!$res = $mysqli->query($sql))
{
    $err
    = 'QUERY FAILURE:'
    . ' ERRNO: '
    . $mysqli->errno
    . ' ERROR: '
    . $mysqli->error
    . ' QUERY: '
    . $sql
    ;
    trigger_error($err, E_USER_ERROR);
}

// PRE-LOAD SOME SCHEDULED RESOURCES INTO THE CALENDAR
$mysqli->query("INSERT INTO my_calendar (rname, alpha, omega) VALUES ( 'One', '2013-06-20T08:00:00', '2013-06-20T09:59:59' )");
$mysqli->query("INSERT INTO my_calendar (rname, alpha, omega) VALUES ( 'Two', '2013-06-20T08:00:00', '2013-06-20T09:59:59' )");
$mysqli->query("INSERT INTO my_calendar (rname, alpha, omega) VALUES ( 'Two', '2013-06-20T11:00:00', '2013-06-20T12:59:59' )");


// SHOW THE CALENDAR
$res = $mysqli->query("SELECT * FROM my_calendar");
while ($row = $res->fetch_object()) { print_r($row); }


// SCHEDULE A RESOURCE FROM 1:00pm TO 3:00pm
$rname = 'One';
$alpha = date('c', strtotime('June 20, 2013 1:00pm'));
$omega = date('c', strtotime('June 20, 2013 3:00pm - 1 SECOND'));

$res = $mysqli->query("LOCK TABLES my_calendar");
$res = $mysqli->query("SELECT id FROM my_calendar WHERE rname = '$rname' AND ( (alpha BETWEEN '$alpha' AND '$omega') OR (omega BETWEEN '$alpha' AND '$omega') )  LIMIT 1");
$num = $res->num_rows;
if ($num == 0)
{
    $res = $mysqli->query("INSERT INTO my_calendar (rname, alpha, omega) VALUES ( '$rname', '$alpha', '$omega' )");
    echo "SCHEDULED $rname FROM $alpha TO $omega" . PHP_EOL;
}
else
{
    echo "CONFLICT! $rname FROM $alpha TO $omega" . PHP_EOL;
}
$res = $mysqli->query("UNLOCK TABLES my_calendar");


// TRY TO SCHEDULE THE SAME RESOURCE FROM 2:00pm TO 4:00pm
$rname = 'One';
$alpha = date('c', strtotime('June 20, 2013 2:00pm'));
$omega = date('c', strtotime('June 20, 2013 4:00pm - 1 SECOND'));

$res = $mysqli->query("LOCK TABLES my_calendar");
$res = $mysqli->query("SELECT id FROM my_calendar WHERE rname = '$rname' AND ( (alpha BETWEEN '$alpha' AND '$omega') OR (omega BETWEEN '$alpha' AND '$omega') )  LIMIT 1");
$num = $res->num_rows;
if ($num == 0)
{
    $mysqli->query("INSERT INTO my_calendar (rname, alpha, omega) VALUES ( '$rname', '$alpha', '$omega' )");
    echo "SCHEDULED $rname FROM $alpha TO $omega" . PHP_EOL;
}
else
{
    echo "CONFLICT! $rname FROM $alpha TO $omega" . PHP_EOL;
}
$res = $mysqli->query("UNLOCK TABLES my_calendar");


// TRY TO SCHEDULE A DIFFERENT RESOURCE FROM 2:00pm TO 4:00pm
$rname = 'Two';
$alpha = date('c', strtotime('June 20, 2013 2:00pm'));
$omega = date('c', strtotime('June 20, 2013 4:00pm - 1 SECOND'));

$res = $mysqli->query("LOCK TABLES my_calendar");
$res = $mysqli->query("SELECT id FROM my_calendar WHERE rname = '$rname' AND ( (alpha BETWEEN '$alpha' AND '$omega') OR (omega BETWEEN '$alpha' AND '$omega') )  LIMIT 1");
$num = $res->num_rows;
if ($num == 0)
{
    $mysqli->query("INSERT INTO my_calendar (rname, alpha, omega) VALUES ( '$rname', '$alpha', '$omega' )");
    echo "SCHEDULED $rname FROM $alpha TO $omega" . PHP_EOL;
}
else
{
    echo "CONFLICT! $rname FROM $alpha TO $omega" . PHP_EOL;
    while ($row = $res->fetch_object()) { var_dump($row); }
}
$res = $mysqli->query("UNLOCK TABLES my_calendar");


// SHOW THE RESULTING CALENDAR
$res = $mysqli->query("SELECT * FROM my_calendar");
while ($row = $res->fetch_object()) { print_r($row); }


Practical Application #13

What time will the sun rise or set?  PHP can tell you this if you set your location and timezone correctly.  However your location in the default PHP installation is probably wrong unless you're in Israel near the PHP developers at Zend.  There are two initialization settings for your location: latitude and longitude, and conveniently they are settable in your PHP script.  They can also be set in the function calls to date_sunrise() and date_sunset().  Interestingly, they are independent of your timezone settings, and if you don't have all three settings in consonance, you may get incorrect output from the functions.  The code snippet shows how to determine the time of sunrise and sunset.  The user-contributed notes for date_sunrise() have some interesting and useful observations, too.


<?php // demo/sunrise.php
error_reporting(E_ALL);

// REF: http://php.net/manual/en/datetime.configuration.php
// REF: http://php.net/manual/en/function.date-sunrise.php

/**
 * There is no date_default_longitude_set() or date_default_longitude_get()
 * These values are settable PHP_INI_ALL and gettable with ini_get()
 * If you're using the default values and you're not located in Israel...
 * your computations will probably come out wrong.
 */

// GET VALUES BASED ON EXISTING DEFAULTS
$lat = ini_get('date.default_latitude');
$lon = ini_get('date.default_longitude');
$dtz = date_default_timezone_get();

$sun = date_sunrise(strtotime('TODAY'), SUNFUNCS_RET_TIMESTAMP);
echo PHP_EOL . "LOCATION: $lat, $lon ($dtz) OBSERVES SUNRISE AT: ";
echo date('r', $sun);

// CHANGE VALUES TO LOS ANGELES
ini_set('date.default_latitude',    '34.071103');
ini_set('date.default_longitude', '-118.440150');
date_default_timezone_set('America/Los_Angeles');

$lat = ini_get('date.default_latitude');
$lon = ini_get('date.default_longitude');
$dtz = date_default_timezone_get();

$sun = date_sunrise(strtotime('TODAY'), SUNFUNCS_RET_TIMESTAMP);
echo PHP_EOL . "LOCATION: $lat, $lon ($dtz) OBSERVES SUNRISE AT: ";
echo date('r', $sun);

// SHOW THAT THERE IS NO RELATIONSHIP BETWEEN TIME ZONE AND LAT/LON
date_default_timezone_set('America/New_York');
$dtz = date_default_timezone_get();

$sun = date_sunrise(strtotime('TODAY'), SUNFUNCS_RET_TIMESTAMP);
echo PHP_EOL . "LOCATION: $lat, $lon ($dtz) DOES <i>NOT</i> OBSERVE SUNRISE AT: ";
echo date('r', $sun);


Practical Application #14

When it is some given time in one time zone, what time is it in another time zone?  This function can tell you.  It is much easier to use PHP's built-in time computations than trying to write your own because PHP will automatically account for leap years and daylight savings time. 


If you need a list of the timezone identifiers, PHP can give you that: print_r( timezone_identifiers_list() );


<?php // demo/timezone_difference.php

/**
 * http://www.experts-exchange.com/questions/28701866/PHP-Difference-between-timezones.html
 */
error_reporting(E_ALL);


// A FUNCTION TO COMPUTE THE CLOCK-SETTING DIFFERENCE BETWEEN TWO TIMEZONES
function timezone_difference($tz1, $tz2, $date='NOW')
{
    $now = strtotime($date);
    $ond = date('c', $now);
    $sig = NULL;

    // THE RETURN ARRAY HAS THESE FIELDS
    $ret = array
    ( 'from' => $tz1
    , 'is'   => 'same as'
    , 'to'   => $tz2
    , 'secs' => '0'
    , 'hhmm' => '00:00'
    , 'asof' => $ond
    )
    ;

    // SAVE ORIGINAL TIMEZONE
    $old = date_default_timezone_get();

    // GET TIMEZONE SETTINGS
    date_default_timezone_set($tz1);
    $zz1 = date('Z', $now);
    date_default_timezone_set($tz2);
    $zz2 = date('Z', $now);

    // RESTORE ORIGINAL TIMEZONE
    date_default_timezone_set($old);

    // COMPUTE THE CLOCK DIFFERENCE
    $dif = $zz2 - $zz1;
    if ($dif < 0) $sig = '-';
    if ($dif > 0) $sig = '+';
    if ($sig == '-') $ret['is'] = 'ahead of';
    if ($sig == '+') $ret['is'] = 'behind';

    $dif = abs($dif);
    $fmt = date('H:i', strtotime("TODAY + $dif SECONDS"));
    $ret['secs'] = $sig . $dif;
    $ret['hhmm'] = $sig . $fmt;
    return $ret;
}

// OBSERVE THE FUNCTION IN ACTION
echo '<pre>';
$tz1 = 'America/Chicago';
$tz2 = 'America/St_Johns';
$tzd = timezone_difference($tz1, $tz2);
var_dump($tzd);


Practical Application #15

Given a country code, what are its time zones?  Given a time zone, what country is in play?  This application can answer that question.


<?php // demo/country_time_zones.php

/**
 * http://php.net/manual/en/datetimezone.construct.php
 * http://php.net/manual/en/datetimezone.listidentifiers.php
 * http://php.net/manual/en/datetimezone.getlocation.php
 */
error_reporting(E_ALL);


// CLASSES TO HOLD TIMEZONE-TO-ISO_3166 DATA
Class CountryTimeZone
{
    public function __construct($timezone, $iso_3166)
    {
        $this->timezone = $timezone;
        $this->country  = $iso_3166;
    }
}

Class CountryTimeZones
{
    public $timezones = [];
    public $countries = [];
    public function add($timezone, $iso_3166)
    {
        $this->timezones[$timezone] = new CountryTimeZone($timezone, $iso_3166);
        $this->countries[$iso_3166][] = $timezone;
    }
    public function getCountry($timezone)
    {
        return array_key_exists($timezone, $this->timezones)
        ? $this->timezones[$timezone]->country
        : FALSE
        ;
    }
    public function getTimeZones($iso_3166)
    {
        return array_key_exists($iso_3166, $this->countries)
        ? $this->countries[$iso_3166]
        : FALSE
        ;
    }
}


// COLLECT THE DATA SET
$ctz = new CountryTimeZones;
$arr = DateTimeZone::listIdentifiers(DateTimeZone::ALL);

foreach ($arr as $tz_name)
{
    $obj = new DateTimeZone($tz_name);
    $sub = $obj->getLocation();
    $iso = $sub['country_code'];
    $ctz->add($tz_name, $iso);
}


// DEMONSTRATE THE CLASS
echo '<pre>';
var_dump($ctz->getCountry('America/New_York'));
var_dump($ctz->getCountry('America/Panama'));
var_dump($ctz->getCountry('America/Los_Angeles'));
var_dump($ctz->getCountry('UTC'));
var_dump($ctz->getCountry('wtf'));

var_dump($ctz->getTimeZones('PA'));
var_dump($ctz->getTimeZones('CN'));
var_dump($ctz->getTimeZones('US'));
var_dump($ctz->getTimeZones('??'));


// SAVE THE DATA SET
var_export($ctz);


Practical Application #16

How can I create a range of dates (inspired by this question: http://www.experts-exchange.com/questions/28705704/Looping-through-Dates-in-PHP.html)


<?php // demo/temp_apd_toronto.php

/**
 * http://www.experts-exchange.com/questions/28705704/Looping-through-Dates-in-PHP.html
 *
 * How can I create a range of dates?
 * How can I represent internal dates consistently? ISO-8601
 * How can I add one day to a given date?
 */
error_reporting(E_ALL);

// A DATE SETTING MAY BE REQUIRED - DEPENDS ON PHP INSTALLATION SETTINGS
date_default_timezone_set('America/Chicago');

// SET THE START AND END DATES
$alpha = 'July 15, 2015';
$omega = 'Aug  14, 2015';

// COMPUTE AND DISPLAY THE RANGE OF DATES
$range = date_range($alpha, $omega);
print_r($range);


function date_range($a, $z)
{
    $return = [];

    // NORMALIZE THE DATES
    $alpha = date('c', strtotime($a));
    $omega = date('c', strtotime($z));

    // SANITIZE THE INPUT ARGUMENTS (OR USE TYPE HINTING)
    if (!$alpha || !$omega) trigger_error('Invalid input to date_range()', E_USER_ERROR);

    // CREATE THE RANGE
    while ($alpha <= $omega)
    {
        $return[] = $alpha;
        $alpha = date('c', strtotime($alpha . ' + 1 DAY'));
    }
    return $return;
}


Practical Application #17

You might like the jQuery datepicker.  This is not a PHP issue, but it's a popular question, so I'll just leave the example here.


<?php // demo/datepicker.php
/**
 * Demonstrate the jQuery Date Picker
 */
error_reporting(E_ALL);
echo '<pre>';

// IF THE DATE WAS SUBMITTED, USE IT IN PHP
if (!empty($_POST['chosen_date']))
{
    $chosen_date = 'Invalid date value';
    $timestamp = strtotime($_POST['chosen_date']);
    if ($timestamp)
    {
        $chosen_date = date('Y-m-d', $timestamp);
    }
    echo PHP_EOL . "You chose date: <b>$chosen_date</b>";
}
// CREATE THE HTML USING HEREDOC NOTATION (ADAPTED FROM http://jqueryui.com/datepicker/)
$htm = <<<EOD
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>jQuery UI Datepicker - Default functionality</title>
<link rel="stylesheet" href="//code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css">
<link rel="stylesheet" href="http://jqueryui.com/datepicker/resources/demos/style.css">
<script src="https://code.jquery.com/jquery-1.12.4.js"></script>
<script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script>
<script>
$(function() {
    $("#datepicker").datepicker();
});
</script>
</head>
<body>
<form method="post">
<p>Date: <input name="chosen_date" type="text" id="datepicker" /></p>
<input type="submit" />
</form>
</body>
</html>
EOD;

echo $htm;


Practical Application #18

How long does it take your client to fill out a form?  A bit of jQuery can identify the load time and the submit time.  The difference between these two times gives us the answer.

<?php // demo/submit_time_example.php
/**
 * Find out how long it takes your client to submit a form
 *
 * JavaScript date().toString() returns something like this:
 *    Sat Apr 09 2016 07:33:27 GMT-0400 (Eastern Daylight Time)
 * PHP needs something like this (33 characters):
 *    Sat Apr 09 2016 07:33:27 GMT-0400
 */
error_reporting(E_ALL);


if (!empty($_POST))
{
    if ($alpha = strtotime( substr($_POST['alpha'],0,33) ))
    {
        if ($omega = strtotime( substr($_POST['omega'],0,33) ))
        {
            $lapse = $omega - $alpha;
            $timex = date('i:s', strtotime("TODAY + $lapse SECONDS"));
            echo PHP_EOL . "It took $timex (minutes:seconds) to submit this form";
        } else { echo 'Error in omega value'; }
    } else { echo 'Error in alpha value'; }

     // ACTIVATE THIS TO SHOW THE REQUEST
     // var_dump($_POST);
}


// CREATE OUR WEB PAGE IN HTML5 FORMAT
$htm = <<<HTML5
<!DOCTYPE html>
<html dir="ltr" lang="en-US">
<head>
<meta charset="utf-8" />
<meta name="robots" content="noindex, nofollow" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />

<style type="text/css">
/* STYLE SHEET HERE */
</style>

<script src="http://code.jquery.com/jquery-latest.min.js"></script>
<script>
$(document).ready(function(){
    var tload = new Date().toString();
    var alpha = $("<input>").attr("type", "hidden").attr("name", "alpha").val(tload);
    $("#myForm").append($(alpha));

    $("#myForm").submit(function(e){
        var tsubm = new Date().toString();
        var omega = $("<input>").attr("type", "hidden").attr("name", "omega").val(tsubm);
        $("#myForm").append($(omega));
    });
});
</script>

<title>HTML5 Page With jQuery in UTF-8 Encoding</title>
</head>
<body>

<noscript>Your browsing experience will be much better with JavaScript enabled!</noscript>

<form id="myForm" method="post">
<input type="submit" />
</form>

</body>
</html>
HTML5;


// RENDER THE WEB PAGE
echo $htm;


Object-Oriented DATETIME Notation

See http://php.net/manual/en/class.datetime.php for object-oriented ways of processing date and time information.  It's the same set of concepts but wrapped in OOP notation that may play better with some object-oriented frameworks.


Creativity and Routine

Like most things in computer programming, there are several ways to achieve equivalent results.  This article has shown some tried-and-true ways to use PHP's powerful date / time processing functions.  The methods shown here will help you get quick and accurate answers to many common questions, all with a minimum risk of introducing bugs into the code.  Some additional references may be of interest to you.  See http://php.net/manual/en/ref.calendar.php and http://php.net/manual/en/refs.calendar.php for more interesting information about calendars, dates and times.  See http://php.net/manual/en/datetime.formats.relative.php for some interesting things that play well with strToTime()


Please give us your feedback!

If you found this article helpful, please click the "thumb's up" button below. Doing so lets the E-E community know what is valuable for E-E members and helps provide direction for future articles.  If you have questions or comments, please add them.  Thanks!

 

50
Comment
Author:Ray Paseur
7 Comments
 
LVL 54

Expert Comment

by:b0lsc0tt
Good info and post.  Seems like a nice, comprehensive summary on it.  Thanks for the time to work on it and post!

If you don't mind a general suggestion since it was posted as an article.  I would recommend future articles have a little smoother flow and some more "content."  Not that I am trying to make it longer but my impression was alot of it seemed appropriate for bullet points and a links "section" instead of a "published" article.  Not that either is wrong or all posts here have to be the latter.  Just a thought and feedback that I hope will help.  I hope this isn't the only one, especially if you have other "nuggets" of wisdom and experience to share like this. :)

Thanks again and good job!

bol
0
 
LVL 60

Expert Comment

by:Kevin Cross
Good work, Ray.
Voted yes above.
0
 
LVL 32

Expert Comment

by:DrDamnit
Voted Yes! Good job Ray.
0
Technology Partners: We Want Your Opinion!

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

 
LVL 32

Expert Comment

by:DrDamnit
Ray_Paseur:

Always a fan of your work. :-)
0
 
LVL 18

Expert Comment

by:WaterStreet

Voted Yes, as helpful, at top of article
0
 

Expert Comment

by:DanielAttard
Great stuff.  Lots to learn in this article.  Thanks Ray.
0
 
LVL 12

Expert Comment

by:Nathan Riley
Thank Ray, great article and #6 solved my problems with ease!
0

Featured Post

Microsoft Certification Exam 74-409

Veeam® is happy to provide the Microsoft community with a study guide prepared by MVP and MCT, Orin Thomas. This guide will take you through each of the exam objectives, helping you to prepare for and pass the examination.

Join & Write a Comment

The viewer will learn how to count occurrences of each item in an array.
HTML5 has deprecated a few of the older ways of showing media as well as offering up a new way to create games and animations. Audio, video, and canvas are just a few of the adjustments made between XHTML and HTML5. As we learned in our last micr…
Suggested Courses

Keep in touch with Experts Exchange

Tech news and trends delivered to your inbox every month