Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

PHP strtotime +1 month behaviour

Tags:

php

strtotime

I know about the unwanted behaviour of PHP's function

strtotime

For example, when adding a month (+1 month) to dates like: 31.01.2011 -> 03.03.2011

I know it's not officially a PHP bug, and that this solution has some arguments behind it, but at least for me, this behavior has caused a lot waste of time (in the past and present) and I personally hate it.


What I found even stranger is that for example in:

MySQL: DATE_ADD('2011-01-31', INTERVAL 1 MONTH) returns 2011-02-28 or

C# where new DateTime(2011, 01, 31).AddMonths(1); will return 28.02.2011

wolframalpha.com giving 31.01.2013 + 1 month as input; will return Thursday, February 28, 2013

It sees to me that others have found a more decent solution to the stupid question that I saw alot in PHP bug reports "what day will it be, if I say we meet in a month from now" or something like that. The answer is: if 31 does not exists in next month, get me the last day of that month, but please stick to next month.


So MY QUESTION IS: is there a PHP function (written by somebody) that resolves this not officially recognized bug? As I don't think I am the only one who wants another behavior when adding / subtracting months.

I am particulary interested in solutions what also work not just for the end of the month, but a complete replacement of strtotime. Also the case strotime +n months should be also dealt with.

Happy coding!

like image 325
Valentin Despa Avatar asked Aug 19 '11 09:08

Valentin Despa


People also ask

What does Strtotime do in PHP?

The strtotime() function parses an English textual datetime into a Unix timestamp (the number of seconds since January 1 1970 00:00:00 GMT). Note: If the year is specified in a two-digit format, values between 0-69 are mapped to 2000-2069 and values between 70-100 are mapped to 1970-2000.

Why is Strtotime return false?

strtotime expects a "English textual datetime" (according to the manual), which Y-D-M is not. Any time strtotime returns false, it simply doesn't understand your string, which in this application is expected.

What does Strtotime return?

Return Values PHP strtotime() function returns a timestamp value for the given date string. Incase of failure, this function returns the boolean value false.

How do I convert Strtotime to date format?

Code for converting a string to dateTime$date = strtotime ( $input ); echo date ( 'd/M/Y h:i:s' , $date );


2 Answers

what you need is to tell PHP to be smarter

$the_date = strtotime('31.01.2011');
echo date('r', strtotime('last day of next month', $the_date));

$the_date = strtotime('31.03.2011');
echo date('r', strtotime('last day of next month', $the_date));

assuming you are only interesting on the last day of next month

reference - http://www.php.net/manual/en/datetime.formats.relative.php

like image 60
ajreal Avatar answered Oct 13 '22 17:10

ajreal


PHP devs surely don't consider this as bug. But in strtotime's docs there are few comments with solutions for your problem (look for 28th Feb examples ;)), i.e. this one extending DateTime class:

<?php
// this will give us 2010-02-28 ()
echo PHPDateTime::DateNextMonth(strftime('%F', strtotime("2010-01-31 00:00:00")), 31);
?>

Class PHPDateTime:

<?php
/**
 * IA FrameWork
 * @package: Classes & Object Oriented Programming
 * @subpackage: Date & Time Manipulation
 * @author: ItsAsh <ash at itsash dot co dot uk>
 */

final class PHPDateTime extends DateTime {

    // Public Methods
    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    /**
     * Calculate time difference between two dates
     * ...
     */

    public static function TimeDifference($date1, $date2)
        $date1 = is_int($date1) ? $date1 : strtotime($date1);
        $date2 = is_int($date2) ? $date2 : strtotime($date2);

        if (($date1 !== false) && ($date2 !== false)) {
            if ($date2 >= $date1) {
                $diff = ($date2 - $date1);

                if ($days = intval((floor($diff / 86400))))
                    $diff %= 86400;
                if ($hours = intval((floor($diff / 3600))))
                    $diff %= 3600;
                if ($minutes = intval((floor($diff / 60))))
                    $diff %= 60;

                return array($days, $hours, $minutes, intval($diff));
            }
        }

        return false;
    }

    /**
     * Formatted time difference between two dates
     *
     * ...
     */

    public static function StringTimeDifference($date1, $date2) {
        $i = array();
        list($d, $h, $m, $s) = (array) self::TimeDifference($date1, $date2);

        if ($d > 0)
            $i[] = sprintf('%d Days', $d);
        if ($h > 0)
            $i[] = sprintf('%d Hours', $h);
        if (($d == 0) && ($m > 0))
            $i[] = sprintf('%d Minutes', $m);
        if (($h == 0) && ($s > 0))
            $i[] = sprintf('%d Seconds', $s);

        return count($i) ? implode(' ', $i) : 'Just Now';
    }

    /**
     * Calculate the date next month
     *
     * ...
     */

    public static function DateNextMonth($now, $date = 0) {
        $mdate = array(0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31);
        list($y, $m, $d) = explode('-', (is_int($now) ? strftime('%F', $now) : $now));

        if ($date)
            $d = $date;

        if (++$m == 2)
            $d = (($y % 4) === 0) ? (($d <= 29) ? $d : 29) : (($d <= 28) ? $d : 28);
        else
            $d = ($d <= $mdate[$m]) ? $d : $mdate[$m];

        return strftime('%F', mktime(0, 0, 0, $m, $d, $y));
    }

}
?>
like image 21
Xaerxess Avatar answered Oct 13 '22 17:10

Xaerxess