Strange behavior with the strtotime function passing a wrong date and time format
Asked Answered
G

2

6

I'm trying to understand the following behavior of the date function strtotime():

  • strtotime(1420066800) returns false, as expected.
  • strtotime(1451602800) returns 26197048320! But expected false as 1451602800 is not a valid date and time format. (same thing with new DateTime(1451602800) as well)

I read the whole Supported Date and Time Formats page, but didn't find any match for this input.

By the way, these are timestamps of 2015-01-01 and 2016-01-01 relatively.

Any insights will be appreciated.

Update:
Different versions test results:

PHP 5.1.6  => false
PHP 5.2.17 => 26197048320
PHP 5.3.29 => 26197048320
PHP 5.4.34 => 26197048320
PHP 5.5.18 => 26197048320
PHP 5.6.2  => 26197048320
PHP 5.6.17 => 26197048320
PHP 7.0.2  => 26197048320

Running on Mac OS X 10.11.3

Groundspeed answered 25/2, 2016 at 16:17 Comment(10)
both return false for meButtonball
Tested with PHP v7.0.2Groundspeed
If you use PHP 7 why not use OOP ? You have DateTime class to this purposes, and you don't need to use never more the procedural functionsBrachiopod
Great question and great answers. I tested too on 7.0.2 and get the same result what OP get.Bifid
@MarcosPérezGude the DateTime class internally uses the same code as strtotime() to parse the string provided as first argument to its constructor. The problem and the outcome is the same.Rident
@Rident I'm not agree while I can't see the proof of what you said. Where did you see the same code of the parser? I think that This section describes all the different formats that the strtotime(), DateTime and date_create() parser understands. doesn't means that's the same code. However, it's better to use classes instead of functions if you start to coding in PHP 7. PHP people wants to improve the language image, to avoid java developers said php is a crap (due this kind of reasons)Brachiopod
@MarcosPérezGude First of all, JAVA sucks! :-) Second, I agree with you that using OOP is better practice, but this particular script is not a part of an application, but something small and dirty. In this case, I can say that's why I like PHP, because I can write fast and dirty script and in most of the cases it will be reliable and fast. But when I write an application, I have strict rules of "law and order" and also apply the best of Design Patterns as possible.Groundspeed
Ok, @SlavikMe I agree with you in all your comment's said. And java sucks it's a reality but don't tell that to a java developer, he can make a java toy gun and trigger a toy bullet in your head. Be carefull!! LOL (I work with lots of java developers that make jokes with me because I'm php/javascript developer. I laugh when a tomcat doesn't start due a missing semicolon, or the performance of a webpage with one form that loads in 15 seconds. I love php for the easy-to-learn and fast-to-run)Brachiopod
@MarcosPérezGude It doesn't make much sense to write a different parser for DateTime that supports the same formats as strtotime() supports, don't you think? If you need a solid proof then feel free to check the source code of PHP. Search for function timelib_strtotime() (it implements the parsing) in file ext/date/php_date.c. You will find it called by strtotime(), indirectly by DateTime::__construct(), by date_parse(), by DateInterval::createFromDateString() and some others.Rident
@Rident that's a big great explanation. Now I'm trust in you sightless :) Thank you!!Brachiopod
R
6

Let's start with the right way to do it. If you want to pass a timestamp to strtotime you have to prefix it with '@'. It is explained on the Compound Formats page, under "Localized notations" -> "Unix Timestamp".

echo(date('r', strtotime('@1420066800'))."\n");
echo(date('r', strtotime('@1451602800'))."\n");

The output is:

Thu, 01 Jan 2015 01:00:00 +0200
Fri, 01 Jan 2016 01:00:00 +0200

Now, why strtotime() returns false for 1420066800 and 26197048320 for 1451602800?

It expects to receive a string and if it receives a number it doesn't care and converts it to string first. Then it follows some rules and tries to identify the date components into the string. Because neither '1420066800' nor '1451602800' contains any separator for components, it probably tries to guess the order of components.

Today, 2016-02-25, strtotime('1451602800') produces a timestamp that, converted to a printable date looks like: 'Fri, 25 Feb 2800 14:52:00 +0200'

It makes me think it interprets the input string as follows: 14:51:60 is the time, 2800 is the year, the other components (day, month) are initialized from the current time.

The documentation says:

The function expects to be given a string containing an English date format and will try to parse that format into a Unix timestamp (the number of seconds since January 1 1970 00:00:00 UTC), relative to the timestamp given in $now, or the current time if $now is not supplied.

Since the "date" you provide doesn't follow any valid date-time format, strtotime() is free to return any value. It is called "garbage in, garbage out".

Rident answered 25/2, 2016 at 16:38 Comment(5)
Thanks for the explanation. I didn't try to parse the value as a timestamp, but expected to receive an error in order to handle it differently.Groundspeed
How do you explain the version difference? on PHP 5.1.6 it returns false as expected.Groundspeed
@SlavikMe I don't know, they probably added more formats and attempts to extract something from the given input. See the changelog of the function; its behaviour changed a lot, there were bugs and fixes because of code additions and improvements between PHP 5.0 and PHP 5.3. I remember the DateTime class also had a lot of improvements, behaviour changes and bug fixes in the same interval of versions.Rident
@SlavikMe regarding the error you expect, I think you can use is_numeric() to make a first decision about how to handle the value. A non-numeric value is probably a date; pass it to strtotime() and hope it will parse it correctly. A numeric value is probably a timestamp. Either compare it with a hardcoded timestamp or convert it to a date (using DateTime f.e.) then check its value against an interval of valid dates; pick the way that suits your application better.Rident
@Rident that is exactly what I've done, I just wanted to improve it a bit, and found out this strange bug. Anyway, thanks for the help!Groundspeed
F
4

1451602800 gives a timestamp of 2800-02-25 14:52:00; and is being interpreted as:

1451602800
------
  ^
14:51:60 (or 14:52) time

today in the year

1451602800
      ----
       ^
      2800

but only on 64-bit systems that can handle that date range

Fumble answered 25/2, 2016 at 16:39 Comment(2)
If you are right, then it should have been able to convert the strtotime(1420066800) same way to date 6800-02-25 14:20:06, but it didn't. How do you explain it? IMHO it's a bug rather than a feature.Groundspeed
The year 6800 is likely to be outside the year range.... and yes, it almost certainly is a bug rather than a featureFumble

© 2022 - 2024 — McMap. All rights reserved.