PHP strtotime +1 month behaviour

前端 未结 8 2134
再見小時候
再見小時候 2020-11-30 10:10

I know about the unwanted behaviour of PHP\'s function

strtotime

For example, when adding a month (+1

相关标签:
8条回答
  • 2020-11-30 10:29

    Here is an implementation of an improved version of Juhana's answer above:

    <?php
    function sameDateNextMonth(DateTime $createdDate, DateTime $currentDate) {
        $addMon = clone $currentDate;
        $addMon->add(new DateInterval("P1M"));
    
        $nextMon = clone $currentDate;
        $nextMon->modify("last day of next month");
    
        if ($addMon->format("n") == $nextMon->format("n")) {
            $recurDay = $createdDate->format("j");
            $daysInMon = $addMon->format("t");
            $currentDay = $currentDate->format("j");
            if ($recurDay > $currentDay && $recurDay <= $daysInMon) {
                $addMon->setDate($addMon->format("Y"), $addMon->format("n"), $recurDay);
            }
            return $addMon;
        } else {
            return $nextMon;
        }
    }
    

    This version takes $createdDate under the presumption that you are dealing with a recurring monthly period, such as a subscription, that started on a specific date, such as the 31st. It always takes $createdDate so late "recurs on" dates won't shift to lower values as they are pushed forward thru lesser-valued months (e.g., so all 29th, 30th or 31st recur dates won't eventually get stuck on the 28th after passing thru a non-leap-year February).

    Here is some driver code to test the algorithm:

    $createdDate = new DateTime("2015-03-31");
    echo "created date = " . $createdDate->format("Y-m-d") . PHP_EOL;
    
    $next = sameDateNextMonth($createdDate, $createdDate);
    echo "   next date = " . $next->format("Y-m-d") . PHP_EOL;
    
    foreach(range(1, 12) as $i) {
        $next = sameDateNextMonth($createdDate, $next);
        echo "   next date = " . $next->format("Y-m-d") . PHP_EOL;
    }
    

    Which outputs:

    created date = 2015-03-31
       next date = 2015-04-30
       next date = 2015-05-31
       next date = 2015-06-30
       next date = 2015-07-31
       next date = 2015-08-31
       next date = 2015-09-30
       next date = 2015-10-31
       next date = 2015-11-30
       next date = 2015-12-31
       next date = 2016-01-31
       next date = 2016-02-29
       next date = 2016-03-31
       next date = 2016-04-30
    
    0 讨论(0)
  • 2020-11-30 10:30

    I have solved it by this way:

    $startDate = date("Y-m-d");
    $month = date("m",strtotime($startDate));
    $nextmonth = date("m",strtotime("$startDate +1 month"));
    if((($nextmonth-$month) > 1) || ($month == 12 && $nextmonth != 1))
    {
        $nextDate = date( 't.m.Y',strtotime("$initialDate +1 week"));
    }else
    {
        $nextDate = date("Y-m-d",strtotime("$initialDate +1 month"));
    }
    echo $nextDate;
    
    0 讨论(0)
  • 2020-11-30 10:31

    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

    0 讨论(0)
  • 2020-11-30 10:35

    Somewhat similar to the Juhana's answer but more intuitive and less complications expected. Idea is like this:

    1. Store original date and the +n month(s) date in variables
    2. Extract the day part of both variables
    3. If days do not match, subtract number of days from the future date

    Plus side of this solution is that works for any date (not just the border dates) and it also works for subtracting months (by putting - instead of +). Here is an example implementation:

    $start = mktime(0,0,0,1,31,2015);
    for ($contract = 0; $contract < 12; $contract++) {
        $end = strtotime('+ ' . $contract . ' months', $start);
        if (date('d', $start) != date('d', $end)) { 
            $end = strtotime('- ' . date('d', $end) . ' days', $end);
        }
        echo date('d-m-Y', $end) . '|';
    }
    

    And the output is following:

    31-01-2015|28-02-2015|31-03-2015|30-04-2015|31-05-2015|30-06-2015|31-07-2015|31-08-2015|30-09-2015|31-10-2015|30-11-2015|31-12-2015|
    
    0 讨论(0)
  • 2020-11-30 10:42

    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));
        }
    
    }
    ?>
    
    0 讨论(0)
  • 2020-11-30 10:42

    Had the same issue recently and ended up writing a class that handles adding/subtracting various time intervals to DateTime objects.
    Here's the code:
    https://gist.github.com/pavlepredic/6220041#file-gistfile1-php
    I've been using this class for a while and it seems to work fine, but I'm really interested in some peer review. What you do is create a TimeInterval object (in your case, you would specify 1 month as the interval) and then call addToDate() method, making sure you set $preventMonthOverflow argument to true. The code will make sure that the resulting date does not overflow into next month.

    Sample usage:

    $int = new TimeInterval(1, TimeInterval::MONTH);
    $date = date_create('2013-01-31');
    $future = $int->addToDate($date, true);
    echo $future->format('Y-m-d');
    

    Resulting date is: 2013-02-28

    0 讨论(0)
提交回复
热议问题