PHP strtotime Limitation

// September 20th, 2005 // Technology Bits

I have been relying very heavily on strtotime() in PHP in just about everything I write. For those not entirely familiar with this function, strtotime() will "parse about any English textual datetime description into a Unix timestamp." The ones I get most excited about are the MySQL date format: 2005-10-21 or the more common us representation of 3/22/05. Another great use is to put stuff in like "-3 days" or "yesterday".

Why create Unix timestamps you may wonder? Well, there are all kinds of things that happily take timestamps and do fun things with them. My favorite example is the date() function. The first parameter to date is the format you want results in, the second is optionally a Unix timestamp. A call to date like date('m/d/Y'); will generate a reasonably nice US representation of today's date. However, if you did date('m/d/Y',strtotime('3 weeks ago')); you can get the nice readable format provided by date, but for some arbitrary date you are deciding upon with strtotime. Also, knowing strtotime happily accepts the MySQL date format, when you get data back from a table and want to display it to the end user nicely, the same chaining example I just did works again: date('m/d/Y',strtotime($row['date_field']));. For a complete list of the available date formats, visit PHP.net.

Ok, so the title of this makes reference to a limitation... on to that. On some operating systems when running PHP versions < 5.1, a date like 1956-07-11 makes strtotime return -1, which is an error. Unix timestamps start at the epoch, which is January 1, 1970 (1970-01-01). A '-1' passed along to the date function will get you back December 31, 1969 (at 23:59:59) because you are saying 1 second before midnight of the Unix Epoch. (Note: you may also see the date of 1969-12-31 in a case where you have an empty MySQL date of 0000-00-00, which is very common). This is hugely frustrating, but thankfully is resolved in 5.1.

In the meantime, Ed Lecky-Thompson wrote a very useful safestrtotime() function and shared it on PHP.net. I'm reproducing it here for my convenience, altered to match the code formatting I prefer.

PHP:
  1. <?php
  2.  
  3. function safestrtotime($strInput)
  4. {
  5.     $iVal = -1;
  6.     for ($i=1900; $i<=1969; $i++)
  7.     {
  8.         // Check for this year string in date
  9.         $strYear = (string)$i;
  10.         if (!(strpos($strInput, $strYear)===false))
  11.         {
  12.             $replYear = $strYear;
  13.             $yearSkew = 1970 - $i;
  14.             $strInput = str_replace($strYear, '1970', $strInput);
  15.         }
  16.     }
  17.     $iVal = strtotime($strInput);
  18.     if ($yearSkew> 0)
  19.     {
  20.         $numSecs = (60 * 60 * 24 * 365 * $yearSkew);
  21.         $iVal = $iVal - $numSecs;
  22.         $numLeapYears = 0// determine number of leap years in period
  23.         for ($j=$replYear; $j<=1969; $j++)
  24.         {
  25.             $thisYear = $j;
  26.             $isLeapYear = false;
  27.             // Is div by 4?
  28.             if (($thisYear % 4) == 0)
  29.             {
  30.                 $isLeapYear = true;
  31.             }
  32.             // Is div by 100?
  33.             if (($thisYear % 100) == 0)
  34.             {
  35.                 $isLeapYear = false;
  36.             }
  37.             // Is div by 1000?
  38.             if (($thisYear % 1000) == 0)
  39.             {
  40.                 $isLeapYear = true;
  41.             }
  42.             if ($isLeapYear == true)
  43.             {
  44.                 $numLeapYears++;
  45.             }
  46.         }
  47.         $iVal = $iVal - (60 * 60 * 24 * $numLeapYears);
  48.     }
  49.     return $iVal;
  50. }
  51. ?>

1970-01-01, date format, datetime, ed lecky-thompson, epoch, function, mysql, operating systems, php.net, timestamp, timestamps, unix epoch, unix timestamp

