strtotime() considered harmful?
It seems like a lot of people struggle with date/time issues in PHP, and inevitably, many of the accepted answers tend to be "Use strtotime
in this way."
Is this really the best way to direct people dealing with date problems? I'm beginning to feel like strtotime
is sort of a nifty trick that shouldn't necessarily be relied on for important date/time calculations, and by the nature of it taking arbitrary strings, it seems like a potential source of buggy, hard-to-predict behavior. Its inability to differentiate between MM/DD/YYYY and DD/MM/YYYY is sort of a big deal, no?
StackOverflow is usually very good at promoting good practices (I rarely see a mysql_real_escape_string
conversation that doesn't have someone say "Use PDO instead.")
But there doesn't seem to be an accepted norm around date issues in PHP, with a lot of people falling back on the crutch that is strtotime
.
So, what should we be doing about this, if any开发者_JAVA技巧thing at all? Is there a better norm we should be enforcing for people asking questions like "How do I add 1 week to X", or "How do I convert this date format to this other date format?"
What is the best, most reliable way to deal with Date/Time issues like strtotime
tries to, but too often fails to?
I'll start off by saying that I am a big advocate for using the DateTime object, which allows you to use the DateTime::createFromFormat()
function. The DateTime object makes the code much more readable and avoids the need to do the whole Unix timestamp modification using 60 * 60 * 24 to advance the date days.
That being said, the arbitrary string that strtotime() takes is not very hard to predict. Supported Date and Time Formats lists the formats that are supported.
As per your example of not being able to differentiate between MM/DD/YYYY and DD/MM/YYYY it does differentiate based on the Date Formats. Dates that use slashes are always read as American format. So a date in the format 00/00/0000 will always be read as MM/DD/YYYY. Alternatively using dashes or periods will be DMY. e.g. 00-00-0000 will always be read as DD-MM-YYYY.
Here are some examples:
<?php
$dates = array(
// MM DD YYYY
'11/12/2013' => strtotime('2013-11-12'),
// Using 0 goes to the previous month
'0/12/2013' => strtotime('2012-12-12'),
// 31st of November (30 days) goes to 1st December
'11/31/2013' => strtotime('2013-12-01'),
// There isn't a 25th month... expect false
'25/12/2013' => false,
// DD MM YYYY
'11-12-2013' => strtotime('2013-12-11'),
'11.12.2013' => strtotime('2013-12-11'),
'31.12.2013' => strtotime('2013-12-31'),
// There isn't a 25th month expect false
'12.25.2013' => false,
);
foreach($dates as $date => $expected) {
assert(strtotime($date) == $expected);
}
As you can see a couple of key examples are 25/12/2013
and 12.25.2013
which would both be valid if read the with the opposite format, however they return false
as they are invalid according the the Suported Date and Time Formats...
So as you can see the behaviour is quite predictable. As always if you are receiving a date from use input you should be validating that input first. No method will work if you are not validating the input first.
If you want to be very specific about the date being read, or the format you are being given isn't a supported format, then I recommend using DateTime::createFromFormat()
.
strtotime()
is suggested because it's been in PHP since the v4.x days, so is basically guaranteed to be available. Availability trumps the odd time (no pun intended) that it'll turn around and bite you in the butt with a mis-parsed date.
The current "proper" way to do date math would be using the DateTime/DateInterval objects, but those are more recent additions to PHP (5.2/5.3, I think) and therefore not always available - there's plenty of hosts out there still on 4.x.
I use strtotime()
frequently. Thing is, you shouldn't use it as a crutch, but you should be aware of its limitations.
If you wanted to enforce a standard, then I suppose you should go with the c-based mktime()
. For instance, to get 1 week later:
date('Y-m-d', mktime(0, 0, 0, date('n'), date('j') + 7);
The problem is that you must select a strict data format for your date/time/zone values (ISO8601:2004) across all the layers of your application and find the PHP function that best handles this format, which is : DateTime::createFromFormat().
IMHO having different date/time/zone formats is for the user interface (GUI layer), trying to guess the date input within a PHP function means you have not performed any sanitization of your data from the client side (using Javascript maybe?) and you are transfering the data from your web formular in an unknown format.
If the date/time/zone format doesn't match ISO8601 on the server side, you simply reject it, et voilà !
Hope This Helps!
Derek Rethans had a talk at Froscon 2010 on 'Advanced Date and Time Handling in PHP'.
Find the slides as a PDF at http://derickrethans.nl/talks/time-froscon10.pdf. These rely on a recent PHP (5.2 and above), the use of which should also be a best practice.
It certainly is possible in PHP: Check the strtotime manual, especially this comment.
If you have a MySQL connection available, SELECT DATE_ADD( '2011-05-31', INTERVAL 1 MONTH ) would be less redundant since the (correct) functionality is already implemented without you having to implement it yourself.
UPDATE: For PHP >= 5.3 date_parse_from_format() should be used instead. Note strptime()
could have a different behaviour on different operating systems because it relies on the strptime()
exposed by the system's C library.
The most reliable way for date calculations in PHP is by using the timestamp.
It is true that it is limited by the 32bit integer, but using the Date object is not an option while typing in some odd environment, like PHP 4 for example.
In order to be sure that you get the most correct behavior I would suggest using the strptime()
to get the timestamp from the human readable date. This is better than strtotime()
and strftime()
because it does not try to guess what is the format of the date, but rather use the date format you provide.
After you have obtained the correct timestamp you can use multiple methods in PHP to get the job done.
精彩评论