admin管理员组

文章数量:1287879

Let's say I need to change a datetime object by adding or subtracting some months.

How to avoid the well known PHP month calculations?

$monthstoadd=-1;
//today is 2025-01-24

$date=new DateTime("last day of last month");

echo $date->format('Y-m-d');
//out: is 2024-12-31

$date->modify(($monthstoadd>0?'+':'').$monthstoadd.' month');

echo $date->format('Y-m-d');
//out: is 2024-12-01

Starting from last day of month, I expect to get always the last day of the month (floor).

Is there any way to modify a date with this preference?

 mysql> SELECT '2025-01-30' + interval 1 month
        -> '2025-02-28'

 pgsql> SELECT date '2025-01-30' + interval '1 month';
        -> '2025-02-28 00:00:00'

sqlite> SELECT date('2025-01-30', '+1 month');
        -> '2025-03-02'

Let's say I need to change a datetime object by adding or subtracting some months.

How to avoid the well known PHP month calculations?

$monthstoadd=-1;
//today is 2025-01-24

$date=new DateTime("last day of last month");

echo $date->format('Y-m-d');
//out: is 2024-12-31

$date->modify(($monthstoadd>0?'+':'').$monthstoadd.' month');

echo $date->format('Y-m-d');
//out: is 2024-12-01

Starting from last day of month, I expect to get always the last day of the month (floor).

Is there any way to modify a date with this preference?

 mysql> SELECT '2025-01-30' + interval 1 month
        -> '2025-02-28'

 pgsql> SELECT date '2025-01-30' + interval '1 month';
        -> '2025-02-28 00:00:00'

sqlite> SELECT date('2025-01-30', '+1 month');
        -> '2025-03-02'
Share Improve this question edited Jan 27 at 18:25 hakre 198k55 gold badges446 silver badges854 bronze badges Recognized by PHP Collective asked Jan 24 at 13:05 TobiaTobia 9,50629 gold badges120 silver badges242 bronze badges 12
  • 4 adding months is a tricky business. When you're adding 1 month to January, 30th, which date you expect? – Your Common Sense Commented Jan 24 at 13:10
  • 1 Seriously, it's tricky. Now add one month to 28th Feb. Is it 30th March or 28th? Do you want +1+1 to equal +2? – Álvaro González Commented Jan 24 at 17:21
  • 1 "Starting from last day of month, I expect to get always the last day of the month" and "Now add one month to 28th Feb…It's 28th march" - aren’t these two in conflict? – Chris Haas Commented Jan 26 at 13:49
  • 1 For your case I would go into percentage base solution - per given month define what it 100% and apply the resulted percentage to the next month. Feb 28 (100% of non leap year) +1 month = March 31 (100%). As for rounding it up or down, you will have to decide it yourself :]. – Mortimer Commented Jan 27 at 12:50
  • 1 Just for reference, in the MySQL documentation they say "If you add MONTH... and the resulting date has a day that is larger than the maximum day for the new month, the day is adjusted to the maximum days in the new month:" – Chris Haas Commented Jan 27 at 14:01
 |  Show 7 more comments

1 Answer 1

Reset to default 0

So you like the way MySQL is doing it, ¹ and then you stumble over how the modify expressions work in PHP:

$start = new DateTimeImmutable("2025-01-30");
echo min(
    $start->modify('last day of next month'),
    $start->modify('+1 month')
)->format('Y-m-d'), PHP_EOL; // 2025-02-28

Let's recap how MySQL is doing it: ¹

$addXMonths = static fn(string $date, int $months) => 1 > $months
    ? throw new InvalidArgumentException(sprintf('$months: 1 > %d', $months))
    : (static fn(array $p) => sprintf('%04d-%02d-%02d',
        ($year = $p['year'] + intdiv($months, 12)),
        ($month = $p['month'] + $months % 12),
        min($p['day'], date_create("$year-$month-01")->format('t'))
    ))(date_parse($date));

echo $addXMonths('2025-01-30', 1), PHP_EOL; // 2025-02-28

¹ From Temporal Intervals (mysql.com):

If you add MONTH, YEAR_MONTH, or YEAR and the resulting date has a day that is larger than the maximum day for the new month, the day is adjusted to the maximum days in the new month:

mysql> SELECT DATE_ADD('2019-01-30', INTERVAL 1 MONTH);
       -> '2019-02-28'

本文标签: phpHow to subtractadd yearsmonth intervals to a date (like MySQL INTERVAL expression)Stack Overflow