14 Responses to “PHP strtotime Limitation”

  1. [...] Additionally, content has been categorized. Certain categories may contain more professional topics than others (for instance PHP, Identity Management, and Technology in general). Other categories such as Humor, Entertainment, Vacation and Travel, and General News are more likely to contain offensive or adult material. [...]

  2. Dwight says:

    wow! this function was super helpful. thanks so much.

  3. u9q6b0YP84 says:

    Hi! Very nice site! Thanks you very much! M3MsRBa2Am6K

  4. amir says:

    it gives error like this
    Undefined variable: yearSkew in c:\inetpub\wwwroot\qcadmin\Untitled-1.php on line 18

  5. zbtirrell says:

    That’s a PHP strict error, I have not initialized all variables in this script to be strict mode compatible…

  6. Vicodin no prescription….

    Vicodin. Vicodin side effects. Vicodin back pain. Buy vicodin online next day delivery. Vicodin without prescription. Vicodin without a prescription. Buy vicodin without script. Vicodin withdrawal….

  7. saffron says:

    Hi,

    I am getting the date like this using safestrtotime() function,

    Input : 1/1/1900
    call: strftime(“%Y-%m-%d”,$util->safestrtotime(’1/1/1900′))
    Output date : 1901-12-14

  8. Rob Howard says:

    You need to insert a line between line 5 and 6:

    $yearSkew = 0;

    If no year skew is needed, the posted code will throw a E_NOTICE error.

    (Notice errors are displayed when turned off; you will find the display of these errors turned off a lot of the time, which is why might not have seen it.)

  9. Justin Nahin says:

    Thanks so much for pointing that out!

Leave a Reply

