Having dealt with a lot of dates and times — and the calculations between them —
recently, I thought it might be a good idea to write down some of the issues
I’ve encountered.
The truth is, dates and times are hard! There are many different rules
that make dealing with dates and times non-trivial. Often, people
make mistakes, causing loss of information and general confusion.
Deciding on a format
One of the observations I’ve made is that nearly every system out there
lacks a standard for passing around date and time values.
The most common formats are MySQL (YYYY-MM-DD HH:MM:SS),
timestamps (what time() produces),
and DateTime.
There are benefits to all of these, but it’s important that you need to decide on one.
Make sure any date/time that enters your
app through any means is converted immediately to this standard.
This includes, for example, the result of a MySQL query, data from $_POST,
or the response from a web service.
Make sure this enters your coding standards document.
Using DateTime
Deciding on a standard date format and following it religiously is much more
important than which standard you choose. If you have a choice, though, I recommend
the DateTime object.
The standard inclusion of the DateTime object in PHP is one of
the best things that has happened to PHP in recent times. It replaces all the
functionality of the existing date functions, and there’s really no need
to use anything else.
One of the benefits you’ll get is that you can type hint
it like every other class, which means it will be easier to spot bugs, and
it’s self-documenting. Also, you can pretty much treat it like a timestamp:
$dt1 = new DateTime('yesterday');
$dt2 = new DateTime('next wednesday');
if ($dt1 > $dt2) {
echo "Hell froze over!\n"
};
As you can see, because DateTime is actually a PHP extension, it
gets the privilege of overloading operators such as < and >. Plus,
it handles timezones.
DateTime also makes it easy to do relative calculations. If you want to
increase a date by exactly one month, how many seconds you need to add the
start date depends on the month of the year, wether it’s a leap year or not,
which timezone you’re in, and whether a
leap second has been
scheduled that year. It’s easier let DateTime do it:
$dt = new DateTime('December 25th, 2011');
$dt->modify('+1 month');
Using time zones
I’ve noticed that a lot of people simply use their local
timezone. This is a bad idea, because it’s not always
immediately clear, it’s ambiguous, and it can cause
obscure bugs. It will also not always be possible to convert to
different timezones if your app ends up going international.
The reason it’s ambiguous is simple. Every year, in most European countries,
the end of Daylight savings time is the last Sunday in
October. On that day, every second between 02:00
and 03:00 occurs twice. Thus, one ambiguity when using time zones is that
these seconds that occur twice are not unique, so you can no longer determine
if a time is referring to before the switchover or after. This can cause bugs when
doing date comparisons. Adding a second could mean the same thing
as subtracting 59 minutes and 59 seconds.
The start of DST is similar, which is why you might see gaps in log files and
whatnot.
So, make sure that when you deploy your app, the server as
well as PHP are configured to use UTC or GMT. UTC time is time zone independent,
so every second of the year is, well, unique.
You’ll have to get into the habit of converting times
to your local time zone in your presentation layer. On the plus side,
it will be easy to support other time zones in the future.
You can always accurately convert from UTC to your local
time zone, but you cannot always accurately convert from your local time zone to
UTC. (It is a lossy conversion.)
// Changing time zones
$dt = new DateTime('2011-10-07 12:00:00');
$dt->setTimeZone(new DateTimeZone('Europe/Lisbon'));
echo $dt->format(DateTime::RFC2822);
Storing dates and times in MySQL
There are three possible data types you can use to store dates and times in MySQL. You can
use DATETIME, TIMESTAMP, or INT (for storing a timestamp).
DATETIME is a popular choice, and in most cases, it’s fine.
Just remember that DATETIME is 8 bytes, whereas a TIMESTAMP
Truncated by Planet PHP, read more at the original (another 2941 bytes)