Dates7 min readExamples run on PHP 8.5.10

date_add()

Learn how PHP's date_add() adds a DateInterval to a DateTime object in place, with tested examples, month-end edge cases and common mistakes.

Signature

date_add(DateTime $object, DateInterval $interval): DateTime
Available since PHP 5.3.07.48.08.18.28.38.48.5All versions →

Purpose

date_add() is the procedural alias of DateTime::add(). It takes a date-time object and a DateInterval, moves the date-time forward by that interval, and gives you back the same object. Use it when you work in a procedural style, for example next to date_create() and date_format(), and need to compute a due date, an expiry, or the next step in a schedule.

The key point is that the function modifies the object you pass in. It does not create a new date.

Signature

Parameters and defaults

  • $object: a mutable DateTime instance, typically created with date_create() or new DateTime(). It is changed in place. There is no default.
  • $interval: a DateInterval, such as new DateInterval('P1M10D'). There is no default. The interval can hold years, months, days, hours, minutes and seconds. An interval marked as inverted is subtracted instead of added.

Return value and failure cases

The function returns the same DateTime object that you passed in, now updated. Because it is the same instance, $result === $object is true.

Failures show up as type errors, not as a false return value:

  • Passing a DateTimeImmutable throws a TypeError, because the parameter is typed as DateTime. Use DateTimeImmutable::add(), which returns a new object, for immutable values.
  • Passing a string or integer instead of a DateInterval also throws a TypeError.
  • An invalid interval specification fails earlier, when you construct the DateInterval, and that constructor throws an exception.

Example 1: Add a mixed interval

The interval P1M10D means one month and ten days. The time of day is kept unchanged.

Example
<?php
date_default_timezone_set('UTC');
$date = date_create('2024-01-15 10:30:00');
$interval = new DateInterval('P1M10D');
date_add($date, $interval);
echo date_format($date, 'Y-m-d H:i:s'), "\n";
Output
2024-02-25 10:30:00

Example 2: The object is mutated and returned

The return value is not a copy. Both variables point to one object, and the 36-hour interval crosses a day boundary.

Example
<?php
$a = new DateTime('2024-03-01 00:00:00', new DateTimeZone('UTC'));
$b = date_add($a, new DateInterval('PT36H'));
echo json_encode(['same' => $a === $b, 'value' => $a->format('Y-m-d H:i')], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), "\n";
Output
{"same":true,"value":"2024-03-02 12:00"}

Example 3: Keep the original with clone

If you still need the starting date, clone it before calling date_add(). This example also shows month-end overflow, described below.

Example
<?php
$start = date_create('2024-01-31', new DateTimeZone('UTC'));
$end = clone $start;
date_add($end, new DateInterval('P1M'));
echo json_encode([$start->format('Y-m-d'), $end->format('Y-m-d')], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), "\n";
Output
["2024-01-31","2024-03-02"]

Edge cases

  • Month-end overflow. Adding one month to January 31 first produces the non-existent February 31. PHP then rolls the extra days into March, so in 2024 the result is March 2. If you need "last day of next month", use a modifier such as last day of next month instead of an interval.
  • Time zones and daylight saving. Intervals made of days are applied to the local calendar date, while intervals made of hours are elapsed time. Around a daylight saving change, P1D and PT24H may therefore give different wall-clock results. Set the time zone explicitly when results must be reproducible.
  • Inverted intervals. An interval with its invert flag set, such as one from DateInterval::createFromDateString('-1 day'), moves the date backward when passed to date_add(). Many developers prefer date_sub() for clarity.
  • Weeks and days in the format. In an ISO 8601 interval string, weeks are written as W and days as D, for example P2W or P1W3D. You can combine them in one string, but the date part comes before the T that introduces the time part.

Common mistakes

  1. Expecting a new object. Code such as $next = date_add($today, $interval) leaves $today and $next identical. Clone first, or use DateTimeImmutable.
  2. Forgetting the T. In P1H the H is invalid, because time units must come after T, as in PT1H. Likewise, P1M is one month, while PT1M is one minute.
  3. Passing an immutable date. date_add() only accepts DateTime. Use $immutable->add($interval) for DateTimeImmutable.
  4. Assuming month arithmetic is clamped. PHP overflows into the next month rather than stopping at the last valid day.
  5. Depending on the default time zone. Always pass a DateTimeZone or set the default zone yourself so the same code gives the same answer everywhere.
  • DateTime::add(): the method form of the same operation.
  • date_sub(): subtracts an interval from a DateTime.
  • date_create() and date_create_immutable(): create date objects.
  • date_interval_create_from_date_string(): builds an interval from a relative string.
  • date_diff(): computes the interval between two dates.
  • date_format(): formats the resulting date.

Recap

date_add() applies a DateInterval to a DateTime in place and returns that same object. Clone the date when you need to keep the original, watch for month-end overflow, and set the time zone explicitly. For immutable dates, call the add() method on the immutable object instead.

Tested examples

Add a mixed interval to a date
<?php
date_default_timezone_set('UTC');
$date = date_create('2024-01-15 10:30:00');
$interval = new DateInterval('P1M10D');
date_add($date, $interval);
echo date_format($date, 'Y-m-d H:i:s'), "\n";
Output
2024-02-25 10:30:00
The object is mutated and returned
<?php
$a = new DateTime('2024-03-01 00:00:00', new DateTimeZone('UTC'));
$b = date_add($a, new DateInterval('PT36H'));
echo json_encode(['same' => $a === $b, 'value' => $a->format('Y-m-d H:i')], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), "\n";
Output
{"same":true,"value":"2024-03-02 12:00"}
Keep the original with clone
<?php
$start = date_create('2024-01-31', new DateTimeZone('UTC'));
$end = clone $start;
date_add($end, new DateInterval('P1M'));
echo json_encode([$start->format('Y-m-d'), $end->format('Y-m-d')], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), "\n";
Output
["2024-01-31","2024-03-02"]