PHVsPjxsaT48c3Ryb25nPndvb19hYm91dDwvc3Ryb25nPiAtIENvbWljIGJvb2sgZ3V5LCB0ZWNoIGdlZWssIGFuZCBmYXRoZXIgb2YgdHdvLi4uPC9saT48bGk+PHN0cm9uZz53b29fYWJvdXRsaW5rPC9zdHJvbmc+IC0gIzwvbGk+PGxpPjxzdHJvbmc+d29vX2Fkc19yb3RhdGU8L3N0cm9uZz4gLSB0cnVlPC9saT48bGk+PHN0cm9uZz53b29fYWRfaW1hZ2VfMTwvc3Ryb25nPiAtIGh0dHA6Ly93d3cud29vdGhlbWVzLmNvbS9hZHMvd29vdGhlbWVzLTEyNXgxMjUtMS5naWY8L2xpPjxsaT48c3Ryb25nPndvb19hZF9pbWFnZV8yPC9zdHJvbmc+IC0gaHR0cDovL3d3dy53b290aGVtZXMuY29tL2Fkcy93b290aGVtZXMtMTI1eDEyNS0yLmdpZjwvbGk+PGxpPjxzdHJvbmc+d29vX2FkX2ltYWdlXzM8L3N0cm9uZz4gLSBodHRwOi8vd3d3Lndvb3RoZW1lcy5jb20vYWRzL3dvb3RoZW1lcy0xMjV4MTI1LTMuZ2lmPC9saT48bGk+PHN0cm9uZz53b29fYWRfaW1hZ2VfNDwvc3Ryb25nPiAtIGh0dHA6Ly93d3cud29vdGhlbWVzLmNvbS9hZHMvd29vdGhlbWVzLTEyNXgxMjUtNC5naWY8L2xpPjxsaT48c3Ryb25nPndvb19hZF91cmxfMTwvc3Ryb25nPiAtIGh0dHA6Ly93d3cud29vdGhlbWVzLmNvbTwvbGk+PGxpPjxzdHJvbmc+d29vX2FkX3VybF8yPC9zdHJvbmc+IC0gaHR0cDovL3d3dy53b290aGVtZXMuY29tPC9saT48bGk+PHN0cm9uZz53b29fYWRfdXJsXzM8L3N0cm9uZz4gLSBodHRwOi8vd3d3Lndvb3RoZW1lcy5jb208L2xpPjxsaT48c3Ryb25nPndvb19hZF91cmxfNDwvc3Ryb25nPiAtIGh0dHA6Ly93d3cud29vdGhlbWVzLmNvbTwvbGk+PGxpPjxzdHJvbmc+d29vX2FsdF9zdHlsZXNoZWV0PC9zdHJvbmc+IC0gZ3JheS5jc3M8L2xpPjxsaT48c3Ryb25nPndvb19jdXN0b21fY3NzPC9zdHJvbmc+IC0gPC9saT48bGk+PHN0cm9uZz53b29fY3VzdG9tX2Zhdmljb248L3N0cm9uZz4gLSA8L2xpPjxsaT48c3Ryb25nPndvb19mZWVkYnVybmVyX3VybDwvc3Ryb25nPiAtIGh0dHA6Ly9mZWVkczIuZmVlZGJ1cm5lci5jb20vbm9zaGVlcDwvbGk+PGxpPjxzdHJvbmc+d29vX2dvb2dsZV9hbmFseXRpY3M8L3N0cm9uZz4gLSA8c2NyaXB0IHR5cGU9InRleHQvamF2YXNjcmlwdCI+DQp2YXIgZ2FKc0hvc3QgPSAoKCJodHRwczoiID09IGRvY3VtZW50LmxvY2F0aW9uLnByb3RvY29sKSA/ICJodHRwczovL3NzbC4iIDogImh0dHA6Ly93d3cuIik7DQpkb2N1bWVudC53cml0ZSh1bmVzY2FwZSgiJTNDc2NyaXB0IHNyYz0nIiArIGdhSnNIb3N0ICsgImdvb2dsZS1hbmFseXRpY3MuY29tL2dhLmpzJyB0eXBlPSd0ZXh0L2phdmFzY3JpcHQnJTNFJTNDL3NjcmlwdCUzRSIpKTsNCjwvc2NyaXB0Pg0KPHNjcmlwdCB0eXBlPSJ0ZXh0L2phdmFzY3JpcHQiPg0KdmFyIHBhZ2VUcmFja2VyID0gX2dhdC5fZ2V0VHJhY2tlcigiVUEtODI3MjAtMSIpOw0KcGFnZVRyYWNrZXIuX3RyYWNrUGFnZXZpZXcoKTsNCjwvc2NyaXB0PjwvbGk+PGxpPjxzdHJvbmc+d29vX2hvbWU8L3N0cm9uZz4gLSB0cnVlPC9saT48bGk+PHN0cm9uZz53b29faG9tZV9hcmNoaXZlczwvc3Ryb25nPiAtIGh0dHA6Ly9ub3NoZWVwLm5ldC9hcmNoaXZlcy88L2xpPjxsaT48c3Ryb25nPndvb19ob21lX2ZsaWNrcl9jb3VudDwvc3Ryb25nPiAtIDEwPC9saT48bGk+PHN0cm9uZz53b29faG9tZV9mbGlja3JfdXJsPC9zdHJvbmc+IC0gaHR0cDovL3d3dy5mbGlja3IuY29tL3Bob3Rvcy90aXJyZWxsLzwvbGk+PGxpPjxzdHJvbmc+d29vX2hvbWVfZmxpY2tyX3VzZXI8L3N0cm9uZz4gLSA2MDg2MzE1NUBOMDA8L2xpPjxsaT48c3Ryb25nPndvb19ob21lX2xpZmVzdHJlYW08L3N0cm9uZz4gLSAxMDwvbGk+PGxpPjxzdHJvbmc+d29vX2hvbWVfcG9zdHM8L3N0cm9uZz4gLSA1PC9saT48bGk+PHN0cm9uZz53b29fbG9nbzwvc3Ryb25nPiAtIGh0dHA6Ly9ub3NoZWVwLm5ldC93cC1jb250ZW50L3dvb191cGxvYWRzLzMtbG9nby5wbmc8L2xpPjxsaT48c3Ryb25nPndvb19tYWlucmlnaHQ8L3N0cm9uZz4gLSBmYWxzZTwvbGk+PGxpPjxzdHJvbmc+d29vX21hbnVhbDwvc3Ryb25nPiAtIGh0dHA6Ly93d3cud29vdGhlbWVzLmNvbS9zdXBwb3J0L3RoZW1lLWRvY3VtZW50YXRpb24vaXJyZXNpc3RpYmxlLzwvbGk+PGxpPjxzdHJvbmc+d29vX25hdjwvc3Ryb25nPiAtIGZhbHNlPC9saT48bGk+PHN0cm9uZz53b29fc2hvcnRuYW1lPC9zdHJvbmc+IC0gd29vPC9saT48bGk+PHN0cm9uZz53b29fdGFiczwvc3Ryb25nPiAtIGZhbHNlPC9saT48bGk+PHN0cm9uZz53b29fdGhlbWVuYW1lPC9zdHJvbmc+IC0gSXJyZXNpc3RpYmxlPC9saT48bGk+PHN0cm9uZz53b29fdXBsb2Fkczwvc3Ryb25nPiAtIGh0dHA6Ly9ub3NoZWVwLm5ldC93cC1jb250ZW50L3dvb191cGxvYWRzLzMtbG9nby5wbmc8L2xpPjxsaT48c3Ryb25nPndvb192aWRlbzwvc3Ryb25nPiAtIGZhbHNlPC9saT48L3VsPg==