Hello all,
Last week I submitted a bug report on the issue described below. The
response (also below) was that this is not a bug. I fail to see how it
could not be a bug given that strtotime is parsing an invalid date
into a seemingly-arbitrary and definitely-meaningless number, rather
than returning false as it is supposed to. Can someone explain to me
why this is "intended" behavior? We do rely on strtotime returning
false on invalid dates, and this behavior changed between 5.2.5 and
5.2.6. Do we need to update our code to check for this arbitrary
negative integer? I would prefer not to do that.
Thanks,
Jack Steadman
Smarter Travel Media LLC
Operating system: RHEL 4/5
PHP version: 5.2.6
PHP Bug Type: Date/time related
Bug description: strtotime parses 0000-00-00 00:00:00 as valid date
Description:
MySQL uses '0000-00-00 00:00:00' as a default value for non-null
datetime
fields. Until 5.2.6, strtotime correctly returned false (or -1 before
5.1)
when passed this value - it's not a valid date/time.
5.2.6 returns '-62169966000' which is not useful.
Reproduce code:
<?php echo (int)strtotime('0000-00-00 00:00:00');
Expected result:
Should print 0.
Actual result:
Actually prints -62169966000.
[18 Jul 7:25am UTC] derick@php.net
Please do not submit the same bug more than once. An existing
bug report already describes this very problem. Even if you feel
that your issue is somewhat different, the resolution is likely
to be the same.
Thank you for your interest in PHP.
http://bugs.php.net/bug.php?id=42971
http://bugs.php.net/bug.php?id=44453
This is not a bug. MySQL is being silly to use this string as a default
value. In PHP 5.3 you can now detect this however, by using
date_parse_from_format( "Y-m-d H:i:s"); and then check the contents of
date_get_last_errors()
.
[18 Jul 1:31pm UTC] jack+phpdotnet at smartertravelmedia dot com
First of all, those previously-reported bugs are with the DateTime
class, not strtotime. If DateTime uses strtotime or its underlying
implementation, that's not for me to know. I searched the bug database
before submitting this report and did not find the two you mentioned.
Your "similar bugs" prompt didn't flag them either.
Second, how is this not a bug? strtotime's manual page says, "Returns a
timestamp on success, FALSE
otherwise." The string of zeroes that mysql
uses is NOT a valid datetime string, so how is strtotime "successfully"
parsing it into a random negative number? That's aside from the fact
that this is a behavior change from 5.2.5 to 5.2.6 - that alone makes
it a problem.
Third, you can think mysql is silly for using this as a default all you
want, but they've been doing it since at least version 3.23 if not
before. Just because you don't agree with it doesn't mean PHP shouldn't
handle it as advertised - namely to return false if an invalid date/time
string is passed to strtotime.
Hello all,
Last week I submitted a bug report on the issue described below. The
response (also below) was that this is not a bug. I fail to see how it
could not be a bug given that strtotime is parsing an invalid date
into a seemingly-arbitrary and definitely-meaningless number, rather
than returning false as it is supposed to. Can someone explain to me
why this is "intended" behavior? We do rely on strtotime returning
false on invalid dates, and this behavior changed between 5.2.5 and
5.2.6. Do we need to update our code to check for this arbitrary
negative integer? I would prefer not to do that.
Date is not invalid. Some diety was born at -62169966000 Unixtime.
--
Tomas
Hi,
Last week I submitted a bug report on the issue described below. The
response (also below) was that this is not a bug. I fail to see how it
could not be a bug given that strtotime is parsing an invalid date
into a seemingly-arbitrary and definitely-meaningless number,
strtotime()
has always accepted month and day numbers 0 in order to
express "last month of the previos year" and "last day of the previous
month". Take:
var_dump (date ('Y-m-d H:i:s', strtotime ('2008-00-01 12:00:00')));
This gives:
string(19) "2007-12-01 12:00:00"
Here, the 00 is interpreted as "month before January" and thus December
of the previous year. Then, take:
var_dump (date ('Y-m-d H:i:s', strtotime ('2008-02-00 12:00:00')));
This gives:
string(19) "2008-01-31 12:00:00"
Here, the 00 is interpreted as "day before the first day of that month"
and thus the 31st of January.
Take both together, you get:
var_dump (date ('Y-m-d H:i:s', strtotime ('2008-00-00 12:00:00')));
string(19) "2007-11-30 12:00:00"
Now, take the date '0000-01-01'. This is a valid date according to ISO
8601 and simply means the 1st of January in 1 BC (but according to a
proleptic gregorian calendar, because ISO 8601 defines it as that). So
'0000-00-00' is actually the 30th of November of 2 BC (proleptic
gregorian calendar).
Now, the return value of strtotime()
is actually defined as the number
of seconds since the 1st of January 1970, 00:00:00 UTC. If you add
-62169966000 seconds to that date, you get the 30th of November 2 BC
(proleptic gregorian calendar) at 00:00:00 of your time zone.
Also, it is not a regression. On my 32bit CPU, strtotime()
still returns
false for your date since a 32bit integer cannot represent such a large
number and thus strtotime()
can't return a valid timestamp. But on a
64bit CPU, integers are large enough to hold that number and strtotime()
can return a valid timestamp. If you compile a very old PHP version on a
64bit system, it will yield the same results.
As somebody already commented in the bug reports: It's not a bug.
You can write yourself a wrapper if you want to catch that specific date:
function my_strtotime($str, $ts = null) {
if ($ts === null) $ts = time ();
return $str == '0000-00-00 00:00:00' ? false : strtotime ($str, $ts);
}
Regards,
Christian
Thank you for taking the time to explain this to me. A couple more
points below:
strtotime()
has always accepted month and day numbers 0 in order to
express "last month of the previos year" and "last day of the previous
month". Take:
So my biggest issue with this is that it's exception-case behavior,
treating truly invalid dates as valid dates for no obvious reason. Who
needs this functionality? Why is it even a good idea?
A close second is that this behavior is completely undocumented.
http://us.php.net/strtotime states that the time argument to strtotime
is "The string to parse, according to the GNU > Date Input Formats
syntax" and that false is returned on failure. GNU date formats do NOT
allow for zero months and days (see
http://www.gnu.org/software/tar/manual/html_node/Calendar-date-items.htm
l#SEC116). No official mention is made of the exception case that you
describe. The closest the docs come is a user comment on the strtotime
page from June 19 of this year (perhaps someone who upgraded to 5.2.6
and found this behavior for the first time?) warning other users that
2008-00-14 is interpreted as 2007-12-14.
Also, it is not a regression. On my 32bit CPU,
strtotime()
still returns false for your date since a 32bit integer
cannot represent such a large number and thusstrtotime()
can't return a valid timestamp. But on a 64bit CPU,
integers are large enough to hold that number andstrtotime()
can return a valid timestamp. If you compile a very old PHP
version on a 64bit system, it will yield the same results.
This is not actually the case. Take one of our machines (first part of
php -i included here):
System => Linux wollaston 2.6.9-55.0.9.ELsmp #1 SMP Tue Sep 25 02:16:15
EDT 2007 x86_64
Build Date => Jul 22 2008 11:41:54
Configure Command => './configure' '--with-mysql=/usr/lib64'
'--with-gettext' '--with-openssl' '--enable-ftp'
'--with-zlib-dir=/usr/lib64' '--enable-pcntl' '--with-pdo-mysql=/usr'
'--without-sqlite' '--without-pdo-sqlite' '--with-apac
he=../apache_1.3.41' '--with-curl' '--enable-cli'
Server API => Command Line Interface
Virtual Directory Support => disabled
Configuration File (php.ini) Path => /usr/local/lib
Loaded Configuration File => /usr/local/Zend/Platform/etc/php.ini
PHP API => 20041225
PHP Extension => 20060613
Zend Extension => 220060519
Debug Build => no
Thread Safety => disabled
Zend Memory Manager => enabled
IPv6 Support => enabled
Registered PHP Streams => php, file, data, http, ftp, compress.zlib,
https, ftps
Registered Stream Socket Transports => tcp, udp, unix, udg, ssl, sslv3,
sslv2, tls
Registered Stream Filters => string.rot13, string.toupper,
string.tolower, string.strip_tags, convert., consumed, convert.iconv.,
zlib.*
This program makes use of the Zend Scripting Language Engine:
Zend Engine v2.2.0, Copyright (c) 1998-2007 Zend Technologies
with Zend Extension Manager v1.2.0, Copyright (c) 2003-2006, by Zend
Technologies
with Zend Optimizer v3.2.8, Copyright (c) 1998-2007, by Zend
Technologies
with jobqueue_client wrapper v1.0, Copyright (c) 2004-2007, by Zend
Technologies
with DISABLED Zend Download Server v1.0.6, Copyright (c) 2003-2005,
by Zend Technologies
with DISABLED Zend Platform v3.0.3, Copyright (c) 1999-2007, by The
Zend Platform presently supports only Apache, ISAPI and FastCGI SAPIs
with Zend Debugger v5.2.8, Copyright (c) 1999-2007, by Zend
Technologies
with gd wrapper v1.0, Copyright (c) 2004-2007, by Zend Technologies
with mod_cluster wrapper v1.0, Copyright (c) 2004-2007, by Zend
Technologies
This 64-bit machine is running 5.2.5 and returns false on the all-zero
datetime string. It was upgraded last week to 5.2.6 and started
returning the large negative integer. Downgrading back to 5.2.5 fixed
the problem and it again returns false. Aside from that, the original
tests I ran and included with the bug report were both run on 64-bit
machines, one running 5.2.5 and the other (the machine whose info is
above) running 5.2.6. In fact we don't have any 32-bit machines for me
to even try this out on.
Something changed in date handling between 5.2.5 and 5.2.6. Whether
you perpetuate the exception case or not, it is a regression.
Jack
Hi!
This 64-bit machine is running 5.2.5 and returns false on the all-zero
datetime string. It was upgraded last week to 5.2.6 and started
returning the large negative integer.
Ah, yes, the change responsible is the following:
It fixes bug #44209 http://bugs.php.net/bug.php?id=44209. timelib.h
didn't include limits.h and thus always defined LONG_MAX / LONG_MIN to
the 32bit values for longs and thus the check in timelib.c for LONG_MIN
failed in 5.2.5 for your timestamp. But as 5.2.6 fixed that (LONG_MIN
from limits.h used when present), your timestamp became valid and
strtotime()
returned it.
Or to put it that way: The current behaviour of PHP 5.2.6 is the
expected behaviour (at least what reading the source tells about the
author's intentions) and it was broken before.
As to whether it's a good idea that strtotime()
accepts invalid dates,
I'll stay out of that discussion.
Regards,
Christian
Ah, yes, the change responsible is the following:
<http://cvs.php.net/viewvc.cgi/php-src/ext/date/lib/timelib.h?
r1=1.10.2.11.2.4&r2=1.10.2.11.2.5&pathrev=PHP_5_2>It fixes bug #44209 http://bugs.php.net/bug.php?id=44209.
OK, thanks, it's good to understand the change in behavior.
Or to put it that way: The current behaviour of PHP 5.2.6 is the
expected behaviour (at least what reading the source tells about the
author's intentions) and it was broken before.As to whether it's a good idea that
strtotime()
accepts invalid dates,
I'll stay out of that discussion.
If this is the way it's always been (accepting zero month and day values
as you described earlier), then I suspect that you'll be reluctant to
change behavior without significant thought (and of course, I wouldn't
expect otherwise, even though I've expressed my disagreement with the
current behavior).
What's the best way to get this fully documented? Should I submit a new
bug report relating to the documentation, rather than a PHP bug?
Jack
I'm not sure that there's any problem with the documentation (although it
wouldn't hurt to mention it in the comments). What is it about "0000-00-00
00:00:00" that makes it an invalid date? (See Christian's explanation)
Zach
On Thu, Jul 24, 2008 at 11:09 AM, Jack Steadman jack@smartertravelmedia.com
wrote:
Ah, yes, the change responsible is the following:
<http://cvs.php.net/viewvc.cgi/php-src/ext/date/lib/timelib.h?
r1=1.10.2.11.2.4&r2=1.10.2.11.2.5&pathrev=PHP_5_2>It fixes bug #44209 http://bugs.php.net/bug.php?id=44209.
OK, thanks, it's good to understand the change in behavior.
Or to put it that way: The current behaviour of PHP 5.2.6 is the
expected behaviour (at least what reading the source tells about the
author's intentions) and it was broken before.As to whether it's a good idea that
strtotime()
accepts invalid dates,
I'll stay out of that discussion.If this is the way it's always been (accepting zero month and day values
as you described earlier), then I suspect that you'll be reluctant to
change behavior without significant thought (and of course, I wouldn't
expect otherwise, even though I've expressed my disagreement with the
current behavior).What's the best way to get this fully documented? Should I submit a new
bug report relating to the documentation, rather than a PHP bug?Jack
I'm not sure that there's any problem with the documentation
(although it wouldn't hurt to mention it in the comments).
What is it about "0000-00-00 00:00:00" that makes it an
invalid date? (See Christian's explanation)
Zero values for month and day are NOT valid. PHP treats them as valid,
but according to GNU (on whose requirements strtotime's parsing is
based, according to the docs), month values must be in the range of 1-12
and day values must be 1-31.
Come to think of it, I wonder how PHP handles something like
'2008-06-31' - does it treat that as July 1? Or does it call it an
invalid date? Seems like the same sort of behavior - should be
considered invalid, but I'm betting that PHP handles it transparently.
The upshot is that we can't actually rely on strtotime to determine
whether a string is a valid date/time. The docs don't ever explicitly
say that the date strings themselves are checked for validity, but
returning false on "failure" implies to me that it will return false if
it can't get a valid date out of the string.
Jack
The upshot is that we can't actually rely on strtotime to determine
whether a string is a valid date/time. The docs don't ever explicitly
say that the date strings themselves are checked for validity, but
returning false on "failure" implies to me that it will return false
if
it can't get a valid date out of the string.
-62169966000 is a valid date. Perhaps instead of checking if strtotime
returns a false for your error condition, check if it returns a value
< 1.
--
Jordan CM Wambaugh
Lead Software Engineer
Forrent Media Solutions
jordan.wambaugh@forrent.com
Thank you for taking the time to explain this to me. A couple more
points below:
strtotime()
has always accepted month and day numbers 0 in order to
express "last month of the previos year" and "last day of the previous
month". Take:So my biggest issue with this is that it's exception-case behavior,
treating truly invalid dates as valid dates for no obvious reason. Who
needs this functionality? Why is it even a good idea?
It's nice to find the last day of next month.
A close second is that this behavior is completely undocumented.
http://us.php.net/strtotime states that the time argument to strtotime
is "The string to parse, according to the GNU > Date Input Formats
syntax" and that false is returned on failure. GNU date formats do NOT
allow for zero months and days (see
http://www.gnu.org/software/tar/manual/html_node/Calendar-date-items.htm
l#SEC116). No official mention is made of the exception case that you
describe. The closest the docs come is a user comment on the strtotime
page from June 19 of this year (perhaps someone who upgraded to 5.2.6
and found this behavior for the first time?) warning other users that
2008-00-14 is interpreted as 2007-12-14.
Yes, the documentation is broken. I'm working on fixing that.
<snip>This is not actually the case. Take one of our machines (first part of
php -i included here):
<snip>This 64-bit machine is running 5.2.5 and returns false on the all-zero
datetime string. It was upgraded last week to 5.2.6 and started
returning the large negative integer.
Something changed in date handling between 5.2.5 and 5.2.6. Whether
you perpetuate the exception case or not, it is a regression.
Yes, because there was a regression in 5.2.[45]:
php-5.2.6$ sapi/cli/php -n -r 'var_dump( strtotime( "0000-00-00 00:00" ) );'
int(-62169987600)
php-5.2.5$ sapi/cli/php -n -r 'var_dump( strtotime( "0000-00-00 00:00" ) );'
bool(false)
php-5.2.4$ sapi/cli/php -n -r 'var_dump( strtotime( "0000-00-00 00:00" ) );'
bool(false)
even in PHP 5.1 it doesn't return false (although it's incorrect):
$ php-5.1dev -n -r 'var_dump( strtotime( "0000-00-00 00:00" ) );'
int(943916400)
and even on 4.3.9:
$ php-4.3.9 -n -r 'var_dump( strtotime( "0000-00-00 00:00" ) );'
int(943916400)
regards,
Derick
--
HEAD before 5_3!: http://tinyurl.com/6d2esb
http://derickrethans.nl | http://ezcomponents.org | http://xdebug.org